using Avalonia.Controls; using DynamicData; using DynamicData.Binding; using LibHac; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Shim; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Models; using Ryujinx.HLE.FileSystem; using Ryujinx.Ui.App.Common; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Threading.Tasks; using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; namespace Ryujinx.Ava.UI.Controls { public partial class SaveManager : UserControl { private readonly UserProfile _userProfile; private readonly HorizonClient _horizonClient; private readonly VirtualFileSystem _virtualFileSystem; private int _sortIndex; private int _orderIndex; private ObservableCollection _view = new ObservableCollection(); private string _search; public ObservableCollection Saves { get; set; } = new ObservableCollection(); public ObservableCollection View { get => _view; set => _view = value; } public int SortIndex { get => _sortIndex; set { _sortIndex = value; Sort(); } } public int OrderIndex { get => _orderIndex; set { _orderIndex = value; Sort(); } } public string Search { get => _search; set { _search = value; Sort(); } } public SaveManager() { InitializeComponent(); } public SaveManager(UserProfile userProfile, HorizonClient horizonClient, VirtualFileSystem virtualFileSystem) { _userProfile = userProfile; _horizonClient = horizonClient; _virtualFileSystem = virtualFileSystem; InitializeComponent(); DataContext = this; Task.Run(LoadSaves); } public void LoadSaves() { Saves.Clear(); var saveDataFilter = SaveDataFilter.Make(programId: default, saveType: SaveDataType.Account, new UserId((ulong)_userProfile.UserId.High, (ulong)_userProfile.UserId.Low), saveDataId: default, index: default); using var saveDataIterator = new UniqueRef(); _horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure(); Span saveDataInfo = stackalloc SaveDataInfo[10]; while (true) { saveDataIterator.Get.ReadSaveDataInfo(out long readCount, saveDataInfo).ThrowIfFailure(); if (readCount == 0) { break; } for (int i = 0; i < readCount; i++) { var save = saveDataInfo[i]; if (save.ProgramId.Value != 0) { var saveModel = new SaveModel(save, _horizonClient, _virtualFileSystem); Saves.Add(saveModel); saveModel.DeleteAction = () => { Saves.Remove(saveModel); }; } Sort(); } } } private void Sort() { Saves.AsObservableChangeSet() .Filter(Filter) .Sort(GetComparer()) .Bind(out var view).AsObservableList(); _view.Clear(); _view.AddRange(view); } private IComparer GetComparer() { switch (SortIndex) { case 0: return OrderIndex == 0 ? SortExpressionComparer.Ascending(save => save.Title) : SortExpressionComparer.Descending(save => save.Title); case 1: return OrderIndex == 0 ? SortExpressionComparer.Ascending(save => save.Size) : SortExpressionComparer.Descending(save => save.Size); default: return null; } } private bool Filter(object arg) { if (arg is SaveModel save) { return string.IsNullOrWhiteSpace(_search) || save.Title.ToLower().Contains(_search.ToLower()); } return false; } } }