Compare commits

..

4 Commits

Author SHA1 Message Date
riperiperi
476b4683cf Fix CB0 alignment with addresses used for 8/16-bit LDG/STG (#3897)
This replacement is meant to be done with the original identified byteOffset, not the one assigned later on by the below conditionals (that already has the constant offset added, for instance).

This fixes videos being pixelated in Xenoblade 3, and other regressions that might have happened since #3847.
2022-11-25 14:39:03 +00:00
Mary-nyan
5fb5079730 chore: Update Avalonia related dependencies (#3891) 2022-11-25 13:27:41 +00:00
Ac_K
3fbacd0f49 ava: Rework DLC Manager, Add various fixes and cleanup (#3896)
* Fixes Everything Part.2

* Change sorting, fix remove and heading
2022-11-25 12:41:34 +01:00
dependabot[bot]
7aa6abc120 nuget: bump SharpZipLib from 1.3.3 to 1.4.1 (#3893)
Bumps [SharpZipLib](https://github.com/icsharpcode/SharpZipLib) from 1.3.3 to 1.4.1.
- [Release notes](https://github.com/icsharpcode/SharpZipLib/releases)
- [Changelog](https://github.com/icsharpcode/SharpZipLib/blob/master/docs/Changes.txt)
- [Commits](https://github.com/icsharpcode/SharpZipLib/compare/v1.3.3...v1.4.1)

---
updated-dependencies:
- dependency-name: SharpZipLib
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-24 20:30:17 +01:00
14 changed files with 428 additions and 356 deletions

View File

@@ -410,6 +410,8 @@
"DlcManagerTableHeadingContainerPathLabel": "Container Path", "DlcManagerTableHeadingContainerPathLabel": "Container Path",
"DlcManagerTableHeadingFullPathLabel": "Full Path", "DlcManagerTableHeadingFullPathLabel": "Full Path",
"DlcManagerRemoveAllButton": "Remove All", "DlcManagerRemoveAllButton": "Remove All",
"DlcManagerEnableAllButton": "Enable All",
"DlcManagerDisableAllButton": "Disable All",
"MenuBarOptionsChangeLanguage": "Change Language", "MenuBarOptionsChangeLanguage": "Change Language",
"CommonSort": "Sort", "CommonSort": "Sort",
"CommonShowNames": "Show Names", "CommonShowNames": "Show Names",
@@ -567,7 +569,7 @@
"DlcWindowTitle": "Manage Game DLC", "DlcWindowTitle": "Manage Game DLC",
"UpdateWindowTitle": "Manage Game Updates", "UpdateWindowTitle": "Manage Game Updates",
"CheatWindowHeading": "Cheats Available for {0} [{1}]", "CheatWindowHeading": "Cheats Available for {0} [{1}]",
"DlcWindowHeading": "DLC Available for {0} [{1}]", "DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})",
"UserProfilesEditProfile": "Edit Selected", "UserProfilesEditProfile": "Edit Selected",
"Cancel": "Cancel", "Cancel": "Cancel",
"Save": "Save", "Save": "Save",

View File

@@ -23,19 +23,18 @@ namespace Ryujinx.Ava
{ {
internal class Program internal class Program
{ {
public static double WindowScaleFactor { get; set; } public static double WindowScaleFactor { get; set; }
public static double ActualScaleFactor { get; set; } public static double ActualScaleFactor { get; set; }
public static string Version { get; private set; } public static string Version { get; private set; }
public static string ConfigurationPath { get; private set; } public static string ConfigurationPath { get; private set; }
public static bool PreviewerDetached { get; private set; } public static bool PreviewerDetached { get; private set; }
public static RenderTimer RenderTimer { get; private set; }
public static RenderTimer RenderTimer { get; private set; }
[DllImport("user32.dll", SetLastError = true)] [DllImport("user32.dll", SetLastError = true)]
public static extern int MessageBoxA(IntPtr hWnd, string text, string caption, uint type); public static extern int MessageBoxA(IntPtr hWnd, string text, string caption, uint type);
private const uint MB_ICONWARNING = 0x30; private const uint MB_ICONWARNING = 0x30;
private const int BaseDpi = 96; private const int BaseDpi = 96;
public static void Main(string[] args) public static void Main(string[] args)
{ {
@@ -43,7 +42,7 @@ namespace Ryujinx.Ava
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134)) 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; PreviewerDetached = true;
@@ -64,16 +63,16 @@ namespace Ryujinx.Ava
.With(new X11PlatformOptions .With(new X11PlatformOptions
{ {
EnableMultiTouch = true, EnableMultiTouch = true,
EnableIme = true, EnableIme = true,
UseEGL = false, UseEGL = false,
UseGpu = false UseGpu = false
}) })
.With(new Win32PlatformOptions .With(new Win32PlatformOptions
{ {
EnableMultitouch = true, EnableMultitouch = true,
UseWgl = false, UseWgl = false,
AllowEglInitialization = false, AllowEglInitialization = false,
CompositionBackdropCornerRadius = 8f, CompositionBackdropCornerRadius = 8.0f,
}) })
.UseSkia() .UseSkia()
.AfterSetup(_ => .AfterSetup(_ =>
@@ -122,12 +121,10 @@ namespace Ryujinx.Ava
PrintSystemInfo(); PrintSystemInfo();
// Enable OGL multithreading on the driver, when available. // Enable OGL multithreading on the driver, when available.
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; DriverUtilities.ToggleOGLThreading(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
DriverUtilities.ToggleOGLThreading(threadingMode == BackendThreading.Off);
// Check if keys exists. // Check if keys exists.
bool hasSystemProdKeys = File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")); if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))
if (!hasSystemProdKeys)
{ {
if (!(AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "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() 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"); string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json");
// Now load the configuration as the other subsystems are now registered // 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}"); Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}");
SystemInfo.Gather().Print(); SystemInfo.Gather().Print();
var enabledLogs = Logger.GetEnabledLevels(); Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(Logger.GetEnabledLevels().Count == 0 ? "<None>" : string.Join(", ", Logger.GetEnabledLevels()))}");
Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(enabledLogs.Count == 0 ? "<None>" : string.Join(", ", enabledLogs))}");
if (AppDataManager.Mode == AppDataManager.LaunchMode.Custom) if (AppDataManager.Mode == AppDataManager.LaunchMode.Custom)
{ {

View File

@@ -19,16 +19,16 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.18" /> <PackageReference Include="Avalonia" Version="0.10.18" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.15" /> <PackageReference Include="Avalonia.Desktop" Version="0.10.18" />
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.15" /> <PackageReference Include="Avalonia.Diagnostics" Version="0.10.18" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.15" /> <PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
<PackageReference Include="Avalonia.Markup.Xaml.Loader" Version="0.10.15" /> <PackageReference Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" />
<PackageReference Include="Avalonia.Svg" Version="0.10.14" /> <PackageReference Include="Avalonia.Svg" Version="0.10.18" />
<PackageReference Include="Avalonia.Svg.Skia" Version="0.10.14" /> <PackageReference Include="Avalonia.Svg.Skia" Version="0.10.18" />
<PackageReference Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" /> <PackageReference Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
<PackageReference Include="DynamicData" Version="7.9.4" /> <PackageReference Include="DynamicData" Version="7.12.8" />
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1" /> <PackageReference Include="FluentAvaloniaUI" Version="1.4.4" />
<PackageReference Include="XamlNameReferenceGenerator" Version="1.3.4" /> <PackageReference Include="XamlNameReferenceGenerator" Version="1.4.2" />
<PackageReference Include="OpenTK.Core" Version="4.7.2" /> <PackageReference Include="OpenTK.Core" Version="4.7.2" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" /> <PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
@@ -37,7 +37,7 @@
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.10.1" /> <PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.10.1" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.10.1" /> <PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.10.1" />
<PackageReference Include="SPB" Version="0.0.4-build28" /> <PackageReference Include="SPB" Version="0.0.4-build28" />
<PackageReference Include="SharpZipLib" Version="1.3.3" /> <PackageReference Include="SharpZipLib" Version="1.4.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" /> <PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
</ItemGroup> </ItemGroup>

View File

@@ -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 TitleId { get; }
public string ContainerPath { get; } public string ContainerPath { get; }
public string FullPath { get; } public string FullPath { get; }

View File

@@ -19,6 +19,7 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE; using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.Modules; using Ryujinx.Modules;
using Ryujinx.Ui.App.Common; using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common; using Ryujinx.Ui.Common;
@@ -47,6 +48,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
private string _loadHeading; private string _loadHeading;
private string _cacheLoadStatus; private string _cacheLoadStatus;
private string _searchText; private string _searchText;
private Timer _searchTimer;
private string _dockedStatusText; private string _dockedStatusText;
private string _fifoStatusText; private string _fifoStatusText;
private string _gameStatusText; private string _gameStatusText;
@@ -115,10 +117,20 @@ namespace Ryujinx.Ava.Ui.ViewModels
{ {
_searchText = value; _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 public ReadOnlyObservableCollection<ApplicationData> AppsObservableList
{ {
get => _appsObservableList; get => _appsObservableList;
@@ -200,22 +212,19 @@ namespace Ryujinx.Ava.Ui.ViewModels
private string _showUikey = "F4"; private string _showUikey = "F4";
private string _pauseKey = "F5"; private string _pauseKey = "F5";
private string _screenshotkey = "F8"; private string _screenshotkey = "F8";
private float _volume; private float _volume;
private string _backendText; private string _backendText;
public ApplicationData SelectedApplication public ApplicationData SelectedApplication
{ {
get get
{ {
switch (Glyph) return Glyph switch
{ {
case Glyph.List: Glyph.List => _owner.GameList.SelectedApplication,
return _owner.GameList.SelectedApplication; Glyph.Grid => _owner.GameGrid.SelectedApplication,
case Glyph.Grid: _ => null,
return _owner.GameGrid.SelectedApplication; };
default:
return null;
}
} }
} }
@@ -408,6 +417,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
{ {
_owner.AppHost.Device.SetVolume(_volume); _owner.AppHost.Device.SetVolume(_volume);
} }
OnPropertyChanged(nameof(VolumeStatusText)); OnPropertyChanged(nameof(VolumeStatusText));
OnPropertyChanged(nameof(VolumeMuted)); OnPropertyChanged(nameof(VolumeMuted));
OnPropertyChanged(); OnPropertyChanged();
@@ -477,38 +487,36 @@ namespace Ryujinx.Ava.Ui.ViewModels
internal void Sort(bool isAscending) internal void Sort(bool isAscending)
{ {
IsAscending = isAscending; IsAscending = isAscending;
RefreshView(); RefreshView();
} }
internal void Sort(ApplicationSort sort) internal void Sort(ApplicationSort sort)
{ {
SortMode = sort; SortMode = sort;
RefreshView(); RefreshView();
} }
private IComparer<ApplicationData> GetComparer() private IComparer<ApplicationData> GetComparer()
{ {
switch (SortMode) return SortMode switch
{ {
case ApplicationSort.LastPlayed: ApplicationSort.LastPlayed => new Models.Generic.LastPlayedSortComparer(IsAscending),
return new Models.Generic.LastPlayedSortComparer(IsAscending); ApplicationSort.FileSize => new Models.Generic.FileSizeSortComparer(IsAscending),
case ApplicationSort.FileSize: ApplicationSort.TotalTimePlayed => new Models.Generic.TimePlayedSortComparer(IsAscending),
return new Models.Generic.FileSizeSortComparer(IsAscending); ApplicationSort.Title => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName)
case ApplicationSort.TotalTimePlayed: : SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName),
return new Models.Generic.TimePlayedSortComparer(IsAscending); ApplicationSort.Favorite => !IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Favorite)
case ApplicationSort.Title: : SortExpressionComparer<ApplicationData>.Descending(app => app.Favorite),
return IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName) : SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName); ApplicationSort.Developer => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Developer)
case ApplicationSort.Favorite: : SortExpressionComparer<ApplicationData>.Descending(app => app.Developer),
return !IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Favorite) : SortExpressionComparer<ApplicationData>.Descending(app => app.Favorite); ApplicationSort.FileType => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileExtension)
case ApplicationSort.Developer: : SortExpressionComparer<ApplicationData>.Descending(app => app.FileExtension),
return IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Developer) : SortExpressionComparer<ApplicationData>.Descending(app => app.Developer); ApplicationSort.Path => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Path)
case ApplicationSort.FileType: : SortExpressionComparer<ApplicationData>.Descending(app => app.Path),
return IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileExtension) : SortExpressionComparer<ApplicationData>.Descending(app => app.FileExtension); _ => null,
case ApplicationSort.Path: };
return IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Path) : SortExpressionComparer<ApplicationData>.Descending(app => app.Path);
default:
return null;
}
} }
private void RefreshView() private void RefreshView()
@@ -611,40 +619,31 @@ namespace Ryujinx.Ava.Ui.ViewModels
} }
} }
public bool IsSortedByFavorite => SortMode == ApplicationSort.Favorite; public bool IsSortedByFavorite => SortMode == ApplicationSort.Favorite;
public bool IsSortedByTitle => SortMode == ApplicationSort.Title; public bool IsSortedByTitle => SortMode == ApplicationSort.Title;
public bool IsSortedByDeveloper => SortMode == ApplicationSort.Developer; public bool IsSortedByDeveloper => SortMode == ApplicationSort.Developer;
public bool IsSortedByLastPlayed => SortMode == ApplicationSort.LastPlayed; public bool IsSortedByLastPlayed => SortMode == ApplicationSort.LastPlayed;
public bool IsSortedByTimePlayed => SortMode == ApplicationSort.TotalTimePlayed; public bool IsSortedByTimePlayed => SortMode == ApplicationSort.TotalTimePlayed;
public bool IsSortedByType => SortMode == ApplicationSort.FileType; public bool IsSortedByType => SortMode == ApplicationSort.FileType;
public bool IsSortedBySize => SortMode == ApplicationSort.FileSize; public bool IsSortedBySize => SortMode == ApplicationSort.FileSize;
public bool IsSortedByPath => SortMode == ApplicationSort.Path; public bool IsSortedByPath => SortMode == ApplicationSort.Path;
public string SortName public string SortName
{ {
get get
{ {
switch (SortMode) return SortMode switch
{ {
case ApplicationSort.Title: ApplicationSort.Title => LocaleManager.Instance["GameListHeaderApplication"],
return LocaleManager.Instance["GameListHeaderApplication"]; ApplicationSort.Developer => LocaleManager.Instance["GameListHeaderDeveloper"],
case ApplicationSort.Developer: ApplicationSort.LastPlayed => LocaleManager.Instance["GameListHeaderLastPlayed"],
return LocaleManager.Instance["GameListHeaderDeveloper"]; ApplicationSort.TotalTimePlayed => LocaleManager.Instance["GameListHeaderTimePlayed"],
case ApplicationSort.LastPlayed: ApplicationSort.FileType => LocaleManager.Instance["GameListHeaderFileExtension"],
return LocaleManager.Instance["GameListHeaderLastPlayed"]; ApplicationSort.FileSize => LocaleManager.Instance["GameListHeaderFileSize"],
case ApplicationSort.TotalTimePlayed: ApplicationSort.Path => LocaleManager.Instance["GameListHeaderPath"],
return LocaleManager.Instance["GameListHeaderTimePlayed"]; ApplicationSort.Favorite => LocaleManager.Instance["CommonFavorite"],
case ApplicationSort.FileType: _ => string.Empty,
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;
} }
} }
@@ -668,6 +667,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
get => KeyGesture.Parse(_showUikey); set get => KeyGesture.Parse(_showUikey); set
{ {
_showUikey = value.ToString(); _showUikey = value.ToString();
OnPropertyChanged(); OnPropertyChanged();
} }
} }
@@ -677,6 +677,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
get => KeyGesture.Parse(_screenshotkey); set get => KeyGesture.Parse(_screenshotkey); set
{ {
_screenshotkey = value.ToString(); _screenshotkey = value.ToString();
OnPropertyChanged(); OnPropertyChanged();
} }
} }
@@ -686,14 +687,15 @@ namespace Ryujinx.Ava.Ui.ViewModels
get => KeyGesture.Parse(_pauseKey); set get => KeyGesture.Parse(_pauseKey); set
{ {
_pauseKey = value.ToString(); _pauseKey = value.ToString();
OnPropertyChanged(); 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 IsGridMedium => ConfigurationState.Instance.Ui.GridSize == 2;
public bool IsGridLarge => ConfigurationState.Instance.Ui.GridSize == 3; public bool IsGridLarge => ConfigurationState.Instance.Ui.GridSize == 3;
public bool IsGridHuge => ConfigurationState.Instance.Ui.GridSize == 4; public bool IsGridHuge => ConfigurationState.Instance.Ui.GridSize == 4;
public int GridSizeScale public int GridSizeScale
{ {
@@ -728,14 +730,14 @@ namespace Ryujinx.Ava.Ui.ViewModels
if (_owner.AppHost.Device.System.SearchingForAmiibo(out int deviceId)) 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); AmiiboWindow window = new(_showAll, _lastScannedAmiiboId, titleId);
await window.ShowDialog(_owner); await window.ShowDialog(_owner);
if (window.IsScanned) if (window.IsScanned)
{ {
_showAll = window.ViewModel.ShowAllAmiibo; _showAll = window.ViewModel.ShowAllAmiibo;
_lastScannedAmiiboId = window.ScannedAmiibo.GetId(); _lastScannedAmiiboId = window.ScannedAmiibo.GetId();
_owner.AppHost.Device.System.ScanAmiibo(deviceId, _lastScannedAmiiboId, window.ViewModel.UseRandomUuid); _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) private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e)
{ {
StatusBarProgressValue = e.NumAppsLoaded; StatusBarProgressValue = e.NumAppsLoaded;
StatusBarProgressMaximum = e.NumAppsFound; StatusBarProgressMaximum = e.NumAppsFound;
LocaleManager.Instance.UpdateDynamicValue("StatusBarGamesLoaded", StatusBarProgressValue, StatusBarProgressMaximum); LocaleManager.Instance.UpdateDynamicValue("StatusBarGamesLoaded", StatusBarProgressValue, StatusBarProgressMaximum);
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
@@ -792,9 +795,11 @@ namespace Ryujinx.Ava.Ui.ViewModels
await Dispatcher.UIThread.InvokeAsync(() => await Dispatcher.UIThread.InvokeAsync(() =>
{ {
Applications.Clear(); Applications.Clear();
_owner.LoadProgressBar.IsVisible = true; _owner.LoadProgressBar.IsVisible = true;
StatusBarProgressMaximum = 0; StatusBarProgressMaximum = 0;
StatusBarProgressValue = 0; StatusBarProgressValue = 0;
LocaleManager.Instance.UpdateDynamicValue("StatusBarGamesLoaded", 0, 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 = "PFS0", Extensions = { "pfs0" } });
dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } });
dialog.Filters.Add(new FileDialogFilter { Name = "NCA", Extensions = { "nca" } }); dialog.Filters.Add(new FileDialogFilter { Name = "NCA", Extensions = { "nca" } });
dialog.Filters.Add(new FileDialogFilter { Name = "NRO", Extensions = { "nro" } }); dialog.Filters.Add(new FileDialogFilter { Name = "NRO", Extensions = { "nro" } });
dialog.Filters.Add(new FileDialogFilter { Name = "NSO", Extensions = { "nso" } }); dialog.Filters.Add(new FileDialogFilter { Name = "NSO", Extensions = { "nso" } });
string[] files = await dialog.ShowAsync(_owner); string[] files = await dialog.ShowAsync(_owner);
@@ -878,10 +883,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
{ {
ShowUiKey = new KeyGesture(showUiKey, KeyModifiers.None); ShowUiKey = new KeyGesture(showUiKey, KeyModifiers.None);
} }
if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey)) if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey))
{ {
ScreenshotKey = new KeyGesture(screenshotKey, KeyModifiers.None); ScreenshotKey = new KeyGesture(screenshotKey, KeyModifiers.None);
} }
if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey)) if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey))
{ {
PauseKey = new KeyGesture(pauseKey, KeyModifiers.None); PauseKey = new KeyGesture(pauseKey, KeyModifiers.None);
@@ -941,9 +948,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
_lastFullscreenToggle = Environment.TickCount64; _lastFullscreenToggle = Environment.TickCount64;
WindowState state = _owner.WindowState; if (_owner.WindowState == WindowState.FullScreen)
if (state == WindowState.FullScreen)
{ {
_owner.WindowState = WindowState.Normal; _owner.WindowState = WindowState.Normal;
@@ -971,8 +976,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
{ {
if (IsGameRunning) 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) else if (IsGameRunning)
{ {
await Task.Delay(100); await Task.Delay(100);
_owner.AppHost?.ShowExitPrompt(); _owner.AppHost?.ShowExitPrompt();
} }
} }
@@ -994,6 +999,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
_owner.SettingsWindow = new(_owner.VirtualFileSystem, _owner.ContentManager); _owner.SettingsWindow = new(_owner.VirtualFileSystem, _owner.ContentManager);
await _owner.SettingsWindow.ShowDialog(_owner); await _owner.SettingsWindow.ShowDialog(_owner);
LoadConfigurableHotKeys(); LoadConfigurableHotKeys();
} }
@@ -1004,9 +1010,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
public async void OpenAboutWindow() public async void OpenAboutWindow()
{ {
AboutWindow window = new(); await new AboutWindow().ShowDialog(_owner);
await window.ShowDialog(_owner);
} }
public void ChangeLanguage(object obj) public void ChangeLanguage(object obj)
@@ -1020,7 +1024,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
try try
{ {
ProgressMaximum = total; ProgressMaximum = total;
ProgressValue = current; ProgressValue = current;
switch (state) switch (state)
{ {
@@ -1030,13 +1034,13 @@ namespace Ryujinx.Ava.Ui.ViewModels
{ {
case PtcLoadingState.Start: case PtcLoadingState.Start:
case PtcLoadingState.Loading: case PtcLoadingState.Loading:
LoadHeading = LocaleManager.Instance["CompilingPPTC"]; LoadHeading = LocaleManager.Instance["CompilingPPTC"];
IsLoadingIndeterminate = false; IsLoadingIndeterminate = false;
break; break;
case PtcLoadingState.Loaded: case PtcLoadingState.Loaded:
LoadHeading = string.Format(LocaleManager.Instance["LoadingHeading"], TitleName); LoadHeading = string.Format(LocaleManager.Instance["LoadingHeading"], TitleName);
IsLoadingIndeterminate = true; IsLoadingIndeterminate = true;
CacheLoadStatus = ""; CacheLoadStatus = "";
break; break;
} }
break; break;
@@ -1046,13 +1050,13 @@ namespace Ryujinx.Ava.Ui.ViewModels
{ {
case ShaderCacheLoadingState.Start: case ShaderCacheLoadingState.Start:
case ShaderCacheLoadingState.Loading: case ShaderCacheLoadingState.Loading:
LoadHeading = LocaleManager.Instance["CompilingShaders"]; LoadHeading = LocaleManager.Instance["CompilingShaders"];
IsLoadingIndeterminate = false; IsLoadingIndeterminate = false;
break; break;
case ShaderCacheLoadingState.Loaded: case ShaderCacheLoadingState.Loaded:
LoadHeading = string.Format(LocaleManager.Instance["LoadingHeading"], TitleName); LoadHeading = string.Format(LocaleManager.Instance["LoadingHeading"], TitleName);
IsLoadingIndeterminate = true; IsLoadingIndeterminate = true;
CacheLoadStatus = ""; CacheLoadStatus = "";
break; break;
} }
break; break;
@@ -1065,14 +1069,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void OpenUserSaveDirectory() public void OpenUserSaveDirectory()
{ {
var selection = SelectedApplication; ApplicationData selection = SelectedApplication;
if (selection != null) if (selection != null)
{ {
Task.Run(() => Task.Run(() =>
{ {
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
out ulong titleIdNumber))
{ {
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.Post(async () =>
{ {
@@ -1082,8 +1084,8 @@ namespace Ryujinx.Ava.Ui.ViewModels
return; return;
} }
var userId = new LibHac.Fs.UserId((ulong)_owner.AccountManager.LastOpenedUser.UserId.High, (ulong)_owner.AccountManager.LastOpenedUser.UserId.Low); UserId userId = new((ulong)_owner.AccountManager.LastOpenedUser.UserId.High, (ulong)_owner.AccountManager.LastOpenedUser.UserId.Low);
var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveType: default, userId, saveDataId: default, index: default); SaveDataFilter saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveType: default, userId, saveDataId: default, index: default);
OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber); OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber);
}); });
} }
@@ -1091,8 +1093,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void ToggleFavorite() public void ToggleFavorite()
{ {
var selection = SelectedApplication; ApplicationData selection = SelectedApplication;
if (selection != null) if (selection != null)
{ {
selection.Favorite = !selection.Favorite; selection.Favorite = !selection.Favorite;
@@ -1108,11 +1109,10 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void OpenModsDirectory() public void OpenModsDirectory()
{ {
var selection = SelectedApplication; ApplicationData selection = SelectedApplication;
if (selection != null) if (selection != null)
{ {
string modsBasePath = _owner.VirtualFileSystem.ModLoader.GetModsBasePath(); string modsBasePath = _owner.VirtualFileSystem.ModLoader.GetModsBasePath();
string titleModsPath = _owner.VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, selection.TitleId); string titleModsPath = _owner.VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, selection.TitleId);
OpenHelper.OpenFolder(titleModsPath); OpenHelper.OpenFolder(titleModsPath);
@@ -1121,12 +1121,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void OpenSdModsDirectory() public void OpenSdModsDirectory()
{ {
var selection = SelectedApplication; ApplicationData selection = SelectedApplication;
if (selection != null) if (selection != null)
{ {
string sdModsBasePath = _owner.VirtualFileSystem.ModLoader.GetSdModsBasePath(); 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); OpenHelper.OpenFolder(titleModsPath);
} }
@@ -1134,13 +1134,11 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void OpenPtcDirectory() public void OpenPtcDirectory()
{ {
var selection = SelectedApplication; ApplicationData selection = SelectedApplication;
if (selection != null) if (selection != null)
{ {
string ptcDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu"); string ptcDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu");
string mainPath = Path.Combine(ptcDir, "0");
string mainPath = Path.Combine(ptcDir, "0");
string backupPath = Path.Combine(ptcDir, "1"); string backupPath = Path.Combine(ptcDir, "1");
if (!Directory.Exists(ptcDir)) if (!Directory.Exists(ptcDir))
@@ -1156,16 +1154,18 @@ namespace Ryujinx.Ava.Ui.ViewModels
public async void PurgePtcCache() public async void PurgePtcCache()
{ {
var selection = SelectedApplication; ApplicationData selection = SelectedApplication;
if (selection != null) 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")); 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?). // FIXME: Found a way to reproduce the bold effect on the title name (fork?).
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"], UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"],
string.Format(LocaleManager.Instance["DialogPPTCDeletionMessage"], selection.TitleName), LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]); string.Format(LocaleManager.Instance["DialogPPTCDeletionMessage"], selection.TitleName),
LocaleManager.Instance["InputDialogYes"],
LocaleManager.Instance["InputDialogNo"],
LocaleManager.Instance["RyujinxConfirm"]);
List<FileInfo> cacheFiles = new(); List<FileInfo> cacheFiles = new();
@@ -1198,8 +1198,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void OpenShaderCacheDirectory() public void OpenShaderCacheDirectory()
{ {
var selection = SelectedApplication; ApplicationData selection = SelectedApplication;
if (selection != null) if (selection != null)
{ {
string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader"); string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader");
@@ -1220,18 +1219,20 @@ namespace Ryujinx.Ava.Ui.ViewModels
public async void PurgeShaderCache() public async void PurgeShaderCache()
{ {
var selection = SelectedApplication; ApplicationData selection = SelectedApplication;
if (selection != null) if (selection != null)
{ {
DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader")); 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?). // FIXME: Found a way to reproduce the bold effect on the title name (fork?).
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"], 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<DirectoryInfo> oldCacheDirectories = new();
List<FileInfo> newCacheFiles = new List<FileInfo>(); List<FileInfo> newCacheFiles = new();
if (shaderCacheDir.Exists) if (shaderCacheDir.Exists)
{ {
@@ -1279,38 +1280,28 @@ namespace Ryujinx.Ava.Ui.ViewModels
public async void OpenTitleUpdateManager() public async void OpenTitleUpdateManager()
{ {
var selection = SelectedApplication; ApplicationData selection = SelectedApplication;
if (selection != null) if (selection != null)
{ {
TitleUpdateWindow titleUpdateManager = await new TitleUpdateWindow(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(_owner);
new(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName);
await titleUpdateManager.ShowDialog(_owner);
} }
} }
public async void OpenDownloadableContentManager() public async void OpenDownloadableContentManager()
{ {
var selection = SelectedApplication; ApplicationData selection = SelectedApplication;
if (selection != null) if (selection != null)
{ {
DownloadableContentManagerWindow downloadableContentManager = new(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName); await new DownloadableContentManagerWindow(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(_owner);
await downloadableContentManager.ShowDialog(_owner);
} }
} }
public async void OpenCheatManager() public async void OpenCheatManager()
{ {
var selection = SelectedApplication; ApplicationData selection = SelectedApplication;
if (selection != null) if (selection != null)
{ {
CheatWindow cheatManager = new(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName); await new CheatWindow(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(_owner);
await cheatManager.ShowDialog(_owner);
} }
} }
@@ -1321,13 +1312,10 @@ namespace Ryujinx.Ava.Ui.ViewModels
return; return;
} }
var application = _owner.AppHost.Device.Application; ApplicationLoader application = _owner.AppHost.Device.Application;
if (application != null) if (application != null)
{ {
CheatWindow cheatManager = new(_owner.VirtualFileSystem, application.TitleIdText, application.TitleName); await new CheatWindow(_owner.VirtualFileSystem, application.TitleIdText, application.TitleName).ShowDialog(_owner);
await cheatManager.ShowDialog(_owner);
_owner.AppHost.Device.EnableCheats(); _owner.AppHost.Device.EnableCheats();
} }
@@ -1335,14 +1323,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void OpenDeviceSaveDirectory() public void OpenDeviceSaveDirectory()
{ {
var selection = SelectedApplication; ApplicationData selection = SelectedApplication;
if (selection != null) if (selection != null)
{ {
Task.Run(() => Task.Run(() =>
{ {
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
out ulong titleIdNumber))
{ {
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.Post(async () =>
{ {
@@ -1360,14 +1346,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void OpenBcatSaveDirectory() public void OpenBcatSaveDirectory()
{ {
var selection = SelectedApplication; ApplicationData selection = SelectedApplication;
if (selection != null) if (selection != null)
{ {
Task.Run(() => Task.Run(() =>
{ {
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
out ulong titleIdNumber))
{ {
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.Post(async () =>
{ {
@@ -1420,12 +1404,10 @@ namespace Ryujinx.Ava.Ui.ViewModels
_owner.Close(); _owner.Close();
} }
private async Task HandleFirmwareInstallation(string path) private async Task HandleFirmwareInstallation(string filename)
{ {
try try
{ {
string filename = path;
SystemVersion firmwareVersion = _owner.ContentManager.VerifyFirmwarePackage(filename); SystemVersion firmwareVersion = _owner.ContentManager.VerifyFirmwarePackage(filename);
if (firmwareVersion == null) if (firmwareVersion == null)
@@ -1437,7 +1419,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
string dialogTitle = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallTitle"], firmwareVersion.VersionString); string dialogTitle = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallTitle"], firmwareVersion.VersionString);
SystemVersion currentVersion = _owner.ContentManager.GetCurrentFirmwareVersion(); SystemVersion currentVersion = _owner.ContentManager.GetCurrentFirmwareVersion();
string dialogMessage = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallMessage"], firmwareVersion.VersionString); 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); string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallSuccessMessage"], firmwareVersion.VersionString);
await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance["InputDialogOk"], "", LocaleManager.Instance["RyujinxInfo"]); await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance["InputDialogOk"], "", LocaleManager.Instance["RyujinxInfo"]);
Logger.Info?.Print(LogClass.Application, message); Logger.Info?.Print(LogClass.Application, message);
// Purge Applet Cache. // 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) if (miiEditorCacheFolder.Exists)
{ {
@@ -1514,8 +1496,8 @@ namespace Ryujinx.Ava.Ui.ViewModels
catch (LibHac.Common.Keys.MissingKeyException ex) catch (LibHac.Common.Keys.MissingKeyException ex)
{ {
Logger.Error?.Print(LogClass.Application, ex.ToString()); 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) catch (Exception ex)
{ {
@@ -1527,8 +1509,8 @@ namespace Ryujinx.Ava.Ui.ViewModels
{ {
OpenFileDialog dialog = new() { AllowMultiple = false }; OpenFileDialog dialog = new() { AllowMultiple = false };
dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance["FileDialogAllTypes"], Extensions = { "xci", "zip" } }); 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 = "XCI", Extensions = { "xci" } });
dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } }); dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } });
string[] file = await dialog.ShowAsync(_owner); string[] file = await dialog.ShowAsync(_owner);

View File

@@ -3,89 +3,126 @@
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 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:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows" xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
SizeToContent="Height" Width="800"
Width="600" MinHeight="500" Height="500" Height="500"
WindowStartupLocation="CenterOwner"
MinWidth="600" MinWidth="600"
MinHeight="500"
SizeToContent="Height"
WindowStartupLocation="CenterOwner"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid Name="DownloadableContentGrid" Margin="15"> <Grid Name="DownloadableContentGrid" Margin="15">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock <TextBlock
Name="Heading"
Grid.Row="1" Grid.Row="1"
MaxWidth="500"
Margin="20,15,20,20" Margin="20,15,20,20"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
MaxWidth="500"
LineHeight="18" LineHeight="18"
TextWrapping="Wrap" TextAlignment="Center"
Text="{Binding Heading}" TextWrapping="Wrap" />
TextAlignment="Center" /> <DockPanel
<Border
Grid.Row="2" 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" Margin="5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
BorderBrush="Gray" BorderBrush="Gray"
BorderThickness="1"> BorderThickness="1">
<DataGrid <ScrollViewer
MinHeight="200"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
Items="{Binding DownloadableContents}"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<DataGrid.Columns> <DataGrid
<DataGridTemplateColumn Width="90"> Name="DlcDataGrid"
<DataGridTemplateColumn.CellTemplate> MinHeight="200"
<DataTemplate> HorizontalAlignment="Stretch"
<CheckBox VerticalAlignment="Stretch"
Width="50" CanUserReorderColumns="False"
MinWidth="40" CanUserResizeColumns="True"
HorizontalAlignment="Right" CanUserSortColumns="True"
IsChecked="{Binding Enabled}" /> HorizontalScrollBarVisibility="Auto"
</DataTemplate> Items="{Binding _downloadableContents}"
</DataGridTemplateColumn.CellTemplate> SelectionMode="Extended"
<DataGridTemplateColumn.Header> VerticalScrollBarVisibility="Auto">
<TextBlock Text="{locale:Locale DlcManagerTableHeadingEnabledLabel}" /> <DataGrid.Styles>
</DataGridTemplateColumn.Header> <Styles>
</DataGridTemplateColumn> <Style Selector="DataGridCell:nth-child(3), DataGridCell:nth-child(4)">
<DataGridTextColumn <Setter Property="HorizontalAlignment" Value="Left" />
Width="190" <Setter Property="HorizontalContentAlignment" Value="Left" />
Binding="{Binding TitleId}" </Style>
CanUserResize="True"> </Styles>
<DataGridTextColumn.Header> <Styles>
<TextBlock Text="{locale:Locale DlcManagerTableHeadingTitleIdLabel}" /> <Style Selector="DataGridCell:nth-child(1)">
</DataGridTextColumn.Header> <Setter Property="HorizontalAlignment" Value="Right" />
</DataGridTextColumn> <Setter Property="HorizontalContentAlignment" Value="Right" />
<DataGridTextColumn </Style>
Width="*" </Styles>
Binding="{Binding ContainerPath}" </DataGrid.Styles>
CanUserResize="True"> <DataGrid.Columns>
<DataGridTextColumn.Header> <DataGridTemplateColumn Width="90">
<TextBlock Text="{locale:Locale DlcManagerTableHeadingContainerPathLabel}" /> <DataGridTemplateColumn.CellTemplate>
</DataGridTextColumn.Header> <DataTemplate>
</DataGridTextColumn> <CheckBox
<DataGridTextColumn Width="50"
Width="*" MinWidth="40"
Binding="{Binding FullPath}" HorizontalAlignment="Center"
CanUserResize="True"> IsChecked="{Binding Enabled}" />
<DataGridTextColumn.Header> </DataTemplate>
<TextBlock Text="{locale:Locale DlcManagerTableHeadingFullPathLabel}" /> </DataGridTemplateColumn.CellTemplate>
</DataGridTextColumn.Header> <DataGridTemplateColumn.Header>
</DataGridTextColumn> <TextBlock Text="{locale:Locale DlcManagerTableHeadingEnabledLabel}" />
</DataGrid.Columns> </DataGridTemplateColumn.Header>
</DataGrid> </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> </Border>
<DockPanel <DockPanel
Grid.Row="3" Grid.Row="4"
Margin="0" Margin="0"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<DockPanel Margin="0" HorizontalAlignment="Left"> <DockPanel Margin="0" HorizontalAlignment="Left">

View File

@@ -8,6 +8,7 @@ using LibHac.FsSystem;
using LibHac.Tools.Fs; using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.FsSystem.NcaUtils;
using LibHac.Tools.FsSystem.Save;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls; using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Models; using Ryujinx.Ava.Ui.Models;
@@ -16,8 +17,11 @@ using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reactive.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Path = System.IO.Path; using Path = System.IO.Path;
@@ -27,14 +31,13 @@ namespace Ryujinx.Ava.Ui.Windows
public partial class DownloadableContentManagerWindow : StyleableWindow public partial class DownloadableContentManagerWindow : StyleableWindow
{ {
private readonly List<DownloadableContentContainer> _downloadableContentContainerList; private readonly List<DownloadableContentContainer> _downloadableContentContainerList;
private readonly string _downloadableContentJsonPath; private readonly string _downloadableContentJsonPath;
public VirtualFileSystem VirtualFileSystem { get; } private VirtualFileSystem _virtualFileSystem { get; }
public AvaloniaList<DownloadableContentModel> DownloadableContents { get; set; } = new AvaloniaList<DownloadableContentModel>(); private AvaloniaList<DownloadableContentModel> _downloadableContents { get; set; }
public ulong TitleId { get; }
public string TitleName { get; }
public string Heading => string.Format(LocaleManager.Instance["DlcWindowHeading"], TitleName, TitleId.ToString("X16")); private ulong TitleId { get; }
private string TitleName { get; }
public DownloadableContentManagerWindow() public DownloadableContentManagerWindow()
{ {
@@ -42,14 +45,15 @@ namespace Ryujinx.Ava.Ui.Windows
InitializeComponent(); 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) public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
{ {
VirtualFileSystem = virtualFileSystem; _virtualFileSystem = virtualFileSystem;
TitleId = titleId; _downloadableContents = new AvaloniaList<DownloadableContentModel>();
TitleName = titleName; TitleId = titleId;
TitleName = titleName;
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json"); _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
@@ -66,9 +70,24 @@ namespace Ryujinx.Ava.Ui.Windows
InitializeComponent(); 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(); 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() private void LoadDownloadableContents()
@@ -79,23 +98,23 @@ namespace Ryujinx.Ava.Ui.Windows
{ {
using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath); 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) 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(); 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) if (nca != null)
{ {
DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), _downloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"),
downloadableContentContainer.ContainerPath, downloadableContentContainer.ContainerPath,
downloadableContentNca.FullPath, downloadableContentNca.FullPath,
downloadableContentNca.Enabled)); downloadableContentNca.Enabled));
} }
} }
} }
@@ -105,11 +124,11 @@ namespace Ryujinx.Ava.Ui.Windows
Save(); Save();
} }
private Nca TryCreateNca(IStorage ncaStorage, string containerPath) private Nca TryOpenNca(IStorage ncaStorage, string containerPath)
{ {
try try
{ {
return new Nca(VirtualFileSystem.KeySet, ncaStorage); return new Nca(_virtualFileSystem.KeySet, ncaStorage);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -124,61 +143,73 @@ namespace Ryujinx.Ava.Ui.Windows
private async Task AddDownloadableContent(string path) 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; return;
} }
using (FileStream containerFile = File.OpenRead(path)) using FileStream containerFile = File.OpenRead(path);
PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage());
bool containsDownloadableContent = false;
_virtualFileSystem.ImportTickets(partitionFileSystem);
foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca"))
{ {
PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); using var ncaFile = new UniqueRef<IFile>();
bool containsDownloadableContent = false;
VirtualFileSystem.ImportTickets(pfs); partitionFileSystem.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path);
if (nca == null)
{ {
using var ncaFile = new UniqueRef<IFile>(); continue;
pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), path);
if (nca == null)
{
continue;
}
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));
containsDownloadableContent = true;
}
} }
if (!containsDownloadableContent) if (nca.Header.ContentType == NcaContentType.PublicData)
{ {
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]); if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != TitleId)
{
break;
}
_downloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true));
containsDownloadableContent = true;
} }
} }
if (!containsDownloadableContent)
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]);
}
} }
private void RemoveDownloadableContents(bool removeSelectedOnly = false) private void RemoveDownloadableContents(bool removeSelectedOnly = false)
{ {
if (removeSelectedOnly) 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 else
{ {
DownloadableContents.Clear(); _downloadableContents.Clear();
} }
PrintHeading();
} }
public void RemoveSelected() public void RemoveSelected()
@@ -191,6 +222,22 @@ namespace Ryujinx.Ava.Ui.Windows
RemoveDownloadableContents(); 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() public async void Add()
{ {
OpenFileDialog dialog = new OpenFileDialog() OpenFileDialog dialog = new OpenFileDialog()
@@ -214,6 +261,8 @@ namespace Ryujinx.Ava.Ui.Windows
await AddDownloadableContent(file); await AddDownloadableContent(file);
} }
} }
PrintHeading();
} }
public void Save() public void Save()
@@ -222,7 +271,7 @@ namespace Ryujinx.Ava.Ui.Windows
DownloadableContentContainer container = default; DownloadableContentContainer container = default;
foreach (DownloadableContentModel downloadableContent in DownloadableContents) foreach (DownloadableContentModel downloadableContent in _downloadableContents)
{ {
if (container.ContainerPath != downloadableContent.ContainerPath) if (container.ContainerPath != downloadableContent.ContainerPath)
{ {

View File

@@ -90,8 +90,8 @@ namespace Ryujinx.Ava.Ui.Windows
Title = $"Ryujinx {Program.Version}"; Title = $"Ryujinx {Program.Version}";
Height = Height / Program.WindowScaleFactor; Height /= Program.WindowScaleFactor;
Width = Width / Program.WindowScaleFactor; Width /= Program.WindowScaleFactor;
if (Program.PreviewerDetached) if (Program.PreviewerDetached)
{ {
@@ -523,23 +523,20 @@ namespace Ryujinx.Ava.Ui.Windows
public static void UpdateGraphicsConfig() public static void UpdateGraphicsConfig()
{ {
int resScale = ConfigurationState.Instance.Graphics.ResScale; GraphicsConfig.ResScale = ConfigurationState.Instance.Graphics.ResScale == -1 ? ConfigurationState.Instance.Graphics.ResScaleCustom : ConfigurationState.Instance.Graphics.ResScale;
float resScaleCustom = ConfigurationState.Instance.Graphics.ResScaleCustom; GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy;
GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
GraphicsConfig.ResScale = resScale == -1 ? resScaleCustom : resScale; GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache;
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.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression;
GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE; GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE;
} }
public void LoadHotKeys() 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(FullscreenHotKey2, new KeyGesture(Key.F11));
HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9)); HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9));
HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape)); HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape));
} }
public static void SaveConfig() public static void SaveConfig()

View File

@@ -75,7 +75,7 @@
Spacing="10"> Spacing="10">
<ListBox <ListBox
Name="GameList" Name="GameList"
MinHeight="150" MinHeight="250"
Items="{Binding GameDirectories}" /> Items="{Binding GameDirectories}" />
<Grid HorizontalAlignment="Stretch"> <Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>

View File

@@ -16,7 +16,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone; using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone;
namespace Ryujinx.Ava.Ui.Windows namespace Ryujinx.Ava.Ui.Windows
@@ -31,7 +30,7 @@ namespace Ryujinx.Ava.Ui.Windows
{ {
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["Settings"]}"; Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["Settings"]}";
ViewModel = new SettingsViewModel(virtualFileSystem, contentManager, this); ViewModel = new SettingsViewModel(virtualFileSystem, contentManager, this);
DataContext = ViewModel; DataContext = ViewModel;
InitializeComponent(); InitializeComponent();
@@ -48,7 +47,7 @@ namespace Ryujinx.Ava.Ui.Windows
public SettingsWindow() public SettingsWindow()
{ {
ViewModel = new SettingsViewModel(); ViewModel = new SettingsViewModel();
DataContext = ViewModel; DataContext = ViewModel;
InitializeComponent(); InitializeComponent();
@@ -79,7 +78,7 @@ namespace Ryujinx.Ava.Ui.Windows
PointerPressed += MouseClick; 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); IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard);
_currentAssigner.GetInputAndAssign(assigner); _currentAssigner.GetInputAndAssign(assigner);
@@ -92,6 +91,7 @@ namespace Ryujinx.Ava.Ui.Windows
_currentAssigner.Cancel(); _currentAssigner.Cancel();
_currentAssigner = null; _currentAssigner = null;
button.IsChecked = false; button.IsChecked = false;
} }
} }
@@ -122,36 +122,19 @@ namespace Ryujinx.Ava.Ui.Windows
{ {
if (e.SelectedItem is NavigationViewItem navitem) if (e.SelectedItem is NavigationViewItem navitem)
{ {
switch (navitem.Tag.ToString()) NavPanel.Content = navitem.Tag.ToString() switch
{ {
case "UiPage": "UiPage" => UiPage,
NavPanel.Content = UiPage; "InputPage" => InputPage,
break; "HotkeysPage" => HotkeysPage,
case "InputPage": "SystemPage" => SystemPage,
NavPanel.Content = InputPage; "CpuPage" => CpuPage,
break; "GraphicsPage" => GraphicsPage,
case "HotkeysPage": "AudioPage" => AudioPage,
NavPanel.Content = HotkeysPage; "NetworkPage" => NetworkPage,
break; "LoggingPage" => LoggingPage,
case "SystemPage": _ => throw new NotImplementedException()
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;
}
} }
} }
@@ -178,13 +161,18 @@ namespace Ryujinx.Ava.Ui.Windows
private void RemoveButton_OnClick(object sender, RoutedEventArgs e) 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.GameDirectories.Remove(path);
ViewModel.DirectoryChanged = true; ViewModel.DirectoryChanged = true;
} }
if (GameList.ItemCount > 0)
{
GameList.SelectedIndex = oldIndex < GameList.ItemCount ? oldIndex : 0;
}
} }
private void TimeZoneBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e) 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) private void SaveButton_Clicked(object sender, RoutedEventArgs e)
{ {
SaveSettings(); SaveSettings();
Close(); Close();
} }
@@ -232,7 +219,6 @@ namespace Ryujinx.Ava.Ui.Windows
private void SaveSettings() private void SaveSettings()
{ {
ViewModel.SaveSettings(); ViewModel.SaveSettings();
ControllerSettings?.SaveCurrentProfile(); ControllerSettings?.SaveCurrentProfile();
if (Owner is MainWindow window && ViewModel.DirectoryChanged) if (Owner is MainWindow window && ViewModel.DirectoryChanged)
@@ -246,8 +232,10 @@ namespace Ryujinx.Ava.Ui.Windows
protected override void OnClosed(EventArgs e) protected override void OnClosed(EventArgs e)
{ {
ControllerSettings.Dispose(); ControllerSettings.Dispose();
_currentAssigner?.Cancel(); _currentAssigner?.Cancel();
_currentAssigner = null; _currentAssigner = null;
base.OnClosed(e); base.OnClosed(e);
} }
} }

View File

@@ -131,6 +131,13 @@ namespace Ryujinx.Ava.Ui.Windows
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
TitleUpdates.Add(new TitleUpdateModel(controlData, path)); TitleUpdates.Add(new TitleUpdateModel(controlData, path));
foreach (var update in TitleUpdates)
{
update.IsEnabled = false;
}
TitleUpdates.Last().IsEnabled = true;
} }
else else
{ {

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 3868; private const uint CodeGenVersion = 3897;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";

View File

@@ -128,6 +128,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), baseAddressCbOffset) : GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), baseAddressCbOffset) :
(null, 0); (null, 0);
if (byteOffset != null)
{
ReplaceAddressAlignment(node.List, addrLow, byteOffset, constantOffset);
}
if (byteOffset == null) if (byteOffset == null)
{ {
Operand baseAddrLow = Cbuf(0, baseAddressCbOffset); Operand baseAddrLow = Cbuf(0, baseAddressCbOffset);
@@ -156,11 +161,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
byteOffset = offset; byteOffset = offset;
} }
if (byteOffset != null)
{
ReplaceAddressAlignment(node.List, addrLow, byteOffset, constantOffset);
}
if (isStg16Or8) if (isStg16Or8)
{ {
return byteOffset; return byteOffset;

View File

@@ -26,7 +26,7 @@
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" /> <PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
<PackageReference Include="OpenTK.Graphics" Version="4.7.2" /> <PackageReference Include="OpenTK.Graphics" Version="4.7.2" />
<PackageReference Include="SPB" Version="0.0.4-build28" /> <PackageReference Include="SPB" Version="0.0.4-build28" />
<PackageReference Include="SharpZipLib" Version="1.3.3" /> <PackageReference Include="SharpZipLib" Version="1.4.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" /> <PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
</ItemGroup> </ItemGroup>