aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Ava/UI/ViewModels
diff options
context:
space:
mode:
authorTSR Berry <20988865+TSRBerry@users.noreply.github.com>2023-04-08 01:22:00 +0200
committerMary <thog@protonmail.com>2023-04-27 23:51:14 +0200
commitcee712105850ac3385cd0091a923438167433f9f (patch)
tree4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /Ryujinx.Ava/UI/ViewModels
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'Ryujinx.Ava/UI/ViewModels')
-rw-r--r--Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs133
-rw-r--r--Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs467
-rw-r--r--Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs363
-rw-r--r--Ryujinx.Ava/UI/ViewModels/BaseModel.cs15
-rw-r--r--Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs899
-rw-r--r--Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs338
-rw-r--r--Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs1907
-rw-r--r--Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs585
-rw-r--r--Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs252
-rw-r--r--Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs230
-rw-r--r--Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs18
-rw-r--r--Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs25
-rw-r--r--Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs120
13 files changed, 0 insertions, 5352 deletions
diff --git a/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs
deleted file mode 100644
index 479411cb..00000000
--- a/Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs
+++ /dev/null
@@ -1,133 +0,0 @@
-using Avalonia;
-using Avalonia.Media.Imaging;
-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;
-
- var assets = AvaloniaLocator.Current.GetService<Avalonia.Platform.IAssetLoader>();
-
- if (ConfigurationState.Instance.Ui.BaseStyle.Value == "Light")
- {
- GithubLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Light.png?assembly=Ryujinx.Ui.Common")));
- DiscordLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Light.png?assembly=Ryujinx.Ui.Common")));
- PatreonLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Light.png?assembly=Ryujinx.Ui.Common")));
- TwitterLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Light.png?assembly=Ryujinx.Ui.Common")));
- }
- else
- {
- GithubLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Dark.png?assembly=Ryujinx.Ui.Common")));
- DiscordLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Dark.png?assembly=Ryujinx.Ui.Common")));
- PatreonLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Dark.png?assembly=Ryujinx.Ui.Common")));
- TwitterLogo = new Bitmap(assets.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];
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs
deleted file mode 100644
index bb92798f..00000000
--- a/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs
+++ /dev/null
@@ -1,467 +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.Threading.Tasks;
-using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext;
-
-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()
- {
- _httpClient.Dispose();
- }
-
- private async Task LoadContentAsync()
- {
- string amiiboJsonString = DefaultJson;
-
- if (File.Exists(_amiiboJsonPath))
- {
- amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath);
-
- if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated))
- {
- amiiboJsonString = await DownloadAmiiboJson();
- }
- }
- else
- {
- try
- {
- amiiboJsonString = await DownloadAmiiboJson();
- }
- catch (Exception ex)
- {
- Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data: {ex}");
-
- ShowInfoDialog();
- }
- }
-
- _amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo;
- _amiiboList = _amiiboList.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.FirstOrDefault(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.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image;
-
- string usageString = "";
-
- 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)
- {
- usageString += Environment.NewLine +
- $"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}";
-
- writable = usageItem.Write;
- }
- }
- }
-
- if (usageString.Length == 0)
- {
- usageString = LocaleManager.Instance[LocaleKeys.Unknown] + ".";
- }
-
- Usage = $"{LocaleManager.Instance[LocaleKeys.Usage]} {(writable ? $" ({LocaleManager.Instance[LocaleKeys.Writable]})" : "")} : {usageString}";
- }
- }
-
- _ = 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;
- }
-
- return false;
- }
- catch (Exception ex)
- {
- Logger.Error?.Print(LogClass.Application, $"Failed to check for amiibo updates: {ex}");
-
- ShowInfoDialog();
-
- return false;
- }
- }
-
- private async Task<string> DownloadAmiiboJson()
- {
- HttpResponseMessage response = await _httpClient.GetAsync("https://amiibo.ryujinx.org/");
-
- if (response.IsSuccessStatusCode)
- {
- string amiiboJsonString = await response.Content.ReadAsStringAsync();
-
- using (FileStream amiiboJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough))
- {
- amiiboJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString));
- }
-
- return amiiboJsonString;
- }
-
- Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}");
-
- await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
- LocaleManager.Instance[LocaleKeys.DialogAmiiboApiFailFetchMessage],
- LocaleManager.Instance[LocaleKeys.InputDialogOk],
- "",
- LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
-
- Close();
-
- return DefaultJson;
- }
-
- 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 async void ShowInfoDialog()
- {
- await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
- LocaleManager.Instance[LocaleKeys.DialogAmiiboApiConnectErrorMessage],
- LocaleManager.Instance[LocaleKeys.InputDialogOk],
- "",
- LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs b/Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs
deleted file mode 100644
index b2b31014..00000000
--- a/Ryujinx.Ava/UI/ViewModels/AvatarProfileViewModel.cs
+++ /dev/null
@@ -1,363 +0,0 @@
-using Avalonia.Media;
-using DynamicData;
-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.Formats.Png;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
-using System;
-using System.Buffers.Binary;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Color = Avalonia.Media.Color;
-
-namespace Ryujinx.Ava.UI.ViewModels
-{
- internal class AvatarProfileViewModel : BaseModel, IDisposable
- {
- private const int MaxImageTasks = 4;
-
- private static readonly Dictionary<string, byte[]> _avatarStore = new();
- private static bool _isPreloading;
- private static Action _loadCompleteAction;
-
- private ObservableCollection<ProfileImageModel> _images;
- private Color _backgroundColor = Colors.White;
-
- private int _selectedIndex;
- private int _imagesLoaded;
- private bool _isActive;
- private byte[] _selectedImage;
- private bool _isIndeterminate = true;
-
- public bool IsActive
- {
- get => _isActive;
- set => _isActive = value;
- }
-
- public AvatarProfileViewModel()
- {
- _images = new ObservableCollection<ProfileImageModel>();
- }
-
- public AvatarProfileViewModel(Action loadCompleteAction)
- {
- _images = new ObservableCollection<ProfileImageModel>();
-
- if (_isPreloading)
- {
- _loadCompleteAction = loadCompleteAction;
- }
- else
- {
- ReloadImages();
- }
- }
-
- public Color BackgroundColor
- {
- get => _backgroundColor;
- set
- {
- _backgroundColor = value;
-
- IsActive = false;
-
- ReloadImages();
- }
- }
-
- public ObservableCollection<ProfileImageModel> Images
- {
- get => _images;
- set
- {
- _images = value;
- OnPropertyChanged();
- }
- }
-
- public bool IsIndeterminate
- {
- get => _isIndeterminate;
- set
- {
- _isIndeterminate = value;
-
- OnPropertyChanged();
- }
- }
-
- public int ImageCount => _avatarStore.Count;
-
- public int ImagesLoaded
- {
- get => _imagesLoaded;
- set
- {
- _imagesLoaded = 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 => _selectedImage;
- private set => _selectedImage = value;
- }
-
- public void ReloadImages()
- {
- if (_isPreloading)
- {
- IsIndeterminate = false;
- return;
- }
- Task.Run(() =>
- {
- IsActive = true;
-
- Images.Clear();
- int selectedIndex = _selectedIndex;
- int index = 0;
-
- ImagesLoaded = 0;
- IsIndeterminate = false;
-
- var keys = _avatarStore.Keys.ToList();
-
- var newImages = new List<ProfileImageModel>();
- var tasks = new List<Task>();
-
- for (int i = 0; i < MaxImageTasks; i++)
- {
- var start = i;
- tasks.Add(Task.Run(() => ImageTask(start)));
- }
-
- Task.WaitAll(tasks.ToArray());
-
- Images.AddRange(newImages);
-
- void ImageTask(int start)
- {
- for (int i = start; i < keys.Count; i += MaxImageTasks)
- {
- if (!IsActive)
- {
- return;
- }
-
- var key = keys[i];
- var image = _avatarStore[keys[i]];
-
- var data = ProcessImage(image);
- newImages.Add(new ProfileImageModel(key, data));
- if (index++ == selectedIndex)
- {
- SelectedImage = data;
- }
-
- Interlocked.Increment(ref _imagesLoaded);
- OnPropertyChanged(nameof(ImagesLoaded));
- }
- }
- });
- }
-
- private byte[] ProcessImage(byte[] data)
- {
- using (MemoryStream streamJpg = new())
- {
- Image avatarImage = Image.Load(data, new PngDecoder());
-
- avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(BackgroundColor.R,
- BackgroundColor.G,
- BackgroundColor.B,
- BackgroundColor.A)));
- avatarImage.SaveAsJpeg(streamJpg);
-
- return streamJpg.ToArray();
- }
- }
-
- public static void PreloadAvatars(ContentManager contentManager, VirtualFileSystem virtualFileSystem)
- {
- try
- {
- if (_avatarStore.Count > 0)
- {
- return;
- }
-
- _isPreloading = true;
-
- 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());
- }
- }
- }
- }
- }
- }
- finally
- {
- _isPreloading = false;
- _loadCompleteAction?.Invoke();
- }
- }
-
- 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;
- }
- }
-
- public void Dispose()
- {
- _loadCompleteAction = null;
- IsActive = false;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/UI/ViewModels/BaseModel.cs b/Ryujinx.Ava/UI/ViewModels/BaseModel.cs
deleted file mode 100644
index 5a3717fd..00000000
--- a/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));
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs
deleted file mode 100644
index dd261b10..00000000
--- a/Ryujinx.Ava/UI/ViewModels/ControllerSettingsViewModel.cs
+++ /dev/null
@@ -1,899 +0,0 @@
-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.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 ControllerSettingsViewModel : 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 = 0;
- private string _controllerImage;
- private int _device;
- private object _configuration;
- private string _profileName;
- private bool _isLoaded;
- private readonly UserControl _owner;
-
- 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 SvgImage();
-
- if (!string.IsNullOrWhiteSpace(_controllerImage))
- {
- SvgSource source = new SvgSource();
-
- 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 ControllerSettingsViewModel(UserControl owner) : this()
- {
- _owner = owner;
-
- if (Program.PreviewerDetached)
- {
- _mainWindow =
- (MainWindow)((IClassicDesktopStyleApplicationLifetime)Avalonia.Application.Current
- .ApplicationLifetime).MainWindow;
-
- AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
-
- _mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
- _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
- if (_mainWindow.ViewModel.AppHost != null)
- {
- _mainWindow.ViewModel.AppHost.NpadManager.BlockInputUpdates();
- }
-
- _isLoaded = false;
-
- LoadDevices();
-
- PlayerId = PlayerIndex.Player1;
- }
- }
-
- public ControllerSettingsViewModel()
- {
- 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 MotionSettingsWindow.Show(this);
- }
-
- public async void ShowRumbleConfig()
- {
- await RumbleSettingsWindow.Show(this);
- }
-
- private void LoadInputDriver()
- {
- if (_device < 0)
- {
- return;
- }
-
- string id = GetCurrentGamepadId();
- var type = Devices[Device].Type;
-
- if (type == DeviceType.None)
- {
- return;
- }
- else 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.Substring(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;
- }
- else
- {
- 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()
- {
- _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
- _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;
-
- _mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates();
-
- SelectedGamepad?.Dispose();
-
- AvaloniaKeyboardDriver.Dispose();
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs b/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs
deleted file mode 100644
index 1d7da9a4..00000000
--- a/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs
+++ /dev/null
@@ -1,338 +0,0 @@
-using Avalonia.Collections;
-using Avalonia.Controls;
-using Avalonia.Controls.ApplicationLifetimes;
-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 Path = System.IO.Path;
-
-namespace Ryujinx.Ava.UI.ViewModels
-{
- public class DownloadableContentManagerViewModel : BaseModel
- {
- private readonly List<DownloadableContentContainer> _downloadableContentContainerList;
- private readonly string _downloadableContentJsonPath;
-
- private VirtualFileSystem _virtualFileSystem;
- private AvaloniaList<DownloadableContentModel> _downloadableContents = new();
- private AvaloniaList<DownloadableContentModel> _views = new();
- private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new();
-
- private string _search;
- private ulong _titleId;
- private string _titleName;
-
- 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, string titleName)
- {
- _virtualFileSystem = virtualFileSystem;
-
- _titleId = titleId;
- _titleName = titleName;
-
- _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(containerFile.AsStorage());
-
- _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.DialogLoadNcaErrorMessage], ex.Message, containerPath));
- });
- }
-
- return null;
- }
-
- public async void Add()
- {
- OpenFileDialog dialog = new OpenFileDialog()
- {
- Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle],
- AllowMultiple = true
- };
-
- dialog.Filters.Add(new FileDialogFilter
- {
- Name = "NSP",
- Extensions = { "nsp" }
- });
-
- if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- string[] files = await dialog.ShowAsync(desktop.MainWindow);
-
- if (files != null)
- {
- foreach (string file in files)
- {
- await AddDownloadableContent(file);
- }
- }
- }
- }
-
- 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(containerFile.AsStorage());
- 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);
- }
-
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
deleted file mode 100644
index 14d7a0fe..00000000
--- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
+++ /dev/null
@@ -1,1907 +0,0 @@
-using Avalonia.Controls;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Input;
-using Avalonia.Media;
-using Avalonia.Threading;
-using DynamicData;
-using DynamicData.Binding;
-using LibHac.Common;
-using LibHac.Fs;
-using LibHac.Tools.FsSystem.NcaUtils;
-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.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.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.Globalization;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using Path = System.IO.Path;
-using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
-using UserId = LibHac.Fs.UserId;
-
-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 string _backendText;
-
- private bool _canUpdate = true;
- private Cursor _cursor;
- private string _title;
- private string _currentEmulatedGamePath;
- private AutoResetEvent _rendererWaitEvent;
- private WindowState _windowState;
- private bool _isActive;
-
- public ApplicationData ListSelectedApplication;
- public ApplicationData GridSelectedApplication;
-
- public event Action ReloadGameList;
-
- 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,
- ApplicationLibrary applicationLibrary,
- VirtualFileSystem virtualFileSystem,
- AccountManager accountManager,
- Ryujinx.Input.HLE.InputManager inputManager,
- UserChannelPersistence userChannelPersistence,
- LibHacHorizonManager libHacHorizonManager,
- IHostUiHandler uiHandler,
- Action<bool> showLoading,
- Action<bool> switchToGameControl,
- Action<Control> setMainContent,
- TopLevel topLevel)
- {
- ContentManager = contentManager;
- 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 && Modules.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(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 EnabledUserSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0;
-
- public bool EnabledDeviceSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;
-
- public bool EnabledBcatSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
-
- 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 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 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 ApplicationLibrary ApplicationLibrary { get; private set; }
- public VirtualFileSystem VirtualFileSystem { get; private set; }
- public AccountManager AccountManager { get; private set; }
- public Ryujinx.Input.HLE.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
- {
- ApplicationSort.LastPlayed => new Models.Generic.LastPlayedSortComparer(IsAscending),
- ApplicationSort.FileSize => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileSizeBytes)
- : SortExpressionComparer<ApplicationData>.Descending(app => app.FileSizeBytes),
- ApplicationSort.TotalTimePlayed => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TimePlayedNum)
- : SortExpressionComparer<ApplicationData>.Descending(app => app.TimePlayedNum),
- ApplicationSort.Title => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName)
- : SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName),
- ApplicationSort.Favorite => !IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Favorite)
- : SortExpressionComparer<ApplicationData>.Descending(app => app.Favorite),
- ApplicationSort.Developer => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Developer)
- : SortExpressionComparer<ApplicationData>.Descending(app => app.Developer),
- ApplicationSort.FileType => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileExtension)
- : SortExpressionComparer<ApplicationData>.Descending(app => app.FileExtension),
- ApplicationSort.Path => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Path)
- : SortExpressionComparer<ApplicationData>.Descending(app => app.Path),
- _ => null,
- };
- }
-
- private 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 DirectoryInfo(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 (LibHac.Common.Keys.MissingKeyException ex)
- {
- if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- Logger.Error?.Print(LogClass.Application, ex.ToString());
-
- async void Action() => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, (desktop.MainWindow as MainWindow));
-
- Dispatcher.UIThread.Post(Action);
- }
- }
- 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.Loaded:
- LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
- IsLoadingIndeterminate = true;
- CacheLoadStatus = "";
- break;
- }
- break;
- default:
- throw new ArgumentException($"Unknown Progress Handler type {typeof(T)}");
- }
- }));
- }
-
- private async void ExtractLogo()
- {
- if (SelectedApplication != null)
- {
- await ApplicationHelper.ExtractSection(NcaSectionType.Logo, SelectedApplication.Path, SelectedApplication.TitleName);
- }
- }
-
- private async void ExtractRomFs()
- {
- if (SelectedApplication != null)
- {
- await ApplicationHelper.ExtractSection(NcaSectionType.Data, SelectedApplication.Path, SelectedApplication.TitleName);
- }
- }
-
- private async void ExtractExeFs()
- {
- if (SelectedApplication != null)
- {
- await ApplicationHelper.ExtractSection(NcaSectionType.Code, SelectedApplication.Path, SelectedApplication.TitleName);
- }
- }
-
- private void PrepareLoadScreen()
- {
- using MemoryStream stream = new(SelectedIcon);
- using var gameIconBmp = SixLabors.ImageSharp.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 void HandleRelaunch()
- {
- if (UserChannelPersistence.PreviousIndex != -1 && UserChannelPersistence.ShouldRestart)
- {
- UserChannelPersistence.ShouldRestart = false;
-
- Dispatcher.UIThread.Post(() =>
- {
- 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(() =>
- {
- Avalonia.Application.Current.Styles.TryGetResource(args.VSyncEnabled
- ? "VsyncEnabled"
- : "VsyncDisabled", 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((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi, out var showUiKey))
- {
- ShowUiKey = new KeyGesture(showUiKey);
- }
-
- if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey))
- {
- ScreenshotKey = new KeyGesture(screenshotKey);
- }
-
- if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.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 SetListMode()
- {
- Glyph = Glyph.List;
- }
-
- public void SetGridMode()
- {
- Glyph = Glyph.Grid;
- }
-
- public async void InstallFirmwareFromFile()
- {
- if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- OpenFileDialog dialog = new() { AllowMultiple = false };
- dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance[LocaleKeys.FileDialogAllTypes], Extensions = { "xci", "zip" } });
- dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } });
- dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } });
-
- string[] file = await dialog.ShowAsync(desktop.MainWindow);
-
- if (file != null && file.Length > 0)
- {
- await HandleFirmwareInstallation(file[0]);
- }
- }
- }
-
- public async void InstallFirmwareFromFolder()
- {
- if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- OpenFolderDialog dialog = new();
-
- string folder = await dialog.ShowAsync(desktop.MainWindow);
-
- if (!string.IsNullOrEmpty(folder))
- {
- await HandleFirmwareInstallation(folder);
- }
- }
- }
-
- public static void OpenRyujinxFolder()
- {
- OpenHelper.OpenFolder(AppDataManager.BaseDirPath);
- }
-
- public static void OpenLogsFolder()
- {
- string logPath = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "Logs");
-
- new DirectoryInfo(logPath).Create();
-
- OpenHelper.OpenFolder(logPath);
- }
-
- public void ToggleDockMode()
- {
- if (IsGameRunning)
- {
- ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
- }
- }
-
- public async void ExitCurrentState()
- {
- if (WindowState == WindowState.FullScreen)
- {
- ToggleFullscreen();
- }
- else if (IsGameRunning)
- {
- await Task.Delay(100);
-
- AppHost?.ShowExitPrompt();
- }
- }
-
- public 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 void ToggleFileType(string fileType)
- {
- _ = fileType switch
- {
- "NSP" => ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSP,
- "PFS0" => ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.PFS0,
- "XCI" => ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.XCI,
- "NCA" => ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NCA,
- "NRO" => ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NRO,
- "NSO" => ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSO,
- _ => throw new ArgumentOutOfRangeException(fileType),
- };
-
- ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
- LoadApplications();
- }
-
- public async void ManageProfiles()
- {
- await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient);
- }
-
- public void OpenPtcDirectory()
- {
- ApplicationData selection = SelectedApplication;
- if (selection != null)
- {
- string ptcDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu");
- string mainPath = Path.Combine(ptcDir, "0");
- string backupPath = Path.Combine(ptcDir, "1");
-
- if (!Directory.Exists(ptcDir))
- {
- Directory.CreateDirectory(ptcDir);
- Directory.CreateDirectory(mainPath);
- Directory.CreateDirectory(backupPath);
- }
-
- OpenHelper.OpenFolder(ptcDir);
- }
- }
-
- public async void PurgePtcCache()
- {
- ApplicationData selection = SelectedApplication;
- if (selection != null)
- {
- DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "0"));
- DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "1"));
-
- // FIXME: Found a way to reproduce the bold effect on the title name (fork?).
- UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
- LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, selection.TitleName),
- LocaleManager.Instance[LocaleKeys.InputDialogYes],
- LocaleManager.Instance[LocaleKeys.InputDialogNo],
- LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
-
- List<FileInfo> cacheFiles = new();
-
- if (mainDir.Exists)
- {
- cacheFiles.AddRange(mainDir.EnumerateFiles("*.cache"));
- }
-
- if (backupDir.Exists)
- {
- cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache"));
- }
-
- if (cacheFiles.Count > 0 && result == UserResult.Yes)
- {
- foreach (FileInfo file in cacheFiles)
- {
- try
- {
- file.Delete();
- }
- catch (Exception e)
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, file.Name, e));
- }
- }
- }
- }
- }
-
- public void OpenShaderCacheDirectory()
- {
- ApplicationData selection = SelectedApplication;
- if (selection != null)
- {
- string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader");
-
- if (!Directory.Exists(shaderCacheDir))
- {
- Directory.CreateDirectory(shaderCacheDir);
- }
-
- OpenHelper.OpenFolder(shaderCacheDir);
- }
- }
-
- public void SimulateWakeUpMessage()
- {
- AppHost.Device.System.SimulateWakeUpMessage();
- }
-
- public async void PurgeShaderCache()
- {
- ApplicationData selection = SelectedApplication;
- if (selection != null)
- {
- DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader"));
-
- // FIXME: Found a way to reproduce the bold effect on the title name (fork?).
- UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
- LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, selection.TitleName),
- LocaleManager.Instance[LocaleKeys.InputDialogYes],
- LocaleManager.Instance[LocaleKeys.InputDialogNo],
- LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
-
- List<DirectoryInfo> oldCacheDirectories = new();
- List<FileInfo> newCacheFiles = new();
-
- if (shaderCacheDir.Exists)
- {
- oldCacheDirectories.AddRange(shaderCacheDir.EnumerateDirectories("*"));
- newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.toc"));
- newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.data"));
- }
-
- if ((oldCacheDirectories.Count > 0 || newCacheFiles.Count > 0) && result == UserResult.Yes)
- {
- foreach (DirectoryInfo directory in oldCacheDirectories)
- {
- try
- {
- directory.Delete(true);
- }
- catch (Exception e)
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, directory.Name, e));
- }
- }
- }
-
- foreach (FileInfo file in newCacheFiles)
- {
- try
- {
- file.Delete();
- }
- catch (Exception e)
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.ShaderCachePurgeError, file.Name, e));
- }
- }
- }
- }
-
- public void ToggleFavorite()
- {
- ApplicationData selection = SelectedApplication;
- if (selection != null)
- {
- selection.Favorite = !selection.Favorite;
-
- ApplicationLibrary.LoadAndSaveMetaData(selection.TitleId, appMetadata =>
- {
- appMetadata.Favorite = selection.Favorite;
- });
-
- RefreshView();
- }
- }
-
- public void OpenUserSaveDirectory()
- {
- OpenSaveDirectory(SaveDataType.Account, userId: new UserId((ulong)AccountManager.LastOpenedUser.UserId.High, (ulong)AccountManager.LastOpenedUser.UserId.Low));
- }
-
- public void OpenDeviceSaveDirectory()
- {
- OpenSaveDirectory(SaveDataType.Device, userId: default);
- }
-
- public void OpenBcatSaveDirectory()
- {
- OpenSaveDirectory(SaveDataType.Bcat, userId: default);
- }
-
- private void OpenSaveDirectory(SaveDataType saveDataType, UserId userId)
- {
- if (SelectedApplication != null)
- {
- if (!ulong.TryParse(SelectedApplication.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
- {
- Dispatcher.UIThread.InvokeAsync(async () =>
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
- });
-
- return;
- }
-
- var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveDataType, userId, saveDataId: default, index: default);
-
- ApplicationHelper.OpenSaveDir(in saveDataFilter, titleIdNumber, SelectedApplication.ControlHolder, SelectedApplication.TitleName);
- }
- }
-
- public void OpenModsDirectory()
- {
- if (SelectedApplication != null)
- {
- string modsBasePath = VirtualFileSystem.ModLoader.GetModsBasePath();
- string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, SelectedApplication.TitleId);
-
- OpenHelper.OpenFolder(titleModsPath);
- }
- }
-
- public void OpenSdModsDirectory()
- {
- if (SelectedApplication != null)
- {
- string sdModsBasePath = VirtualFileSystem.ModLoader.GetSdModsBasePath();
- string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, SelectedApplication.TitleId);
-
- OpenHelper.OpenFolder(titleModsPath);
- }
- }
-
- public async void OpenTitleUpdateManager()
- {
- if (SelectedApplication != null)
- {
- await TitleUpdateWindow.Show(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName);
- }
- }
-
- public async void OpenDownloadableContentManager()
- {
- if (SelectedApplication != null)
- {
- await DownloadableContentManagerWindow.Show(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName);
- }
- }
-
- public async void OpenCheatManager()
- {
- if (SelectedApplication != null)
- {
- await new CheatWindow(VirtualFileSystem, SelectedApplication.TitleId, SelectedApplication.TitleName).ShowDialog(TopLevel as Window);
- }
- }
-
- public async void LoadApplications()
- {
- await Dispatcher.UIThread.InvokeAsync(() =>
- {
- Applications.Clear();
-
- StatusBarVisible = true;
- StatusBarProgressMaximum = 0;
- StatusBarProgressValue = 0;
-
- LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
- });
-
- ReloadGameList?.Invoke();
- }
-
- public async void OpenFile()
- {
- if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- OpenFileDialog dialog = new()
- {
- Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle]
- };
-
- dialog.Filters.Add(new FileDialogFilter
- {
- Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats],
- Extensions =
- {
- "nsp",
- "pfs0",
- "xci",
- "nca",
- "nro",
- "nso"
- }
- });
-
- dialog.Filters.Add(new FileDialogFilter { Name = "NSP", Extensions = { "nsp" } });
- dialog.Filters.Add(new FileDialogFilter { Name = "PFS0", Extensions = { "pfs0" } });
- dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } });
- dialog.Filters.Add(new FileDialogFilter { Name = "NCA", Extensions = { "nca" } });
- dialog.Filters.Add(new FileDialogFilter { Name = "NRO", Extensions = { "nro" } });
- dialog.Filters.Add(new FileDialogFilter { Name = "NSO", Extensions = { "nso" } });
-
- string[] files = await dialog.ShowAsync(desktop.MainWindow);
-
- if (files != null && files.Length > 0)
- {
- LoadApplication(files[0]);
- }
- }
- }
-
- public async void OpenFolder()
- {
- if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- OpenFolderDialog dialog = new()
- {
- Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle]
- };
-
- string folder = await dialog.ShowAsync(desktop.MainWindow);
-
- if (!string.IsNullOrWhiteSpace(folder) && Directory.Exists(folder))
- {
- LoadApplication(folder);
- }
- }
- }
-
- public async void 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();
-
- if (SelectedIcon == null)
- {
- SelectedIcon = ApplicationLibrary.GetApplicationIcon(path);
- }
-
- PrepareLoadScreen();
-
- RendererHostControl = new RendererHost();
-
- AppHost = new AppHost(
- RendererHostControl,
- InputManager,
- path,
- VirtualFileSystem,
- ContentManager,
- AccountManager,
- UserChannelPersistence,
- this,
- TopLevel);
-
- async void Action()
- {
- 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();
- }
-
- Dispatcher.UIThread.Post(Action);
- }
-
- public void SwitchToRenderer(bool startFullscreen)
- {
- Dispatcher.UIThread.Post(() =>
- {
- SwitchToGameControl(startFullscreen);
-
- SetMainContent(RendererHostControl);
-
- RendererHostControl.Focus();
- });
- }
-
- public void UpdateGameMetadata(string titleId)
- {
- ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
- {
- if (DateTime.TryParse(appMetadata.LastPlayed, out DateTime lastPlayedDateTime))
- {
- double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds;
-
- appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
- }
- });
- }
-
- 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(() =>
- {
- ShowMenuAndStatusBar = true;
- ShowContent = true;
- ShowLoadProgress = false;
- IsLoadingIndeterminate = false;
- CanUpdate = true;
- Cursor = Cursor.Default;
-
- SetMainContent(null);
-
- AppHost = null;
-
- 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
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs
deleted file mode 100644
index 232c9d43..00000000
--- a/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs
+++ /dev/null
@@ -1,585 +0,0 @@
-using Avalonia.Collections;
-using Avalonia.Controls;
-using Avalonia.Threading;
-using DynamicData;
-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.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.Runtime.InteropServices;
-using System.Net.NetworkInformation;
-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 List<string> _gpuIds = new();
- private KeyboardHotkeys _keyboardHotkeys;
- private int _graphicsBackendIndex;
- private string _customThemePath;
- private int _scalingFilter;
- private int _scalingFilterLevel;
-
- public event Action CloseWindow;
- public event Action SaveSettingsEvent;
- private int _networkInterfaceIndex;
-
- 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.Post(async () =>
- {
- await 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 bool HideCursorOnIdle { 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 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 EnableCustomTheme { 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 string CustomThemePath
- {
- get
- {
- return _customThemePath;
- }
- set
- {
- _customThemePath = value;
-
- OnPropertyChanged();
- }
- }
-
- 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 DateOffset { get; set; }
- public TimeSpan TimeOffset { 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 AvaloniaList<string>(_networkInterfaces.Keys);
- }
-
- 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 SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
- {
- _virtualFileSystem = virtualFileSystem;
- _contentManager = contentManager;
- if (Program.PreviewerDetached)
- {
- LoadTimeZones();
- }
- }
-
- public SettingsViewModel()
- {
- GameDirectories = new AvaloniaList<string>();
- TimeZones = new AvaloniaList<TimeZone>();
- AvailableGpus = new ObservableCollection<ComboBoxItem>();
- _validTzRegions = new List<string>();
- _networkInterfaces = new Dictionary<string, string>();
-
- CheckSoundBackends();
- PopulateNetworkInterfaces();
-
- if (Program.PreviewerDetached)
- {
- LoadAvailableGpus();
- LoadCurrentConfiguration();
- }
- }
-
- public void CheckSoundBackends()
- {
- IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported;
- IsSoundIoEnabled = SoundIoHardwareDeviceDriver.IsSupported;
- IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported;
- }
-
- private void LoadAvailableGpus()
- {
- _gpuIds = new List<string>();
- List<string> names = new();
- var devices = VulkanRenderer.GetPhysicalDevices();
-
- if (devices.Length == 0)
- {
- IsVulkanAvailable = false;
- GraphicsBackendIndex = 1;
- }
- else
- {
- foreach (var device in devices)
- {
- _gpuIds.Add(device.Id);
- names.Add($"{device.Name} {(device.IsDiscrete ? "(dGPU)" : "")}");
- }
- }
-
- AvailableGpus.Clear();
- AvailableGpus.AddRange(names.Select(x => new ComboBoxItem { Content = x }));
- }
-
- public void 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;
-
- TimeZones.Add(new TimeZone($"UTC{hours:+0#;-0#;+00}:{minutes:D2}", location, abbr2));
-
- _validTzRegions.Add(location);
- }
- }
-
- private void PopulateNetworkInterfaces()
- {
- _networkInterfaces.Clear();
- _networkInterfaces.Add(LocaleManager.Instance[LocaleKeys.NetworkInterfaceDefault], "0");
-
- foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces())
- {
- _networkInterfaces.Add(networkInterface.Name, networkInterface.Id);
- }
- }
-
- 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;
- HideCursorOnIdle = config.HideCursorOnIdle;
-
- GameDirectories.Clear();
- GameDirectories.AddRange(config.Ui.GameDirs.Value);
-
- EnableCustomTheme = config.Ui.EnableCustomTheme;
- CustomThemePath = config.Ui.CustomThemePath;
- 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 dateTimeOffset = DateTime.Now.AddSeconds(config.System.SystemTimeOffset);
-
- DateOffset = dateTimeOffset.Date;
- TimeOffset = dateTimeOffset.TimeOfDay;
- 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;
- PreferredGpuIndex = _gpuIds.Contains(config.Graphics.PreferredGpu) ? _gpuIds.IndexOf(config.Graphics.PreferredGpu) : 0;
- EnableShaderCache = config.Graphics.EnableShaderCache;
- EnableTextureRecompression = config.Graphics.EnableTextureRecompression;
- EnableMacroHLE = config.Graphics.EnableMacroHLE;
- 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;
-
- // 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;
-
- NetworkInterfaceIndex = _networkInterfaces.Values.ToList().IndexOf(config.Multiplayer.LanInterfaceId.Value);
- }
-
- public void SaveSettings()
- {
- ConfigurationState config = ConfigurationState.Instance;
-
- // User Interface
- config.EnableDiscordIntegration.Value = EnableDiscordIntegration;
- config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart;
- config.ShowConfirmExit.Value = ShowConfirmExit;
- config.HideCursorOnIdle.Value = HideCursorOnIdle;
-
- if (_directoryChanged)
- {
- List<string> gameDirs = new(GameDirectories);
- config.Ui.GameDirs.Value = gameDirs;
- }
-
- config.Ui.EnableCustomTheme.Value = EnableCustomTheme;
- config.Ui.CustomThemePath.Value = CustomThemePath;
- 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;
- }
-
- TimeSpan systemTimeOffset = DateOffset - DateTime.Now;
-
- config.System.SystemTimeOffset.Value = systemTimeOffset.Seconds;
- 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.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.ToFileFormat().SaveConfig(Program.ConfigurationPath);
-
- MainWindow.UpdateGraphicsConfig();
-
- SaveSettingsEvent?.Invoke();
-
- _directoryChanged = false;
- }
-
- public void RevertIfNotSaved()
- {
- Program.ReloadConfig();
- }
-
- public void ApplyButton()
- {
- SaveSettings();
- }
-
- public void OkButton()
- {
- SaveSettings();
- CloseWindow?.Invoke();
- }
-
- public void CancelButton()
- {
- RevertIfNotSaved();
- CloseWindow?.Invoke();
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs
deleted file mode 100644
index 1f4e3c62..00000000
--- a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs
+++ /dev/null
@@ -1,252 +0,0 @@
-using Avalonia.Collections;
-using Avalonia.Controls;
-using Avalonia.Controls.ApplicationLifetimes;
-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.HLE.HOS;
-using Ryujinx.Ui.App.Common;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-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 string _titleName { 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 TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
- {
- _virtualFileSystem = virtualFileSystem;
-
- _titleId = titleId;
- _titleName = titleName;
-
- _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;
- }
- else 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
- {
- (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _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.Post(async () =>
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
- });
- }
- }
- catch (Exception ex)
- {
- Dispatcher.UIThread.Post(async () =>
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
- });
- }
- }
- }
-
- public void RemoveUpdate(TitleUpdateModel update)
- {
- TitleUpdates.Remove(update);
-
- SortUpdates();
- }
-
- public async void Add()
- {
- OpenFileDialog dialog = new()
- {
- Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
- AllowMultiple = true
- };
-
- dialog.Filters.Add(new FileDialogFilter
- {
- Name = "NSP",
- Extensions = { "nsp" }
- });
-
- if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- string[] files = await dialog.ShowAsync(desktop.MainWindow);
-
- if (files != null)
- {
- foreach (string file in files)
- {
- AddUpdate(file);
- }
- }
- }
-
- 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);
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs b/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs
deleted file mode 100644
index 558cad5a..00000000
--- a/Ryujinx.Ava/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs
+++ /dev/null
@@ -1,230 +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;
- private byte[] _selectedImage;
-
- 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 => _selectedImage;
- private set => _selectedImage = value;
- }
-
- 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;
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs b/Ryujinx.Ava/UI/ViewModels/UserProfileImageSelectorViewModel.cs
deleted file mode 100644
index 7261631c..00000000
--- a/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();
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs b/Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs
deleted file mode 100644
index 8f997efc..00000000
--- a/Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Microsoft.IdentityModel.Tokens;
-using System;
-using System.Collections.ObjectModel;
-using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
-
-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() { }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs b/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs
deleted file mode 100644
index 097634a8..00000000
--- a/Ryujinx.Ava/UI/ViewModels/UserSaveManagerViewModel.cs
+++ /dev/null
@@ -1,120 +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 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()
- {
- switch (SortIndex)
- {
- case 0:
- return OrderIndex == 0
- ? SortExpressionComparer<SaveModel>.Ascending(save => save.Title)
- : SortExpressionComparer<SaveModel>.Descending(save => save.Title);
- case 1:
- return OrderIndex == 0
- ? SortExpressionComparer<SaveModel>.Ascending(save => save.Size)
- : SortExpressionComparer<SaveModel>.Descending(save => save.Size);
- default:
- return null;
- }
- }
- }
-} \ No newline at end of file