aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.Ava/Assets/Locales/en_US.json4
-rw-r--r--Ryujinx.Ava/Program.cs42
-rw-r--r--Ryujinx.Ava/Ui/Models/DownloadableContentModel.cs20
-rw-r--r--Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs288
-rw-r--r--Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml145
-rw-r--r--Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs151
-rw-r--r--Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs23
-rw-r--r--Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml2
-rw-r--r--Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs62
-rw-r--r--Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml.cs7
10 files changed, 408 insertions, 336 deletions
diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json
index 5e3ddb8a..e98988b7 100644
--- a/Ryujinx.Ava/Assets/Locales/en_US.json
+++ b/Ryujinx.Ava/Assets/Locales/en_US.json
@@ -410,6 +410,8 @@
"DlcManagerTableHeadingContainerPathLabel": "Container Path",
"DlcManagerTableHeadingFullPathLabel": "Full Path",
"DlcManagerRemoveAllButton": "Remove All",
+ "DlcManagerEnableAllButton": "Enable All",
+ "DlcManagerDisableAllButton": "Disable All",
"MenuBarOptionsChangeLanguage": "Change Language",
"CommonSort": "Sort",
"CommonShowNames": "Show Names",
@@ -567,7 +569,7 @@
"DlcWindowTitle": "Manage Game DLC",
"UpdateWindowTitle": "Manage Game Updates",
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
- "DlcWindowHeading": "DLC Available for {0} [{1}]",
+ "DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})",
"UserProfilesEditProfile": "Edit Selected",
"Cancel": "Cancel",
"Save": "Save",
diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs
index 053bccab..a941302b 100644
--- a/Ryujinx.Ava/Program.cs
+++ b/Ryujinx.Ava/Program.cs
@@ -23,19 +23,18 @@ namespace Ryujinx.Ava
{
internal class Program
{
- public static double WindowScaleFactor { get; set; }
- public static double ActualScaleFactor { get; set; }
- public static string Version { get; private set; }
- public static string ConfigurationPath { get; private set; }
- public static bool PreviewerDetached { get; private set; }
-
- public static RenderTimer RenderTimer { get; private set; }
+ public static double WindowScaleFactor { get; set; }
+ public static double ActualScaleFactor { get; set; }
+ public static string Version { get; private set; }
+ public static string ConfigurationPath { get; private set; }
+ public static bool PreviewerDetached { get; private set; }
+ public static RenderTimer RenderTimer { get; private set; }
[DllImport("user32.dll", SetLastError = true)]
public static extern int MessageBoxA(IntPtr hWnd, string text, string caption, uint type);
private const uint MB_ICONWARNING = 0x30;
- private const int BaseDpi = 96;
+ private const int BaseDpi = 96;
public static void Main(string[] args)
{
@@ -43,7 +42,7 @@ namespace Ryujinx.Ava
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
{
- MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MB_ICONWARNING);
+ _ = MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MB_ICONWARNING);
}
PreviewerDetached = true;
@@ -64,16 +63,16 @@ namespace Ryujinx.Ava
.With(new X11PlatformOptions
{
EnableMultiTouch = true,
- EnableIme = true,
- UseEGL = false,
- UseGpu = false
+ EnableIme = true,
+ UseEGL = false,
+ UseGpu = false
})
.With(new Win32PlatformOptions
{
- EnableMultitouch = true,
- UseWgl = false,
- AllowEglInitialization = false,
- CompositionBackdropCornerRadius = 8f,
+ EnableMultitouch = true,
+ UseWgl = false,
+ AllowEglInitialization = false,
+ CompositionBackdropCornerRadius = 8.0f,
})
.UseSkia()
.AfterSetup(_ =>
@@ -122,12 +121,10 @@ namespace Ryujinx.Ava
PrintSystemInfo();
// Enable OGL multithreading on the driver, when available.
- BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
- DriverUtilities.ToggleOGLThreading(threadingMode == BackendThreading.Off);
+ DriverUtilities.ToggleOGLThreading(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
// Check if keys exists.
- bool hasSystemProdKeys = File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"));
- if (!hasSystemProdKeys)
+ if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))
{
if (!(AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys"))))
{
@@ -143,7 +140,7 @@ namespace Ryujinx.Ava
public static void ReloadConfig()
{
- string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
+ string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json");
// Now load the configuration as the other subsystems are now registered
@@ -197,8 +194,7 @@ namespace Ryujinx.Ava
Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}");
SystemInfo.Gather().Print();
- var enabledLogs = Logger.GetEnabledLevels();
- Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(enabledLogs.Count == 0 ? "<None>" : string.Join(", ", enabledLogs))}");
+ Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(Logger.GetEnabledLevels().Count == 0 ? "<None>" : string.Join(", ", Logger.GetEnabledLevels()))}");
if (AppDataManager.Mode == AppDataManager.LaunchMode.Custom)
{
diff --git a/Ryujinx.Ava/Ui/Models/DownloadableContentModel.cs b/Ryujinx.Ava/Ui/Models/DownloadableContentModel.cs
index 67530f62..5f3ca031 100644
--- a/Ryujinx.Ava/Ui/Models/DownloadableContentModel.cs
+++ b/Ryujinx.Ava/Ui/Models/DownloadableContentModel.cs
@@ -1,8 +1,22 @@
-namespace Ryujinx.Ava.Ui.Models
+using Ryujinx.Ava.Ui.ViewModels;
+
+namespace Ryujinx.Ava.Ui.Models
{
- public class DownloadableContentModel
+ public class DownloadableContentModel : BaseModel
{
- public bool Enabled { get; set; }
+ private bool _enabled;
+
+ public bool Enabled
+ {
+ get => _enabled;
+ set
+ {
+ _enabled = value;
+
+ OnPropertyChanged();
+ }
+ }
+
public string TitleId { get; }
public string ContainerPath { get; }
public string FullPath { get; }
diff --git a/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs
index 3672f963..d5d3b760 100644
--- a/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs
+++ b/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs
@@ -19,6 +19,7 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem;
+using Ryujinx.HLE.HOS;
using Ryujinx.Modules;
using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common;
@@ -47,6 +48,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
private string _loadHeading;
private string _cacheLoadStatus;
private string _searchText;
+ private Timer _searchTimer;
private string _dockedStatusText;
private string _fifoStatusText;
private string _gameStatusText;
@@ -115,10 +117,20 @@ namespace Ryujinx.Ava.Ui.ViewModels
{
_searchText = value;
- RefreshView();
+ _searchTimer?.Dispose();
+
+ _searchTimer = new Timer(TimerCallback, null, 1000, 0);
}
}
+ private void TimerCallback(object obj)
+ {
+ RefreshView();
+
+ _searchTimer.Dispose();
+ _searchTimer = null;
+ }
+
public ReadOnlyObservableCollection<ApplicationData> AppsObservableList
{
get => _appsObservableList;
@@ -200,22 +212,19 @@ namespace Ryujinx.Ava.Ui.ViewModels
private string _showUikey = "F4";
private string _pauseKey = "F5";
private string _screenshotkey = "F8";
- private float _volume;
+ private float _volume;
private string _backendText;
public ApplicationData SelectedApplication
{
get
{
- switch (Glyph)
+ return Glyph switch
{
- case Glyph.List:
- return _owner.GameList.SelectedApplication;
- case Glyph.Grid:
- return _owner.GameGrid.SelectedApplication;
- default:
- return null;
- }
+ Glyph.List => _owner.GameList.SelectedApplication,
+ Glyph.Grid => _owner.GameGrid.SelectedApplication,
+ _ => null,
+ };
}
}
@@ -408,6 +417,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
{
_owner.AppHost.Device.SetVolume(_volume);
}
+
OnPropertyChanged(nameof(VolumeStatusText));
OnPropertyChanged(nameof(VolumeMuted));
OnPropertyChanged();
@@ -477,38 +487,36 @@ namespace Ryujinx.Ava.Ui.ViewModels
internal void Sort(bool isAscending)
{
IsAscending = isAscending;
+
RefreshView();
}
internal void Sort(ApplicationSort sort)
{
SortMode = sort;
+
RefreshView();
}
private IComparer<ApplicationData> GetComparer()
{
- switch (SortMode)
- {
- case ApplicationSort.LastPlayed:
- return new Models.Generic.LastPlayedSortComparer(IsAscending);
- case ApplicationSort.FileSize:
- return new Models.Generic.FileSizeSortComparer(IsAscending);
- case ApplicationSort.TotalTimePlayed:
- return new Models.Generic.TimePlayedSortComparer(IsAscending);
- case ApplicationSort.Title:
- return IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName) : SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName);
- case ApplicationSort.Favorite:
- return !IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Favorite) : SortExpressionComparer<ApplicationData>.Descending(app => app.Favorite);
- case ApplicationSort.Developer:
- return IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Developer) : SortExpressionComparer<ApplicationData>.Descending(app => app.Developer);
- case ApplicationSort.FileType:
- return IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileExtension) : SortExpressionComparer<ApplicationData>.Descending(app => app.FileExtension);
- case ApplicationSort.Path:
- return IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Path) : SortExpressionComparer<ApplicationData>.Descending(app => app.Path);
- default:
- return null;
- }
+ return SortMode switch
+ {
+ ApplicationSort.LastPlayed => new Models.Generic.LastPlayedSortComparer(IsAscending),
+ ApplicationSort.FileSize => new Models.Generic.FileSizeSortComparer(IsAscending),
+ ApplicationSort.TotalTimePlayed => new Models.Generic.TimePlayedSortComparer(IsAscending),
+ 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()
@@ -611,40 +619,31 @@ namespace Ryujinx.Ava.Ui.ViewModels
}
}
- public bool IsSortedByFavorite => SortMode == ApplicationSort.Favorite;
- public bool IsSortedByTitle => SortMode == ApplicationSort.Title;
- public bool IsSortedByDeveloper => SortMode == ApplicationSort.Developer;
+ 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 IsSortedByType => SortMode == ApplicationSort.FileType;
+ public bool IsSortedBySize => SortMode == ApplicationSort.FileSize;
+ public bool IsSortedByPath => SortMode == ApplicationSort.Path;
public string SortName
{
get
{
- switch (SortMode)
+ return SortMode switch
{
- case ApplicationSort.Title:
- return LocaleManager.Instance["GameListHeaderApplication"];
- case ApplicationSort.Developer:
- return LocaleManager.Instance["GameListHeaderDeveloper"];
- case ApplicationSort.LastPlayed:
- return LocaleManager.Instance["GameListHeaderLastPlayed"];
- case ApplicationSort.TotalTimePlayed:
- return LocaleManager.Instance["GameListHeaderTimePlayed"];
- case ApplicationSort.FileType:
- return LocaleManager.Instance["GameListHeaderFileExtension"];
- case ApplicationSort.FileSize:
- return LocaleManager.Instance["GameListHeaderFileSize"];
- case ApplicationSort.Path:
- return LocaleManager.Instance["GameListHeaderPath"];
- case ApplicationSort.Favorite:
- return LocaleManager.Instance["CommonFavorite"];
- }
-
- return string.Empty;
+ ApplicationSort.Title => LocaleManager.Instance["GameListHeaderApplication"],
+ ApplicationSort.Developer => LocaleManager.Instance["GameListHeaderDeveloper"],
+ ApplicationSort.LastPlayed => LocaleManager.Instance["GameListHeaderLastPlayed"],
+ ApplicationSort.TotalTimePlayed => LocaleManager.Instance["GameListHeaderTimePlayed"],
+ ApplicationSort.FileType => LocaleManager.Instance["GameListHeaderFileExtension"],
+ ApplicationSort.FileSize => LocaleManager.Instance["GameListHeaderFileSize"],
+ ApplicationSort.Path => LocaleManager.Instance["GameListHeaderPath"],
+ ApplicationSort.Favorite => LocaleManager.Instance["CommonFavorite"],
+ _ => string.Empty,
+ };
}
}
@@ -668,6 +667,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
get => KeyGesture.Parse(_showUikey); set
{
_showUikey = value.ToString();
+
OnPropertyChanged();
}
}
@@ -677,6 +677,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
get => KeyGesture.Parse(_screenshotkey); set
{
_screenshotkey = value.ToString();
+
OnPropertyChanged();
}
}
@@ -686,14 +687,15 @@ namespace Ryujinx.Ava.Ui.ViewModels
get => KeyGesture.Parse(_pauseKey); set
{
_pauseKey = value.ToString();
+
OnPropertyChanged();
}
}
- public bool IsGridSmall => ConfigurationState.Instance.Ui.GridSize == 1;
+ 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;
+ public bool IsGridLarge => ConfigurationState.Instance.Ui.GridSize == 3;
+ public bool IsGridHuge => ConfigurationState.Instance.Ui.GridSize == 4;
public int GridSizeScale
{
@@ -728,14 +730,14 @@ namespace Ryujinx.Ava.Ui.ViewModels
if (_owner.AppHost.Device.System.SearchingForAmiibo(out int deviceId))
{
- string titleId = _owner.AppHost.Device.Application.TitleIdText.ToUpper();
+ string titleId = _owner.AppHost.Device.Application.TitleIdText.ToUpper();
AmiiboWindow window = new(_showAll, _lastScannedAmiiboId, titleId);
await window.ShowDialog(_owner);
if (window.IsScanned)
{
- _showAll = window.ViewModel.ShowAllAmiibo;
+ _showAll = window.ViewModel.ShowAllAmiibo;
_lastScannedAmiiboId = window.ScannedAmiibo.GetId();
_owner.AppHost.Device.System.ScanAmiibo(deviceId, _lastScannedAmiiboId, window.ViewModel.UseRandomUuid);
@@ -766,8 +768,9 @@ namespace Ryujinx.Ava.Ui.ViewModels
private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e)
{
- StatusBarProgressValue = e.NumAppsLoaded;
+ StatusBarProgressValue = e.NumAppsLoaded;
StatusBarProgressMaximum = e.NumAppsFound;
+
LocaleManager.Instance.UpdateDynamicValue("StatusBarGamesLoaded", StatusBarProgressValue, StatusBarProgressMaximum);
Dispatcher.UIThread.Post(() =>
@@ -792,9 +795,11 @@ namespace Ryujinx.Ava.Ui.ViewModels
await Dispatcher.UIThread.InvokeAsync(() =>
{
Applications.Clear();
+
_owner.LoadProgressBar.IsVisible = true;
- StatusBarProgressMaximum = 0;
- StatusBarProgressValue = 0;
+ StatusBarProgressMaximum = 0;
+ StatusBarProgressValue = 0;
+
LocaleManager.Instance.UpdateDynamicValue("StatusBarGamesLoaded", 0, 0);
});
@@ -842,12 +847,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
}
});
- dialog.Filters.Add(new FileDialogFilter { Name = "NSP", Extensions = { "nsp" } });
+ 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" } });
+ 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(_owner);
@@ -878,10 +883,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
{
ShowUiKey = new KeyGesture(showUiKey, KeyModifiers.None);
}
+
if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey))
{
ScreenshotKey = new KeyGesture(screenshotKey, KeyModifiers.None);
}
+
if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey))
{
PauseKey = new KeyGesture(pauseKey, KeyModifiers.None);
@@ -941,9 +948,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
_lastFullscreenToggle = Environment.TickCount64;
- WindowState state = _owner.WindowState;
-
- if (state == WindowState.FullScreen)
+ if (_owner.WindowState == WindowState.FullScreen)
{
_owner.WindowState = WindowState.Normal;
@@ -971,8 +976,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
{
if (IsGameRunning)
{
- ConfigurationState.Instance.System.EnableDockedMode.Value =
- !ConfigurationState.Instance.System.EnableDockedMode.Value;
+ ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
}
}
@@ -985,6 +989,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
else if (IsGameRunning)
{
await Task.Delay(100);
+
_owner.AppHost?.ShowExitPrompt();
}
}
@@ -994,6 +999,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
_owner.SettingsWindow = new(_owner.VirtualFileSystem, _owner.ContentManager);
await _owner.SettingsWindow.ShowDialog(_owner);
+
LoadConfigurableHotKeys();
}
@@ -1004,9 +1010,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
public async void OpenAboutWindow()
{
- AboutWindow window = new();
-
- await window.ShowDialog(_owner);
+ await new AboutWindow().ShowDialog(_owner);
}
public void ChangeLanguage(object obj)
@@ -1020,7 +1024,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
try
{
ProgressMaximum = total;
- ProgressValue = current;
+ ProgressValue = current;
switch (state)
{
@@ -1030,13 +1034,13 @@ namespace Ryujinx.Ava.Ui.ViewModels
{
case PtcLoadingState.Start:
case PtcLoadingState.Loading:
- LoadHeading = LocaleManager.Instance["CompilingPPTC"];
+ LoadHeading = LocaleManager.Instance["CompilingPPTC"];
IsLoadingIndeterminate = false;
break;
case PtcLoadingState.Loaded:
- LoadHeading = string.Format(LocaleManager.Instance["LoadingHeading"], TitleName);
+ LoadHeading = string.Format(LocaleManager.Instance["LoadingHeading"], TitleName);
IsLoadingIndeterminate = true;
- CacheLoadStatus = "";
+ CacheLoadStatus = "";
break;
}
break;
@@ -1046,13 +1050,13 @@ namespace Ryujinx.Ava.Ui.ViewModels
{
case ShaderCacheLoadingState.Start:
case ShaderCacheLoadingState.Loading:
- LoadHeading = LocaleManager.Instance["CompilingShaders"];
+ LoadHeading = LocaleManager.Instance["CompilingShaders"];
IsLoadingIndeterminate = false;
break;
case ShaderCacheLoadingState.Loaded:
- LoadHeading = string.Format(LocaleManager.Instance["LoadingHeading"], TitleName);
+ LoadHeading = string.Format(LocaleManager.Instance["LoadingHeading"], TitleName);
IsLoadingIndeterminate = true;
- CacheLoadStatus = "";
+ CacheLoadStatus = "";
break;
}
break;
@@ -1065,14 +1069,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void OpenUserSaveDirectory()
{
- var selection = SelectedApplication;
-
+ ApplicationData selection = SelectedApplication;
if (selection != null)
{
Task.Run(() =>
{
- if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
- out ulong titleIdNumber))
+ if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
{
Dispatcher.UIThread.Post(async () =>
{
@@ -1082,8 +1084,8 @@ namespace Ryujinx.Ava.Ui.ViewModels
return;
}
- var userId = new LibHac.Fs.UserId((ulong)_owner.AccountManager.LastOpenedUser.UserId.High, (ulong)_owner.AccountManager.LastOpenedUser.UserId.Low);
- var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveType: default, userId, saveDataId: default, index: default);
+ UserId userId = new((ulong)_owner.AccountManager.LastOpenedUser.UserId.High, (ulong)_owner.AccountManager.LastOpenedUser.UserId.Low);
+ SaveDataFilter saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveType: default, userId, saveDataId: default, index: default);
OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber);
});
}
@@ -1091,8 +1093,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void ToggleFavorite()
{
- var selection = SelectedApplication;
-
+ ApplicationData selection = SelectedApplication;
if (selection != null)
{
selection.Favorite = !selection.Favorite;
@@ -1108,11 +1109,10 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void OpenModsDirectory()
{
- var selection = SelectedApplication;
-
+ ApplicationData selection = SelectedApplication;
if (selection != null)
{
- string modsBasePath = _owner.VirtualFileSystem.ModLoader.GetModsBasePath();
+ string modsBasePath = _owner.VirtualFileSystem.ModLoader.GetModsBasePath();
string titleModsPath = _owner.VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, selection.TitleId);
OpenHelper.OpenFolder(titleModsPath);
@@ -1121,12 +1121,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void OpenSdModsDirectory()
{
- var selection = SelectedApplication;
+ ApplicationData selection = SelectedApplication;
if (selection != null)
{
string sdModsBasePath = _owner.VirtualFileSystem.ModLoader.GetSdModsBasePath();
- string titleModsPath = _owner.VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, selection.TitleId);
+ string titleModsPath = _owner.VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, selection.TitleId);
OpenHelper.OpenFolder(titleModsPath);
}
@@ -1134,13 +1134,11 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void OpenPtcDirectory()
{
- var selection = SelectedApplication;
-
+ ApplicationData selection = SelectedApplication;
if (selection != null)
{
- string ptcDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu");
-
- string mainPath = Path.Combine(ptcDir, "0");
+ 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))
@@ -1156,16 +1154,18 @@ namespace Ryujinx.Ava.Ui.ViewModels
public async void PurgePtcCache()
{
- var selection = SelectedApplication;
-
+ ApplicationData selection = SelectedApplication;
if (selection != null)
{
- DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "0"));
+ 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["DialogWarning"],
- string.Format(LocaleManager.Instance["DialogPPTCDeletionMessage"], selection.TitleName), LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]);
+ UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"],
+ string.Format(LocaleManager.Instance["DialogPPTCDeletionMessage"], selection.TitleName),
+ LocaleManager.Instance["InputDialogYes"],
+ LocaleManager.Instance["InputDialogNo"],
+ LocaleManager.Instance["RyujinxConfirm"]);
List<FileInfo> cacheFiles = new();
@@ -1198,8 +1198,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void OpenShaderCacheDirectory()
{
- var selection = SelectedApplication;
-
+ ApplicationData selection = SelectedApplication;
if (selection != null)
{
string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader");
@@ -1220,18 +1219,20 @@ namespace Ryujinx.Ava.Ui.ViewModels
public async void PurgeShaderCache()
{
- var selection = SelectedApplication;
-
+ 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["DialogWarning"],
- string.Format(LocaleManager.Instance["DialogShaderDeletionMessage"], selection.TitleName), LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]);
+ string.Format(LocaleManager.Instance["DialogShaderDeletionMessage"], selection.TitleName),
+ LocaleManager.Instance["InputDialogYes"],
+ LocaleManager.Instance["InputDialogNo"],
+ LocaleManager.Instance["RyujinxConfirm"]);
- List<DirectoryInfo> oldCacheDirectories = new List<DirectoryInfo>();
- List<FileInfo> newCacheFiles = new List<FileInfo>();
+ List<DirectoryInfo> oldCacheDirectories = new();
+ List<FileInfo> newCacheFiles = new();
if (shaderCacheDir.Exists)
{
@@ -1279,38 +1280,28 @@ namespace Ryujinx.Ava.Ui.ViewModels
public async void OpenTitleUpdateManager()
{
- var selection = SelectedApplication;
-
+ ApplicationData selection = SelectedApplication;
if (selection != null)
{
- TitleUpdateWindow titleUpdateManager =
- new(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName);
-
- await titleUpdateManager.ShowDialog(_owner);
+ await new TitleUpdateWindow(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(_owner);
}
}
public async void OpenDownloadableContentManager()
{
- var selection = SelectedApplication;
-
+ ApplicationData selection = SelectedApplication;
if (selection != null)
{
- DownloadableContentManagerWindow downloadableContentManager = new(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName);
-
- await downloadableContentManager.ShowDialog(_owner);
+ await new DownloadableContentManagerWindow(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(_owner);
}
}
public async void OpenCheatManager()
{
- var selection = SelectedApplication;
-
+ ApplicationData selection = SelectedApplication;
if (selection != null)
{
- CheatWindow cheatManager = new(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName);
-
- await cheatManager.ShowDialog(_owner);
+ await new CheatWindow(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(_owner);
}
}
@@ -1321,13 +1312,10 @@ namespace Ryujinx.Ava.Ui.ViewModels
return;
}
- var application = _owner.AppHost.Device.Application;
-
+ ApplicationLoader application = _owner.AppHost.Device.Application;
if (application != null)
{
- CheatWindow cheatManager = new(_owner.VirtualFileSystem, application.TitleIdText, application.TitleName);
-
- await cheatManager.ShowDialog(_owner);
+ await new CheatWindow(_owner.VirtualFileSystem, application.TitleIdText, application.TitleName).ShowDialog(_owner);
_owner.AppHost.Device.EnableCheats();
}
@@ -1335,14 +1323,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void OpenDeviceSaveDirectory()
{
- var selection = SelectedApplication;
-
+ ApplicationData selection = SelectedApplication;
if (selection != null)
{
Task.Run(() =>
{
- if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
- out ulong titleIdNumber))
+ if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
{
Dispatcher.UIThread.Post(async () =>
{
@@ -1360,14 +1346,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void OpenBcatSaveDirectory()
{
- var selection = SelectedApplication;
-
+ ApplicationData selection = SelectedApplication;
if (selection != null)
{
Task.Run(() =>
{
- if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
- out ulong titleIdNumber))
+ if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
{
Dispatcher.UIThread.Post(async () =>
{
@@ -1420,12 +1404,10 @@ namespace Ryujinx.Ava.Ui.ViewModels
_owner.Close();
}
- private async Task HandleFirmwareInstallation(string path)
+ private async Task HandleFirmwareInstallation(string filename)
{
try
{
- string filename = path;
-
SystemVersion firmwareVersion = _owner.ContentManager.VerifyFirmwarePackage(filename);
if (firmwareVersion == null)
@@ -1437,7 +1419,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
string dialogTitle = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallTitle"], firmwareVersion.VersionString);
-
SystemVersion currentVersion = _owner.ContentManager.GetCurrentFirmwareVersion();
string dialogMessage = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallMessage"], firmwareVersion.VersionString);
@@ -1480,11 +1461,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallSuccessMessage"], firmwareVersion.VersionString);
await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance["InputDialogOk"], "", LocaleManager.Instance["RyujinxInfo"]);
+
Logger.Info?.Print(LogClass.Application, message);
// Purge Applet Cache.
- DirectoryInfo miiEditorCacheFolder = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache"));
+ DirectoryInfo miiEditorCacheFolder = new DirectoryInfo(Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache"));
if (miiEditorCacheFolder.Exists)
{
@@ -1514,8 +1496,8 @@ namespace Ryujinx.Ava.Ui.ViewModels
catch (LibHac.Common.Keys.MissingKeyException ex)
{
Logger.Error?.Print(LogClass.Application, ex.ToString());
- Dispatcher.UIThread.Post(async () => await
- UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, _owner));
+
+ Dispatcher.UIThread.Post(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, _owner));
}
catch (Exception ex)
{
@@ -1527,8 +1509,8 @@ namespace Ryujinx.Ava.Ui.ViewModels
{
OpenFileDialog dialog = new() { AllowMultiple = false };
dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance["FileDialogAllTypes"], Extensions = { "xci", "zip" } });
- dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } });
- dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } });
+ dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } });
+ dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } });
string[] file = await dialog.ShowAsync(_owner);
diff --git a/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml b/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml
index 068ea826..0189c505 100644
--- a/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml
+++ b/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml
@@ -3,89 +3,126 @@
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
+ Width="800"
+ Height="500"
+ MinWidth="600"
+ MinHeight="500"
SizeToContent="Height"
- Width="600" MinHeight="500" Height="500"
WindowStartupLocation="CenterOwner"
- MinWidth="600"
mc:Ignorable="d">
<Grid Name="DownloadableContentGrid" Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
+ <RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
+ Name="Heading"
Grid.Row="1"
+ MaxWidth="500"
Margin="20,15,20,20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
- MaxWidth="500"
LineHeight="18"
- TextWrapping="Wrap"
- Text="{Binding Heading}"
- TextAlignment="Center" />
- <Border
+ TextAlignment="Center"
+ TextWrapping="Wrap" />
+ <DockPanel
Grid.Row="2"
+ Margin="0"
+ HorizontalAlignment="Left">
+ <Button
+ Name="EnableAllButton"
+ MinWidth="90"
+ Margin="5"
+ Command="{Binding EnableAll}">
+ <TextBlock Text="{locale:Locale DlcManagerEnableAllButton}" />
+ </Button>
+ <Button
+ Name="DisableAllButton"
+ MinWidth="90"
+ Margin="5"
+ Command="{Binding DisableAll}">
+ <TextBlock Text="{locale:Locale DlcManagerDisableAllButton}" />
+ </Button>
+ </DockPanel>
+ <Border
+ Grid.Row="3"
Margin="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
BorderBrush="Gray"
BorderThickness="1">
- <DataGrid
- MinHeight="200"
- HorizontalAlignment="Stretch"
+ <ScrollViewer
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Auto"
- Items="{Binding DownloadableContents}"
VerticalScrollBarVisibility="Auto">
- <DataGrid.Columns>
- <DataGridTemplateColumn Width="90">
- <DataGridTemplateColumn.CellTemplate>
- <DataTemplate>
- <CheckBox
- Width="50"
- MinWidth="40"
- HorizontalAlignment="Right"
- IsChecked="{Binding Enabled}" />
- </DataTemplate>
- </DataGridTemplateColumn.CellTemplate>
- <DataGridTemplateColumn.Header>
- <TextBlock Text="{locale:Locale DlcManagerTableHeadingEnabledLabel}" />
- </DataGridTemplateColumn.Header>
- </DataGridTemplateColumn>
- <DataGridTextColumn
- Width="190"
- Binding="{Binding TitleId}"
- CanUserResize="True">
- <DataGridTextColumn.Header>
- <TextBlock Text="{locale:Locale DlcManagerTableHeadingTitleIdLabel}" />
- </DataGridTextColumn.Header>
- </DataGridTextColumn>
- <DataGridTextColumn
- Width="*"
- Binding="{Binding ContainerPath}"
- CanUserResize="True">
- <DataGridTextColumn.Header>
- <TextBlock Text="{locale:Locale DlcManagerTableHeadingContainerPathLabel}" />
- </DataGridTextColumn.Header>
- </DataGridTextColumn>
- <DataGridTextColumn
- Width="*"
- Binding="{Binding FullPath}"
- CanUserResize="True">
- <DataGridTextColumn.Header>
- <TextBlock Text="{locale:Locale DlcManagerTableHeadingFullPathLabel}" />
- </DataGridTextColumn.Header>
- </DataGridTextColumn>
- </DataGrid.Columns>
- </DataGrid>
+ <DataGrid
+ Name="DlcDataGrid"
+ MinHeight="200"
+ HorizontalAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ CanUserReorderColumns="False"
+ CanUserResizeColumns="True"
+ CanUserSortColumns="True"
+ HorizontalScrollBarVisibility="Auto"
+ Items="{Binding _downloadableContents}"
+ SelectionMode="Extended"
+ VerticalScrollBarVisibility="Auto">
+ <DataGrid.Styles>
+ <Styles>
+ <Style Selector="DataGridCell:nth-child(3), DataGridCell:nth-child(4)">
+ <Setter Property="HorizontalAlignment" Value="Left" />
+ <Setter Property="HorizontalContentAlignment" Value="Left" />
+ </Style>
+ </Styles>
+ <Styles>
+ <Style Selector="DataGridCell:nth-child(1)">
+ <Setter Property="HorizontalAlignment" Value="Right" />
+ <Setter Property="HorizontalContentAlignment" Value="Right" />
+ </Style>
+ </Styles>
+ </DataGrid.Styles>
+ <DataGrid.Columns>
+ <DataGridTemplateColumn Width="90">
+ <DataGridTemplateColumn.CellTemplate>
+ <DataTemplate>
+ <CheckBox
+ Width="50"
+ MinWidth="40"
+ HorizontalAlignment="Center"
+ IsChecked="{Binding Enabled}" />
+ </DataTemplate>
+ </DataGridTemplateColumn.CellTemplate>
+ <DataGridTemplateColumn.Header>
+ <TextBlock Text="{locale:Locale DlcManagerTableHeadingEnabledLabel}" />
+ </DataGridTemplateColumn.Header>
+ </DataGridTemplateColumn>
+ <DataGridTextColumn Width="140" Binding="{Binding TitleId}">
+ <DataGridTextColumn.Header>
+ <TextBlock Text="{locale:Locale DlcManagerTableHeadingTitleIdLabel}" />
+ </DataGridTextColumn.Header>
+ </DataGridTextColumn>
+ <DataGridTextColumn Width="280" Binding="{Binding FullPath}">
+ <DataGridTextColumn.Header>
+ <TextBlock Text="{locale:Locale DlcManagerTableHeadingFullPathLabel}" />
+ </DataGridTextColumn.Header>
+ </DataGridTextColumn>
+ <DataGridTextColumn Binding="{Binding ContainerPath}">
+ <DataGridTextColumn.Header>
+ <TextBlock Text="{locale:Locale DlcManagerTableHeadingContainerPathLabel}" />
+ </DataGridTextColumn.Header>
+ </DataGridTextColumn>
+ </DataGrid.Columns>
+ </DataGrid>
+ </ScrollViewer>
</Border>
<DockPanel
- Grid.Row="3"
+ Grid.Row="4"
Margin="0"
HorizontalAlignment="Stretch">
<DockPanel Margin="0" HorizontalAlignment="Left">
diff --git a/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs
index 972ffbc3..b1c86afc 100644
--- a/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs
+++ b/Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs
@@ -8,6 +8,7 @@ using LibHac.FsSystem;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
+using LibHac.Tools.FsSystem.Save;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Models;
@@ -16,8 +17,11 @@ using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
using System.IO;
using System.Linq;
+using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;
using Path = System.IO.Path;
@@ -27,14 +31,13 @@ namespace Ryujinx.Ava.Ui.Windows
public partial class DownloadableContentManagerWindow : StyleableWindow
{
private readonly List<DownloadableContentContainer> _downloadableContentContainerList;
- private readonly string _downloadableContentJsonPath;
+ private readonly string _downloadableContentJsonPath;
- public VirtualFileSystem VirtualFileSystem { get; }
- public AvaloniaList<DownloadableContentModel> DownloadableContents { get; set; } = new AvaloniaList<DownloadableContentModel>();
- public ulong TitleId { get; }
- public string TitleName { get; }
+ private VirtualFileSystem _virtualFileSystem { get; }
+ private AvaloniaList<DownloadableContentModel> _downloadableContents { get; set; }
- public string Heading => string.Format(LocaleManager.Instance["DlcWindowHeading"], TitleName, TitleId.ToString("X16"));
+ private ulong TitleId { get; }
+ private string TitleName { get; }
public DownloadableContentManagerWindow()
{
@@ -42,14 +45,15 @@ namespace Ryujinx.Ava.Ui.Windows
InitializeComponent();
- Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["DlcWindowTitle"];
+ Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {TitleName} ({TitleId:X16})";
}
public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
{
- VirtualFileSystem = virtualFileSystem;
- TitleId = titleId;
- TitleName = titleName;
+ _virtualFileSystem = virtualFileSystem;
+ _downloadableContents = new AvaloniaList<DownloadableContentModel>();
+ TitleId = titleId;
+ TitleName = titleName;
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
@@ -66,9 +70,24 @@ namespace Ryujinx.Ava.Ui.Windows
InitializeComponent();
- Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["DlcWindowTitle"];
+ RemoveButton.IsEnabled = false;
+
+ DlcDataGrid.SelectionChanged += DlcDataGrid_SelectionChanged;
+
+ Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {TitleName} ({TitleId:X16})";
LoadDownloadableContents();
+ PrintHeading();
+ }
+
+ private void DlcDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ RemoveButton.IsEnabled = (DlcDataGrid.SelectedItems.Count > 0);
+ }
+
+ private void PrintHeading()
+ {
+ Heading.Text = string.Format(LocaleManager.Instance["DlcWindowHeading"], _downloadableContents.Count, TitleName, TitleId.ToString("X16"));
}
private void LoadDownloadableContents()
@@ -79,23 +98,23 @@ namespace Ryujinx.Ava.Ui.Windows
{
using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath);
- PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage());
+ PartitionFileSystem pfs = new(containerFile.AsStorage());
- VirtualFileSystem.ImportTickets(pfs);
+ _virtualFileSystem.ImportTickets(pfs);
foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList)
{
- using var ncaFile = new UniqueRef<IFile>();
+ using UniqueRef<IFile> ncaFile = new();
pfs.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
- Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
+ Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
if (nca != null)
{
- DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"),
- downloadableContentContainer.ContainerPath,
- downloadableContentNca.FullPath,
- downloadableContentNca.Enabled));
+ _downloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"),
+ downloadableContentContainer.ContainerPath,
+ downloadableContentNca.FullPath,
+ downloadableContentNca.Enabled));
}
}
}
@@ -105,11 +124,11 @@ namespace Ryujinx.Ava.Ui.Windows
Save();
}
- private Nca TryCreateNca(IStorage ncaStorage, string containerPath)
+ private Nca TryOpenNca(IStorage ncaStorage, string containerPath)
{
try
{
- return new Nca(VirtualFileSystem.KeySet, ncaStorage);
+ return new Nca(_virtualFileSystem.KeySet, ncaStorage);
}
catch (Exception ex)
{
@@ -124,48 +143,46 @@ namespace Ryujinx.Ava.Ui.Windows
private async Task AddDownloadableContent(string path)
{
- if (!File.Exists(path) || DownloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null)
+ if (!File.Exists(path) || _downloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null)
{
return;
}
- using (FileStream containerFile = File.OpenRead(path))
- {
- PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage());
- bool containsDownloadableContent = false;
+ using FileStream containerFile = File.OpenRead(path);
- VirtualFileSystem.ImportTickets(pfs);
+ PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage());
+ bool containsDownloadableContent = false;
- foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
- {
- using var ncaFile = new UniqueRef<IFile>();
+ _virtualFileSystem.ImportTickets(partitionFileSystem);
+
+ foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca"))
+ {
+ using var ncaFile = new UniqueRef<IFile>();
- pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+ partitionFileSystem.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
- Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), path);
+ Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path);
+ if (nca == null)
+ {
+ continue;
+ }
- if (nca == null)
+ if (nca.Header.ContentType == NcaContentType.PublicData)
+ {
+ if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != TitleId)
{
- continue;
+ break;
}
- if (nca.Header.ContentType == NcaContentType.PublicData)
- {
- if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != TitleId)
- {
- break;
- }
-
- DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true));
+ _downloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true));
- containsDownloadableContent = true;
- }
+ containsDownloadableContent = true;
}
+ }
- if (!containsDownloadableContent)
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]);
- }
+ if (!containsDownloadableContent)
+ {
+ await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]);
}
}
@@ -173,12 +190,26 @@ namespace Ryujinx.Ava.Ui.Windows
{
if (removeSelectedOnly)
{
- DownloadableContents.RemoveAll(DownloadableContents.Where(x => x.Enabled).ToList());
+ AvaloniaList<DownloadableContentModel> removedItems = new();
+
+ foreach (var item in DlcDataGrid.SelectedItems)
+ {
+ removedItems.Add(item as DownloadableContentModel);
+ }
+
+ DlcDataGrid.SelectedItems.Clear();
+
+ foreach (var item in removedItems)
+ {
+ _downloadableContents.RemoveAll(_downloadableContents.Where(x => x.TitleId == item.TitleId).ToList());
+ }
}
else
{
- DownloadableContents.Clear();
+ _downloadableContents.Clear();
}
+
+ PrintHeading();
}
public void RemoveSelected()
@@ -191,6 +222,22 @@ namespace Ryujinx.Ava.Ui.Windows
RemoveDownloadableContents();
}
+ public void EnableAll()
+ {
+ foreach(var item in _downloadableContents)
+ {
+ item.Enabled = true;
+ }
+ }
+
+ public void DisableAll()
+ {
+ foreach (var item in _downloadableContents)
+ {
+ item.Enabled = false;
+ }
+ }
+
public async void Add()
{
OpenFileDialog dialog = new OpenFileDialog()
@@ -214,6 +261,8 @@ namespace Ryujinx.Ava.Ui.Windows
await AddDownloadableContent(file);
}
}
+
+ PrintHeading();
}
public void Save()
@@ -222,7 +271,7 @@ namespace Ryujinx.Ava.Ui.Windows
DownloadableContentContainer container = default;
- foreach (DownloadableContentModel downloadableContent in DownloadableContents)
+ foreach (DownloadableContentModel downloadableContent in _downloadableContents)
{
if (container.ContainerPath != downloadableContent.ContainerPath)
{
diff --git a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs
index be4517c8..e874982a 100644
--- a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs
+++ b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs
@@ -90,8 +90,8 @@ namespace Ryujinx.Ava.Ui.Windows
Title = $"Ryujinx {Program.Version}";
- Height = Height / Program.WindowScaleFactor;
- Width = Width / Program.WindowScaleFactor;
+ Height /= Program.WindowScaleFactor;
+ Width /= Program.WindowScaleFactor;
if (Program.PreviewerDetached)
{
@@ -523,23 +523,20 @@ namespace Ryujinx.Ava.Ui.Windows
public static void UpdateGraphicsConfig()
{
- int resScale = ConfigurationState.Instance.Graphics.ResScale;
- float resScaleCustom = ConfigurationState.Instance.Graphics.ResScaleCustom;
-
- GraphicsConfig.ResScale = resScale == -1 ? resScaleCustom : resScale;
- GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy;
- GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
- GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache;
+ GraphicsConfig.ResScale = ConfigurationState.Instance.Graphics.ResScale == -1 ? ConfigurationState.Instance.Graphics.ResScaleCustom : ConfigurationState.Instance.Graphics.ResScale;
+ GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy;
+ GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
+ GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache;
GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression;
- GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE;
+ GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE;
}
public void LoadHotKeys()
{
- HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt));
+ HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt));
HotKeyManager.SetHotKey(FullscreenHotKey2, new KeyGesture(Key.F11));
- HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9));
- HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape));
+ HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9));
+ HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape));
}
public static void SaveConfig()
diff --git a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml
index 0946a2eb..1791d8ea 100644
--- a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml
+++ b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml
@@ -75,7 +75,7 @@
Spacing="10">
<ListBox
Name="GameList"
- MinHeight="150"
+ MinHeight="250"
Items="{Binding GameDirectories}" />
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
diff --git a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs
index 810beb3e..0e610d77 100644
--- a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs
+++ b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs
@@ -16,7 +16,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Threading.Tasks;
using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone;
namespace Ryujinx.Ava.Ui.Windows
@@ -31,7 +30,7 @@ namespace Ryujinx.Ava.Ui.Windows
{
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["Settings"]}";
- ViewModel = new SettingsViewModel(virtualFileSystem, contentManager, this);
+ ViewModel = new SettingsViewModel(virtualFileSystem, contentManager, this);
DataContext = ViewModel;
InitializeComponent();
@@ -48,7 +47,7 @@ namespace Ryujinx.Ava.Ui.Windows
public SettingsWindow()
{
- ViewModel = new SettingsViewModel();
+ ViewModel = new SettingsViewModel();
DataContext = ViewModel;
InitializeComponent();
@@ -79,7 +78,7 @@ namespace Ryujinx.Ava.Ui.Windows
PointerPressed += MouseClick;
- IKeyboard keyboard = (IKeyboard)ViewModel.AvaloniaKeyboardDriver.GetGamepad(ViewModel.AvaloniaKeyboardDriver.GamepadsIds[0]);
+ IKeyboard keyboard = (IKeyboard)ViewModel.AvaloniaKeyboardDriver.GetGamepad(ViewModel.AvaloniaKeyboardDriver.GamepadsIds[0]);
IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard);
_currentAssigner.GetInputAndAssign(assigner);
@@ -92,6 +91,7 @@ namespace Ryujinx.Ava.Ui.Windows
_currentAssigner.Cancel();
_currentAssigner = null;
+
button.IsChecked = false;
}
}
@@ -122,36 +122,19 @@ namespace Ryujinx.Ava.Ui.Windows
{
if (e.SelectedItem is NavigationViewItem navitem)
{
- switch (navitem.Tag.ToString())
+ NavPanel.Content = navitem.Tag.ToString() switch
{
- case "UiPage":
- NavPanel.Content = UiPage;
- break;
- case "InputPage":
- NavPanel.Content = InputPage;
- break;
- case "HotkeysPage":
- NavPanel.Content = HotkeysPage;
- break;
- case "SystemPage":
- NavPanel.Content = SystemPage;
- break;
- case "CpuPage":
- NavPanel.Content = CpuPage;
- break;
- case "GraphicsPage":
- NavPanel.Content = GraphicsPage;
- break;
- case "AudioPage":
- NavPanel.Content = AudioPage;
- break;
- case "NetworkPage":
- NavPanel.Content = NetworkPage;
- break;
- case "LoggingPage":
- NavPanel.Content = LoggingPage;
- break;
- }
+ "UiPage" => UiPage,
+ "InputPage" => InputPage,
+ "HotkeysPage" => HotkeysPage,
+ "SystemPage" => SystemPage,
+ "CpuPage" => CpuPage,
+ "GraphicsPage" => GraphicsPage,
+ "AudioPage" => AudioPage,
+ "NetworkPage" => NetworkPage,
+ "LoggingPage" => LoggingPage,
+ _ => throw new NotImplementedException()
+ };
}
}
@@ -178,13 +161,18 @@ namespace Ryujinx.Ava.Ui.Windows
private void RemoveButton_OnClick(object sender, RoutedEventArgs e)
{
- List<string> selected = new(GameList.SelectedItems.Cast<string>());
+ int oldIndex = GameList.SelectedIndex;
- foreach (string path in selected)
+ foreach (string path in new List<string>(GameList.SelectedItems.Cast<string>()))
{
ViewModel.GameDirectories.Remove(path);
ViewModel.DirectoryChanged = true;
}
+
+ if (GameList.ItemCount > 0)
+ {
+ GameList.SelectedIndex = oldIndex < GameList.ItemCount ? oldIndex : 0;
+ }
}
private void TimeZoneBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
@@ -214,7 +202,6 @@ namespace Ryujinx.Ava.Ui.Windows
private void SaveButton_Clicked(object sender, RoutedEventArgs e)
{
SaveSettings();
-
Close();
}
@@ -232,7 +219,6 @@ namespace Ryujinx.Ava.Ui.Windows
private void SaveSettings()
{
ViewModel.SaveSettings();
-
ControllerSettings?.SaveCurrentProfile();
if (Owner is MainWindow window && ViewModel.DirectoryChanged)
@@ -246,8 +232,10 @@ namespace Ryujinx.Ava.Ui.Windows
protected override void OnClosed(EventArgs e)
{
ControllerSettings.Dispose();
+
_currentAssigner?.Cancel();
_currentAssigner = null;
+
base.OnClosed(e);
}
}
diff --git a/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml.cs
index b4d5d5b5..751c7afc 100644
--- a/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml.cs
+++ b/Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml.cs
@@ -131,6 +131,13 @@ namespace Ryujinx.Ava.Ui.Windows
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
+
+ foreach (var update in TitleUpdates)
+ {
+ update.IsEnabled = false;
+ }
+
+ TitleUpdates.Last().IsEnabled = true;
}
else
{