aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Ava/UI/ViewModels
diff options
context:
space:
mode:
authorMary Guillemard <mary@mary.zone>2024-03-02 12:51:05 +0100
committerGitHub <noreply@github.com>2024-03-02 12:51:05 +0100
commitec6cb0abb4b7669895b6e96fd7581c93b5abd691 (patch)
tree128c862ff5faea0b219467656d4023bee7faefb5 /src/Ryujinx.Ava/UI/ViewModels
parent53b5985da6b9d7b281d9fc25b93bfd1d1918a107 (diff)
infra: Make Avalonia the default UI (#6375)
* misc: Move Ryujinx project to Ryujinx.Gtk3 This breaks release CI for now but that's fine. Signed-off-by: Mary Guillemard <mary@mary.zone> * misc: Move Ryujinx.Ava project to Ryujinx This breaks CI for now, but it's fine. Signed-off-by: Mary Guillemard <mary@mary.zone> * infra: Make Avalonia the default UI Should fix CI after the previous changes. GTK3 isn't build by the release job anymore, only by PR CI. This also ensure that the test-ava update package is still generated to allow update from the old testing channel. Signed-off-by: Mary Guillemard <mary@mary.zone> * Fix missing copy in create_app_bundle.sh Signed-off-by: Mary Guillemard <mary@mary.zone> * Fix syntax error Signed-off-by: Mary Guillemard <mary@mary.zone> --------- Signed-off-by: Mary Guillemard <mary@mary.zone>
Diffstat (limited to 'src/Ryujinx.Ava/UI/ViewModels')
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs131
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs518
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/BaseModel.cs15
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs897
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs340
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs1708
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/ModManagerViewModel.cs336
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs93
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs27
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs614
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs249
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs222
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs18
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs28
-rw-r--r--src/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs117
15 files changed, 0 insertions, 5313 deletions
diff --git a/src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs
deleted file mode 100644
index 6020f40e..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-using Avalonia.Media.Imaging;
-using Avalonia.Platform;
-using Avalonia.Threading;
-using Ryujinx.Ava.Common.Locale;
-using Ryujinx.Common.Utilities;
-using Ryujinx.UI.Common.Configuration;
-using System;
-using System.Net.Http;
-using System.Net.NetworkInformation;
-using System.Threading.Tasks;
-
-namespace Ryujinx.Ava.UI.ViewModels
-{
- public class AboutWindowViewModel : BaseModel
- {
- private Bitmap _githubLogo;
- private Bitmap _discordLogo;
- private Bitmap _patreonLogo;
- private Bitmap _twitterLogo;
-
- private string _version;
- private string _supporters;
-
- public Bitmap GithubLogo
- {
- get => _githubLogo;
- set
- {
- _githubLogo = value;
- OnPropertyChanged();
- }
- }
-
- public Bitmap DiscordLogo
- {
- get => _discordLogo;
- set
- {
- _discordLogo = value;
- OnPropertyChanged();
- }
- }
-
- public Bitmap PatreonLogo
- {
- get => _patreonLogo;
- set
- {
- _patreonLogo = value;
- OnPropertyChanged();
- }
- }
-
- public Bitmap TwitterLogo
- {
- get => _twitterLogo;
- set
- {
- _twitterLogo = value;
- OnPropertyChanged();
- }
- }
-
- public string Supporters
- {
- get => _supporters;
- set
- {
- _supporters = value;
- OnPropertyChanged();
- }
- }
-
- public string Version
- {
- get => _version;
- set
- {
- _version = value;
- OnPropertyChanged();
- }
- }
-
- public string Developers => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.AboutPageDeveloperListMore, "gdkchan, Ac_K, marysaka, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, GoffyDude, TSRBerry, IsaacMarovitz");
-
- public AboutWindowViewModel()
- {
- Version = Program.Version;
-
- if (ConfigurationState.Instance.UI.BaseStyle.Value == "Light")
- {
- GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_GitHub_Light.png?assembly=Ryujinx.UI.Common")));
- DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Discord_Light.png?assembly=Ryujinx.UI.Common")));
- PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Patreon_Light.png?assembly=Ryujinx.UI.Common")));
- TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Twitter_Light.png?assembly=Ryujinx.UI.Common")));
- }
- else
- {
- GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_GitHub_Dark.png?assembly=Ryujinx.UI.Common")));
- DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Discord_Dark.png?assembly=Ryujinx.UI.Common")));
- PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Patreon_Dark.png?assembly=Ryujinx.UI.Common")));
- TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Twitter_Dark.png?assembly=Ryujinx.UI.Common")));
- }
-
- Dispatcher.UIThread.InvokeAsync(DownloadPatronsJson);
- }
-
- private async Task DownloadPatronsJson()
- {
- if (!NetworkInterface.GetIsNetworkAvailable())
- {
- Supporters = LocaleManager.Instance[LocaleKeys.ConnectionError];
-
- return;
- }
-
- HttpClient httpClient = new();
-
- try
- {
- string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
-
- Supporters = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray)) + "\n\n";
- }
- catch
- {
- Supporters = LocaleManager.Instance[LocaleKeys.ApiError];
- }
- }
- }
-}
diff --git a/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs
deleted file mode 100644
index 8f09568a..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs
+++ /dev/null
@@ -1,518 +0,0 @@
-using Avalonia;
-using Avalonia.Collections;
-using Avalonia.Media.Imaging;
-using Avalonia.Threading;
-using Ryujinx.Ava.Common.Locale;
-using Ryujinx.Ava.UI.Helpers;
-using Ryujinx.Ava.UI.Windows;
-using Ryujinx.Common;
-using Ryujinx.Common.Configuration;
-using Ryujinx.Common.Logging;
-using Ryujinx.Common.Utilities;
-using Ryujinx.UI.Common.Models.Amiibo;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.IO;
-using System.Linq;
-using System.Net.Http;
-using System.Text;
-using System.Text.Json;
-using System.Threading.Tasks;
-
-namespace Ryujinx.Ava.UI.ViewModels
-{
- public class AmiiboWindowViewModel : BaseModel, IDisposable
- {
- private const string DefaultJson = "{ \"amiibo\": [] }";
- private const float AmiiboImageSize = 350f;
-
- private readonly string _amiiboJsonPath;
- private readonly byte[] _amiiboLogoBytes;
- private readonly HttpClient _httpClient;
- private readonly StyleableWindow _owner;
-
- private Bitmap _amiiboImage;
- private List<AmiiboApi> _amiiboList;
- private AvaloniaList<AmiiboApi> _amiibos;
- private ObservableCollection<string> _amiiboSeries;
-
- private int _amiiboSelectedIndex;
- private int _seriesSelectedIndex;
- private bool _enableScanning;
- private bool _showAllAmiibo;
- private bool _useRandomUuid;
- private string _usage;
-
- private static readonly AmiiboJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
-
- public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId)
- {
- _owner = owner;
-
- _httpClient = new HttpClient
- {
- Timeout = TimeSpan.FromSeconds(30),
- };
-
- LastScannedAmiiboId = lastScannedAmiiboId;
- TitleId = titleId;
-
- Directory.CreateDirectory(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo"));
-
- _amiiboJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json");
- _amiiboList = new List<AmiiboApi>();
- _amiiboSeries = new ObservableCollection<string>();
- _amiibos = new AvaloniaList<AmiiboApi>();
-
- _amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.UI.Common/Resources/Logo_Amiibo.png");
-
- _ = LoadContentAsync();
- }
-
- public AmiiboWindowViewModel() { }
-
- public string TitleId { get; set; }
- public string LastScannedAmiiboId { get; set; }
-
- public UserResult Response { get; private set; }
-
- public bool UseRandomUuid
- {
- get => _useRandomUuid;
- set
- {
- _useRandomUuid = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool ShowAllAmiibo
- {
- get => _showAllAmiibo;
- set
- {
- _showAllAmiibo = value;
-
- ParseAmiiboData();
-
- OnPropertyChanged();
- }
- }
-
- public AvaloniaList<AmiiboApi> AmiiboList
- {
- get => _amiibos;
- set
- {
- _amiibos = value;
-
- OnPropertyChanged();
- }
- }
-
- public ObservableCollection<string> AmiiboSeries
- {
- get => _amiiboSeries;
- set
- {
- _amiiboSeries = value;
- OnPropertyChanged();
- }
- }
-
- public int SeriesSelectedIndex
- {
- get => _seriesSelectedIndex;
- set
- {
- _seriesSelectedIndex = value;
-
- FilterAmiibo();
-
- OnPropertyChanged();
- }
- }
-
- public int AmiiboSelectedIndex
- {
- get => _amiiboSelectedIndex;
- set
- {
- _amiiboSelectedIndex = value;
-
- EnableScanning = _amiiboSelectedIndex >= 0 && _amiiboSelectedIndex < _amiibos.Count;
-
- SetAmiiboDetails();
-
- OnPropertyChanged();
- }
- }
-
- public Bitmap AmiiboImage
- {
- get => _amiiboImage;
- set
- {
- _amiiboImage = value;
-
- OnPropertyChanged();
- }
- }
-
- public string Usage
- {
- get => _usage;
- set
- {
- _usage = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool EnableScanning
- {
- get => _enableScanning;
- set
- {
- _enableScanning = value;
-
- OnPropertyChanged();
- }
- }
-
- public void Dispose()
- {
- GC.SuppressFinalize(this);
- _httpClient.Dispose();
- }
-
- private static bool TryGetAmiiboJson(string json, out AmiiboJson amiiboJson)
- {
- if (string.IsNullOrEmpty(json))
- {
- amiiboJson = JsonHelper.Deserialize(DefaultJson, _serializerContext.AmiiboJson);
-
- return false;
- }
-
- try
- {
- amiiboJson = JsonHelper.Deserialize(json, _serializerContext.AmiiboJson);
-
- return true;
- }
- catch (JsonException exception)
- {
- Logger.Error?.Print(LogClass.Application, $"Unable to deserialize amiibo data: {exception}");
- amiiboJson = JsonHelper.Deserialize(DefaultJson, _serializerContext.AmiiboJson);
-
- return false;
- }
- }
-
- private async Task<AmiiboJson> GetMostRecentAmiiboListOrDefaultJson()
- {
- bool localIsValid = false;
- bool remoteIsValid = false;
- AmiiboJson amiiboJson = new();
-
- try
- {
- try
- {
- if (File.Exists(_amiiboJsonPath))
- {
- localIsValid = TryGetAmiiboJson(await File.ReadAllTextAsync(_amiiboJsonPath), out amiiboJson);
- }
- }
- catch (Exception exception)
- {
- Logger.Warning?.Print(LogClass.Application, $"Unable to read data from '{_amiiboJsonPath}': {exception}");
- }
-
- if (!localIsValid || await NeedsUpdate(amiiboJson.LastUpdated))
- {
- remoteIsValid = TryGetAmiiboJson(await DownloadAmiiboJson(), out amiiboJson);
- }
- }
- catch (Exception exception)
- {
- if (!(localIsValid || remoteIsValid))
- {
- Logger.Error?.Print(LogClass.Application, $"Couldn't get valid amiibo data: {exception}");
-
- // Neither local or remote files are valid JSON, close window.
- ShowInfoDialog();
- Close();
- }
- else if (!remoteIsValid)
- {
- Logger.Warning?.Print(LogClass.Application, $"Couldn't update amiibo data: {exception}");
-
- // Only the local file is valid, the local one should be used
- // but the user should be warned.
- ShowInfoDialog();
- }
- }
-
- return amiiboJson;
- }
-
- private async Task LoadContentAsync()
- {
- AmiiboJson amiiboJson = await GetMostRecentAmiiboListOrDefaultJson();
-
- _amiiboList = amiiboJson.Amiibo.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
-
- ParseAmiiboData();
- }
-
- private void ParseAmiiboData()
- {
- _amiiboSeries.Clear();
- _amiibos.Clear();
-
- for (int i = 0; i < _amiiboList.Count; i++)
- {
- if (!_amiiboSeries.Contains(_amiiboList[i].AmiiboSeries))
- {
- if (!ShowAllAmiibo)
- {
- foreach (AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch)
- {
- if (game != null)
- {
- if (game.GameId.Contains(TitleId))
- {
- AmiiboSeries.Add(_amiiboList[i].AmiiboSeries);
-
- break;
- }
- }
- }
- }
- else
- {
- AmiiboSeries.Add(_amiiboList[i].AmiiboSeries);
- }
- }
- }
-
- if (LastScannedAmiiboId != "")
- {
- SelectLastScannedAmiibo();
- }
- else
- {
- SeriesSelectedIndex = 0;
- }
- }
-
- private void SelectLastScannedAmiibo()
- {
- AmiiboApi scanned = _amiiboList.Find(amiibo => amiibo.GetId() == LastScannedAmiiboId);
-
- SeriesSelectedIndex = AmiiboSeries.IndexOf(scanned.AmiiboSeries);
- AmiiboSelectedIndex = AmiiboList.IndexOf(scanned);
- }
-
- private void FilterAmiibo()
- {
- _amiibos.Clear();
-
- if (_seriesSelectedIndex < 0)
- {
- return;
- }
-
- List<AmiiboApi> amiiboSortedList = _amiiboList
- .Where(amiibo => amiibo.AmiiboSeries == _amiiboSeries[SeriesSelectedIndex])
- .OrderBy(amiibo => amiibo.Name).ToList();
-
- for (int i = 0; i < amiiboSortedList.Count; i++)
- {
- if (!_amiibos.Contains(amiiboSortedList[i]))
- {
- if (!_showAllAmiibo)
- {
- foreach (AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch)
- {
- if (game != null)
- {
- if (game.GameId.Contains(TitleId))
- {
- _amiibos.Add(amiiboSortedList[i]);
-
- break;
- }
- }
- }
- }
- else
- {
- _amiibos.Add(amiiboSortedList[i]);
- }
- }
- }
-
- AmiiboSelectedIndex = 0;
- }
-
- private void SetAmiiboDetails()
- {
- ResetAmiiboPreview();
-
- Usage = string.Empty;
-
- if (_amiiboSelectedIndex < 0)
- {
- return;
- }
-
- AmiiboApi selected = _amiibos[_amiiboSelectedIndex];
-
- string imageUrl = _amiiboList.Find(amiibo => amiibo.Equals(selected)).Image;
-
- StringBuilder usageStringBuilder = new();
-
- for (int i = 0; i < _amiiboList.Count; i++)
- {
- if (_amiiboList[i].Equals(selected))
- {
- bool writable = false;
-
- foreach (AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch)
- {
- if (item.GameId.Contains(TitleId))
- {
- foreach (AmiiboApiUsage usageItem in item.AmiiboUsage)
- {
- usageStringBuilder.Append($"{Environment.NewLine}- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}");
-
- writable = usageItem.Write;
- }
- }
- }
-
- if (usageStringBuilder.Length == 0)
- {
- usageStringBuilder.Append($"{LocaleManager.Instance[LocaleKeys.Unknown]}.");
- }
-
- Usage = $"{LocaleManager.Instance[LocaleKeys.Usage]} {(writable ? $" ({LocaleManager.Instance[LocaleKeys.Writable]})" : "")} : {usageStringBuilder}";
- }
- }
-
- _ = UpdateAmiiboPreview(imageUrl);
- }
-
- private async Task<bool> NeedsUpdate(DateTime oldLastModified)
- {
- try
- {
- HttpResponseMessage response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, "https://amiibo.ryujinx.org/"));
-
- if (response.IsSuccessStatusCode)
- {
- return response.Content.Headers.LastModified != oldLastModified;
- }
- }
- catch (HttpRequestException exception)
- {
- Logger.Error?.Print(LogClass.Application, $"Unable to check for amiibo data updates: {exception}");
- }
-
- return false;
- }
-
- private async Task<string> DownloadAmiiboJson()
- {
- try
- {
- HttpResponseMessage response = await _httpClient.GetAsync("https://amiibo.ryujinx.org/");
-
- if (response.IsSuccessStatusCode)
- {
- string amiiboJsonString = await response.Content.ReadAsStringAsync();
-
- try
- {
- using FileStream dlcJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough);
- dlcJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString));
- }
- catch (Exception exception)
- {
- Logger.Warning?.Print(LogClass.Application, $"Couldn't write amiibo data to file '{_amiiboJsonPath}: {exception}'");
- }
-
- return amiiboJsonString;
- }
-
- Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}");
- }
- catch (HttpRequestException exception)
- {
- Logger.Error?.Print(LogClass.Application, $"Failed to request amiibo data: {exception}");
- }
-
- await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
- LocaleManager.Instance[LocaleKeys.DialogAmiiboApiFailFetchMessage],
- LocaleManager.Instance[LocaleKeys.InputDialogOk],
- "",
- LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
-
- return null;
- }
-
- private void Close()
- {
- Dispatcher.UIThread.Post(_owner.Close);
- }
-
- private async Task UpdateAmiiboPreview(string imageUrl)
- {
- HttpResponseMessage response = await _httpClient.GetAsync(imageUrl);
-
- if (response.IsSuccessStatusCode)
- {
- byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync();
- using MemoryStream memoryStream = new(amiiboPreviewBytes);
-
- Bitmap bitmap = new(memoryStream);
-
- double ratio = Math.Min(AmiiboImageSize / bitmap.Size.Width,
- AmiiboImageSize / bitmap.Size.Height);
-
- int resizeHeight = (int)(bitmap.Size.Height * ratio);
- int resizeWidth = (int)(bitmap.Size.Width * ratio);
-
- AmiiboImage = bitmap.CreateScaledBitmap(new PixelSize(resizeWidth, resizeHeight));
- }
- else
- {
- Logger.Error?.Print(LogClass.Application, $"Failed to get amiibo preview. Response status code: {response.StatusCode}");
- }
- }
-
- private void ResetAmiiboPreview()
- {
- using MemoryStream memoryStream = new(_amiiboLogoBytes);
-
- Bitmap bitmap = new(memoryStream);
-
- AmiiboImage = bitmap;
- }
-
- private static async void ShowInfoDialog()
- {
- await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
- LocaleManager.Instance[LocaleKeys.DialogAmiiboApiConnectErrorMessage],
- LocaleManager.Instance[LocaleKeys.InputDialogOk],
- "",
- LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
- }
- }
-}
diff --git a/src/Ryujinx.Ava/UI/ViewModels/BaseModel.cs b/src/Ryujinx.Ava/UI/ViewModels/BaseModel.cs
deleted file mode 100644
index 4db9cf81..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/BaseModel.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.ComponentModel;
-using System.Runtime.CompilerServices;
-
-namespace Ryujinx.Ava.UI.ViewModels
-{
- public class BaseModel : INotifyPropertyChanged
- {
- public event PropertyChangedEventHandler PropertyChanged;
-
- protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
- }
-}
diff --git a/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs
deleted file mode 100644
index 71ad2c12..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs
+++ /dev/null
@@ -1,897 +0,0 @@
-using Avalonia;
-using Avalonia.Collections;
-using Avalonia.Controls;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Svg.Skia;
-using Avalonia.Threading;
-using Ryujinx.Ava.Common.Locale;
-using Ryujinx.Ava.Input;
-using Ryujinx.Ava.UI.Helpers;
-using Ryujinx.Ava.UI.Models;
-using Ryujinx.Ava.UI.Views.Input;
-using Ryujinx.Ava.UI.Windows;
-using Ryujinx.Common;
-using Ryujinx.Common.Configuration;
-using Ryujinx.Common.Configuration.Hid;
-using Ryujinx.Common.Configuration.Hid.Controller;
-using Ryujinx.Common.Configuration.Hid.Controller.Motion;
-using Ryujinx.Common.Configuration.Hid.Keyboard;
-using Ryujinx.Common.Logging;
-using Ryujinx.Common.Utilities;
-using Ryujinx.Input;
-using Ryujinx.UI.Common.Configuration;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.IO;
-using System.Linq;
-using System.Text.Json;
-using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId;
-using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
-using Key = Ryujinx.Common.Configuration.Hid.Key;
-
-namespace Ryujinx.Ava.UI.ViewModels
-{
- public class ControllerInputViewModel : BaseModel, IDisposable
- {
- private const string Disabled = "disabled";
- private const string ProControllerResource = "Ryujinx.UI.Common/Resources/Controller_ProCon.svg";
- private const string JoyConPairResource = "Ryujinx.UI.Common/Resources/Controller_JoyConPair.svg";
- private const string JoyConLeftResource = "Ryujinx.UI.Common/Resources/Controller_JoyConLeft.svg";
- private const string JoyConRightResource = "Ryujinx.UI.Common/Resources/Controller_JoyConRight.svg";
- private const string KeyboardString = "keyboard";
- private const string ControllerString = "controller";
- private readonly MainWindow _mainWindow;
-
- private PlayerIndex _playerId;
- private int _controller;
- private int _controllerNumber;
- private string _controllerImage;
- private int _device;
- private object _configuration;
- private string _profileName;
- private bool _isLoaded;
-
- private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
-
- public IGamepadDriver AvaloniaKeyboardDriver { get; }
- public IGamepad SelectedGamepad { get; private set; }
-
- public ObservableCollection<PlayerModel> PlayerIndexes { get; set; }
- public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; }
- internal ObservableCollection<ControllerModel> Controllers { get; set; }
- public AvaloniaList<string> ProfilesList { get; set; }
- public AvaloniaList<string> DeviceList { get; set; }
-
- // XAML Flags
- public bool ShowSettings => _device > 0;
- public bool IsController => _device > 1;
- public bool IsKeyboard => !IsController;
- public bool IsRight { get; set; }
- public bool IsLeft { get; set; }
-
- public bool IsModified { get; set; }
-
- public object Configuration
- {
- get => _configuration;
- set
- {
- _configuration = value;
-
- OnPropertyChanged();
- }
- }
-
- public PlayerIndex PlayerId
- {
- get => _playerId;
- set
- {
- if (IsModified)
- {
- return;
- }
-
- IsModified = false;
- _playerId = value;
-
- if (!Enum.IsDefined(typeof(PlayerIndex), _playerId))
- {
- _playerId = PlayerIndex.Player1;
- }
-
- LoadConfiguration();
- LoadDevice();
- LoadProfiles();
-
- _isLoaded = true;
-
- OnPropertyChanged();
- }
- }
-
- public int Controller
- {
- get => _controller;
- set
- {
- _controller = value;
-
- if (_controller == -1)
- {
- _controller = 0;
- }
-
- if (Controllers.Count > 0 && value < Controllers.Count && _controller > -1)
- {
- ControllerType controller = Controllers[_controller].Type;
-
- IsLeft = true;
- IsRight = true;
-
- switch (controller)
- {
- case ControllerType.Handheld:
- ControllerImage = JoyConPairResource;
- break;
- case ControllerType.ProController:
- ControllerImage = ProControllerResource;
- break;
- case ControllerType.JoyconPair:
- ControllerImage = JoyConPairResource;
- break;
- case ControllerType.JoyconLeft:
- ControllerImage = JoyConLeftResource;
- IsRight = false;
- break;
- case ControllerType.JoyconRight:
- ControllerImage = JoyConRightResource;
- IsLeft = false;
- break;
- }
-
- LoadInputDriver();
- LoadProfiles();
- }
-
- OnPropertyChanged();
- NotifyChanges();
- }
- }
-
- public string ControllerImage
- {
- get => _controllerImage;
- set
- {
- _controllerImage = value;
-
- OnPropertyChanged();
- OnPropertyChanged(nameof(Image));
- }
- }
-
- public SvgImage Image
- {
- get
- {
- SvgImage image = new();
-
- if (!string.IsNullOrWhiteSpace(_controllerImage))
- {
- SvgSource source = new(default(Uri));
-
- source.Load(EmbeddedResources.GetStream(_controllerImage));
-
- image.Source = source;
- }
-
- return image;
- }
- }
-
- public string ProfileName
- {
- get => _profileName; set
- {
- _profileName = value;
-
- OnPropertyChanged();
- }
- }
-
- public int Device
- {
- get => _device;
- set
- {
- _device = value < 0 ? 0 : value;
-
- if (_device >= Devices.Count)
- {
- return;
- }
-
- var selected = Devices[_device].Type;
-
- if (selected != DeviceType.None)
- {
- LoadControllers();
-
- if (_isLoaded)
- {
- LoadConfiguration(LoadDefaultConfiguration());
- }
- }
-
- OnPropertyChanged();
- NotifyChanges();
- }
- }
-
- public InputConfig Config { get; set; }
-
- public ControllerInputViewModel(UserControl owner) : this()
- {
- if (Program.PreviewerDetached)
- {
- _mainWindow =
- (MainWindow)((IClassicDesktopStyleApplicationLifetime)Application.Current
- .ApplicationLifetime).MainWindow;
-
- AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
-
- _mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
- _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
-
- _mainWindow.ViewModel.AppHost?.NpadManager.BlockInputUpdates();
-
- _isLoaded = false;
-
- LoadDevices();
-
- PlayerId = PlayerIndex.Player1;
- }
- }
-
- public ControllerInputViewModel()
- {
- PlayerIndexes = new ObservableCollection<PlayerModel>();
- Controllers = new ObservableCollection<ControllerModel>();
- Devices = new ObservableCollection<(DeviceType Type, string Id, string Name)>();
- ProfilesList = new AvaloniaList<string>();
- DeviceList = new AvaloniaList<string>();
-
- ControllerImage = ProControllerResource;
-
- PlayerIndexes.Add(new(PlayerIndex.Player1, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer1]));
- PlayerIndexes.Add(new(PlayerIndex.Player2, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer2]));
- PlayerIndexes.Add(new(PlayerIndex.Player3, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer3]));
- PlayerIndexes.Add(new(PlayerIndex.Player4, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer4]));
- PlayerIndexes.Add(new(PlayerIndex.Player5, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer5]));
- PlayerIndexes.Add(new(PlayerIndex.Player6, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer6]));
- PlayerIndexes.Add(new(PlayerIndex.Player7, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer7]));
- PlayerIndexes.Add(new(PlayerIndex.Player8, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer8]));
- PlayerIndexes.Add(new(PlayerIndex.Handheld, LocaleManager.Instance[LocaleKeys.ControllerSettingsHandheld]));
- }
-
- private void LoadConfiguration(InputConfig inputConfig = null)
- {
- Config = inputConfig ?? ConfigurationState.Instance.Hid.InputConfig.Value.Find(inputConfig => inputConfig.PlayerIndex == _playerId);
-
- if (Config is StandardKeyboardInputConfig keyboardInputConfig)
- {
- Configuration = new InputConfiguration<Key, ConfigStickInputId>(keyboardInputConfig);
- }
-
- if (Config is StandardControllerInputConfig controllerInputConfig)
- {
- Configuration = new InputConfiguration<ConfigGamepadInputId, ConfigStickInputId>(controllerInputConfig);
- }
- }
-
- public void LoadDevice()
- {
- if (Config == null || Config.Backend == InputBackendType.Invalid)
- {
- Device = 0;
- }
- else
- {
- var type = DeviceType.None;
-
- if (Config is StandardKeyboardInputConfig)
- {
- type = DeviceType.Keyboard;
- }
-
- if (Config is StandardControllerInputConfig)
- {
- type = DeviceType.Controller;
- }
-
- var item = Devices.FirstOrDefault(x => x.Type == type && x.Id == Config.Id);
- if (item != default)
- {
- Device = Devices.ToList().FindIndex(x => x.Id == item.Id);
- }
- else
- {
- Device = 0;
- }
- }
- }
-
- public async void ShowMotionConfig()
- {
- await MotionInputView.Show(this);
- }
-
- public async void ShowRumbleConfig()
- {
- await RumbleInputView.Show(this);
- }
-
- private void LoadInputDriver()
- {
- if (_device < 0)
- {
- return;
- }
-
- string id = GetCurrentGamepadId();
- var type = Devices[Device].Type;
-
- if (type == DeviceType.None)
- {
- return;
- }
-
- if (type == DeviceType.Keyboard)
- {
- if (_mainWindow.InputManager.KeyboardDriver is AvaloniaKeyboardDriver)
- {
- // NOTE: To get input in this window, we need to bind a custom keyboard driver instead of using the InputManager one as the main window isn't focused...
- SelectedGamepad = AvaloniaKeyboardDriver.GetGamepad(id);
- }
- else
- {
- SelectedGamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
- }
- }
- else
- {
- SelectedGamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
- }
- }
-
- private void HandleOnGamepadDisconnected(string id)
- {
- Dispatcher.UIThread.Post(() =>
- {
- LoadDevices();
- });
- }
-
- private void HandleOnGamepadConnected(string id)
- {
- Dispatcher.UIThread.Post(() =>
- {
- LoadDevices();
- });
- }
-
- private string GetCurrentGamepadId()
- {
- if (_device < 0)
- {
- return string.Empty;
- }
-
- var device = Devices[Device];
-
- if (device.Type == DeviceType.None)
- {
- return null;
- }
-
- return device.Id.Split(" ")[0];
- }
-
- public void LoadControllers()
- {
- Controllers.Clear();
-
- if (_playerId == PlayerIndex.Handheld)
- {
- Controllers.Add(new(ControllerType.Handheld, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeHandheld]));
-
- Controller = 0;
- }
- else
- {
- Controllers.Add(new(ControllerType.ProController, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeProController]));
- Controllers.Add(new(ControllerType.JoyconPair, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeJoyConPair]));
- Controllers.Add(new(ControllerType.JoyconLeft, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeJoyConLeft]));
- Controllers.Add(new(ControllerType.JoyconRight, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeJoyConRight]));
-
- if (Config != null && Controllers.ToList().FindIndex(x => x.Type == Config.ControllerType) != -1)
- {
- Controller = Controllers.ToList().FindIndex(x => x.Type == Config.ControllerType);
- }
- else
- {
- Controller = 0;
- }
- }
- }
-
- private static string GetShortGamepadName(string str)
- {
- const string Ellipsis = "...";
- const int MaxSize = 50;
-
- if (str.Length > MaxSize)
- {
- return $"{str.AsSpan(0, MaxSize - Ellipsis.Length)}{Ellipsis}";
- }
-
- return str;
- }
-
- private static string GetShortGamepadId(string str)
- {
- const string Hyphen = "-";
- const int Offset = 1;
-
- return str[(str.IndexOf(Hyphen) + Offset)..];
- }
-
- public void LoadDevices()
- {
- lock (Devices)
- {
- Devices.Clear();
- DeviceList.Clear();
- Devices.Add((DeviceType.None, Disabled, LocaleManager.Instance[LocaleKeys.ControllerSettingsDeviceDisabled]));
-
- foreach (string id in _mainWindow.InputManager.KeyboardDriver.GamepadsIds)
- {
- using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
-
- if (gamepad != null)
- {
- Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)}"));
- }
- }
-
- foreach (string id in _mainWindow.InputManager.GamepadDriver.GamepadsIds)
- {
- using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
-
- if (gamepad != null)
- {
- if (Devices.Any(controller => GetShortGamepadId(controller.Id) == GetShortGamepadId(gamepad.Id)))
- {
- _controllerNumber++;
- }
-
- Devices.Add((DeviceType.Controller, id, $"{GetShortGamepadName(gamepad.Name)} ({_controllerNumber})"));
- }
- }
-
- _controllerNumber = 0;
-
- DeviceList.AddRange(Devices.Select(x => x.Name));
- Device = Math.Min(Device, DeviceList.Count);
- }
- }
-
- private string GetProfileBasePath()
- {
- string path = AppDataManager.ProfilesDirPath;
- var type = Devices[Device == -1 ? 0 : Device].Type;
-
- if (type == DeviceType.Keyboard)
- {
- path = Path.Combine(path, KeyboardString);
- }
- else if (type == DeviceType.Controller)
- {
- path = Path.Combine(path, ControllerString);
- }
-
- return path;
- }
-
- private void LoadProfiles()
- {
- ProfilesList.Clear();
-
- string basePath = GetProfileBasePath();
-
- if (!Directory.Exists(basePath))
- {
- Directory.CreateDirectory(basePath);
- }
-
- ProfilesList.Add((LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault]));
-
- foreach (string profile in Directory.GetFiles(basePath, "*.json", SearchOption.AllDirectories))
- {
- ProfilesList.Add(Path.GetFileNameWithoutExtension(profile));
- }
-
- if (string.IsNullOrWhiteSpace(ProfileName))
- {
- ProfileName = LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault];
- }
- }
-
- public InputConfig LoadDefaultConfiguration()
- {
- var activeDevice = Devices.FirstOrDefault();
-
- if (Devices.Count > 0 && Device < Devices.Count && Device >= 0)
- {
- activeDevice = Devices[Device];
- }
-
- InputConfig config;
- if (activeDevice.Type == DeviceType.Keyboard)
- {
- string id = activeDevice.Id;
-
- config = new StandardKeyboardInputConfig
- {
- Version = InputConfig.CurrentVersion,
- Backend = InputBackendType.WindowKeyboard,
- Id = id,
- ControllerType = ControllerType.ProController,
- LeftJoycon = new LeftJoyconCommonConfig<Key>
- {
- DpadUp = Key.Up,
- DpadDown = Key.Down,
- DpadLeft = Key.Left,
- DpadRight = Key.Right,
- ButtonMinus = Key.Minus,
- ButtonL = Key.E,
- ButtonZl = Key.Q,
- ButtonSl = Key.Unbound,
- ButtonSr = Key.Unbound,
- },
- LeftJoyconStick =
- new JoyconConfigKeyboardStick<Key>
- {
- StickUp = Key.W,
- StickDown = Key.S,
- StickLeft = Key.A,
- StickRight = Key.D,
- StickButton = Key.F,
- },
- RightJoycon = new RightJoyconCommonConfig<Key>
- {
- ButtonA = Key.Z,
- ButtonB = Key.X,
- ButtonX = Key.C,
- ButtonY = Key.V,
- ButtonPlus = Key.Plus,
- ButtonR = Key.U,
- ButtonZr = Key.O,
- ButtonSl = Key.Unbound,
- ButtonSr = Key.Unbound,
- },
- RightJoyconStick = new JoyconConfigKeyboardStick<Key>
- {
- StickUp = Key.I,
- StickDown = Key.K,
- StickLeft = Key.J,
- StickRight = Key.L,
- StickButton = Key.H,
- },
- };
- }
- else if (activeDevice.Type == DeviceType.Controller)
- {
- bool isNintendoStyle = Devices.ToList().Find(x => x.Id == activeDevice.Id).Name.Contains("Nintendo");
-
- string id = activeDevice.Id.Split(" ")[0];
-
- config = new StandardControllerInputConfig
- {
- Version = InputConfig.CurrentVersion,
- Backend = InputBackendType.GamepadSDL2,
- Id = id,
- ControllerType = ControllerType.ProController,
- DeadzoneLeft = 0.1f,
- DeadzoneRight = 0.1f,
- RangeLeft = 1.0f,
- RangeRight = 1.0f,
- TriggerThreshold = 0.5f,
- LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId>
- {
- DpadUp = ConfigGamepadInputId.DpadUp,
- DpadDown = ConfigGamepadInputId.DpadDown,
- DpadLeft = ConfigGamepadInputId.DpadLeft,
- DpadRight = ConfigGamepadInputId.DpadRight,
- ButtonMinus = ConfigGamepadInputId.Minus,
- ButtonL = ConfigGamepadInputId.LeftShoulder,
- ButtonZl = ConfigGamepadInputId.LeftTrigger,
- ButtonSl = ConfigGamepadInputId.Unbound,
- ButtonSr = ConfigGamepadInputId.Unbound,
- },
- LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
- {
- Joystick = ConfigStickInputId.Left,
- StickButton = ConfigGamepadInputId.LeftStick,
- InvertStickX = false,
- InvertStickY = false,
- },
- RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
- {
- ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B,
- ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A,
- ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y,
- ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X,
- ButtonPlus = ConfigGamepadInputId.Plus,
- ButtonR = ConfigGamepadInputId.RightShoulder,
- ButtonZr = ConfigGamepadInputId.RightTrigger,
- ButtonSl = ConfigGamepadInputId.Unbound,
- ButtonSr = ConfigGamepadInputId.Unbound,
- },
- RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
- {
- Joystick = ConfigStickInputId.Right,
- StickButton = ConfigGamepadInputId.RightStick,
- InvertStickX = false,
- InvertStickY = false,
- },
- Motion = new StandardMotionConfigController
- {
- MotionBackend = MotionInputBackendType.GamepadDriver,
- EnableMotion = true,
- Sensitivity = 100,
- GyroDeadzone = 1,
- },
- Rumble = new RumbleConfigController
- {
- StrongRumble = 1f,
- WeakRumble = 1f,
- EnableRumble = false,
- },
- };
- }
- else
- {
- config = new InputConfig();
- }
-
- config.PlayerIndex = _playerId;
-
- return config;
- }
-
- public async void LoadProfile()
- {
- if (Device == 0)
- {
- return;
- }
-
- InputConfig config = null;
-
- if (string.IsNullOrWhiteSpace(ProfileName))
- {
- return;
- }
-
- if (ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault])
- {
- config = LoadDefaultConfiguration();
- }
- else
- {
- string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
-
- if (!File.Exists(path))
- {
- var index = ProfilesList.IndexOf(ProfileName);
- if (index != -1)
- {
- ProfilesList.RemoveAt(index);
- }
- return;
- }
-
- try
- {
- config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig);
- }
- catch (JsonException) { }
- catch (InvalidOperationException)
- {
- Logger.Error?.Print(LogClass.Configuration, $"Profile {ProfileName} is incompatible with the current input configuration system.");
-
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogProfileInvalidProfileErrorMessage, ProfileName));
-
- return;
- }
- }
-
- if (config != null)
- {
- _isLoaded = false;
-
- LoadConfiguration(config);
-
- LoadDevice();
-
- _isLoaded = true;
-
- NotifyChanges();
- }
- }
-
- public async void SaveProfile()
- {
- if (Device == 0)
- {
- return;
- }
-
- if (Configuration == null)
- {
- return;
- }
-
- if (ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault])
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileDefaultProfileOverwriteErrorMessage]);
-
- return;
- }
-
- bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
-
- if (validFileName)
- {
- string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
-
- InputConfig config = null;
-
- if (IsKeyboard)
- {
- config = (Configuration as InputConfiguration<Key, ConfigStickInputId>).GetConfig();
- }
- else if (IsController)
- {
- config = (Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>).GetConfig();
- }
-
- config.ControllerType = Controllers[_controller].Type;
-
- string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig);
-
- await File.WriteAllTextAsync(path, jsonString);
-
- LoadProfiles();
- }
- else
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
- }
- }
-
- public async void RemoveProfile()
- {
- if (Device == 0 || ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault] || ProfilesList.IndexOf(ProfileName) == -1)
- {
- return;
- }
-
- UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
- LocaleManager.Instance[LocaleKeys.DialogProfileDeleteProfileTitle],
- LocaleManager.Instance[LocaleKeys.DialogProfileDeleteProfileMessage],
- LocaleManager.Instance[LocaleKeys.InputDialogYes],
- LocaleManager.Instance[LocaleKeys.InputDialogNo],
- LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
-
- if (result == UserResult.Yes)
- {
- string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
-
- if (File.Exists(path))
- {
- File.Delete(path);
- }
-
- LoadProfiles();
- }
- }
-
- public void Save()
- {
- IsModified = false;
-
- List<InputConfig> newConfig = new();
-
- newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);
-
- newConfig.Remove(newConfig.Find(x => x == null));
-
- if (Device == 0)
- {
- newConfig.Remove(newConfig.Find(x => x.PlayerIndex == this.PlayerId));
- }
- else
- {
- var device = Devices[Device];
-
- if (device.Type == DeviceType.Keyboard)
- {
- var inputConfig = Configuration as InputConfiguration<Key, ConfigStickInputId>;
- inputConfig.Id = device.Id;
- }
- else
- {
- var inputConfig = Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>;
- inputConfig.Id = device.Id.Split(" ")[0];
- }
-
- var config = !IsController
- ? (Configuration as InputConfiguration<Key, ConfigStickInputId>).GetConfig()
- : (Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>).GetConfig();
- config.ControllerType = Controllers[_controller].Type;
- config.PlayerIndex = _playerId;
-
- int i = newConfig.FindIndex(x => x.PlayerIndex == PlayerId);
- if (i == -1)
- {
- newConfig.Add(config);
- }
- else
- {
- newConfig[i] = config;
- }
- }
-
- _mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
-
- // Atomically replace and signal input change.
- // NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event.
- ConfigurationState.Instance.Hid.InputConfig.Value = newConfig;
-
- ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
- }
-
- public void NotifyChange(string property)
- {
- OnPropertyChanged(property);
- }
-
- public void NotifyChanges()
- {
- OnPropertyChanged(nameof(Configuration));
- OnPropertyChanged(nameof(IsController));
- OnPropertyChanged(nameof(ShowSettings));
- OnPropertyChanged(nameof(IsKeyboard));
- OnPropertyChanged(nameof(IsRight));
- OnPropertyChanged(nameof(IsLeft));
- }
-
- public void Dispose()
- {
- GC.SuppressFinalize(this);
-
- _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
- _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;
-
- _mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates();
-
- SelectedGamepad?.Dispose();
-
- AvaloniaKeyboardDriver.Dispose();
- }
- }
-}
diff --git a/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs
deleted file mode 100644
index 2cd714f4..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs
+++ /dev/null
@@ -1,340 +0,0 @@
-using Avalonia.Collections;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Platform.Storage;
-using Avalonia.Threading;
-using DynamicData;
-using LibHac.Common;
-using LibHac.Fs;
-using LibHac.Fs.Fsa;
-using LibHac.FsSystem;
-using LibHac.Tools.Fs;
-using LibHac.Tools.FsSystem;
-using LibHac.Tools.FsSystem.NcaUtils;
-using Ryujinx.Ava.Common.Locale;
-using Ryujinx.Ava.UI.Helpers;
-using Ryujinx.Ava.UI.Models;
-using Ryujinx.Common.Configuration;
-using Ryujinx.Common.Logging;
-using Ryujinx.Common.Utilities;
-using Ryujinx.HLE.FileSystem;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Application = Avalonia.Application;
-using Path = System.IO.Path;
-
-namespace Ryujinx.Ava.UI.ViewModels
-{
- public class DownloadableContentManagerViewModel : BaseModel
- {
- private readonly List<DownloadableContentContainer> _downloadableContentContainerList;
- private readonly string _downloadableContentJsonPath;
-
- private readonly VirtualFileSystem _virtualFileSystem;
- private AvaloniaList<DownloadableContentModel> _downloadableContents = new();
- private AvaloniaList<DownloadableContentModel> _views = new();
- private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new();
-
- private string _search;
- private readonly ulong _titleId;
- private readonly IStorageProvider _storageProvider;
-
- private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
-
- public AvaloniaList<DownloadableContentModel> DownloadableContents
- {
- get => _downloadableContents;
- set
- {
- _downloadableContents = value;
- OnPropertyChanged();
- OnPropertyChanged(nameof(UpdateCount));
- Sort();
- }
- }
-
- public AvaloniaList<DownloadableContentModel> Views
- {
- get => _views;
- set
- {
- _views = value;
- OnPropertyChanged();
- }
- }
-
- public AvaloniaList<DownloadableContentModel> SelectedDownloadableContents
- {
- get => _selectedDownloadableContents;
- set
- {
- _selectedDownloadableContents = value;
- OnPropertyChanged();
- }
- }
-
- public string Search
- {
- get => _search;
- set
- {
- _search = value;
- OnPropertyChanged();
- Sort();
- }
- }
-
- public string UpdateCount
- {
- get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count);
- }
-
- public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId)
- {
- _virtualFileSystem = virtualFileSystem;
-
- _titleId = titleId;
-
- if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- _storageProvider = desktop.MainWindow.StorageProvider;
- }
-
- _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
-
- try
- {
- _downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, _serializerContext.ListDownloadableContentContainer);
- }
- catch
- {
- Logger.Error?.Print(LogClass.Configuration, "Downloadable Content JSON failed to deserialize.");
- _downloadableContentContainerList = new List<DownloadableContentContainer>();
- }
-
- LoadDownloadableContents();
- }
-
- private void LoadDownloadableContents()
- {
- foreach (DownloadableContentContainer downloadableContentContainer in _downloadableContentContainerList)
- {
- if (File.Exists(downloadableContentContainer.ContainerPath))
- {
- using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath);
-
- PartitionFileSystem partitionFileSystem = new();
- partitionFileSystem.Initialize(containerFile.AsStorage()).ThrowIfFailure();
-
- _virtualFileSystem.ImportTickets(partitionFileSystem);
-
- foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList)
- {
- using UniqueRef<IFile> ncaFile = new();
-
- partitionFileSystem.OpenFile(ref ncaFile.Ref, downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
-
- Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
- if (nca != null)
- {
- var content = new DownloadableContentModel(nca.Header.TitleId.ToString("X16"),
- downloadableContentContainer.ContainerPath,
- downloadableContentNca.FullPath,
- downloadableContentNca.Enabled);
-
- DownloadableContents.Add(content);
-
- if (content.Enabled)
- {
- SelectedDownloadableContents.Add(content);
- }
-
- OnPropertyChanged(nameof(UpdateCount));
- }
- }
- }
- }
-
- // NOTE: Save the list again to remove leftovers.
- Save();
- Sort();
- }
-
- public void Sort()
- {
- DownloadableContents.AsObservableChangeSet()
- .Filter(Filter)
- .Bind(out var view).AsObservableList();
-
- _views.Clear();
- _views.AddRange(view);
- OnPropertyChanged(nameof(Views));
- }
-
- private bool Filter(object arg)
- {
- if (arg is DownloadableContentModel content)
- {
- return string.IsNullOrWhiteSpace(_search) || content.FileName.ToLower().Contains(_search.ToLower()) || content.TitleId.ToLower().Contains(_search.ToLower());
- }
-
- return false;
- }
-
- private Nca TryOpenNca(IStorage ncaStorage, string containerPath)
- {
- try
- {
- return new Nca(_virtualFileSystem.KeySet, ncaStorage);
- }
- catch (Exception ex)
- {
- Dispatcher.UIThread.InvokeAsync(async () =>
- {
- await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogLoadFileErrorMessage], ex.Message, containerPath));
- });
- }
-
- return null;
- }
-
- public async void Add()
- {
- var result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
- {
- Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle],
- AllowMultiple = true,
- FileTypeFilter = new List<FilePickerFileType>
- {
- new("NSP")
- {
- Patterns = new[] { "*.nsp" },
- AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
- MimeTypes = new[] { "application/x-nx-nsp" },
- },
- },
- });
-
- foreach (var file in result)
- {
- await AddDownloadableContent(file.Path.LocalPath);
- }
- }
-
- private async Task AddDownloadableContent(string path)
- {
- if (!File.Exists(path) || DownloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null)
- {
- return;
- }
-
- using FileStream containerFile = File.OpenRead(path);
-
- PartitionFileSystem partitionFileSystem = new();
- partitionFileSystem.Initialize(containerFile.AsStorage()).ThrowIfFailure();
- bool containsDownloadableContent = false;
-
- _virtualFileSystem.ImportTickets(partitionFileSystem);
-
- foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca"))
- {
- using var ncaFile = new UniqueRef<IFile>();
-
- partitionFileSystem.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
-
- Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path);
- if (nca == null)
- {
- continue;
- }
-
- if (nca.Header.ContentType == NcaContentType.PublicData)
- {
- if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != _titleId)
- {
- break;
- }
-
- var content = new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true);
- DownloadableContents.Add(content);
- SelectedDownloadableContents.Add(content);
-
- OnPropertyChanged(nameof(UpdateCount));
- Sort();
-
- containsDownloadableContent = true;
- }
- }
-
- if (!containsDownloadableContent)
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]);
- }
- }
-
- public void Remove(DownloadableContentModel model)
- {
- DownloadableContents.Remove(model);
- OnPropertyChanged(nameof(UpdateCount));
- Sort();
- }
-
- public void RemoveAll()
- {
- DownloadableContents.Clear();
- OnPropertyChanged(nameof(UpdateCount));
- Sort();
- }
-
- public void EnableAll()
- {
- SelectedDownloadableContents = new(DownloadableContents);
- }
-
- public void DisableAll()
- {
- SelectedDownloadableContents.Clear();
- }
-
- public void Save()
- {
- _downloadableContentContainerList.Clear();
-
- DownloadableContentContainer container = default;
-
- foreach (DownloadableContentModel downloadableContent in DownloadableContents)
- {
- if (container.ContainerPath != downloadableContent.ContainerPath)
- {
- if (!string.IsNullOrWhiteSpace(container.ContainerPath))
- {
- _downloadableContentContainerList.Add(container);
- }
-
- container = new DownloadableContentContainer
- {
- ContainerPath = downloadableContent.ContainerPath,
- DownloadableContentNcaList = new List<DownloadableContentNca>(),
- };
- }
-
- container.DownloadableContentNcaList.Add(new DownloadableContentNca
- {
- Enabled = downloadableContent.Enabled,
- TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16),
- FullPath = downloadableContent.FullPath,
- });
- }
-
- if (!string.IsNullOrWhiteSpace(container.ContainerPath))
- {
- _downloadableContentContainerList.Add(container);
- }
-
- JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, _serializerContext.ListDownloadableContentContainer);
- }
-
- }
-}
diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
deleted file mode 100644
index 17bd69b1..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
+++ /dev/null
@@ -1,1708 +0,0 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Input;
-using Avalonia.Media;
-using Avalonia.Platform.Storage;
-using Avalonia.Threading;
-using DynamicData;
-using DynamicData.Binding;
-using LibHac.Common;
-using Ryujinx.Ava.Common;
-using Ryujinx.Ava.Common.Locale;
-using Ryujinx.Ava.Input;
-using Ryujinx.Ava.UI.Controls;
-using Ryujinx.Ava.UI.Helpers;
-using Ryujinx.Ava.UI.Models;
-using Ryujinx.Ava.UI.Models.Generic;
-using Ryujinx.Ava.UI.Renderer;
-using Ryujinx.Ava.UI.Windows;
-using Ryujinx.Common;
-using Ryujinx.Common.Configuration;
-using Ryujinx.Common.Logging;
-using Ryujinx.Cpu;
-using Ryujinx.HLE;
-using Ryujinx.HLE.FileSystem;
-using Ryujinx.HLE.HOS;
-using Ryujinx.HLE.HOS.Services.Account.Acc;
-using Ryujinx.HLE.UI;
-using Ryujinx.Input.HLE;
-using Ryujinx.Modules;
-using Ryujinx.UI.App.Common;
-using Ryujinx.UI.Common;
-using Ryujinx.UI.Common.Configuration;
-using Ryujinx.UI.Common.Helper;
-using SixLabors.ImageSharp.PixelFormats;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using Image = SixLabors.ImageSharp.Image;
-using Key = Ryujinx.Input.Key;
-using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
-using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
-
-namespace Ryujinx.Ava.UI.ViewModels
-{
- public class MainWindowViewModel : BaseModel
- {
- private const int HotKeyPressDelayMs = 500;
-
- private ObservableCollection<ApplicationData> _applications;
- private string _aspectStatusText;
-
- private string _loadHeading;
- private string _cacheLoadStatus;
- private string _searchText;
- private Timer _searchTimer;
- private string _dockedStatusText;
- private string _fifoStatusText;
- private string _gameStatusText;
- private string _volumeStatusText;
- private string _gpuStatusText;
- private bool _isAmiiboRequested;
- private bool _isGameRunning;
- private bool _isFullScreen;
- private int _progressMaximum;
- private int _progressValue;
- private long _lastFullscreenToggle = Environment.TickCount64;
- private bool _showLoadProgress;
- private bool _showMenuAndStatusBar = true;
- private bool _showStatusSeparator;
- private Brush _progressBarForegroundColor;
- private Brush _progressBarBackgroundColor;
- private Brush _vsyncColor;
- private byte[] _selectedIcon;
- private bool _isAppletMenuActive;
- private int _statusBarProgressMaximum;
- private int _statusBarProgressValue;
- private bool _isPaused;
- private bool _showContent = true;
- private bool _isLoadingIndeterminate = true;
- private bool _showAll;
- private string _lastScannedAmiiboId;
- private bool _statusBarVisible;
- private ReadOnlyObservableCollection<ApplicationData> _appsObservableList;
-
- private string _showUiKey = "F4";
- private string _pauseKey = "F5";
- private string _screenshotKey = "F8";
- private float _volume;
- private float _volumeBeforeMute;
- private string _backendText;
-
- private bool _canUpdate = true;
- private Cursor _cursor;
- private string _title;
- private string _currentEmulatedGamePath;
- private readonly AutoResetEvent _rendererWaitEvent;
- private WindowState _windowState;
- private double _windowWidth;
- private double _windowHeight;
-
- private bool _isActive;
-
- public ApplicationData ListSelectedApplication;
- public ApplicationData GridSelectedApplication;
-
- private string TitleName { get; set; }
- internal AppHost AppHost { get; set; }
-
- public MainWindowViewModel()
- {
- Applications = new ObservableCollection<ApplicationData>();
-
- Applications.ToObservableChangeSet()
- .Filter(Filter)
- .Sort(GetComparer())
- .Bind(out _appsObservableList).AsObservableList();
-
- _rendererWaitEvent = new AutoResetEvent(false);
-
- if (Program.PreviewerDetached)
- {
- LoadConfigurableHotKeys();
-
- Volume = ConfigurationState.Instance.System.AudioVolume;
- }
- }
-
- public void Initialize(
- ContentManager contentManager,
- IStorageProvider storageProvider,
- ApplicationLibrary applicationLibrary,
- VirtualFileSystem virtualFileSystem,
- AccountManager accountManager,
- InputManager inputManager,
- UserChannelPersistence userChannelPersistence,
- LibHacHorizonManager libHacHorizonManager,
- IHostUIHandler uiHandler,
- Action<bool> showLoading,
- Action<bool> switchToGameControl,
- Action<Control> setMainContent,
- TopLevel topLevel)
- {
- ContentManager = contentManager;
- StorageProvider = storageProvider;
- ApplicationLibrary = applicationLibrary;
- VirtualFileSystem = virtualFileSystem;
- AccountManager = accountManager;
- InputManager = inputManager;
- UserChannelPersistence = userChannelPersistence;
- LibHacHorizonManager = libHacHorizonManager;
- UiHandler = uiHandler;
-
- ShowLoading = showLoading;
- SwitchToGameControl = switchToGameControl;
- SetMainContent = setMainContent;
- TopLevel = topLevel;
- }
-
- #region Properties
-
- public string SearchText
- {
- get => _searchText;
- set
- {
- _searchText = value;
-
- _searchTimer?.Dispose();
-
- _searchTimer = new Timer(TimerCallback, null, 1000, 0);
- }
- }
-
- private void TimerCallback(object obj)
- {
- RefreshView();
-
- _searchTimer.Dispose();
- _searchTimer = null;
- }
-
- public bool CanUpdate
- {
- get => _canUpdate && EnableNonGameRunningControls && Updater.CanUpdate(false);
- set
- {
- _canUpdate = value;
- OnPropertyChanged();
- }
- }
-
- public Cursor Cursor
- {
- get => _cursor;
- set
- {
- _cursor = value;
- OnPropertyChanged();
- }
- }
-
- public ReadOnlyObservableCollection<ApplicationData> AppsObservableList
- {
- get => _appsObservableList;
- set
- {
- _appsObservableList = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool IsPaused
- {
- get => _isPaused;
- set
- {
- _isPaused = value;
-
- OnPropertyChanged();
- }
- }
-
- public long LastFullscreenToggle
- {
- get => _lastFullscreenToggle;
- set
- {
- _lastFullscreenToggle = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool StatusBarVisible
- {
- get => _statusBarVisible && EnableNonGameRunningControls;
- set
- {
- _statusBarVisible = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool EnableNonGameRunningControls => !IsGameRunning;
-
- public bool ShowFirmwareStatus => !ShowLoadProgress;
-
- public bool IsGameRunning
- {
- get => _isGameRunning;
- set
- {
- _isGameRunning = value;
-
- if (!value)
- {
- ShowMenuAndStatusBar = false;
- }
-
- OnPropertyChanged();
- OnPropertyChanged(nameof(EnableNonGameRunningControls));
- OnPropertyChanged(nameof(IsAppletMenuActive));
- OnPropertyChanged(nameof(StatusBarVisible));
- OnPropertyChanged(nameof(ShowFirmwareStatus));
- }
- }
-
- public bool IsAmiiboRequested
- {
- get => _isAmiiboRequested && _isGameRunning;
- set
- {
- _isAmiiboRequested = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool ShowLoadProgress
- {
- get => _showLoadProgress;
- set
- {
- _showLoadProgress = value;
-
- OnPropertyChanged();
- OnPropertyChanged(nameof(ShowFirmwareStatus));
- }
- }
-
- public string GameStatusText
- {
- get => _gameStatusText;
- set
- {
- _gameStatusText = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool IsFullScreen
- {
- get => _isFullScreen;
- set
- {
- _isFullScreen = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool ShowAll
- {
- get => _showAll;
- set
- {
- _showAll = value;
-
- OnPropertyChanged();
- }
- }
-
- public string LastScannedAmiiboId
- {
- get => _lastScannedAmiiboId;
- set
- {
- _lastScannedAmiiboId = value;
-
- OnPropertyChanged();
- }
- }
-
- public ApplicationData SelectedApplication
- {
- get
- {
- return Glyph switch
- {
- Glyph.List => ListSelectedApplication,
- Glyph.Grid => GridSelectedApplication,
- _ => null,
- };
- }
- }
-
- public bool OpenUserSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0;
-
- public bool OpenDeviceSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;
-
- public bool OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
-
- public bool CreateShortcutEnabled => !ReleaseInformation.IsFlatHubBuild;
-
- public string LoadHeading
- {
- get => _loadHeading;
- set
- {
- _loadHeading = value;
-
- OnPropertyChanged();
- }
- }
-
- public string CacheLoadStatus
- {
- get => _cacheLoadStatus;
- set
- {
- _cacheLoadStatus = value;
-
- OnPropertyChanged();
- }
- }
-
- public Brush ProgressBarBackgroundColor
- {
- get => _progressBarBackgroundColor;
- set
- {
- _progressBarBackgroundColor = value;
-
- OnPropertyChanged();
- }
- }
-
- public Brush ProgressBarForegroundColor
- {
- get => _progressBarForegroundColor;
- set
- {
- _progressBarForegroundColor = value;
-
- OnPropertyChanged();
- }
- }
-
- public Brush VsyncColor
- {
- get => _vsyncColor;
- set
- {
- _vsyncColor = value;
-
- OnPropertyChanged();
- }
- }
-
- public byte[] SelectedIcon
- {
- get => _selectedIcon;
- set
- {
- _selectedIcon = value;
-
- OnPropertyChanged();
- }
- }
-
- public int ProgressMaximum
- {
- get => _progressMaximum;
- set
- {
- _progressMaximum = value;
-
- OnPropertyChanged();
- }
- }
-
- public int ProgressValue
- {
- get => _progressValue;
- set
- {
- _progressValue = value;
-
- OnPropertyChanged();
- }
- }
-
- public int StatusBarProgressMaximum
- {
- get => _statusBarProgressMaximum;
- set
- {
- _statusBarProgressMaximum = value;
-
- OnPropertyChanged();
- }
- }
-
- public int StatusBarProgressValue
- {
- get => _statusBarProgressValue;
- set
- {
- _statusBarProgressValue = value;
-
- OnPropertyChanged();
- }
- }
-
- public string FifoStatusText
- {
- get => _fifoStatusText;
- set
- {
- _fifoStatusText = value;
-
- OnPropertyChanged();
- }
- }
-
- public string GpuNameText
- {
- get => _gpuStatusText;
- set
- {
- _gpuStatusText = value;
-
- OnPropertyChanged();
- }
- }
-
- public string BackendText
- {
- get => _backendText;
- set
- {
- _backendText = value;
-
- OnPropertyChanged();
- }
- }
-
- public string DockedStatusText
- {
- get => _dockedStatusText;
- set
- {
- _dockedStatusText = value;
-
- OnPropertyChanged();
- }
- }
-
- public string AspectRatioStatusText
- {
- get => _aspectStatusText;
- set
- {
- _aspectStatusText = value;
-
- OnPropertyChanged();
- }
- }
-
- public string VolumeStatusText
- {
- get => _volumeStatusText;
- set
- {
- _volumeStatusText = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool VolumeMuted => _volume == 0;
-
- public float Volume
- {
- get => _volume;
- set
- {
- _volume = value;
-
- if (_isGameRunning)
- {
- AppHost.Device.SetVolume(_volume);
- }
-
- OnPropertyChanged(nameof(VolumeStatusText));
- OnPropertyChanged(nameof(VolumeMuted));
- OnPropertyChanged();
- }
- }
-
- public float VolumeBeforeMute
- {
- get => _volumeBeforeMute;
- set
- {
- _volumeBeforeMute = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool ShowStatusSeparator
- {
- get => _showStatusSeparator;
- set
- {
- _showStatusSeparator = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool ShowMenuAndStatusBar
- {
- get => _showMenuAndStatusBar;
- set
- {
- _showMenuAndStatusBar = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool IsLoadingIndeterminate
- {
- get => _isLoadingIndeterminate;
- set
- {
- _isLoadingIndeterminate = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool IsActive
- {
- get => _isActive;
- set
- {
- _isActive = value;
-
- OnPropertyChanged();
- }
- }
-
-
- public bool ShowContent
- {
- get => _showContent;
- set
- {
- _showContent = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool IsAppletMenuActive
- {
- get => _isAppletMenuActive && EnableNonGameRunningControls;
- set
- {
- _isAppletMenuActive = value;
-
- OnPropertyChanged();
- }
- }
-
- public WindowState WindowState
- {
- get => _windowState;
- internal set
- {
- _windowState = value;
-
- OnPropertyChanged();
- }
- }
-
- public double WindowWidth
- {
- get => _windowWidth;
- set
- {
- _windowWidth = value;
-
- OnPropertyChanged();
- }
- }
-
- public double WindowHeight
- {
- get => _windowHeight;
- set
- {
- _windowHeight = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool IsGrid => Glyph == Glyph.Grid;
- public bool IsList => Glyph == Glyph.List;
-
- internal void Sort(bool isAscending)
- {
- IsAscending = isAscending;
-
- RefreshView();
- }
-
- internal void Sort(ApplicationSort sort)
- {
- SortMode = sort;
-
- RefreshView();
- }
-
- public bool StartGamesInFullscreen
- {
- get => ConfigurationState.Instance.UI.StartFullscreen;
- set
- {
- ConfigurationState.Instance.UI.StartFullscreen.Value = value;
-
- ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
-
- OnPropertyChanged();
- }
- }
-
- public bool ShowConsole
- {
- get => ConfigurationState.Instance.UI.ShowConsole;
- set
- {
- ConfigurationState.Instance.UI.ShowConsole.Value = value;
-
- ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
-
- OnPropertyChanged();
- }
- }
-
- public string Title
- {
- get => _title;
- set
- {
- _title = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool ShowConsoleVisible
- {
- get => ConsoleHelper.SetConsoleWindowStateSupported;
- }
-
- public bool ManageFileTypesVisible
- {
- get => FileAssociationHelper.IsTypeAssociationSupported;
- }
-
- public ObservableCollection<ApplicationData> Applications
- {
- get => _applications;
- set
- {
- _applications = value;
-
- OnPropertyChanged();
- }
- }
-
- public Glyph Glyph
- {
- get => (Glyph)ConfigurationState.Instance.UI.GameListViewMode.Value;
- set
- {
- ConfigurationState.Instance.UI.GameListViewMode.Value = (int)value;
-
- OnPropertyChanged();
- OnPropertyChanged(nameof(IsGrid));
- OnPropertyChanged(nameof(IsList));
-
- ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
- }
- }
-
- public bool ShowNames
- {
- get => ConfigurationState.Instance.UI.ShowNames && ConfigurationState.Instance.UI.GridSize > 1; set
- {
- ConfigurationState.Instance.UI.ShowNames.Value = value;
-
- OnPropertyChanged();
- OnPropertyChanged(nameof(GridSizeScale));
- OnPropertyChanged(nameof(GridItemSelectorSize));
-
- ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
- }
- }
-
- internal ApplicationSort SortMode
- {
- get => (ApplicationSort)ConfigurationState.Instance.UI.ApplicationSort.Value;
- private set
- {
- ConfigurationState.Instance.UI.ApplicationSort.Value = (int)value;
-
- OnPropertyChanged();
- OnPropertyChanged(nameof(SortName));
-
- ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
- }
- }
-
- public int ListItemSelectorSize
- {
- get
- {
- return ConfigurationState.Instance.UI.GridSize.Value switch
- {
- 1 => 78,
- 2 => 100,
- 3 => 120,
- 4 => 140,
- _ => 16,
- };
- }
- }
-
- public int GridItemSelectorSize
- {
- get
- {
- return ConfigurationState.Instance.UI.GridSize.Value switch
- {
- 1 => 120,
- 2 => ShowNames ? 210 : 150,
- 3 => ShowNames ? 240 : 180,
- 4 => ShowNames ? 280 : 220,
- _ => 16,
- };
- }
- }
-
- public int GridSizeScale
- {
- get => ConfigurationState.Instance.UI.GridSize;
- set
- {
- ConfigurationState.Instance.UI.GridSize.Value = value;
-
- if (value < 2)
- {
- ShowNames = false;
- }
-
- OnPropertyChanged();
- OnPropertyChanged(nameof(IsGridSmall));
- OnPropertyChanged(nameof(IsGridMedium));
- OnPropertyChanged(nameof(IsGridLarge));
- OnPropertyChanged(nameof(IsGridHuge));
- OnPropertyChanged(nameof(ListItemSelectorSize));
- OnPropertyChanged(nameof(GridItemSelectorSize));
- OnPropertyChanged(nameof(ShowNames));
-
- ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
- }
- }
-
- public string SortName
- {
- get
- {
- return SortMode switch
- {
- ApplicationSort.Title => LocaleManager.Instance[LocaleKeys.GameListHeaderApplication],
- ApplicationSort.Developer => LocaleManager.Instance[LocaleKeys.GameListHeaderDeveloper],
- ApplicationSort.LastPlayed => LocaleManager.Instance[LocaleKeys.GameListHeaderLastPlayed],
- ApplicationSort.TotalTimePlayed => LocaleManager.Instance[LocaleKeys.GameListHeaderTimePlayed],
- ApplicationSort.FileType => LocaleManager.Instance[LocaleKeys.GameListHeaderFileExtension],
- ApplicationSort.FileSize => LocaleManager.Instance[LocaleKeys.GameListHeaderFileSize],
- ApplicationSort.Path => LocaleManager.Instance[LocaleKeys.GameListHeaderPath],
- ApplicationSort.Favorite => LocaleManager.Instance[LocaleKeys.CommonFavorite],
- _ => string.Empty,
- };
- }
- }
-
- public bool IsAscending
- {
- get => ConfigurationState.Instance.UI.IsAscendingOrder;
- private set
- {
- ConfigurationState.Instance.UI.IsAscendingOrder.Value = value;
-
- OnPropertyChanged();
- OnPropertyChanged(nameof(SortMode));
- OnPropertyChanged(nameof(SortName));
-
- ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
- }
- }
-
- public KeyGesture ShowUiKey
- {
- get => KeyGesture.Parse(_showUiKey);
- set
- {
- _showUiKey = value.ToString();
-
- OnPropertyChanged();
- }
- }
-
- public KeyGesture ScreenshotKey
- {
- get => KeyGesture.Parse(_screenshotKey);
- set
- {
- _screenshotKey = value.ToString();
-
- OnPropertyChanged();
- }
- }
-
- public KeyGesture PauseKey
- {
- get => KeyGesture.Parse(_pauseKey); set
- {
- _pauseKey = value.ToString();
-
- OnPropertyChanged();
- }
- }
-
- public ContentManager ContentManager { get; private set; }
- public IStorageProvider StorageProvider { get; private set; }
- public ApplicationLibrary ApplicationLibrary { get; private set; }
- public VirtualFileSystem VirtualFileSystem { get; private set; }
- public AccountManager AccountManager { get; private set; }
- public InputManager InputManager { get; private set; }
- public UserChannelPersistence UserChannelPersistence { get; private set; }
- public Action<bool> ShowLoading { get; private set; }
- public Action<bool> SwitchToGameControl { get; private set; }
- public Action<Control> SetMainContent { get; private set; }
- public TopLevel TopLevel { get; private set; }
- public RendererHost RendererHostControl { get; private set; }
- public bool IsClosing { get; set; }
- public LibHacHorizonManager LibHacHorizonManager { get; internal set; }
- public IHostUIHandler UiHandler { get; internal set; }
- public bool IsSortedByFavorite => SortMode == ApplicationSort.Favorite;
- public bool IsSortedByTitle => SortMode == ApplicationSort.Title;
- public bool IsSortedByDeveloper => SortMode == ApplicationSort.Developer;
- public bool IsSortedByLastPlayed => SortMode == ApplicationSort.LastPlayed;
- public bool IsSortedByTimePlayed => SortMode == ApplicationSort.TotalTimePlayed;
- public bool IsSortedByType => SortMode == ApplicationSort.FileType;
- public bool IsSortedBySize => SortMode == ApplicationSort.FileSize;
- public bool IsSortedByPath => SortMode == ApplicationSort.Path;
- public bool IsGridSmall => ConfigurationState.Instance.UI.GridSize == 1;
- public bool IsGridMedium => ConfigurationState.Instance.UI.GridSize == 2;
- public bool IsGridLarge => ConfigurationState.Instance.UI.GridSize == 3;
- public bool IsGridHuge => ConfigurationState.Instance.UI.GridSize == 4;
-
- #endregion
-
- #region PrivateMethods
-
- private IComparer<ApplicationData> GetComparer()
- {
- return SortMode switch
- {
-#pragma warning disable IDE0055 // Disable formatting
- ApplicationSort.Title => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName)
- : SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName),
- ApplicationSort.Developer => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Developer)
- : SortExpressionComparer<ApplicationData>.Descending(app => app.Developer),
- ApplicationSort.LastPlayed => new LastPlayedSortComparer(IsAscending),
- ApplicationSort.TotalTimePlayed => new TimePlayedSortComparer(IsAscending),
- ApplicationSort.FileType => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileExtension)
- : SortExpressionComparer<ApplicationData>.Descending(app => app.FileExtension),
- ApplicationSort.FileSize => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileSize)
- : SortExpressionComparer<ApplicationData>.Descending(app => app.FileSize),
- ApplicationSort.Path => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Path)
- : SortExpressionComparer<ApplicationData>.Descending(app => app.Path),
- ApplicationSort.Favorite => !IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Favorite)
- : SortExpressionComparer<ApplicationData>.Descending(app => app.Favorite),
- _ => null,
-#pragma warning restore IDE0055
- };
- }
-
- public void RefreshView()
- {
- RefreshGrid();
- }
-
- private void RefreshGrid()
- {
- Applications.ToObservableChangeSet()
- .Filter(Filter)
- .Sort(GetComparer())
- .Bind(out _appsObservableList).AsObservableList();
-
- OnPropertyChanged(nameof(AppsObservableList));
- }
-
- private bool Filter(object arg)
- {
- if (arg is ApplicationData app)
- {
- return string.IsNullOrWhiteSpace(_searchText) || app.TitleName.ToLower().Contains(_searchText.ToLower());
- }
-
- return false;
- }
-
- private async Task HandleFirmwareInstallation(string filename)
- {
- try
- {
- SystemVersion firmwareVersion = ContentManager.VerifyFirmwarePackage(filename);
-
- if (firmwareVersion == null)
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareNotFoundErrorMessage, filename));
-
- return;
- }
-
- string dialogTitle = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallTitle, firmwareVersion.VersionString);
- string dialogMessage = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallMessage, firmwareVersion.VersionString);
-
- SystemVersion currentVersion = ContentManager.GetCurrentFirmwareVersion();
- if (currentVersion != null)
- {
- dialogMessage += LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallSubMessage, currentVersion.VersionString);
- }
-
- dialogMessage += LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallConfirmMessage];
-
- UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
- dialogTitle,
- dialogMessage,
- LocaleManager.Instance[LocaleKeys.InputDialogYes],
- LocaleManager.Instance[LocaleKeys.InputDialogNo],
- LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
-
- UpdateWaitWindow waitingDialog = new(dialogTitle, LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallWaitMessage]);
-
- if (result == UserResult.Yes)
- {
- Logger.Info?.Print(LogClass.Application, $"Installing firmware {firmwareVersion.VersionString}");
-
- Thread thread = new(() =>
- {
- Dispatcher.UIThread.InvokeAsync(delegate
- {
- waitingDialog.Show();
- });
-
- try
- {
- ContentManager.InstallFirmware(filename);
-
- Dispatcher.UIThread.InvokeAsync(async delegate
- {
- waitingDialog.Close();
-
- string message = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallSuccessMessage, firmwareVersion.VersionString);
-
- await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance[LocaleKeys.InputDialogOk], "", LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
-
- Logger.Info?.Print(LogClass.Application, message);
-
- // Purge Applet Cache.
-
- DirectoryInfo miiEditorCacheFolder = new(Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache"));
-
- if (miiEditorCacheFolder.Exists)
- {
- miiEditorCacheFolder.Delete(true);
- }
- });
- }
- catch (Exception ex)
- {
- Dispatcher.UIThread.InvokeAsync(async () =>
- {
- waitingDialog.Close();
-
- await ContentDialogHelper.CreateErrorDialog(ex.Message);
- });
- }
- finally
- {
- RefreshFirmwareStatus();
- }
- })
- {
- Name = "GUI.FirmwareInstallerThread",
- };
-
- thread.Start();
- }
- }
- catch (MissingKeyException ex)
- {
- if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- Logger.Error?.Print(LogClass.Application, ex.ToString());
-
- await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys);
- }
- }
- catch (Exception ex)
- {
- await ContentDialogHelper.CreateErrorDialog(ex.Message);
- }
- }
-
- private void ProgressHandler<T>(T state, int current, int total) where T : Enum
- {
- Dispatcher.UIThread.Post((() =>
- {
- ProgressMaximum = total;
- ProgressValue = current;
-
- switch (state)
- {
- case LoadState ptcState:
- CacheLoadStatus = $"{current} / {total}";
- switch (ptcState)
- {
- case LoadState.Unloaded:
- case LoadState.Loading:
- LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingPPTC];
- IsLoadingIndeterminate = false;
- break;
- case LoadState.Loaded:
- LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
- IsLoadingIndeterminate = true;
- CacheLoadStatus = "";
- break;
- }
- break;
- case ShaderCacheLoadingState shaderCacheState:
- CacheLoadStatus = $"{current} / {total}";
- switch (shaderCacheState)
- {
- case ShaderCacheLoadingState.Start:
- case ShaderCacheLoadingState.Loading:
- LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingShaders];
- IsLoadingIndeterminate = false;
- break;
- case ShaderCacheLoadingState.Packaging:
- LoadHeading = LocaleManager.Instance[LocaleKeys.PackagingShaders];
- IsLoadingIndeterminate = false;
- break;
- case ShaderCacheLoadingState.Loaded:
- LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
- IsLoadingIndeterminate = true;
- CacheLoadStatus = "";
- break;
- }
- break;
- default:
- throw new ArgumentException($"Unknown Progress Handler type {typeof(T)}");
- }
- }));
- }
-
- private void PrepareLoadScreen()
- {
- using MemoryStream stream = new(SelectedIcon);
- using var gameIconBmp = Image.Load<Bgra32>(stream);
-
- var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp).ToPixel<Bgra32>();
-
- const float ColorMultiple = 0.5f;
-
- Color progressFgColor = Color.FromRgb(dominantColor.R, dominantColor.G, dominantColor.B);
- Color progressBgColor = Color.FromRgb(
- (byte)(dominantColor.R * ColorMultiple),
- (byte)(dominantColor.G * ColorMultiple),
- (byte)(dominantColor.B * ColorMultiple));
-
- ProgressBarForegroundColor = new SolidColorBrush(progressFgColor);
- ProgressBarBackgroundColor = new SolidColorBrush(progressBgColor);
- }
-
- private void InitializeGame()
- {
- RendererHostControl.WindowCreated += RendererHost_Created;
-
- AppHost.StatusUpdatedEvent += Update_StatusBar;
- AppHost.AppExit += AppHost_AppExit;
-
- _rendererWaitEvent.WaitOne();
-
- AppHost?.Start();
-
- AppHost?.DisposeContext();
- }
-
- private async Task HandleRelaunch()
- {
- if (UserChannelPersistence.PreviousIndex != -1 && UserChannelPersistence.ShouldRestart)
- {
- UserChannelPersistence.ShouldRestart = false;
-
- await LoadApplication(_currentEmulatedGamePath);
- }
- else
- {
- // Otherwise, clear state.
- UserChannelPersistence = new UserChannelPersistence();
- _currentEmulatedGamePath = null;
- }
- }
-
- private void Update_StatusBar(object sender, StatusUpdatedEventArgs args)
- {
- if (ShowMenuAndStatusBar && !ShowLoadProgress)
- {
- Dispatcher.UIThread.InvokeAsync(() =>
- {
- Application.Current.Styles.TryGetResource(args.VSyncEnabled
- ? "VsyncEnabled"
- : "VsyncDisabled",
- Application.Current.ActualThemeVariant,
- out object color);
-
- if (color is not null)
- {
- VsyncColor = new SolidColorBrush((Color)color);
- }
-
- DockedStatusText = args.DockedMode;
- AspectRatioStatusText = args.AspectRatio;
- GameStatusText = args.GameStatus;
- VolumeStatusText = args.VolumeStatus;
- FifoStatusText = args.FifoStatus;
- GpuNameText = args.GpuName;
- BackendText = args.GpuBackend;
-
- ShowStatusSeparator = true;
- });
- }
- }
-
- private void RendererHost_Created(object sender, EventArgs e)
- {
- ShowLoading(false);
-
- _rendererWaitEvent.Set();
- }
-
- #endregion
-
- #region PublicMethods
-
- public void SetUiProgressHandlers(Switch emulationContext)
- {
- if (emulationContext.Processes.ActiveApplication.DiskCacheLoadState != null)
- {
- emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged -= ProgressHandler;
- emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged += ProgressHandler;
- }
-
- emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler;
- emulationContext.Gpu.ShaderCacheStateChanged += ProgressHandler;
- }
-
- public void LoadConfigurableHotKeys()
- {
- if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI, out var showUiKey))
- {
- ShowUiKey = new KeyGesture(showUiKey);
- }
-
- if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey))
- {
- ScreenshotKey = new KeyGesture(screenshotKey);
- }
-
- if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey))
- {
- PauseKey = new KeyGesture(pauseKey);
- }
- }
-
- public void TakeScreenshot()
- {
- AppHost.ScreenshotRequested = true;
- }
-
- public void HideUi()
- {
- ShowMenuAndStatusBar = false;
- }
-
- public void ToggleStartGamesInFullscreen()
- {
- StartGamesInFullscreen = !StartGamesInFullscreen;
- }
-
- public void ToggleShowConsole()
- {
- ShowConsole = !ShowConsole;
- }
-
- public void SetListMode()
- {
- Glyph = Glyph.List;
- }
-
- public void SetGridMode()
- {
- Glyph = Glyph.Grid;
- }
-
- public void SetAspectRatio(AspectRatio aspectRatio)
- {
- ConfigurationState.Instance.Graphics.AspectRatio.Value = aspectRatio;
- }
-
- public async Task InstallFirmwareFromFile()
- {
- var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
- {
- AllowMultiple = false,
- FileTypeFilter = new List<FilePickerFileType>
- {
- new(LocaleManager.Instance[LocaleKeys.FileDialogAllTypes])
- {
- Patterns = new[] { "*.xci", "*.zip" },
- AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci", "public.zip-archive" },
- MimeTypes = new[] { "application/x-nx-xci", "application/zip" },
- },
- new("XCI")
- {
- Patterns = new[] { "*.xci" },
- AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
- MimeTypes = new[] { "application/x-nx-xci" },
- },
- new("ZIP")
- {
- Patterns = new[] { "*.zip" },
- AppleUniformTypeIdentifiers = new[] { "public.zip-archive" },
- MimeTypes = new[] { "application/zip" },
- },
- },
- });
-
- if (result.Count > 0)
- {
- await HandleFirmwareInstallation(result[0].Path.LocalPath);
- }
- }
-
- public async Task InstallFirmwareFromFolder()
- {
- var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
- {
- AllowMultiple = false,
- });
-
- if (result.Count > 0)
- {
- await HandleFirmwareInstallation(result[0].Path.LocalPath);
- }
- }
-
- public void OpenRyujinxFolder()
- {
- OpenHelper.OpenFolder(AppDataManager.BaseDirPath);
- }
-
- public void OpenLogsFolder()
- {
- string logPath = AppDataManager.GetOrCreateLogsDir();
- if (!string.IsNullOrEmpty(logPath))
- {
- OpenHelper.OpenFolder(logPath);
- }
- }
-
- public void ToggleDockMode()
- {
- if (IsGameRunning)
- {
- ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
- }
- }
-
- public async Task ExitCurrentState()
- {
- if (WindowState == WindowState.FullScreen)
- {
- ToggleFullscreen();
- }
- else if (IsGameRunning)
- {
- await Task.Delay(100);
-
- AppHost?.ShowExitPrompt();
- }
- }
-
- public static void ChangeLanguage(object languageCode)
- {
- LocaleManager.Instance.LoadLanguage((string)languageCode);
-
- if (Program.PreviewerDetached)
- {
- ConfigurationState.Instance.UI.LanguageCode.Value = (string)languageCode;
- ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
- }
- }
-
- public async Task ManageProfiles()
- {
- await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient);
- }
-
- public void SimulateWakeUpMessage()
- {
- AppHost.Device.System.SimulateWakeUpMessage();
- }
-
- public async Task OpenFile()
- {
- var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
- {
- Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle],
- AllowMultiple = false,
- FileTypeFilter = new List<FilePickerFileType>
- {
- new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
- {
- Patterns = new[] { "*.nsp", "*.xci", "*.nca", "*.nro", "*.nso" },
- AppleUniformTypeIdentifiers = new[]
- {
- "com.ryujinx.nsp",
- "com.ryujinx.xci",
- "com.ryujinx.nca",
- "com.ryujinx.nro",
- "com.ryujinx.nso",
- },
- MimeTypes = new[]
- {
- "application/x-nx-nsp",
- "application/x-nx-xci",
- "application/x-nx-nca",
- "application/x-nx-nro",
- "application/x-nx-nso",
- },
- },
- new("NSP")
- {
- Patterns = new[] { "*.nsp" },
- AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
- MimeTypes = new[] { "application/x-nx-nsp" },
- },
- new("XCI")
- {
- Patterns = new[] { "*.xci" },
- AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
- MimeTypes = new[] { "application/x-nx-xci" },
- },
- new("NCA")
- {
- Patterns = new[] { "*.nca" },
- AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nca" },
- MimeTypes = new[] { "application/x-nx-nca" },
- },
- new("NRO")
- {
- Patterns = new[] { "*.nro" },
- AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nro" },
- MimeTypes = new[] { "application/x-nx-nro" },
- },
- new("NSO")
- {
- Patterns = new[] { "*.nso" },
- AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nso" },
- MimeTypes = new[] { "application/x-nx-nso" },
- },
- },
- });
-
- if (result.Count > 0)
- {
- await LoadApplication(result[0].Path.LocalPath);
- }
- }
-
- public async Task OpenFolder()
- {
- var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
- {
- Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle],
- AllowMultiple = false,
- });
-
- if (result.Count > 0)
- {
- await LoadApplication(result[0].Path.LocalPath);
- }
- }
-
- public async Task LoadApplication(string path, bool startFullscreen = false, string titleName = "")
- {
- if (AppHost != null)
- {
- await ContentDialogHelper.CreateInfoDialog(
- LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedMessage],
- LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedSubMessage],
- LocaleManager.Instance[LocaleKeys.InputDialogOk],
- "",
- LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
-
- return;
- }
-
-#if RELEASE
- await PerformanceCheck();
-#endif
-
- Logger.RestartTime();
-
- SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path, ConfigurationState.Instance.System.Language);
-
- PrepareLoadScreen();
-
- RendererHostControl = new RendererHost();
-
- AppHost = new AppHost(
- RendererHostControl,
- InputManager,
- path,
- VirtualFileSystem,
- ContentManager,
- AccountManager,
- UserChannelPersistence,
- this,
- TopLevel);
-
- if (!await AppHost.LoadGuestApplication())
- {
- AppHost.DisposeContext();
- AppHost = null;
-
- return;
- }
-
- CanUpdate = false;
-
- LoadHeading = TitleName = titleName;
-
- if (string.IsNullOrWhiteSpace(titleName))
- {
- LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name);
- TitleName = AppHost.Device.Processes.ActiveApplication.Name;
- }
-
- SwitchToRenderer(startFullscreen);
-
- _currentEmulatedGamePath = path;
-
- Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
- gameThread.Start();
- }
-
- public void SwitchToRenderer(bool startFullscreen)
- {
- Dispatcher.UIThread.Post(() =>
- {
- SwitchToGameControl(startFullscreen);
-
- SetMainContent(RendererHostControl);
-
- RendererHostControl.Focus();
- });
- }
-
- public static void UpdateGameMetadata(string titleId)
- {
- ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
- {
- appMetadata.UpdatePostGame();
- });
- }
-
- public void RefreshFirmwareStatus()
- {
- SystemVersion version = null;
- try
- {
- version = ContentManager.GetCurrentFirmwareVersion();
- }
- catch (Exception) { }
-
- bool hasApplet = false;
-
- if (version != null)
- {
- LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, version.VersionString);
-
- hasApplet = version.Major > 3;
- }
- else
- {
- LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0");
- }
-
- IsAppletMenuActive = hasApplet;
- }
-
- public void AppHost_AppExit(object sender, EventArgs e)
- {
- if (IsClosing)
- {
- return;
- }
-
- IsGameRunning = false;
-
- Dispatcher.UIThread.InvokeAsync(async () =>
- {
- ShowMenuAndStatusBar = true;
- ShowContent = true;
- ShowLoadProgress = false;
- IsLoadingIndeterminate = false;
- CanUpdate = true;
- Cursor = Cursor.Default;
-
- SetMainContent(null);
-
- AppHost = null;
-
- await HandleRelaunch();
- });
-
- RendererHostControl.WindowCreated -= RendererHost_Created;
- RendererHostControl = null;
-
- SelectedIcon = null;
-
- Dispatcher.UIThread.InvokeAsync(() =>
- {
- Title = $"Ryujinx {Program.Version}";
- });
- }
-
- public void ToggleFullscreen()
- {
- if (Environment.TickCount64 - LastFullscreenToggle < HotKeyPressDelayMs)
- {
- return;
- }
-
- LastFullscreenToggle = Environment.TickCount64;
-
- if (WindowState == WindowState.FullScreen)
- {
- WindowState = WindowState.Normal;
-
- if (IsGameRunning)
- {
- ShowMenuAndStatusBar = true;
- }
- }
- else
- {
- WindowState = WindowState.FullScreen;
-
- if (IsGameRunning)
- {
- ShowMenuAndStatusBar = false;
- }
- }
-
- IsFullScreen = WindowState == WindowState.FullScreen;
- }
-
- public static void SaveConfig()
- {
- ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
- }
-
- public static async Task PerformanceCheck()
- {
- if (ConfigurationState.Instance.Logger.EnableTrace.Value)
- {
- string mainMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledMessage];
- string secondaryMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledConfirmMessage];
-
- UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
- mainMessage,
- secondaryMessage,
- LocaleManager.Instance[LocaleKeys.InputDialogYes],
- LocaleManager.Instance[LocaleKeys.InputDialogNo],
- LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
-
- if (result == UserResult.Yes)
- {
- ConfigurationState.Instance.Logger.EnableTrace.Value = false;
-
- SaveConfig();
- }
- }
-
- if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value))
- {
- string mainMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledMessage];
- string secondaryMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledConfirmMessage];
-
- UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
- mainMessage,
- secondaryMessage,
- LocaleManager.Instance[LocaleKeys.InputDialogYes],
- LocaleManager.Instance[LocaleKeys.InputDialogNo],
- LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
-
- if (result == UserResult.Yes)
- {
- ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = "";
-
- SaveConfig();
- }
- }
- }
- #endregion
- }
-}
diff --git a/src/Ryujinx.Ava/UI/ViewModels/ModManagerViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/ModManagerViewModel.cs
deleted file mode 100644
index 8321bf89..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/ModManagerViewModel.cs
+++ /dev/null
@@ -1,336 +0,0 @@
-using Avalonia;
-using Avalonia.Collections;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Platform.Storage;
-using Avalonia.Threading;
-using DynamicData;
-using Ryujinx.Ava.Common.Locale;
-using Ryujinx.Ava.UI.Helpers;
-using Ryujinx.Ava.UI.Models;
-using Ryujinx.Common.Configuration;
-using Ryujinx.Common.Logging;
-using Ryujinx.Common.Utilities;
-using Ryujinx.HLE.HOS;
-using System;
-using System.IO;
-using System.Linq;
-
-namespace Ryujinx.Ava.UI.ViewModels
-{
- public class ModManagerViewModel : BaseModel
- {
- private readonly string _modJsonPath;
-
- private AvaloniaList<ModModel> _mods = new();
- private AvaloniaList<ModModel> _views = new();
- private AvaloniaList<ModModel> _selectedMods = new();
-
- private string _search;
- private readonly ulong _applicationId;
- private readonly IStorageProvider _storageProvider;
-
- private static readonly ModMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
-
- public AvaloniaList<ModModel> Mods
- {
- get => _mods;
- set
- {
- _mods = value;
- OnPropertyChanged();
- OnPropertyChanged(nameof(ModCount));
- Sort();
- }
- }
-
- public AvaloniaList<ModModel> Views
- {
- get => _views;
- set
- {
- _views = value;
- OnPropertyChanged();
- }
- }
-
- public AvaloniaList<ModModel> SelectedMods
- {
- get => _selectedMods;
- set
- {
- _selectedMods = value;
- OnPropertyChanged();
- }
- }
-
- public string Search
- {
- get => _search;
- set
- {
- _search = value;
- OnPropertyChanged();
- Sort();
- }
- }
-
- public string ModCount
- {
- get => string.Format(LocaleManager.Instance[LocaleKeys.ModWindowHeading], Mods.Count);
- }
-
- public ModManagerViewModel(ulong applicationId)
- {
- _applicationId = applicationId;
-
- _modJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationId.ToString("x16"), "mods.json");
-
- if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- _storageProvider = desktop.MainWindow.StorageProvider;
- }
-
- LoadMods(applicationId);
- }
-
- private void LoadMods(ulong applicationId)
- {
- Mods.Clear();
- SelectedMods.Clear();
-
- string[] modsBasePaths = [ModLoader.GetSdModsBasePath(), ModLoader.GetModsBasePath()];
-
- foreach (var path in modsBasePaths)
- {
- var inSd = path == ModLoader.GetSdModsBasePath();
- var modCache = new ModLoader.ModCache();
-
- ModLoader.QueryContentsDir(modCache, new DirectoryInfo(Path.Combine(path, "contents")), applicationId);
-
- foreach (var mod in modCache.RomfsDirs)
- {
- var modModel = new ModModel(mod.Path.Parent.FullName, mod.Name, mod.Enabled, inSd);
- if (Mods.All(x => x.Path != mod.Path.Parent.FullName))
- {
- Mods.Add(modModel);
- }
- }
-
- foreach (var mod in modCache.RomfsContainers)
- {
- Mods.Add(new ModModel(mod.Path.FullName, mod.Name, mod.Enabled, inSd));
- }
-
- foreach (var mod in modCache.ExefsDirs)
- {
- var modModel = new ModModel(mod.Path.Parent.FullName, mod.Name, mod.Enabled, inSd);
- if (Mods.All(x => x.Path != mod.Path.Parent.FullName))
- {
- Mods.Add(modModel);
- }
- }
-
- foreach (var mod in modCache.ExefsContainers)
- {
- Mods.Add(new ModModel(mod.Path.FullName, mod.Name, mod.Enabled, inSd));
- }
- }
-
- Sort();
- }
-
- public void Sort()
- {
- Mods.AsObservableChangeSet()
- .Filter(Filter)
- .Bind(out var view).AsObservableList();
-
- _views.Clear();
- _views.AddRange(view);
-
- SelectedMods = new(Views.Where(x => x.Enabled));
-
- OnPropertyChanged(nameof(ModCount));
- OnPropertyChanged(nameof(Views));
- OnPropertyChanged(nameof(SelectedMods));
- }
-
- private bool Filter(object arg)
- {
- if (arg is ModModel content)
- {
- return string.IsNullOrWhiteSpace(_search) || content.Name.ToLower().Contains(_search.ToLower());
- }
-
- return false;
- }
-
- public void Save()
- {
- ModMetadata modData = new();
-
- foreach (ModModel mod in Mods)
- {
- modData.Mods.Add(new Mod
- {
- Name = mod.Name,
- Path = mod.Path,
- Enabled = SelectedMods.Contains(mod),
- });
- }
-
- JsonHelper.SerializeToFile(_modJsonPath, modData, _serializerContext.ModMetadata);
- }
-
- public void Delete(ModModel model)
- {
- var isSubdir = true;
- var pathToDelete = model.Path;
- var basePath = model.InSd ? ModLoader.GetSdModsBasePath() : ModLoader.GetModsBasePath();
- var modsDir = ModLoader.GetApplicationDir(basePath, _applicationId.ToString("x16"));
-
- if (new DirectoryInfo(model.Path).Parent?.FullName == modsDir)
- {
- isSubdir = false;
- }
-
- if (isSubdir)
- {
- var parentDir = String.Empty;
-
- foreach (var dir in Directory.GetDirectories(modsDir, "*", SearchOption.TopDirectoryOnly))
- {
- if (Directory.GetDirectories(dir, "*", SearchOption.AllDirectories).Contains(model.Path))
- {
- parentDir = dir;
- break;
- }
- }
-
- if (parentDir == String.Empty)
- {
- Dispatcher.UIThread.Post(async () =>
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(
- LocaleKeys.DialogModDeleteNoParentMessage,
- model.Path));
- });
- return;
- }
- }
-
- Logger.Info?.Print(LogClass.Application, $"Deleting mod at \"{pathToDelete}\"");
- Directory.Delete(pathToDelete, true);
-
- Mods.Remove(model);
- OnPropertyChanged(nameof(ModCount));
- Sort();
- }
-
- private void AddMod(DirectoryInfo directory)
- {
- string[] directories;
-
- try
- {
- directories = Directory.GetDirectories(directory.ToString(), "*", SearchOption.AllDirectories);
- }
- catch (Exception exception)
- {
- Dispatcher.UIThread.Post(async () =>
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(
- LocaleKeys.DialogLoadFileErrorMessage,
- exception.ToString(),
- directory));
- });
- return;
- }
-
- var destinationDir = ModLoader.GetApplicationDir(ModLoader.GetSdModsBasePath(), _applicationId.ToString("x16"));
-
- // TODO: More robust checking for valid mod folders
- var isDirectoryValid = true;
-
- if (directories.Length == 0)
- {
- isDirectoryValid = false;
- }
-
- if (!isDirectoryValid)
- {
- Dispatcher.UIThread.Post(async () =>
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogModInvalidMessage]);
- });
- return;
- }
-
- foreach (var dir in directories)
- {
- string dirToCreate = dir.Replace(directory.Parent.ToString(), destinationDir);
-
- // Mod already exists
- if (Directory.Exists(dirToCreate))
- {
- Dispatcher.UIThread.Post(async () =>
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(
- LocaleKeys.DialogLoadFileErrorMessage,
- LocaleManager.Instance[LocaleKeys.DialogModAlreadyExistsMessage],
- dirToCreate));
- });
-
- return;
- }
-
- Directory.CreateDirectory(dirToCreate);
- }
-
- var files = Directory.GetFiles(directory.ToString(), "*", SearchOption.AllDirectories);
-
- foreach (var file in files)
- {
- File.Copy(file, file.Replace(directory.Parent.ToString(), destinationDir), true);
- }
-
- LoadMods(_applicationId);
- }
-
- public async void Add()
- {
- var result = await _storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
- {
- Title = LocaleManager.Instance[LocaleKeys.SelectModDialogTitle],
- AllowMultiple = true,
- });
-
- foreach (var folder in result)
- {
- AddMod(new DirectoryInfo(folder.Path.LocalPath));
- }
- }
-
- public void DeleteAll()
- {
- foreach (var mod in Mods)
- {
- Delete(mod);
- }
-
- Mods.Clear();
- OnPropertyChanged(nameof(ModCount));
- Sort();
- }
-
- public void EnableAll()
- {
- SelectedMods = new(Mods);
- }
-
- public void DisableAll()
- {
- SelectedMods.Clear();
- }
- }
-}
diff --git a/src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs
deleted file mode 100644
index 0b12a51f..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-namespace Ryujinx.Ava.UI.ViewModels
-{
- public class MotionInputViewModel : BaseModel
- {
- private int _slot;
- public int Slot
- {
- get => _slot;
- set
- {
- _slot = value;
- OnPropertyChanged();
- }
- }
-
- private int _altSlot;
- public int AltSlot
- {
- get => _altSlot;
- set
- {
- _altSlot = value;
- OnPropertyChanged();
- }
- }
-
- private string _dsuServerHost;
- public string DsuServerHost
- {
- get => _dsuServerHost;
- set
- {
- _dsuServerHost = value;
- OnPropertyChanged();
- }
- }
-
- private int _dsuServerPort;
- public int DsuServerPort
- {
- get => _dsuServerPort;
- set
- {
- _dsuServerPort = value;
- OnPropertyChanged();
- }
- }
-
- private bool _mirrorInput;
- public bool MirrorInput
- {
- get => _mirrorInput;
- set
- {
- _mirrorInput = value;
- OnPropertyChanged();
- }
- }
-
- private int _sensitivity;
- public int Sensitivity
- {
- get => _sensitivity;
- set
- {
- _sensitivity = value;
- OnPropertyChanged();
- }
- }
-
- private double _gryoDeadzone;
- public double GyroDeadzone
- {
- get => _gryoDeadzone;
- set
- {
- _gryoDeadzone = value;
- OnPropertyChanged();
- }
- }
-
- private bool _enableCemuHookMotion;
- public bool EnableCemuHookMotion
- {
- get => _enableCemuHookMotion;
- set
- {
- _enableCemuHookMotion = value;
- OnPropertyChanged();
- }
- }
- }
-}
diff --git a/src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs
deleted file mode 100644
index 49de1993..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace Ryujinx.Ava.UI.ViewModels
-{
- public class RumbleInputViewModel : BaseModel
- {
- private float _strongRumble;
- public float StrongRumble
- {
- get => _strongRumble;
- set
- {
- _strongRumble = value;
- OnPropertyChanged();
- }
- }
-
- private float _weakRumble;
- public float WeakRumble
- {
- get => _weakRumble;
- set
- {
- _weakRumble = value;
- OnPropertyChanged();
- }
- }
- }
-}
diff --git a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs
deleted file mode 100644
index bcaa0860..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs
+++ /dev/null
@@ -1,614 +0,0 @@
-using Avalonia.Collections;
-using Avalonia.Controls;
-using Avalonia.Threading;
-using LibHac.Tools.FsSystem;
-using Ryujinx.Audio.Backends.OpenAL;
-using Ryujinx.Audio.Backends.SDL2;
-using Ryujinx.Audio.Backends.SoundIo;
-using Ryujinx.Ava.Common.Locale;
-using Ryujinx.Ava.UI.Helpers;
-using Ryujinx.Ava.UI.Windows;
-using Ryujinx.Common.Configuration;
-using Ryujinx.Common.Configuration.Hid;
-using Ryujinx.Common.Configuration.Multiplayer;
-using Ryujinx.Common.GraphicsDriver;
-using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.Vulkan;
-using Ryujinx.HLE.FileSystem;
-using Ryujinx.HLE.HOS.Services.Time.TimeZone;
-using Ryujinx.UI.Common.Configuration;
-using Ryujinx.UI.Common.Configuration.System;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Net.NetworkInformation;
-using System.Runtime.InteropServices;
-using System.Threading.Tasks;
-using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
-
-namespace Ryujinx.Ava.UI.ViewModels
-{
- public class SettingsViewModel : BaseModel
- {
- private readonly VirtualFileSystem _virtualFileSystem;
- private readonly ContentManager _contentManager;
- private TimeZoneContentManager _timeZoneContentManager;
-
- private readonly List<string> _validTzRegions;
-
- private readonly Dictionary<string, string> _networkInterfaces;
-
- private float _customResolutionScale;
- private int _resolutionScale;
- private int _graphicsBackendMultithreadingIndex;
- private float _volume;
- private bool _isVulkanAvailable = true;
- private bool _directoryChanged;
- private readonly List<string> _gpuIds = new();
- private KeyboardHotkeys _keyboardHotkeys;
- private int _graphicsBackendIndex;
- private int _scalingFilter;
- private int _scalingFilterLevel;
-
- public event Action CloseWindow;
- public event Action SaveSettingsEvent;
- private int _networkInterfaceIndex;
- private int _multiplayerModeIndex;
-
- public int ResolutionScale
- {
- get => _resolutionScale;
- set
- {
- _resolutionScale = value;
-
- OnPropertyChanged(nameof(CustomResolutionScale));
- OnPropertyChanged(nameof(IsCustomResolutionScaleActive));
- }
- }
-
- public int GraphicsBackendMultithreadingIndex
- {
- get => _graphicsBackendMultithreadingIndex;
- set
- {
- _graphicsBackendMultithreadingIndex = value;
-
- if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value)
- {
- Dispatcher.UIThread.InvokeAsync(() =>
- ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
- "",
- "",
- LocaleManager.Instance[LocaleKeys.InputDialogOk],
- LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle])
- );
- }
-
- OnPropertyChanged();
- }
- }
-
- public float CustomResolutionScale
- {
- get => _customResolutionScale;
- set
- {
- _customResolutionScale = MathF.Round(value, 1);
-
- OnPropertyChanged();
- }
- }
-
- public bool IsVulkanAvailable
- {
- get => _isVulkanAvailable;
- set
- {
- _isVulkanAvailable = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS();
-
- public bool IsHypervisorAvailable => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
-
- public bool DirectoryChanged
- {
- get => _directoryChanged;
- set
- {
- _directoryChanged = value;
-
- OnPropertyChanged();
- }
- }
-
- public bool IsMacOS => OperatingSystem.IsMacOS();
-
- public bool EnableDiscordIntegration { get; set; }
- public bool CheckUpdatesOnStart { get; set; }
- public bool ShowConfirmExit { get; set; }
- public int HideCursor { get; set; }
- public bool EnableDockedMode { get; set; }
- public bool EnableKeyboard { get; set; }
- public bool EnableMouse { get; set; }
- public bool EnableVsync { get; set; }
- public bool EnablePptc { get; set; }
- public bool EnableInternetAccess { get; set; }
- public bool EnableFsIntegrityChecks { get; set; }
- public bool IgnoreMissingServices { get; set; }
- public bool ExpandDramSize { get; set; }
- public bool EnableShaderCache { get; set; }
- public bool EnableTextureRecompression { get; set; }
- public bool EnableMacroHLE { get; set; }
- public bool EnableColorSpacePassthrough { get; set; }
- public bool ColorSpacePassthroughAvailable => IsMacOS;
- public bool EnableFileLog { get; set; }
- public bool EnableStub { get; set; }
- public bool EnableInfo { get; set; }
- public bool EnableWarn { get; set; }
- public bool EnableError { get; set; }
- public bool EnableTrace { get; set; }
- public bool EnableGuest { get; set; }
- public bool EnableFsAccessLog { get; set; }
- public bool EnableDebug { get; set; }
- public bool IsOpenAlEnabled { get; set; }
- public bool IsSoundIoEnabled { get; set; }
- public bool IsSDL2Enabled { get; set; }
- public bool IsCustomResolutionScaleActive => _resolutionScale == 4;
- public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr;
-
- public bool IsVulkanSelected => GraphicsBackendIndex == 0;
- public bool UseHypervisor { get; set; }
-
- public string TimeZone { get; set; }
- public string ShaderDumpPath { get; set; }
-
- public int Language { get; set; }
- public int Region { get; set; }
- public int FsGlobalAccessLogMode { get; set; }
- public int AudioBackend { get; set; }
- public int MaxAnisotropy { get; set; }
- public int AspectRatio { get; set; }
- public int AntiAliasingEffect { get; set; }
- public string ScalingFilterLevelText => ScalingFilterLevel.ToString("0");
- public int ScalingFilterLevel
- {
- get => _scalingFilterLevel;
- set
- {
- _scalingFilterLevel = value;
- OnPropertyChanged();
- OnPropertyChanged(nameof(ScalingFilterLevelText));
- }
- }
- public int OpenglDebugLevel { get; set; }
- public int MemoryMode { get; set; }
- public int BaseStyleIndex { get; set; }
- public int GraphicsBackendIndex
- {
- get => _graphicsBackendIndex;
- set
- {
- _graphicsBackendIndex = value;
- OnPropertyChanged();
- OnPropertyChanged(nameof(IsVulkanSelected));
- }
- }
- public int ScalingFilter
- {
- get => _scalingFilter;
- set
- {
- _scalingFilter = value;
- OnPropertyChanged();
- OnPropertyChanged(nameof(IsScalingFilterActive));
- }
- }
-
- public int PreferredGpuIndex { get; set; }
-
- public float Volume
- {
- get => _volume;
- set
- {
- _volume = value;
-
- ConfigurationState.Instance.System.AudioVolume.Value = _volume / 100;
-
- OnPropertyChanged();
- }
- }
-
- public DateTimeOffset CurrentDate { get; set; }
- public TimeSpan CurrentTime { get; set; }
-
- internal AvaloniaList<TimeZone> TimeZones { get; set; }
- public AvaloniaList<string> GameDirectories { get; set; }
- public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; }
-
- public AvaloniaList<string> NetworkInterfaceList
- {
- get => new(_networkInterfaces.Keys);
- }
-
- public AvaloniaList<string> MultiplayerModes
- {
- get => new(Enum.GetNames<MultiplayerMode>());
- }
-
- public KeyboardHotkeys KeyboardHotkeys
- {
- get => _keyboardHotkeys;
- set
- {
- _keyboardHotkeys = value;
-
- OnPropertyChanged();
- }
- }
-
- public int NetworkInterfaceIndex
- {
- get => _networkInterfaceIndex;
- set
- {
- _networkInterfaceIndex = value != -1 ? value : 0;
- ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[_networkInterfaceIndex]];
- }
- }
-
- public int MultiplayerModeIndex
- {
- get => _multiplayerModeIndex;
- set
- {
- _multiplayerModeIndex = value;
- ConfigurationState.Instance.Multiplayer.Mode.Value = (MultiplayerMode)_multiplayerModeIndex;
- }
- }
-
- public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
- {
- _virtualFileSystem = virtualFileSystem;
- _contentManager = contentManager;
- if (Program.PreviewerDetached)
- {
- Task.Run(LoadTimeZones);
- }
- }
-
- public SettingsViewModel()
- {
- GameDirectories = new AvaloniaList<string>();
- TimeZones = new AvaloniaList<TimeZone>();
- AvailableGpus = new ObservableCollection<ComboBoxItem>();
- _validTzRegions = new List<string>();
- _networkInterfaces = new Dictionary<string, string>();
-
- Task.Run(CheckSoundBackends);
- Task.Run(PopulateNetworkInterfaces);
-
- if (Program.PreviewerDetached)
- {
- Task.Run(LoadAvailableGpus);
- LoadCurrentConfiguration();
- }
- }
-
- public async Task CheckSoundBackends()
- {
- IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported;
- IsSoundIoEnabled = SoundIoHardwareDeviceDriver.IsSupported;
- IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported;
-
- await Dispatcher.UIThread.InvokeAsync(() =>
- {
- OnPropertyChanged(nameof(IsOpenAlEnabled));
- OnPropertyChanged(nameof(IsSoundIoEnabled));
- OnPropertyChanged(nameof(IsSDL2Enabled));
- });
- }
-
- private async Task LoadAvailableGpus()
- {
- AvailableGpus.Clear();
-
- var devices = VulkanRenderer.GetPhysicalDevices();
-
- if (devices.Length == 0)
- {
- IsVulkanAvailable = false;
- GraphicsBackendIndex = 1;
- }
- else
- {
- foreach (var device in devices)
- {
- await Dispatcher.UIThread.InvokeAsync(() =>
- {
- _gpuIds.Add(device.Id);
-
- AvailableGpus.Add(new ComboBoxItem { Content = $"{device.Name} {(device.IsDiscrete ? "(dGPU)" : "")}" });
- });
- }
- }
-
- // GPU configuration needs to be loaded during the async method or it will always return 0.
- PreferredGpuIndex = _gpuIds.Contains(ConfigurationState.Instance.Graphics.PreferredGpu) ?
- _gpuIds.IndexOf(ConfigurationState.Instance.Graphics.PreferredGpu) : 0;
-
- Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(PreferredGpuIndex)));
- }
-
- public async Task LoadTimeZones()
- {
- _timeZoneContentManager = new TimeZoneContentManager();
-
- _timeZoneContentManager.InitializeInstance(_virtualFileSystem, _contentManager, IntegrityCheckLevel.None);
-
- foreach ((int offset, string location, string abbr) in _timeZoneContentManager.ParseTzOffsets())
- {
- int hours = Math.DivRem(offset, 3600, out int seconds);
- int minutes = Math.Abs(seconds) / 60;
-
- string abbr2 = abbr.StartsWith('+') || abbr.StartsWith('-') ? string.Empty : abbr;
-
- await Dispatcher.UIThread.InvokeAsync(() =>
- {
- TimeZones.Add(new TimeZone($"UTC{hours:+0#;-0#;+00}:{minutes:D2}", location, abbr2));
-
- _validTzRegions.Add(location);
- });
- }
-
- Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(TimeZone)));
- }
-
- private async Task PopulateNetworkInterfaces()
- {
- _networkInterfaces.Clear();
- _networkInterfaces.Add(LocaleManager.Instance[LocaleKeys.NetworkInterfaceDefault], "0");
-
- foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces())
- {
- await Dispatcher.UIThread.InvokeAsync(() =>
- {
- _networkInterfaces.Add(networkInterface.Name, networkInterface.Id);
- });
- }
-
- // Network interface index needs to be loaded during the async method or it will always return 0.
- NetworkInterfaceIndex = _networkInterfaces.Values.ToList().IndexOf(ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value);
-
- Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(NetworkInterfaceIndex)));
- }
-
- public void ValidateAndSetTimeZone(string location)
- {
- if (_validTzRegions.Contains(location))
- {
- TimeZone = location;
- }
- }
-
- public void LoadCurrentConfiguration()
- {
- ConfigurationState config = ConfigurationState.Instance;
-
- // User Interface
- EnableDiscordIntegration = config.EnableDiscordIntegration;
- CheckUpdatesOnStart = config.CheckUpdatesOnStart;
- ShowConfirmExit = config.ShowConfirmExit;
- HideCursor = (int)config.HideCursor.Value;
-
- GameDirectories.Clear();
- GameDirectories.AddRange(config.UI.GameDirs.Value);
-
- BaseStyleIndex = config.UI.BaseStyle == "Light" ? 0 : 1;
-
- // Input
- EnableDockedMode = config.System.EnableDockedMode;
- EnableKeyboard = config.Hid.EnableKeyboard;
- EnableMouse = config.Hid.EnableMouse;
-
- // Keyboard Hotkeys
- KeyboardHotkeys = config.Hid.Hotkeys.Value;
-
- // System
- Region = (int)config.System.Region.Value;
- Language = (int)config.System.Language.Value;
- TimeZone = config.System.TimeZone;
-
- DateTime currentDateTime = DateTime.Now;
-
- CurrentDate = currentDateTime.Date;
- CurrentTime = currentDateTime.TimeOfDay.Add(TimeSpan.FromSeconds(config.System.SystemTimeOffset));
-
- EnableVsync = config.Graphics.EnableVsync;
- EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
- ExpandDramSize = config.System.ExpandRam;
- IgnoreMissingServices = config.System.IgnoreMissingServices;
-
- // CPU
- EnablePptc = config.System.EnablePtc;
- MemoryMode = (int)config.System.MemoryManagerMode.Value;
- UseHypervisor = config.System.UseHypervisor;
-
- // Graphics
- GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value;
- // Physical devices are queried asynchronously hence the prefered index config value is loaded in LoadAvailableGpus().
- EnableShaderCache = config.Graphics.EnableShaderCache;
- EnableTextureRecompression = config.Graphics.EnableTextureRecompression;
- EnableMacroHLE = config.Graphics.EnableMacroHLE;
- EnableColorSpacePassthrough = config.Graphics.EnableColorSpacePassthrough;
- ResolutionScale = config.Graphics.ResScale == -1 ? 4 : config.Graphics.ResScale - 1;
- CustomResolutionScale = config.Graphics.ResScaleCustom;
- MaxAnisotropy = config.Graphics.MaxAnisotropy == -1 ? 0 : (int)(MathF.Log2(config.Graphics.MaxAnisotropy));
- AspectRatio = (int)config.Graphics.AspectRatio.Value;
- GraphicsBackendMultithreadingIndex = (int)config.Graphics.BackendThreading.Value;
- ShaderDumpPath = config.Graphics.ShadersDumpPath;
- AntiAliasingEffect = (int)config.Graphics.AntiAliasing.Value;
- ScalingFilter = (int)config.Graphics.ScalingFilter.Value;
- ScalingFilterLevel = config.Graphics.ScalingFilterLevel.Value;
-
- // Audio
- AudioBackend = (int)config.System.AudioBackend.Value;
- Volume = config.System.AudioVolume * 100;
-
- // Network
- EnableInternetAccess = config.System.EnableInternetAccess;
- // LAN interface index is loaded asynchronously in PopulateNetworkInterfaces()
-
- // Logging
- EnableFileLog = config.Logger.EnableFileLog;
- EnableStub = config.Logger.EnableStub;
- EnableInfo = config.Logger.EnableInfo;
- EnableWarn = config.Logger.EnableWarn;
- EnableError = config.Logger.EnableError;
- EnableTrace = config.Logger.EnableTrace;
- EnableGuest = config.Logger.EnableGuest;
- EnableDebug = config.Logger.EnableDebug;
- EnableFsAccessLog = config.Logger.EnableFsAccessLog;
- FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
- OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
-
- MultiplayerModeIndex = (int)config.Multiplayer.Mode.Value;
- }
-
- public void SaveSettings()
- {
- ConfigurationState config = ConfigurationState.Instance;
-
- // User Interface
- config.EnableDiscordIntegration.Value = EnableDiscordIntegration;
- config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart;
- config.ShowConfirmExit.Value = ShowConfirmExit;
- config.HideCursor.Value = (HideCursorMode)HideCursor;
-
- if (_directoryChanged)
- {
- List<string> gameDirs = new(GameDirectories);
- config.UI.GameDirs.Value = gameDirs;
- }
-
- config.UI.BaseStyle.Value = BaseStyleIndex == 0 ? "Light" : "Dark";
-
- // Input
- config.System.EnableDockedMode.Value = EnableDockedMode;
- config.Hid.EnableKeyboard.Value = EnableKeyboard;
- config.Hid.EnableMouse.Value = EnableMouse;
-
- // Keyboard Hotkeys
- config.Hid.Hotkeys.Value = KeyboardHotkeys;
-
- // System
- config.System.Region.Value = (Region)Region;
- config.System.Language.Value = (Language)Language;
-
- if (_validTzRegions.Contains(TimeZone))
- {
- config.System.TimeZone.Value = TimeZone;
- }
-
- config.System.SystemTimeOffset.Value = Convert.ToInt64((CurrentDate.ToUnixTimeSeconds() + CurrentTime.TotalSeconds) - DateTimeOffset.Now.ToUnixTimeSeconds());
- config.Graphics.EnableVsync.Value = EnableVsync;
- config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
- config.System.ExpandRam.Value = ExpandDramSize;
- config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
-
- // CPU
- config.System.EnablePtc.Value = EnablePptc;
- config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode;
- config.System.UseHypervisor.Value = UseHypervisor;
-
- // Graphics
- config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex;
- config.Graphics.PreferredGpu.Value = _gpuIds.ElementAtOrDefault(PreferredGpuIndex);
- config.Graphics.EnableShaderCache.Value = EnableShaderCache;
- config.Graphics.EnableTextureRecompression.Value = EnableTextureRecompression;
- config.Graphics.EnableMacroHLE.Value = EnableMacroHLE;
- config.Graphics.EnableColorSpacePassthrough.Value = EnableColorSpacePassthrough;
- config.Graphics.ResScale.Value = ResolutionScale == 4 ? -1 : ResolutionScale + 1;
- config.Graphics.ResScaleCustom.Value = CustomResolutionScale;
- config.Graphics.MaxAnisotropy.Value = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy);
- config.Graphics.AspectRatio.Value = (AspectRatio)AspectRatio;
- config.Graphics.AntiAliasing.Value = (AntiAliasing)AntiAliasingEffect;
- config.Graphics.ScalingFilter.Value = (ScalingFilter)ScalingFilter;
- config.Graphics.ScalingFilterLevel.Value = ScalingFilterLevel;
-
- if (ConfigurationState.Instance.Graphics.BackendThreading != (BackendThreading)GraphicsBackendMultithreadingIndex)
- {
- DriverUtilities.ToggleOGLThreading(GraphicsBackendMultithreadingIndex == (int)BackendThreading.Off);
- }
-
- config.Graphics.BackendThreading.Value = (BackendThreading)GraphicsBackendMultithreadingIndex;
- config.Graphics.ShadersDumpPath.Value = ShaderDumpPath;
-
- // Audio
- AudioBackend audioBackend = (AudioBackend)AudioBackend;
- if (audioBackend != config.System.AudioBackend.Value)
- {
- config.System.AudioBackend.Value = audioBackend;
-
- Logger.Info?.Print(LogClass.Application, $"AudioBackend toggled to: {audioBackend}");
- }
-
- config.System.AudioVolume.Value = Volume / 100;
-
- // Network
- config.System.EnableInternetAccess.Value = EnableInternetAccess;
-
- // Logging
- config.Logger.EnableFileLog.Value = EnableFileLog;
- config.Logger.EnableStub.Value = EnableStub;
- config.Logger.EnableInfo.Value = EnableInfo;
- config.Logger.EnableWarn.Value = EnableWarn;
- config.Logger.EnableError.Value = EnableError;
- config.Logger.EnableTrace.Value = EnableTrace;
- config.Logger.EnableGuest.Value = EnableGuest;
- config.Logger.EnableDebug.Value = EnableDebug;
- config.Logger.EnableFsAccessLog.Value = EnableFsAccessLog;
- config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
- config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
-
- config.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[NetworkInterfaceIndex]];
- config.Multiplayer.Mode.Value = (MultiplayerMode)MultiplayerModeIndex;
-
- config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
-
- MainWindow.UpdateGraphicsConfig();
-
- SaveSettingsEvent?.Invoke();
-
- _directoryChanged = false;
- }
-
- private static void RevertIfNotSaved()
- {
- Program.ReloadConfig();
- }
-
- public void ApplyButton()
- {
- SaveSettings();
- }
-
- public void OkButton()
- {
- SaveSettings();
- CloseWindow?.Invoke();
- }
-
- public void CancelButton()
- {
- RevertIfNotSaved();
- CloseWindow?.Invoke();
- }
- }
-}
diff --git a/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs
deleted file mode 100644
index 5989ce09..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs
+++ /dev/null
@@ -1,249 +0,0 @@
-using Avalonia;
-using Avalonia.Collections;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Platform.Storage;
-using Avalonia.Threading;
-using LibHac.Common;
-using LibHac.Fs;
-using LibHac.Fs.Fsa;
-using LibHac.FsSystem;
-using LibHac.Ns;
-using LibHac.Tools.FsSystem;
-using LibHac.Tools.FsSystem.NcaUtils;
-using Ryujinx.Ava.Common.Locale;
-using Ryujinx.Ava.UI.Helpers;
-using Ryujinx.Ava.UI.Models;
-using Ryujinx.Common.Configuration;
-using Ryujinx.Common.Logging;
-using Ryujinx.Common.Utilities;
-using Ryujinx.HLE.FileSystem;
-using Ryujinx.UI.App.Common;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Path = System.IO.Path;
-using SpanHelpers = LibHac.Common.SpanHelpers;
-
-namespace Ryujinx.Ava.UI.ViewModels
-{
- public class TitleUpdateViewModel : BaseModel
- {
- public TitleUpdateMetadata TitleUpdateWindowData;
- public readonly string TitleUpdateJsonPath;
- private VirtualFileSystem VirtualFileSystem { get; }
- private ulong TitleId { get; }
-
- private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
- private AvaloniaList<object> _views = new();
- private object _selectedUpdate;
-
- private static readonly TitleUpdateMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
-
- public AvaloniaList<TitleUpdateModel> TitleUpdates
- {
- get => _titleUpdates;
- set
- {
- _titleUpdates = value;
- OnPropertyChanged();
- }
- }
-
- public AvaloniaList<object> Views
- {
- get => _views;
- set
- {
- _views = value;
- OnPropertyChanged();
- }
- }
-
- public object SelectedUpdate
- {
- get => _selectedUpdate;
- set
- {
- _selectedUpdate = value;
- OnPropertyChanged();
- }
- }
-
- public IStorageProvider StorageProvider;
-
- public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId)
- {
- VirtualFileSystem = virtualFileSystem;
-
- TitleId = titleId;
-
- if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- StorageProvider = desktop.MainWindow.StorageProvider;
- }
-
- TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
-
- try
- {
- TitleUpdateWindowData = JsonHelper.DeserializeFromFile(TitleUpdateJsonPath, _serializerContext.TitleUpdateMetadata);
- }
- catch
- {
- Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {TitleId} at {TitleUpdateJsonPath}");
-
- TitleUpdateWindowData = new TitleUpdateMetadata
- {
- Selected = "",
- Paths = new List<string>(),
- };
-
- Save();
- }
-
- LoadUpdates();
- }
-
- private void LoadUpdates()
- {
- foreach (string path in TitleUpdateWindowData.Paths)
- {
- AddUpdate(path);
- }
-
- TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == TitleUpdateWindowData.Selected, null);
-
- SelectedUpdate = selected;
-
- // NOTE: Save the list again to remove leftovers.
- Save();
- SortUpdates();
- }
-
- public void SortUpdates()
- {
- var list = TitleUpdates.ToList();
-
- list.Sort((first, second) =>
- {
- if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
- {
- return -1;
- }
-
- if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
- {
- return 1;
- }
-
- return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
- });
-
- Views.Clear();
- Views.Add(new BaseModel());
- Views.AddRange(list);
-
- if (SelectedUpdate == null)
- {
- SelectedUpdate = Views[0];
- }
- else if (!TitleUpdates.Contains(SelectedUpdate))
- {
- if (Views.Count > 1)
- {
- SelectedUpdate = Views[1];
- }
- else
- {
- SelectedUpdate = Views[0];
- }
- }
- }
-
- private void AddUpdate(string path)
- {
- if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
- {
- using FileStream file = new(path, FileMode.Open, FileAccess.Read);
-
- try
- {
- var pfs = new PartitionFileSystem();
- pfs.Initialize(file.AsStorage()).ThrowIfFailure();
- (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(VirtualFileSystem, pfs, TitleId.ToString("x16"), 0);
-
- if (controlNca != null && patchNca != null)
- {
- ApplicationControlProperty controlData = new();
-
- using UniqueRef<IFile> nacpFile = new();
-
- controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
- nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
-
- TitleUpdates.Add(new TitleUpdateModel(controlData, path));
- }
- else
- {
- Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]));
- }
- }
- catch (Exception ex)
- {
- Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadFileErrorMessage, ex.Message, path)));
- }
- }
- }
-
- public void RemoveUpdate(TitleUpdateModel update)
- {
- TitleUpdates.Remove(update);
-
- SortUpdates();
- }
-
- public async Task Add()
- {
- var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
- {
- AllowMultiple = true,
- FileTypeFilter = new List<FilePickerFileType>
- {
- new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
- {
- Patterns = new[] { "*.nsp" },
- AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
- MimeTypes = new[] { "application/x-nx-nsp" },
- },
- },
- });
-
- foreach (var file in result)
- {
- AddUpdate(file.Path.LocalPath);
- }
-
- SortUpdates();
- }
-
- public void Save()
- {
- TitleUpdateWindowData.Paths.Clear();
- TitleUpdateWindowData.Selected = "";
-
- foreach (TitleUpdateModel update in TitleUpdates)
- {
- TitleUpdateWindowData.Paths.Add(update.Path);
-
- if (update == SelectedUpdate)
- {
- TitleUpdateWindowData.Selected = update.Path;
- }
- }
-
- JsonHelper.SerializeToFile(TitleUpdateJsonPath, TitleUpdateWindowData, _serializerContext.TitleUpdateMetadata);
- }
- }
-}
diff --git a/src/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs
deleted file mode 100644
index 89b59122..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs
+++ /dev/null
@@ -1,222 +0,0 @@
-using Avalonia.Media;
-using LibHac.Common;
-using LibHac.Fs;
-using LibHac.Fs.Fsa;
-using LibHac.FsSystem;
-using LibHac.Ncm;
-using LibHac.Tools.Fs;
-using LibHac.Tools.FsSystem;
-using LibHac.Tools.FsSystem.NcaUtils;
-using Ryujinx.Ava.UI.Models;
-using Ryujinx.HLE.FileSystem;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.PixelFormats;
-using System;
-using System.Buffers.Binary;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.IO;
-using Color = Avalonia.Media.Color;
-
-namespace Ryujinx.Ava.UI.ViewModels
-{
- internal class UserFirmwareAvatarSelectorViewModel : BaseModel
- {
- private static readonly Dictionary<string, byte[]> _avatarStore = new();
-
- private ObservableCollection<ProfileImageModel> _images;
- private Color _backgroundColor = Colors.White;
-
- private int _selectedIndex;
-
- public UserFirmwareAvatarSelectorViewModel()
- {
- _images = new ObservableCollection<ProfileImageModel>();
-
- LoadImagesFromStore();
- }
-
- public Color BackgroundColor
- {
- get => _backgroundColor;
- set
- {
- _backgroundColor = value;
- OnPropertyChanged();
- ChangeImageBackground();
- }
- }
-
- public ObservableCollection<ProfileImageModel> Images
- {
- get => _images;
- set
- {
- _images = value;
- OnPropertyChanged();
- }
- }
-
- public int SelectedIndex
- {
- get => _selectedIndex;
- set
- {
- _selectedIndex = value;
-
- if (_selectedIndex == -1)
- {
- SelectedImage = null;
- }
- else
- {
- SelectedImage = _images[_selectedIndex].Data;
- }
-
- OnPropertyChanged();
- }
- }
-
- public byte[] SelectedImage { get; private set; }
-
- private void LoadImagesFromStore()
- {
- Images.Clear();
-
- foreach (var image in _avatarStore)
- {
- Images.Add(new ProfileImageModel(image.Key, image.Value));
- }
- }
-
- private void ChangeImageBackground()
- {
- foreach (var image in Images)
- {
- image.BackgroundColor = new SolidColorBrush(BackgroundColor);
- }
- }
-
- public static void PreloadAvatars(ContentManager contentManager, VirtualFileSystem virtualFileSystem)
- {
- if (_avatarStore.Count > 0)
- {
- return;
- }
-
- string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem, NcaContentType.Data);
- string avatarPath = VirtualFileSystem.SwitchPathToSystemPath(contentPath);
-
- if (!string.IsNullOrWhiteSpace(avatarPath))
- {
- using IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open);
-
- Nca nca = new(virtualFileSystem.KeySet, ncaFileStream);
- IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
-
- foreach (DirectoryEntryEx item in romfs.EnumerateEntries())
- {
- // TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy.
- if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs"))
- {
- using var file = new UniqueRef<IFile>();
-
- romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
-
- using MemoryStream stream = new();
- using MemoryStream streamPng = new();
-
- file.Get.AsStream().CopyTo(stream);
-
- stream.Position = 0;
-
- Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256);
-
- avatarImage.SaveAsPng(streamPng);
-
- _avatarStore.Add(item.FullPath, streamPng.ToArray());
- }
- }
- }
- }
-
- private static byte[] DecompressYaz0(Stream stream)
- {
- using BinaryReader reader = new(stream);
-
- reader.ReadInt32(); // Magic
-
- uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32());
-
- reader.ReadInt64(); // Padding
-
- byte[] input = new byte[stream.Length - stream.Position];
- stream.Read(input, 0, input.Length);
-
- uint inputOffset = 0;
-
- byte[] output = new byte[decodedLength];
- uint outputOffset = 0;
-
- ushort mask = 0;
- byte header = 0;
-
- while (outputOffset < decodedLength)
- {
- if ((mask >>= 1) == 0)
- {
- header = input[inputOffset++];
- mask = 0x80;
- }
-
- if ((header & mask) != 0)
- {
- if (outputOffset == output.Length)
- {
- break;
- }
-
- output[outputOffset++] = input[inputOffset++];
- }
- else
- {
- byte byte1 = input[inputOffset++];
- byte byte2 = input[inputOffset++];
-
- uint dist = (uint)((byte1 & 0xF) << 8) | byte2;
- uint position = outputOffset - (dist + 1);
-
- uint length = (uint)byte1 >> 4;
- if (length == 0)
- {
- length = (uint)input[inputOffset++] + 0x12;
- }
- else
- {
- length += 2;
- }
-
- uint gap = outputOffset - position;
- uint nonOverlappingLength = length;
-
- if (nonOverlappingLength > gap)
- {
- nonOverlappingLength = gap;
- }
-
- Buffer.BlockCopy(output, (int)position, output, (int)outputOffset, (int)nonOverlappingLength);
- outputOffset += nonOverlappingLength;
- position += nonOverlappingLength;
- length -= nonOverlappingLength;
-
- while (length-- > 0)
- {
- output[outputOffset++] = output[position++];
- }
- }
- }
-
- return output;
- }
- }
-}
diff --git a/src/Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs
deleted file mode 100644
index 8e7d41a5..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace Ryujinx.Ava.UI.ViewModels
-{
- internal class UserProfileImageSelectorViewModel : BaseModel
- {
- private bool _firmwareFound;
-
- public bool FirmwareFound
- {
- get => _firmwareFound;
-
- set
- {
- _firmwareFound = value;
- OnPropertyChanged();
- }
- }
- }
-}
diff --git a/src/Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs
deleted file mode 100644
index 70274847..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using Microsoft.IdentityModel.Tokens;
-using Ryujinx.Ava.UI.Models;
-using System;
-using System.Collections.ObjectModel;
-
-namespace Ryujinx.Ava.UI.ViewModels
-{
- public class UserProfileViewModel : BaseModel, IDisposable
- {
- public UserProfileViewModel()
- {
- Profiles = new ObservableCollection<BaseModel>();
- LostProfiles = new ObservableCollection<UserProfile>();
- IsEmpty = LostProfiles.IsNullOrEmpty();
- }
-
- public ObservableCollection<BaseModel> Profiles { get; set; }
-
- public ObservableCollection<UserProfile> LostProfiles { get; set; }
-
- public bool IsEmpty { get; set; }
-
- public void Dispose()
- {
- GC.SuppressFinalize(this);
- }
- }
-}
diff --git a/src/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs
deleted file mode 100644
index 85adef00..00000000
--- a/src/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using DynamicData;
-using DynamicData.Binding;
-using Ryujinx.Ava.Common.Locale;
-using Ryujinx.Ava.UI.Models;
-using Ryujinx.HLE.HOS.Services.Account.Acc;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-
-namespace Ryujinx.Ava.UI.ViewModels
-{
- public class UserSaveManagerViewModel : BaseModel
- {
- private int _sortIndex;
- private int _orderIndex;
- private string _search;
- private ObservableCollection<SaveModel> _saves = new();
- private ObservableCollection<SaveModel> _views = new();
- private readonly AccountManager _accountManager;
-
- public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId);
-
- public int SortIndex
- {
- get => _sortIndex;
- set
- {
- _sortIndex = value;
- OnPropertyChanged();
- Sort();
- }
- }
-
- public int OrderIndex
- {
- get => _orderIndex;
- set
- {
- _orderIndex = value;
- OnPropertyChanged();
- Sort();
- }
- }
-
- public string Search
- {
- get => _search;
- set
- {
- _search = value;
- OnPropertyChanged();
- Sort();
- }
- }
-
- public ObservableCollection<SaveModel> Saves
- {
- get => _saves;
- set
- {
- _saves = value;
- OnPropertyChanged();
- Sort();
- }
- }
-
- public ObservableCollection<SaveModel> Views
- {
- get => _views;
- set
- {
- _views = value;
- OnPropertyChanged();
- }
- }
-
- public UserSaveManagerViewModel(AccountManager accountManager)
- {
- _accountManager = accountManager;
- }
-
- public void Sort()
- {
- Saves.AsObservableChangeSet()
- .Filter(Filter)
- .Sort(GetComparer())
- .Bind(out var view).AsObservableList();
-
- _views.Clear();
- _views.AddRange(view);
- OnPropertyChanged(nameof(Views));
- }
-
- private bool Filter(object arg)
- {
- if (arg is SaveModel save)
- {
- return string.IsNullOrWhiteSpace(_search) || save.Title.ToLower().Contains(_search.ToLower());
- }
-
- return false;
- }
-
- private IComparer<SaveModel> GetComparer()
- {
- return SortIndex switch
- {
- 0 => OrderIndex == 0
- ? SortExpressionComparer<SaveModel>.Ascending(save => save.Title)
- : SortExpressionComparer<SaveModel>.Descending(save => save.Title),
- 1 => OrderIndex == 0
- ? SortExpressionComparer<SaveModel>.Ascending(save => save.Size)
- : SortExpressionComparer<SaveModel>.Descending(save => save.Size),
- _ => null,
- };
- }
- }
-}