diff options
| author | Mary Guillemard <mary@mary.zone> | 2024-03-02 12:51:05 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-03-02 12:51:05 +0100 |
| commit | ec6cb0abb4b7669895b6e96fd7581c93b5abd691 (patch) | |
| tree | 128c862ff5faea0b219467656d4023bee7faefb5 /src/Ryujinx.Ava/UI/ViewModels | |
| parent | 53b5985da6b9d7b281d9fc25b93bfd1d1918a107 (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')
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, - }; - } - } -} |
