Compare commits

..

4 Commits

Author SHA1 Message Date
gleng
7805d27e67 MacOS: Fix rendering on AMD GPUs (#5446)
* MacOS: Fix rendering on AMD GPUs

* Only disable MultiViewPort on MoltenVK for AMD GPUs
2023-07-11 03:00:19 +02:00
TSRBerry
6c515e1822 [Ryujinx.Ava] Address dotnet-format issues (#5361)
* dotnet format style --severity info

Some changes were manually reverted.

* dotnet format analyzers --serverity info

Some changes have been minimally adapted.

* Restore a few unused methods and variables

* Silence dotnet format IDE0060 warnings

* Silence dotnet format IDE0052 warnings

* Silence dotnet format IDE0059 warnings

* Address or silence dotnet format IDE1006 warnings

* Address dotnet format CA1816 warnings

* Address dotnet format CA1822 warnings

* Address or silence dotnet format CA1069 warnings

* Make dotnet format succeed in style mode

* Address dotnet format CA1401 warnings

* Address remaining dotnet format analyzer warnings

* Address review comments

* dotnet-format fixes after rebase

* Address most dotnet format whitespace warnings

* Apply dotnet format whitespace formatting

A few of them have been manually reverted and the corresponding warning was silenced

* Format if-blocks correctly

* Another rebase, another dotnet format run

* Run dotnet format whitespace after rebase

* Run dotnet format style after rebase

* Run dotnet format after rebase and remove unused usings

- analyzers
- style
- whitespace

* Add comments to disabled warnings

* Remove a few unused parameters

* Simplify properties and array initialization, Use const when possible, Remove trailing commas

* Start working on disabled warnings

* Fix and silence a few dotnet-format warnings again

* Address IDE0260 warnings

* Address a few disabled IDE0060 warnings

* Silence IDE0060 in .editorconfig

* Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas"

This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e.

* dotnet format whitespace after rebase

* dotnet format pass with new editorconfig

* Fix naming style issues

* Apply suggestions from code review

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Revert one suggestion

* Second dotnet format pass and fix build issues

* Final pass of dotnet format

* Add trailing commas

* Fix formatting issues

* Keep unnecessary assignment in IconColorPicker.cs

* Use using declarations and extend resource lifetimes

* Fix rebase issues

* Adjust comment spacing

* Fix typo

* Fix naming issues

* Apply suggestions from code review

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Revert unintentional change

* Remove unused file

* Remove static keyword from ViewModels

Binding of static members doesn't work and is silently ignored.

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-07-07 23:03:27 +02:00
Mary
8a363b5df2 Revert "sdl: set SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS to 0 (#5433)" (#5439)
This reverts commit 2b5abac809.
2023-07-06 18:08:14 +02:00
SuperSamus
2b5abac809 sdl: set SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS to 0 (#5433)
Nintendo controllers notoriously have the A/B and X/Y buttons swapped, compared to the standard.
In order to combat this, when setting the default controller layout, Ryujinx checks whether the controller name contains "Nintendo", and swaps the mapping accordingly.
However, the reason the mapping is inverted in the first place is because SDL has `SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS` set to 1 by default. By setting it to 0, the mapping will be based on the buttons' position instead.
So, by doing it (and removing the `isNintendoStyle` variable), we get the following advantages:
- The mapping will be the same on all controllers, removing the need to adjust custom mappings depending on what controller is used
- Users who already set `SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS` to 0 globally for other games/applications (like me) won't have a wrong default mapping
- Checking whether the controller name contains "Nintendo" is ugly

Disadvantages:
- Breaks the controller configuration for existing users who are using a Nintendo controller
2023-07-06 17:11:26 +02:00
109 changed files with 1141 additions and 1504 deletions

View File

@@ -5,7 +5,6 @@ using Avalonia.Styling;
using Avalonia.Threading;
using FluentAvalonia.Styling;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common;
@@ -67,7 +66,7 @@ namespace Ryujinx.Ava
if (result == UserResult.Yes)
{
var path = Process.GetCurrentProcess().MainModule.FileName;
var path = Environment.ProcessPath;
var proc = Process.Start(path, CommandLineState.Arguments);
desktop.Shutdown();
Environment.Exit(0);

View File

@@ -1,4 +1,5 @@
using ARMeilleure.Translation;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input;
@@ -26,6 +27,7 @@ using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
@@ -41,7 +43,6 @@ using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SPB.Graphics.Exceptions;
using SPB.Graphics.Vulkan;
using System;
using System.Collections.Generic;
@@ -50,10 +51,12 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing;
using Image = SixLabors.ImageSharp.Image;
using InputManager = Ryujinx.Input.HLE.InputManager;
using Key = Ryujinx.Input.Key;
using MouseButton = Ryujinx.Input.MouseButton;
using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
using Size = Avalonia.Size;
using Switch = Ryujinx.HLE.Switch;
@@ -66,9 +69,9 @@ namespace Ryujinx.Ava
private const int TargetFps = 60;
private const float VolumeDelta = 0.05f;
private static readonly Cursor InvisibleCursor = new(StandardCursorType.None);
private readonly IntPtr InvisibleCursorWin;
private readonly IntPtr DefaultCursorWin;
private static readonly Cursor _invisibleCursor = new(StandardCursorType.None);
private readonly IntPtr _invisibleCursorWin;
private readonly IntPtr _defaultCursorWin;
private readonly long _ticksPerFrame;
private readonly Stopwatch _chrono;
@@ -81,7 +84,7 @@ namespace Ryujinx.Ava
private readonly MainWindowViewModel _viewModel;
private readonly IKeyboard _keyboardInterface;
private readonly TopLevel _topLevel;
public RendererHost _rendererHost;
public RendererHost RendererHost;
private readonly GraphicsDebugLevel _glLogLevel;
private float _newVolume;
@@ -94,7 +97,7 @@ namespace Ryujinx.Ava
private bool _isActive;
private bool _renderingStarted;
private ManualResetEvent _gpuDoneEvent;
private readonly ManualResetEvent _gpuDoneEvent;
private IRenderer _renderer;
private readonly Thread _renderingThread;
@@ -150,14 +153,14 @@ namespace Ryujinx.Ava
VirtualFileSystem = virtualFileSystem;
ContentManager = contentManager;
_rendererHost = renderer;
RendererHost = renderer;
_chrono = new Stopwatch();
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
if (ApplicationPath.StartsWith("@SystemContent"))
{
ApplicationPath = _viewModel.VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath);
ApplicationPath = VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath);
_isFirmwareTitle = true;
}
@@ -170,8 +173,8 @@ namespace Ryujinx.Ava
if (OperatingSystem.IsWindows())
{
InvisibleCursorWin = CreateEmptyCursor();
DefaultCursorWin = CreateArrowCursor();
_invisibleCursorWin = CreateEmptyCursor();
_defaultCursorWin = CreateArrowCursor();
}
ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState;
@@ -196,10 +199,10 @@ namespace Ryujinx.Ava
{
_lastCursorMoveTime = Stopwatch.GetTimestamp();
if (_rendererHost.EmbeddedWindow.TransformedBounds != null)
if (RendererHost.EmbeddedWindow.TransformedBounds != null)
{
var point = e.GetCurrentPoint(window).Position;
var bounds = _rendererHost.EmbeddedWindow.TransformedBounds.Value.Clip;
var bounds = RendererHost.EmbeddedWindow.TransformedBounds.Value.Clip;
_isCursorInRenderer = point.X >= bounds.X &&
point.X <= bounds.Width + bounds.X &&
@@ -220,7 +223,7 @@ namespace Ryujinx.Ava
_renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
}
private void UpdateScalingFilter(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.ScalingFilter> e)
private void UpdateScalingFilter(object sender, ReactiveEventArgs<ScalingFilter> e)
{
_renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
_renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
@@ -234,7 +237,7 @@ namespace Ryujinx.Ava
if (OperatingSystem.IsWindows())
{
SetCursor(DefaultCursorWin);
SetCursor(_defaultCursorWin);
}
});
}
@@ -243,11 +246,11 @@ namespace Ryujinx.Ava
{
Dispatcher.UIThread.Post(() =>
{
_viewModel.Cursor = InvisibleCursor;
_viewModel.Cursor = _invisibleCursor;
if (OperatingSystem.IsWindows())
{
SetCursor(InvisibleCursorWin);
SetCursor(_invisibleCursorWin);
}
});
}
@@ -276,7 +279,7 @@ namespace Ryujinx.Ava
string directory = AppDataManager.Mode switch
{
AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"),
_ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx")
_ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"),
};
string path = Path.Combine(directory, filename);
@@ -305,9 +308,9 @@ namespace Ryujinx.Ava
image.Mutate(x => x.Flip(FlipMode.Vertical));
}
image.SaveAsPng(path, new PngEncoder()
image.SaveAsPng(path, new PngEncoder
{
ColorType = PngColorType.Rgb
ColorType = PngColorType.Rgb,
});
image.Dispose();
@@ -348,9 +351,9 @@ namespace Ryujinx.Ava
_viewModel.Title = $"Ryujinx {Program.Version} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
});
_viewModel.SetUIProgressHandlers(Device);
_viewModel.SetUiProgressHandlers(Device);
_rendererHost.SizeChanged += Window_SizeChanged;
RendererHost.SizeChanged += Window_SizeChanged;
_isActive = true;
@@ -379,7 +382,7 @@ namespace Ryujinx.Ava
}
}
private void UpdateAntiAliasing(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.AntiAliasing> e)
private void UpdateAntiAliasing(object sender, ReactiveEventArgs<AntiAliasing> e)
{
_renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue);
}
@@ -448,7 +451,7 @@ namespace Ryujinx.Ava
{
if (Device.Processes != null)
{
_viewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText);
MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText);
}
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
@@ -477,7 +480,7 @@ namespace Ryujinx.Ava
_windowsMultimediaTimerResolution = null;
}
if (_rendererHost.EmbeddedWindow is EmbeddedWindowOpenGL openGlWindow)
if (RendererHost.EmbeddedWindow is EmbeddedWindowOpenGL openGlWindow)
{
// Try to bind the OpenGL context before calling the shutdown event.
openGlWindow.MakeCurrent(false, false);
@@ -508,7 +511,7 @@ namespace Ryujinx.Ava
SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError))
{
@@ -526,7 +529,7 @@ namespace Ryujinx.Ava
if (result != UserResult.Yes)
{
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow));
await UserErrorDialog.ShowUserErrorDialog(userError);
Device.Dispose();
return false;
@@ -535,7 +538,7 @@ namespace Ryujinx.Ava
if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _))
{
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow));
await UserErrorDialog.ShowUserErrorDialog(userError);
Device.Dispose();
return false;
@@ -558,7 +561,7 @@ namespace Ryujinx.Ava
}
else
{
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow));
await UserErrorDialog.ShowUserErrorDialog(userError);
Device.Dispose();
return false;
@@ -727,7 +730,7 @@ namespace Ryujinx.Ava
{
renderer = new VulkanRenderer(
Vk.GetApi(),
(_rendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface,
(RendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface,
VulkanHelper.GetRequiredInstanceExtensions,
ConfigurationState.Instance.Graphics.PreferredGpu.Value);
}
@@ -738,18 +741,18 @@ namespace Ryujinx.Ava
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
var isGALthreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading);
if (isGALthreaded)
var isGALThreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading);
if (isGALThreaded)
{
renderer = new ThreadedRenderer(renderer);
}
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALthreaded}");
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}");
// Initialize Configuration.
var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? HLE.MemoryConfiguration.MemoryConfiguration6GiB : HLE.MemoryConfiguration.MemoryConfiguration4GiB;
var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB;
HLE.HLEConfiguration configuration = new(VirtualFileSystem,
HLEConfiguration configuration = new(VirtualFileSystem,
_viewModel.LibHacHorizonManager,
ContentManager,
_accountManager,
@@ -780,12 +783,12 @@ namespace Ryujinx.Ava
private static IHardwareDeviceDriver InitializeAudio()
{
var availableBackends = new List<AudioBackend>()
var availableBackends = new List<AudioBackend>
{
AudioBackend.SDL2,
AudioBackend.SoundIo,
AudioBackend.OpenAl,
AudioBackend.Dummy
AudioBackend.Dummy,
};
AudioBackend preferredBackend = ConfigurationState.Instance.System.AudioBackend.Value;
@@ -806,13 +809,11 @@ namespace Ryujinx.Ava
{
return new T();
}
else
{
Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}.");
return null;
}
}
IHardwareDeviceDriver deviceDriver = null;
@@ -826,7 +827,7 @@ namespace Ryujinx.Ava
AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend),
AudioBackend.SoundIo => InitializeAudioBackend<SoundIoHardwareDeviceDriver>(AudioBackend.SoundIo, nextBackend),
AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend),
_ => new DummyHardwareDeviceDriver()
_ => new DummyHardwareDeviceDriver(),
};
if (deviceDriver != null)
@@ -879,7 +880,7 @@ namespace Ryujinx.Ava
_renderer.ScreenCaptured += Renderer_ScreenCaptured;
(_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.InitializeBackgroundContext(_renderer);
(RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.InitializeBackgroundContext(_renderer);
Device.Gpu.Renderer.Initialize(_glLogLevel);
@@ -887,8 +888,8 @@ namespace Ryujinx.Ava
_renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
_renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
Width = (int)_rendererHost.Bounds.Width;
Height = (int)_rendererHost.Bounds.Height;
Width = (int)RendererHost.Bounds.Width;
Height = (int)RendererHost.Bounds.Height;
_renderer.Window.SetSize((int)(Width * _topLevel.PlatformImpl.RenderScaling), (int)(Height * _topLevel.PlatformImpl.RenderScaling));
@@ -923,7 +924,7 @@ namespace Ryujinx.Ava
_viewModel.SwitchToRenderer(false);
}
Device.PresentFrame(() => (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers());
Device.PresentFrame(() => (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers());
}
if (_ticks >= _ticksPerFrame)
@@ -941,7 +942,7 @@ namespace Ryujinx.Ava
_gpuDoneEvent.Set();
});
(_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true);
(RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true);
}
public void UpdateStatus()

View File

@@ -18,7 +18,6 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common.Helper;
@@ -27,6 +26,7 @@ using System.Buffers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ApplicationId = LibHac.Ncm.ApplicationId;
using Path = System.IO.Path;
namespace Ryujinx.Ava.Common
@@ -57,7 +57,7 @@ namespace Ryujinx.Ava.Common
Logger.Info?.Print(LogClass.Application, $"Creating save directory for Title: {titleName} [{titleId:x16}]");
if (Utilities.IsZeros(controlHolder.ByteSpan))
if (controlHolder.ByteSpan.IsZeros())
{
// If the current application doesn't have a loaded control property, create a dummy one
// and set the savedata sizes so a user savedata will be created.
@@ -72,7 +72,7 @@ namespace Ryujinx.Ava.Common
Uid user = new((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low);
result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new LibHac.Ncm.ApplicationId(titleId), in control, in user);
result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new ApplicationId(titleId), in control, in user);
if (result.IsFailure())
{
Dispatcher.UIThread.InvokeAsync(async () =>
@@ -147,7 +147,7 @@ namespace Ryujinx.Ava.Common
{
OpenFolderDialog folderDialog = new()
{
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle]
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle],
};
string destination = await folderDialog.ShowAsync(_owner);
@@ -293,10 +293,11 @@ namespace Ryujinx.Ava.Common
await ContentDialogHelper.CreateErrorDialog(ex.Message);
});
}
});
extractorThread.Name = "GUI.NcaSectionExtractorThread";
extractorThread.IsBackground = true;
})
{
Name = "GUI.NcaSectionExtractorThread",
IsBackground = true,
};
extractorThread.Start();
}
}

View File

@@ -10,6 +10,6 @@ namespace Ryujinx.Ava.Common
FileType,
FileSize,
Path,
Favorite
Favorite,
}
}

View File

@@ -11,6 +11,6 @@
ResScaleUp,
ResScaleDown,
VolumeUp,
VolumeDown
VolumeDown,
}
}

View File

@@ -21,7 +21,7 @@ namespace Ryujinx.Ava.Common.Locale
ReflectionBindingExtension binding = new($"[{keyToUse}]")
{
Mode = BindingMode.OneWay,
Source = LocaleManager.Instance
Source = LocaleManager.Instance,
};
return binding.ProvideValue(serviceProvider);

View File

@@ -13,11 +13,11 @@ namespace Ryujinx.Ava.Common.Locale
{
private const string DefaultLanguageCode = "en_US";
private Dictionary<LocaleKeys, string> _localeStrings;
private readonly Dictionary<LocaleKeys, string> _localeStrings;
private Dictionary<LocaleKeys, string> _localeDefaultStrings;
private readonly ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues;
public static LocaleManager Instance { get; } = new LocaleManager();
public static LocaleManager Instance { get; } = new();
public LocaleManager()
{
@@ -126,7 +126,7 @@ namespace Ryujinx.Ava.Common.Locale
}
}
private Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode)
private static Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode)
{
var localeStrings = new Dictionary<LocaleKeys, string>();
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");

View File

@@ -120,6 +120,7 @@ namespace Ryujinx.Ava.Input
_buttonsUserMapping.Clear();
#pragma warning disable IDE0055 // Disable formatting
// Left JoyCon
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (Key)_configuration.LeftJoyconStick.StickButton));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (Key)_configuration.LeftJoycon.DpadUp));
@@ -143,6 +144,7 @@ namespace Ryujinx.Ava.Input
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl));
#pragma warning restore IDE0055
}
}

View File

@@ -143,7 +143,7 @@ namespace Ryujinx.Ava.Input
AvaKey.OemBackslash,
// NOTE: invalid
AvaKey.None
AvaKey.None,
};
private static readonly Dictionary<AvaKey, Key> _avaKeyMapping;

View File

@@ -31,13 +31,13 @@ namespace Ryujinx.Modules
{
internal static class Updater
{
private const string GitHubApiURL = "https://api.github.com";
private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
private const string GitHubApiUrl = "https://api.github.com";
private static readonly GithubReleasesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory;
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish");
private static readonly int ConnectionCount = 4;
private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory;
private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
private static readonly string _updatePublishDir = Path.Combine(_updateDir, "publish");
private static readonly int _connectionCount = 4;
private static string _buildVer;
private static string _platformExt;
@@ -46,7 +46,7 @@ namespace Ryujinx.Modules
private static bool _updateSuccessful;
private static bool _running;
private static readonly string[] WindowsDependencyDirs = Array.Empty<string>();
private static readonly string[] _windowsDependencyDirs = Array.Empty<string>();
public static async Task BeginParse(Window mainWindow, bool showVersionUpToDate)
{
@@ -99,9 +99,9 @@ namespace Ryujinx.Modules
{
using HttpClient jsonClient = ConstructHttpClient();
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse);
string buildInfoUrl = $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
string fetchedJson = await jsonClient.GetStringAsync(buildInfoUrl);
var fetched = JsonHelper.Deserialize(fetchedJson, _serializerContext.GithubReleasesJsonResponse);
_buildVer = fetched.Name;
foreach (var asset in fetched.Assets)
@@ -195,8 +195,7 @@ namespace Ryujinx.Modules
}
// Fetch build size information to learn chunk sizes.
using (HttpClient buildSizeClient = ConstructHttpClient())
{
using HttpClient buildSizeClient = ConstructHttpClient();
try
{
buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
@@ -212,7 +211,6 @@ namespace Ryujinx.Modules
_buildSize = -1;
}
}
Dispatcher.UIThread.Post(async () =>
{
@@ -248,23 +246,22 @@ namespace Ryujinx.Modules
_updateSuccessful = false;
// Empty update dir, although it shouldn't ever have anything inside it
if (Directory.Exists(UpdateDir))
if (Directory.Exists(_updateDir))
{
Directory.Delete(UpdateDir, true);
Directory.Delete(_updateDir, true);
}
Directory.CreateDirectory(UpdateDir);
Directory.CreateDirectory(_updateDir);
string updateFile = Path.Combine(UpdateDir, "update.bin");
string updateFile = Path.Combine(_updateDir, "update.bin");
TaskDialog taskDialog = new()
{
Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading],
IconSource = new SymbolIconSource { Symbol = Symbol.Download },
Buttons = { },
ShowProgressBar = true,
XamlRoot = parent
XamlRoot = parent,
};
taskDialog.Opened += (s, e) =>
@@ -301,7 +298,7 @@ namespace Ryujinx.Modules
if (OperatingSystem.IsMacOS())
{
string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", ".."));
string newBundlePath = Path.Combine(UpdateDir, "Ryujinx.app");
string newBundlePath = Path.Combine(_updateDir, "Ryujinx.app");
string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh");
string currentPid = Environment.ProcessId.ToString();
@@ -328,7 +325,7 @@ namespace Ryujinx.Modules
ProcessStartInfo processStart = new(ryuName)
{
UseShellExecute = true,
WorkingDirectory = executableDirectory
WorkingDirectory = executableDirectory,
};
foreach (string argument in CommandLineState.Arguments)
@@ -347,22 +344,22 @@ namespace Ryujinx.Modules
private static void DoUpdateWithMultipleThreads(TaskDialog taskDialog, string downloadUrl, string updateFile)
{
// Multi-Threaded Updater
long chunkSize = _buildSize / ConnectionCount;
long remainderChunk = _buildSize % ConnectionCount;
long chunkSize = _buildSize / _connectionCount;
long remainderChunk = _buildSize % _connectionCount;
int completedRequests = 0;
int totalProgressPercentage = 0;
int[] progressPercentage = new int[ConnectionCount];
int[] progressPercentage = new int[_connectionCount];
List<byte[]> list = new(ConnectionCount);
List<WebClient> webClients = new(ConnectionCount);
List<byte[]> list = new(_connectionCount);
List<WebClient> webClients = new(_connectionCount);
for (int i = 0; i < ConnectionCount; i++)
for (int i = 0; i < _connectionCount; i++)
{
list.Add(Array.Empty<byte>());
}
for (int i = 0; i < ConnectionCount; i++)
for (int i = 0; i < _connectionCount; i++)
{
#pragma warning disable SYSLIB0014
// TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient.
@@ -371,7 +368,7 @@ namespace Ryujinx.Modules
webClients.Add(client);
if (i == ConnectionCount - 1)
if (i == _connectionCount - 1)
{
client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}");
}
@@ -388,7 +385,7 @@ namespace Ryujinx.Modules
Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage);
Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage);
taskDialog.SetProgressBarState(totalProgressPercentage / ConnectionCount, TaskDialogProgressState.Normal);
taskDialog.SetProgressBarState(totalProgressPercentage / _connectionCount, TaskDialogProgressState.Normal);
};
client.DownloadDataCompleted += (_, args) =>
@@ -407,10 +404,10 @@ namespace Ryujinx.Modules
list[index] = args.Result;
Interlocked.Increment(ref completedRequests);
if (Equals(completedRequests, ConnectionCount))
if (Equals(completedRequests, _connectionCount))
{
byte[] mergedFileBytes = new byte[_buildSize];
for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++)
for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < _connectionCount; connectionIndex++)
{
Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length);
destinationOffset += list[connectionIndex].Length;
@@ -421,11 +418,10 @@ namespace Ryujinx.Modules
// On macOS, ensure that we remove the quarantine bit to prevent Gatekeeper from blocking execution.
if (OperatingSystem.IsMacOS())
{
using (Process xattrProcess = Process.Start("xattr", new List<string> { "-d", "com.apple.quarantine", updateFile }))
{
using Process xattrProcess = Process.Start("xattr", new List<string> { "-d", "com.apple.quarantine", updateFile });
xattrProcess.WaitForExit();
}
}
try
{
@@ -437,8 +433,6 @@ namespace Ryujinx.Modules
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
return;
}
}
};
@@ -470,9 +464,8 @@ namespace Ryujinx.Modules
// We do not want to timeout while downloading
client.Timeout = TimeSpan.FromDays(1);
using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result)
using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result)
{
using HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result;
using Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result;
using Stream updateFileStream = File.Open(updateFile, FileMode.Create);
long totalBytes = response.Content.Headers.ContentLength.Value;
@@ -495,7 +488,6 @@ namespace Ryujinx.Modules
updateFileStream.Write(buffer, 0, readSize);
}
}
InstallUpdate(taskDialog, updateFile);
}
@@ -510,7 +502,7 @@ namespace Ryujinx.Modules
{
Thread worker = new(() => DoUpdateWithSingleThreadWorker(taskDialog, downloadUrl, updateFile))
{
Name = "Updater.SingleThreadWorker"
Name = "Updater.SingleThreadWorker",
};
worker.Start();
@@ -537,10 +529,8 @@ namespace Ryujinx.Modules
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
using (FileStream outStream = File.OpenWrite(outPath))
{
using FileStream outStream = File.OpenWrite(outPath);
tarStream.CopyEntryContents(outStream);
}
File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode);
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
@@ -566,17 +556,19 @@ namespace Ryujinx.Modules
foreach (ZipEntry zipEntry in zipFile)
{
count++;
if (zipEntry.IsDirectory) continue;
if (zipEntry.IsDirectory)
{
continue;
}
string outPath = Path.Combine(outputDirectoryPath, zipEntry.Name);
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
using (Stream zipStream = zipFile.GetInputStream(zipEntry))
using (FileStream outStream = File.OpenWrite(outPath))
{
using Stream zipStream = zipFile.GetInputStream(zipEntry);
using FileStream outStream = File.OpenWrite(outPath);
zipStream.CopyTo(outStream);
}
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc));
@@ -597,11 +589,11 @@ namespace Ryujinx.Modules
{
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{
ExtractTarGzipFile(taskDialog, updateFile, UpdateDir);
ExtractTarGzipFile(taskDialog, updateFile, _updateDir);
}
else if (OperatingSystem.IsWindows())
{
ExtractZipFile(taskDialog, updateFile, UpdateDir);
ExtractZipFile(taskDialog, updateFile, _updateDir);
}
else
{
@@ -648,10 +640,10 @@ namespace Ryujinx.Modules
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
});
MoveAllFilesOver(UpdatePublishDir, HomeDir, taskDialog);
MoveAllFilesOver(_updatePublishDir, _homeDir, taskDialog);
});
Directory.Delete(UpdateDir, true);
Directory.Delete(_updateDir, true);
}
_updateSuccessful = true;
@@ -738,15 +730,15 @@ namespace Ryujinx.Modules
// NOTE: This method should always reflect the latest build layout.
private static IEnumerable<string> EnumerateFilesToDelete()
{
var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir.
var files = Directory.EnumerateFiles(_homeDir); // All files directly in base dir.
// Determine and exclude user files only when the updater is running, not when cleaning old files
if (_running && !OperatingSystem.IsMacOS())
{
// Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list.
var oldFiles = Directory.EnumerateFiles(HomeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
var newFiles = Directory.EnumerateFiles(UpdatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(HomeDir, filename));
var oldFiles = Directory.EnumerateFiles(_homeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
var newFiles = Directory.EnumerateFiles(_updatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(_homeDir, filename));
// Remove user files from the paths in files.
files = files.Except(userFiles);
@@ -754,9 +746,9 @@ namespace Ryujinx.Modules
if (OperatingSystem.IsWindows())
{
foreach (string dir in WindowsDependencyDirs)
foreach (string dir in _windowsDependencyDirs)
{
string dirPath = Path.Combine(HomeDir, dir);
string dirPath = Path.Combine(_homeDir, dir);
if (Directory.Exists(dirPath))
{
files = files.Concat(Directory.EnumerateFiles(dirPath, "*", SearchOption.AllDirectories));
@@ -798,7 +790,7 @@ namespace Ryujinx.Modules
public static void CleanupUpdate()
{
foreach (string file in Directory.GetFiles(HomeDir, "*.ryuold", SearchOption.AllDirectories))
foreach (string file in Directory.GetFiles(_homeDir, "*.ryuold", SearchOption.AllDirectories))
{
File.Delete(file);
}

View File

@@ -31,7 +31,7 @@ namespace Ryujinx.Ava
[LibraryImport("user32.dll", SetLastError = true)]
public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
private const uint MB_ICONWARNING = 0x30;
private const uint MbIconwarning = 0x30;
public static void Main(string[] args)
{
@@ -39,7 +39,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}", MbIconwarning);
}
PreviewerDetached = true;
@@ -60,7 +60,7 @@ namespace Ryujinx.Ava
EnableMultiTouch = true,
EnableIme = true,
UseEGL = false,
UseGpu = true
UseGpu = true,
})
.With(new Win32PlatformOptions
{
@@ -192,7 +192,7 @@ namespace Ryujinx.Ava
"never" => HideCursorMode.Never,
"onidle" => HideCursorMode.OnIdle,
"always" => HideCursorMode.Always,
_ => ConfigurationState.Instance.HideCursor.Value
_ => ConfigurationState.Instance.HideCursor.Value,
};
}
}

View File

@@ -64,7 +64,7 @@ namespace Ryujinx.Ava.UI.Applet
LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
(int)Symbol.Important,
deferEvent,
async (window) =>
async window =>
{
if (opened)
{
@@ -112,7 +112,7 @@ namespace Ryujinx.Ava.UI.Applet
{
try
{
var response = await SwkbdAppletDialog.ShowInputDialog(_parent, LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args);
var response = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args);
if (response.Result == UserResult.Ok)
{
@@ -142,10 +142,7 @@ namespace Ryujinx.Ava.UI.Applet
public void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value)
{
device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value);
if (_parent.ViewModel.AppHost != null)
{
_parent.ViewModel.AppHost.Stop();
}
_parent.ViewModel.AppHost?.Stop();
}
public bool DisplayErrorAppletDialog(string title, string message, string[] buttons)
@@ -162,7 +159,7 @@ namespace Ryujinx.Ava.UI.Applet
{
Title = title,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
Width = 400
Width = 400,
};
object response = await msgDialog.Run();

View File

@@ -3,13 +3,11 @@ using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Threading;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.Ui;
using System;
using System.Threading;
using HidKey = Ryujinx.Common.Configuration.Hid.Key;
namespace Ryujinx.Ava.UI.Applet
@@ -17,7 +15,7 @@ namespace Ryujinx.Ava.UI.Applet
class AvaloniaDynamicTextInputHandler : IDynamicTextInputHandler
{
private MainWindow _parent;
private OffscreenTextBox _hiddenTextBox;
private readonly OffscreenTextBox _hiddenTextBox;
private bool _canProcessInput;
private IDisposable _textChangedSubscription;
private IDisposable _selectionStartChangedSubscription;
@@ -76,7 +74,7 @@ namespace Ryujinx.Ava.UI.Applet
return;
}
e.RoutedEvent = _hiddenTextBox.GetKeyUpRoutedEvent();
e.RoutedEvent = OffscreenTextBox.GetKeyUpRoutedEvent();
Dispatcher.UIThread.InvokeAsync(() =>
{
@@ -96,7 +94,7 @@ namespace Ryujinx.Ava.UI.Applet
return;
}
e.RoutedEvent = _hiddenTextBox.GetKeyUpRoutedEvent();
e.RoutedEvent = OffscreenTextBox.GetKeyUpRoutedEvent();
Dispatcher.UIThread.InvokeAsync(() =>
{

View File

@@ -9,7 +9,7 @@ namespace Ryujinx.Ava.UI.Applet
{
public AvaloniaHostUiTheme(MainWindow parent)
{
FontFamily = OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000, 0) ? "Segoe UI Variable" : parent.FontFamily.Name;
FontFamily = OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000) ? "Segoe UI Variable" : parent.FontFamily.Name;
DefaultBackgroundColor = BrushToThemeColor(parent.Background);
DefaultForegroundColor = BrushToThemeColor(parent.Foreground);
DefaultBorderColor = BrushToThemeColor(parent.BorderBrush);
@@ -25,7 +25,7 @@ namespace Ryujinx.Ava.UI.Applet
public ThemeColor SelectionBackgroundColor { get; }
public ThemeColor SelectionForegroundColor { get; }
private ThemeColor BrushToThemeColor(IBrush brush)
private static ThemeColor BrushToThemeColor(IBrush brush)
{
if (brush is SolidColorBrush solidColor)
{
@@ -34,10 +34,8 @@ namespace Ryujinx.Ava.UI.Applet
(float)solidColor.Color.G / 255,
(float)solidColor.Color.B / 255);
}
else
{
return new ThemeColor();
}
}
}
}

View File

@@ -1,10 +1,12 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Windows;
using System.Threading.Tasks;
#if DEBUG
using Avalonia;
#endif
namespace Ryujinx.Ava.UI.Applet
{

View File

@@ -1,13 +1,10 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.HOS.Applets;
using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard;
using System;
@@ -22,7 +19,7 @@ namespace Ryujinx.Ava.UI.Controls
private Predicate<string> _checkInput = _ => true;
private int _inputMax;
private int _inputMin;
private string _placeholder;
private readonly string _placeholder;
private ContentDialog _host;
@@ -57,13 +54,13 @@ namespace Ryujinx.Ava.UI.Controls
public string MainText { get; set; } = "";
public string SecondaryText { get; set; } = "";
public static async Task<(UserResult Result, string Input)> ShowInputDialog(StyleableWindow window, string title, SoftwareKeyboardUiArgs args)
public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, SoftwareKeyboardUiArgs args)
{
ContentDialog contentDialog = new ContentDialog();
ContentDialog contentDialog = new();
UserResult result = UserResult.Cancel;
SwkbdAppletDialog content = new SwkbdAppletDialog(args.HeaderText, args.SubtitleText, args.GuideText, args.InitialText);
SwkbdAppletDialog content = new(args.HeaderText, args.SubtitleText, args.GuideText, args.InitialText);
string input = string.Empty;
@@ -78,15 +75,16 @@ namespace Ryujinx.Ava.UI.Controls
contentDialog.CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel];
contentDialog.Content = content;
TypedEventHandler<ContentDialog, ContentDialogClosedEventArgs> handler = (sender, eventArgs) =>
void Handler(ContentDialog sender, ContentDialogClosedEventArgs eventArgs)
{
if (eventArgs.Result == ContentDialogResult.Primary)
{
result = UserResult.Ok;
input = content.Input.Text;
}
};
contentDialog.Closed += handler;
}
contentDialog.Closed += Handler;
await ContentDialogHelper.ShowAsync(contentDialog);

View File

@@ -18,7 +18,6 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Path = System.IO.Path;
using UserId = LibHac.Fs.UserId;
namespace Ryujinx.Ava.UI.Controls
{
@@ -53,7 +52,7 @@ namespace Ryujinx.Ava.UI.Controls
public void OpenUserSaveDirectory_Click(object sender, RoutedEventArgs args)
{
if ((sender as MenuItem)?.DataContext is MainWindowViewModel viewModel)
if (sender is MenuItem { DataContext: MainWindowViewModel viewModel })
{
OpenSaveDirectory(viewModel, SaveDataType.Account, userId: new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low));
}

View File

@@ -2,7 +2,6 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Styling;
using Avalonia.Threading;
using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls;
using LibHac;
using LibHac.Common;
@@ -10,7 +9,6 @@ using LibHac.Fs;
using LibHac.Fs.Shim;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Views.User;
using Ryujinx.HLE.FileSystem;
@@ -19,6 +17,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UserId = Ryujinx.HLE.HOS.Services.Account.Acc.UserId;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.UI.Controls
@@ -56,7 +55,7 @@ namespace Ryujinx.Ava.UI.Controls
InitializeComponent();
}
public void GoBack(object parameter = null)
public void GoBack()
{
if (ContentFrame.BackStack.Count > 0)
{
@@ -75,14 +74,14 @@ namespace Ryujinx.Ava.UI.Controls
VirtualFileSystem ownerVirtualFileSystem, HorizonClient ownerHorizonClient)
{
var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient);
ContentDialog contentDialog = new ContentDialog
ContentDialog contentDialog = new()
{
Title = LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle],
PrimaryButtonText = "",
SecondaryButtonText = "",
CloseButtonText = "",
Content = content,
Padding = new Thickness(0)
Padding = new Thickness(0),
};
contentDialog.Closed += (sender, args) =>
@@ -125,7 +124,7 @@ namespace Ryujinx.Ava.UI.Controls
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
HashSet<HLE.HOS.Services.Account.Acc.UserId> lostAccounts = new();
HashSet<UserId> lostAccounts = new();
while (true)
{
@@ -139,7 +138,7 @@ namespace Ryujinx.Ava.UI.Controls
for (int i = 0; i < readCount; i++)
{
var save = saveDataInfo[i];
var id = new HLE.HOS.Services.Account.Acc.UserId((long)save.UserId.Id.Low, (long)save.UserId.Id.High);
var id = new UserId((long)save.UserId.Id.Low, (long)save.UserId.Id.High);
if (ViewModel.Profiles.Cast<UserProfile>().FirstOrDefault(x => x.UserId == id) == null)
{
lostAccounts.Add(id);
@@ -166,7 +165,7 @@ namespace Ryujinx.Ava.UI.Controls
if (profile == null)
{
async void Action()
static async void Action()
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionWarningMessage]);
}

View File

@@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Threading;
using FluentAvalonia.Core;
@@ -36,13 +37,12 @@ namespace Ryujinx.Ava.UI.Helpers
PrimaryButtonText = primaryButton,
SecondaryButtonText = secondaryButton,
CloseButtonText = closeButton,
Content = content
};
contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
Content = content,
PrimaryButtonCommand = MiniCommand.Create(() =>
{
result = primaryButtonResult;
});
}),
};
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
{
@@ -96,7 +96,6 @@ namespace Ryujinx.Ava.UI.Helpers
Func<Window, Task> doWhileDeferred = null)
{
bool startedDeferring = false;
UserResult result = UserResult.None;
return await ShowTextDialog(
title,
@@ -123,8 +122,6 @@ namespace Ryujinx.Ava.UI.Helpers
var deferral = args.GetDeferral();
result = primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok;
sender.PrimaryButtonClick -= DeferClose;
_ = Task.Run(() =>
@@ -150,10 +147,10 @@ namespace Ryujinx.Ava.UI.Helpers
{
Grid content = new()
{
RowDefinitions = new RowDefinitions() { new RowDefinition(), new RowDefinition() },
ColumnDefinitions = new ColumnDefinitions() { new ColumnDefinition(GridLength.Auto), new ColumnDefinition() },
RowDefinitions = new RowDefinitions { new(), new() },
ColumnDefinitions = new ColumnDefinitions { new(GridLength.Auto), new() },
MinHeight = 80
MinHeight = 80,
};
SymbolIcon icon = new()
@@ -161,7 +158,7 @@ namespace Ryujinx.Ava.UI.Helpers
Symbol = (Symbol)symbol,
Margin = new Thickness(10),
FontSize = 40,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
VerticalAlignment = VerticalAlignment.Center,
};
Grid.SetColumn(icon, 0);
@@ -173,7 +170,7 @@ namespace Ryujinx.Ava.UI.Helpers
Text = primaryText,
Margin = new Thickness(5),
TextWrapping = TextWrapping.Wrap,
MaxWidth = 450
MaxWidth = 450,
};
TextBlock secondaryLabel = new()
@@ -181,7 +178,7 @@ namespace Ryujinx.Ava.UI.Helpers
Text = secondaryText,
Margin = new Thickness(5),
TextWrapping = TextWrapping.Wrap,
MaxWidth = 450
MaxWidth = 450,
};
Grid.SetColumn(primaryLabel, 1);
@@ -318,14 +315,14 @@ namespace Ryujinx.Ava.UI.Helpers
Window parent = GetMainWindow();
if (parent != null && parent.IsActive && (parent as MainWindow).ViewModel.IsGameRunning)
if (parent is { IsActive: true } and MainWindow window && window.ViewModel.IsGameRunning)
{
contentDialogOverlayWindow = new()
{
Height = parent.Bounds.Height,
Width = parent.Bounds.Width,
Position = parent.PointToScreen(new Point()),
ShowInTaskbar = false
ShowInTaskbar = false,
};
parent.PositionChanged += OverlayOnPositionChanged;
@@ -389,7 +386,7 @@ namespace Ryujinx.Ava.UI.Helpers
private static Window GetMainWindow()
{
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al)
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al)
{
foreach (Window item in al.Windows)
{

View File

@@ -1,5 +1,6 @@
using Avalonia.Data;
using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.MarkupExtensions;
using FluentAvalonia.UI.Controls;
using System;
using System.Collections.Generic;
@@ -8,13 +9,13 @@ namespace Ryujinx.Ava.UI.Helpers
{
public class GlyphValueConverter : MarkupExtension
{
private string _key;
private readonly string _key;
private static Dictionary<Glyph, string> _glyphs = new Dictionary<Glyph, string>
private static readonly Dictionary<Glyph, string> _glyphs = new()
{
{ Glyph.List, char.ConvertFromUtf32((int)Symbol.List).ToString() },
{ Glyph.Grid, char.ConvertFromUtf32((int)Symbol.ViewAll).ToString() },
{ Glyph.Chip, char.ConvertFromUtf32(59748).ToString() }
{ Glyph.List, char.ConvertFromUtf32((int)Symbol.List) },
{ Glyph.Grid, char.ConvertFromUtf32((int)Symbol.ViewAll) },
{ Glyph.Chip, char.ConvertFromUtf32(59748) },
};
public GlyphValueConverter(string key)
@@ -37,10 +38,10 @@ namespace Ryujinx.Ava.UI.Helpers
public override object ProvideValue(IServiceProvider serviceProvider)
{
Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension binding = new($"[{_key}]")
ReflectionBindingExtension binding = new($"[{_key}]")
{
Mode = BindingMode.OneWay,
Source = this
Source = this,
};
return binding.ProvideValue(serviceProvider);

View File

@@ -1,15 +1,17 @@
using Avalonia.Logging;
using Avalonia.Utilities;
using Ryujinx.Common.Logging;
using System;
using System.Text;
namespace Ryujinx.Ava.UI.Helpers
{
using AvaLogger = Avalonia.Logging.Logger;
using AvaLogLevel = Avalonia.Logging.LogEventLevel;
using RyuLogClass = Ryujinx.Common.Logging.LogClass;
using AvaLogLevel = LogEventLevel;
using RyuLogClass = LogClass;
using RyuLogger = Ryujinx.Common.Logging.Logger;
internal class LoggerAdapter : Avalonia.Logging.ILogSink
internal class LoggerAdapter : ILogSink
{
public static void Register()
{
@@ -26,7 +28,7 @@ namespace Ryujinx.Ava.UI.Helpers
AvaLogLevel.Warning => RyuLogger.Debug,
AvaLogLevel.Error => RyuLogger.Error,
AvaLogLevel.Fatal => RyuLogger.Error,
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null)
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null),
};
}

View File

@@ -8,7 +8,7 @@ namespace Ryujinx.Ava.UI.Helpers
{
private readonly Action<T> _callback;
private bool _busy;
private Func<T, Task> _asyncCallback;
private readonly Func<T, Task> _asyncCallback;
public MiniCommand(Action<T> callback)
{

View File

@@ -25,7 +25,7 @@ namespace Ryujinx.Ava.UI.Helpers
{
Position = NotificationPosition.BottomRight,
MaxItems = MaxNotifications,
Margin = new Thickness(0, 0, 15, 40)
Margin = new Thickness(0, 0, 15, 40),
};
var maybeAsyncWorkQueue = new Lazy<AsyncWorkQueue<Notification>>(

View File

@@ -6,12 +6,12 @@ namespace Ryujinx.Ava.UI.Helpers
{
public class OffscreenTextBox : TextBox
{
public RoutedEvent<KeyEventArgs> GetKeyDownRoutedEvent()
public static RoutedEvent<KeyEventArgs> GetKeyDownRoutedEvent()
{
return KeyDownEvent;
}
public RoutedEvent<KeyEventArgs> GetKeyUpRoutedEvent()
public static RoutedEvent<KeyEventArgs> GetKeyUpRoutedEvent()
{
return KeyUpEvent;
}
@@ -28,12 +28,12 @@ namespace Ryujinx.Ava.UI.Helpers
public void SendText(string text)
{
OnTextInput(new TextInputEventArgs()
OnTextInput(new TextInputEventArgs
{
Text = text,
Device = KeyboardDevice.Instance,
Source = this,
RoutedEvent = TextInputEvent
RoutedEvent = TextInputEvent,
});
}
}

View File

@@ -1,5 +1,4 @@
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Helper;
using System.Threading.Tasks;
@@ -24,7 +23,7 @@ namespace Ryujinx.Ava.UI.Helpers
UserError.FirmwareParsingFailed => LocaleManager.Instance[LocaleKeys.UserErrorFirmwareParsingFailed],
UserError.ApplicationNotFound => LocaleManager.Instance[LocaleKeys.UserErrorApplicationNotFound],
UserError.Unknown => LocaleManager.Instance[LocaleKeys.UserErrorUnknown],
_ => LocaleManager.Instance[LocaleKeys.UserErrorUndefined]
_ => LocaleManager.Instance[LocaleKeys.UserErrorUndefined],
};
}
@@ -37,7 +36,7 @@ namespace Ryujinx.Ava.UI.Helpers
UserError.FirmwareParsingFailed => LocaleManager.Instance[LocaleKeys.UserErrorFirmwareParsingFailedDescription],
UserError.ApplicationNotFound => LocaleManager.Instance[LocaleKeys.UserErrorApplicationNotFoundDescription],
UserError.Unknown => LocaleManager.Instance[LocaleKeys.UserErrorUnknownDescription],
_ => LocaleManager.Instance[LocaleKeys.UserErrorUndefinedDescription]
_ => LocaleManager.Instance[LocaleKeys.UserErrorUndefinedDescription],
};
}
@@ -48,7 +47,7 @@ namespace Ryujinx.Ava.UI.Helpers
UserError.NoKeys or
UserError.NoFirmware or
UserError.FirmwareParsingFailed => true,
_ => false
_ => false,
};
}
@@ -63,11 +62,11 @@ namespace Ryujinx.Ava.UI.Helpers
{
UserError.NoKeys => SetupGuideUrl + "#initial-setup---placement-of-prodkeys",
UserError.NoFirmware => SetupGuideUrl + "#initial-setup-continued---installation-of-firmware",
_ => SetupGuideUrl
_ => SetupGuideUrl,
};
}
public static async Task ShowUserErrorDialog(UserError error, StyleableWindow owner)
public static async Task ShowUserErrorDialog(UserError error)
{
string errorCode = GetErrorCode(error);

View File

@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
@@ -10,46 +11,47 @@ namespace Ryujinx.Ava.UI.Helpers
[Flags]
public enum ClassStyles : uint
{
CS_CLASSDC = 0x40,
CS_OWNDC = 0x20,
CsClassdc = 0x40,
CsOwndc = 0x20,
}
[Flags]
public enum WindowStyles : uint
{
WS_CHILD = 0x40000000
WsChild = 0x40000000,
}
public enum Cursors : uint
{
IDC_ARROW = 32512
IdcArrow = 32512,
}
[SuppressMessage("Design", "CA1069: Enums values should not be duplicated")]
public enum WindowsMessages : uint
{
MOUSEMOVE = 0x0200,
LBUTTONDOWN = 0x0201,
LBUTTONUP = 0x0202,
LBUTTONDBLCLK = 0x0203,
RBUTTONDOWN = 0x0204,
RBUTTONUP = 0x0205,
RBUTTONDBLCLK = 0x0206,
MBUTTONDOWN = 0x0207,
MBUTTONUP = 0x0208,
MBUTTONDBLCLK = 0x0209,
MOUSEWHEEL = 0x020A,
XBUTTONDOWN = 0x020B,
XBUTTONUP = 0x020C,
XBUTTONDBLCLK = 0x020D,
MOUSEHWHEEL = 0x020E,
MOUSELAST = 0x020E
Mousemove = 0x0200,
Lbuttondown = 0x0201,
Lbuttonup = 0x0202,
Lbuttondblclk = 0x0203,
Rbuttondown = 0x0204,
Rbuttonup = 0x0205,
Rbuttondblclk = 0x0206,
Mbuttondown = 0x0207,
Mbuttonup = 0x0208,
Mbuttondblclk = 0x0209,
Mousewheel = 0x020A,
Xbuttondown = 0x020B,
Xbuttonup = 0x020C,
Xbuttondblclk = 0x020D,
Mousehwheel = 0x020E,
Mouselast = 0x020E,
}
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
internal delegate IntPtr WindowProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential)]
public struct WNDCLASSEX
public struct WndClassEx
{
public int cbSize;
public ClassStyles style;
@@ -64,9 +66,9 @@ namespace Ryujinx.Ava.UI.Helpers
public IntPtr lpszClassName;
public IntPtr hIconSm;
public WNDCLASSEX()
public WndClassEx()
{
cbSize = Marshal.SizeOf<WNDCLASSEX>();
cbSize = Marshal.SizeOf<WndClassEx>();
}
}
@@ -77,17 +79,17 @@ namespace Ryujinx.Ava.UI.Helpers
public static IntPtr CreateArrowCursor()
{
return LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IDC_ARROW);
return LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IdcArrow);
}
[LibraryImport("user32.dll")]
public static partial IntPtr SetCursor(IntPtr handle);
[LibraryImport("user32.dll")]
public static partial IntPtr CreateCursor(IntPtr hInst, int xHotSpot, int yHotSpot, int nWidth, int nHeight, byte[] pvANDPlane, byte[] pvXORPlane);
public static partial IntPtr CreateCursor(IntPtr hInst, int xHotSpot, int yHotSpot, int nWidth, int nHeight, byte[] pvAndPlane, byte[] pvXorPlane);
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassExW")]
public static partial ushort RegisterClassEx(ref WNDCLASSEX param);
public static partial ushort RegisterClassEx(ref WndClassEx param);
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "UnregisterClassW")]
public static partial short UnregisterClass([MarshalAs(UnmanagedType.LPWStr)] string lpClassName, IntPtr instance);

View File

@@ -35,6 +35,6 @@ namespace Ryujinx.Ava.UI.Models
public string Name { get; }
public string CleanName => Name.Substring(1, Name.Length - 8);
public string CleanName => Name[1..^7];
}
}

View File

@@ -4,6 +4,6 @@ namespace Ryujinx.Ava.UI.Models
{
None,
Keyboard,
Controller
Controller,
}
}

View File

@@ -7,16 +7,16 @@ using System;
namespace Ryujinx.Ava.UI.Models
{
internal class InputConfiguration<Key, Stick> : BaseModel
internal class InputConfiguration<TKey, TStick> : BaseModel
{
private float _deadzoneRight;
private float _triggerThreshold;
private float _deadzoneLeft;
private double _gyroDeadzone;
private int _sensitivity;
private bool enableMotion;
private float weakRumble;
private float strongRumble;
private bool _enableMotion;
private float _weakRumble;
private float _strongRumble;
private float _rangeLeft;
private float _rangeRight;
@@ -37,17 +37,17 @@ namespace Ryujinx.Ava.UI.Models
/// </summary>
public PlayerIndex PlayerIndex { get; set; }
public Stick LeftJoystick { get; set; }
public TStick LeftJoystick { get; set; }
public bool LeftInvertStickX { get; set; }
public bool LeftInvertStickY { get; set; }
public bool RightRotate90 { get; set; }
public Key LeftControllerStickButton { get; set; }
public TKey LeftControllerStickButton { get; set; }
public Stick RightJoystick { get; set; }
public TStick RightJoystick { get; set; }
public bool RightInvertStickX { get; set; }
public bool RightInvertStickY { get; set; }
public bool LeftRotate90 { get; set; }
public Key RightControllerStickButton { get; set; }
public TKey RightControllerStickButton { get; set; }
public float DeadzoneLeft
{
@@ -106,38 +106,37 @@ namespace Ryujinx.Ava.UI.Models
public MotionInputBackendType MotionBackend { get; set; }
public Key ButtonMinus { get; set; }
public Key ButtonL { get; set; }
public Key ButtonZl { get; set; }
public Key LeftButtonSl { get; set; }
public Key LeftButtonSr { get; set; }
public Key DpadUp { get; set; }
public Key DpadDown { get; set; }
public Key DpadLeft { get; set; }
public Key DpadRight { get; set; }
public TKey ButtonMinus { get; set; }
public TKey ButtonL { get; set; }
public TKey ButtonZl { get; set; }
public TKey LeftButtonSl { get; set; }
public TKey LeftButtonSr { get; set; }
public TKey DpadUp { get; set; }
public TKey DpadDown { get; set; }
public TKey DpadLeft { get; set; }
public TKey DpadRight { get; set; }
public Key ButtonPlus { get; set; }
public Key ButtonR { get; set; }
public Key ButtonZr { get; set; }
public Key RightButtonSl { get; set; }
public Key RightButtonSr { get; set; }
public Key ButtonX { get; set; }
public Key ButtonB { get; set; }
public Key ButtonY { get; set; }
public Key ButtonA { get; set; }
public TKey ButtonPlus { get; set; }
public TKey ButtonR { get; set; }
public TKey ButtonZr { get; set; }
public TKey RightButtonSl { get; set; }
public TKey RightButtonSr { get; set; }
public TKey ButtonX { get; set; }
public TKey ButtonB { get; set; }
public TKey ButtonY { get; set; }
public TKey ButtonA { get; set; }
public TKey LeftStickUp { get; set; }
public TKey LeftStickDown { get; set; }
public TKey LeftStickLeft { get; set; }
public TKey LeftStickRight { get; set; }
public TKey LeftKeyboardStickButton { get; set; }
public Key LeftStickUp { get; set; }
public Key LeftStickDown { get; set; }
public Key LeftStickLeft { get; set; }
public Key LeftStickRight { get; set; }
public Key LeftKeyboardStickButton { get; set; }
public Key RightStickUp { get; set; }
public Key RightStickDown { get; set; }
public Key RightStickLeft { get; set; }
public Key RightStickRight { get; set; }
public Key RightKeyboardStickButton { get; set; }
public TKey RightStickUp { get; set; }
public TKey RightStickDown { get; set; }
public TKey RightStickLeft { get; set; }
public TKey RightStickRight { get; set; }
public TKey RightKeyboardStickButton { get; set; }
public int Sensitivity
{
@@ -163,9 +162,9 @@ namespace Ryujinx.Ava.UI.Models
public bool EnableMotion
{
get => enableMotion; set
get => _enableMotion; set
{
enableMotion = value;
_enableMotion = value;
OnPropertyChanged();
}
@@ -181,18 +180,18 @@ namespace Ryujinx.Ava.UI.Models
public bool EnableRumble { get; set; }
public float WeakRumble
{
get => weakRumble; set
get => _weakRumble; set
{
weakRumble = value;
_weakRumble = value;
OnPropertyChanged();
}
}
public float StrongRumble
{
get => strongRumble; set
get => _strongRumble; set
{
strongRumble = value;
_strongRumble = value;
OnPropertyChanged();
}
@@ -209,71 +208,71 @@ namespace Ryujinx.Ava.UI.Models
if (config is StandardKeyboardInputConfig keyboardConfig)
{
LeftStickUp = (Key)(object)keyboardConfig.LeftJoyconStick.StickUp;
LeftStickDown = (Key)(object)keyboardConfig.LeftJoyconStick.StickDown;
LeftStickLeft = (Key)(object)keyboardConfig.LeftJoyconStick.StickLeft;
LeftStickRight = (Key)(object)keyboardConfig.LeftJoyconStick.StickRight;
LeftKeyboardStickButton = (Key)(object)keyboardConfig.LeftJoyconStick.StickButton;
LeftStickUp = (TKey)(object)keyboardConfig.LeftJoyconStick.StickUp;
LeftStickDown = (TKey)(object)keyboardConfig.LeftJoyconStick.StickDown;
LeftStickLeft = (TKey)(object)keyboardConfig.LeftJoyconStick.StickLeft;
LeftStickRight = (TKey)(object)keyboardConfig.LeftJoyconStick.StickRight;
LeftKeyboardStickButton = (TKey)(object)keyboardConfig.LeftJoyconStick.StickButton;
RightStickUp = (Key)(object)keyboardConfig.RightJoyconStick.StickUp;
RightStickDown = (Key)(object)keyboardConfig.RightJoyconStick.StickDown;
RightStickLeft = (Key)(object)keyboardConfig.RightJoyconStick.StickLeft;
RightStickRight = (Key)(object)keyboardConfig.RightJoyconStick.StickRight;
RightKeyboardStickButton = (Key)(object)keyboardConfig.RightJoyconStick.StickButton;
RightStickUp = (TKey)(object)keyboardConfig.RightJoyconStick.StickUp;
RightStickDown = (TKey)(object)keyboardConfig.RightJoyconStick.StickDown;
RightStickLeft = (TKey)(object)keyboardConfig.RightJoyconStick.StickLeft;
RightStickRight = (TKey)(object)keyboardConfig.RightJoyconStick.StickRight;
RightKeyboardStickButton = (TKey)(object)keyboardConfig.RightJoyconStick.StickButton;
ButtonA = (Key)(object)keyboardConfig.RightJoycon.ButtonA;
ButtonB = (Key)(object)keyboardConfig.RightJoycon.ButtonB;
ButtonX = (Key)(object)keyboardConfig.RightJoycon.ButtonX;
ButtonY = (Key)(object)keyboardConfig.RightJoycon.ButtonY;
ButtonR = (Key)(object)keyboardConfig.RightJoycon.ButtonR;
RightButtonSl = (Key)(object)keyboardConfig.RightJoycon.ButtonSl;
RightButtonSr = (Key)(object)keyboardConfig.RightJoycon.ButtonSr;
ButtonZr = (Key)(object)keyboardConfig.RightJoycon.ButtonZr;
ButtonPlus = (Key)(object)keyboardConfig.RightJoycon.ButtonPlus;
ButtonA = (TKey)(object)keyboardConfig.RightJoycon.ButtonA;
ButtonB = (TKey)(object)keyboardConfig.RightJoycon.ButtonB;
ButtonX = (TKey)(object)keyboardConfig.RightJoycon.ButtonX;
ButtonY = (TKey)(object)keyboardConfig.RightJoycon.ButtonY;
ButtonR = (TKey)(object)keyboardConfig.RightJoycon.ButtonR;
RightButtonSl = (TKey)(object)keyboardConfig.RightJoycon.ButtonSl;
RightButtonSr = (TKey)(object)keyboardConfig.RightJoycon.ButtonSr;
ButtonZr = (TKey)(object)keyboardConfig.RightJoycon.ButtonZr;
ButtonPlus = (TKey)(object)keyboardConfig.RightJoycon.ButtonPlus;
DpadUp = (Key)(object)keyboardConfig.LeftJoycon.DpadUp;
DpadDown = (Key)(object)keyboardConfig.LeftJoycon.DpadDown;
DpadLeft = (Key)(object)keyboardConfig.LeftJoycon.DpadLeft;
DpadRight = (Key)(object)keyboardConfig.LeftJoycon.DpadRight;
ButtonMinus = (Key)(object)keyboardConfig.LeftJoycon.ButtonMinus;
LeftButtonSl = (Key)(object)keyboardConfig.LeftJoycon.ButtonSl;
LeftButtonSr = (Key)(object)keyboardConfig.LeftJoycon.ButtonSr;
ButtonZl = (Key)(object)keyboardConfig.LeftJoycon.ButtonZl;
ButtonL = (Key)(object)keyboardConfig.LeftJoycon.ButtonL;
DpadUp = (TKey)(object)keyboardConfig.LeftJoycon.DpadUp;
DpadDown = (TKey)(object)keyboardConfig.LeftJoycon.DpadDown;
DpadLeft = (TKey)(object)keyboardConfig.LeftJoycon.DpadLeft;
DpadRight = (TKey)(object)keyboardConfig.LeftJoycon.DpadRight;
ButtonMinus = (TKey)(object)keyboardConfig.LeftJoycon.ButtonMinus;
LeftButtonSl = (TKey)(object)keyboardConfig.LeftJoycon.ButtonSl;
LeftButtonSr = (TKey)(object)keyboardConfig.LeftJoycon.ButtonSr;
ButtonZl = (TKey)(object)keyboardConfig.LeftJoycon.ButtonZl;
ButtonL = (TKey)(object)keyboardConfig.LeftJoycon.ButtonL;
}
else if (config is StandardControllerInputConfig controllerConfig)
{
LeftJoystick = (Stick)(object)controllerConfig.LeftJoyconStick.Joystick;
LeftJoystick = (TStick)(object)controllerConfig.LeftJoyconStick.Joystick;
LeftInvertStickX = controllerConfig.LeftJoyconStick.InvertStickX;
LeftInvertStickY = controllerConfig.LeftJoyconStick.InvertStickY;
LeftRotate90 = controllerConfig.LeftJoyconStick.Rotate90CW;
LeftControllerStickButton = (Key)(object)controllerConfig.LeftJoyconStick.StickButton;
LeftControllerStickButton = (TKey)(object)controllerConfig.LeftJoyconStick.StickButton;
RightJoystick = (Stick)(object)controllerConfig.RightJoyconStick.Joystick;
RightJoystick = (TStick)(object)controllerConfig.RightJoyconStick.Joystick;
RightInvertStickX = controllerConfig.RightJoyconStick.InvertStickX;
RightInvertStickY = controllerConfig.RightJoyconStick.InvertStickY;
RightRotate90 = controllerConfig.RightJoyconStick.Rotate90CW;
RightControllerStickButton = (Key)(object)controllerConfig.RightJoyconStick.StickButton;
RightControllerStickButton = (TKey)(object)controllerConfig.RightJoyconStick.StickButton;
ButtonA = (Key)(object)controllerConfig.RightJoycon.ButtonA;
ButtonB = (Key)(object)controllerConfig.RightJoycon.ButtonB;
ButtonX = (Key)(object)controllerConfig.RightJoycon.ButtonX;
ButtonY = (Key)(object)controllerConfig.RightJoycon.ButtonY;
ButtonR = (Key)(object)controllerConfig.RightJoycon.ButtonR;
RightButtonSl = (Key)(object)controllerConfig.RightJoycon.ButtonSl;
RightButtonSr = (Key)(object)controllerConfig.RightJoycon.ButtonSr;
ButtonZr = (Key)(object)controllerConfig.RightJoycon.ButtonZr;
ButtonPlus = (Key)(object)controllerConfig.RightJoycon.ButtonPlus;
ButtonA = (TKey)(object)controllerConfig.RightJoycon.ButtonA;
ButtonB = (TKey)(object)controllerConfig.RightJoycon.ButtonB;
ButtonX = (TKey)(object)controllerConfig.RightJoycon.ButtonX;
ButtonY = (TKey)(object)controllerConfig.RightJoycon.ButtonY;
ButtonR = (TKey)(object)controllerConfig.RightJoycon.ButtonR;
RightButtonSl = (TKey)(object)controllerConfig.RightJoycon.ButtonSl;
RightButtonSr = (TKey)(object)controllerConfig.RightJoycon.ButtonSr;
ButtonZr = (TKey)(object)controllerConfig.RightJoycon.ButtonZr;
ButtonPlus = (TKey)(object)controllerConfig.RightJoycon.ButtonPlus;
DpadUp = (Key)(object)controllerConfig.LeftJoycon.DpadUp;
DpadDown = (Key)(object)controllerConfig.LeftJoycon.DpadDown;
DpadLeft = (Key)(object)controllerConfig.LeftJoycon.DpadLeft;
DpadRight = (Key)(object)controllerConfig.LeftJoycon.DpadRight;
ButtonMinus = (Key)(object)controllerConfig.LeftJoycon.ButtonMinus;
LeftButtonSl = (Key)(object)controllerConfig.LeftJoycon.ButtonSl;
LeftButtonSr = (Key)(object)controllerConfig.LeftJoycon.ButtonSr;
ButtonZl = (Key)(object)controllerConfig.LeftJoycon.ButtonZl;
ButtonL = (Key)(object)controllerConfig.LeftJoycon.ButtonL;
DpadUp = (TKey)(object)controllerConfig.LeftJoycon.DpadUp;
DpadDown = (TKey)(object)controllerConfig.LeftJoycon.DpadDown;
DpadLeft = (TKey)(object)controllerConfig.LeftJoycon.DpadLeft;
DpadRight = (TKey)(object)controllerConfig.LeftJoycon.DpadRight;
ButtonMinus = (TKey)(object)controllerConfig.LeftJoycon.ButtonMinus;
LeftButtonSl = (TKey)(object)controllerConfig.LeftJoycon.ButtonSl;
LeftButtonSr = (TKey)(object)controllerConfig.LeftJoycon.ButtonSr;
ButtonZl = (TKey)(object)controllerConfig.LeftJoycon.ButtonZl;
ButtonL = (TKey)(object)controllerConfig.LeftJoycon.ButtonL;
DeadzoneLeft = controllerConfig.DeadzoneLeft;
DeadzoneRight = controllerConfig.DeadzoneRight;
@@ -317,65 +316,66 @@ namespace Ryujinx.Ava.UI.Models
{
if (Backend == InputBackendType.WindowKeyboard)
{
return new StandardKeyboardInputConfig()
return new StandardKeyboardInputConfig
{
Id = Id,
Backend = Backend,
PlayerIndex = PlayerIndex,
ControllerType = ControllerType,
LeftJoycon = new LeftJoyconCommonConfig<Ryujinx.Common.Configuration.Hid.Key>()
LeftJoycon = new LeftJoyconCommonConfig<Key>
{
DpadUp = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadUp,
DpadDown = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadDown,
DpadLeft = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadLeft,
DpadRight = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadRight,
ButtonL = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonL,
ButtonZl = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonZl,
ButtonSl = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftButtonSl,
ButtonSr = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftButtonSr,
ButtonMinus = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonMinus
DpadUp = (Key)(object)DpadUp,
DpadDown = (Key)(object)DpadDown,
DpadLeft = (Key)(object)DpadLeft,
DpadRight = (Key)(object)DpadRight,
ButtonL = (Key)(object)ButtonL,
ButtonZl = (Key)(object)ButtonZl,
ButtonSl = (Key)(object)LeftButtonSl,
ButtonSr = (Key)(object)LeftButtonSr,
ButtonMinus = (Key)(object)ButtonMinus,
},
RightJoycon = new RightJoyconCommonConfig<Ryujinx.Common.Configuration.Hid.Key>()
RightJoycon = new RightJoyconCommonConfig<Key>
{
ButtonA = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonA,
ButtonB = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonB,
ButtonX = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonX,
ButtonY = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonY,
ButtonPlus = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonPlus,
ButtonSl = (Ryujinx.Common.Configuration.Hid.Key)(object)RightButtonSl,
ButtonSr = (Ryujinx.Common.Configuration.Hid.Key)(object)RightButtonSr,
ButtonR = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonR,
ButtonZr = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonZr
ButtonA = (Key)(object)ButtonA,
ButtonB = (Key)(object)ButtonB,
ButtonX = (Key)(object)ButtonX,
ButtonY = (Key)(object)ButtonY,
ButtonPlus = (Key)(object)ButtonPlus,
ButtonSl = (Key)(object)RightButtonSl,
ButtonSr = (Key)(object)RightButtonSr,
ButtonR = (Key)(object)ButtonR,
ButtonZr = (Key)(object)ButtonZr,
},
LeftJoyconStick = new JoyconConfigKeyboardStick<Ryujinx.Common.Configuration.Hid.Key>()
LeftJoyconStick = new JoyconConfigKeyboardStick<Key>
{
StickUp = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickUp,
StickDown = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickDown,
StickRight = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickRight,
StickLeft = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickLeft,
StickButton = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftKeyboardStickButton
StickUp = (Key)(object)LeftStickUp,
StickDown = (Key)(object)LeftStickDown,
StickRight = (Key)(object)LeftStickRight,
StickLeft = (Key)(object)LeftStickLeft,
StickButton = (Key)(object)LeftKeyboardStickButton,
},
RightJoyconStick = new JoyconConfigKeyboardStick<Ryujinx.Common.Configuration.Hid.Key>()
RightJoyconStick = new JoyconConfigKeyboardStick<Key>
{
StickUp = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickUp,
StickDown = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickDown,
StickLeft = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickLeft,
StickRight = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickRight,
StickButton = (Ryujinx.Common.Configuration.Hid.Key)(object)RightKeyboardStickButton
StickUp = (Key)(object)RightStickUp,
StickDown = (Key)(object)RightStickDown,
StickLeft = (Key)(object)RightStickLeft,
StickRight = (Key)(object)RightStickRight,
StickButton = (Key)(object)RightKeyboardStickButton,
},
Version = InputConfig.CurrentVersion
Version = InputConfig.CurrentVersion,
};
}
else if (Backend == InputBackendType.GamepadSDL2)
if (Backend == InputBackendType.GamepadSDL2)
{
var config = new StandardControllerInputConfig()
var config = new StandardControllerInputConfig
{
Id = Id,
Backend = Backend,
PlayerIndex = PlayerIndex,
ControllerType = ControllerType,
LeftJoycon = new LeftJoyconCommonConfig<GamepadInputId>()
LeftJoycon = new LeftJoyconCommonConfig<GamepadInputId>
{
DpadUp = (GamepadInputId)(object)DpadUp,
DpadDown = (GamepadInputId)(object)DpadDown,
@@ -387,7 +387,7 @@ namespace Ryujinx.Ava.UI.Models
ButtonSr = (GamepadInputId)(object)LeftButtonSr,
ButtonMinus = (GamepadInputId)(object)ButtonMinus,
},
RightJoycon = new RightJoyconCommonConfig<GamepadInputId>()
RightJoycon = new RightJoyconCommonConfig<GamepadInputId>
{
ButtonA = (GamepadInputId)(object)ButtonA,
ButtonB = (GamepadInputId)(object)ButtonB,
@@ -399,7 +399,7 @@ namespace Ryujinx.Ava.UI.Models
ButtonR = (GamepadInputId)(object)ButtonR,
ButtonZr = (GamepadInputId)(object)ButtonZr,
},
LeftJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>()
LeftJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>
{
Joystick = (StickInputId)(object)LeftJoystick,
InvertStickX = LeftInvertStickX,
@@ -407,7 +407,7 @@ namespace Ryujinx.Ava.UI.Models
Rotate90CW = LeftRotate90,
StickButton = (GamepadInputId)(object)LeftControllerStickButton,
},
RightJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>()
RightJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>
{
Joystick = (StickInputId)(object)RightJoystick,
InvertStickX = RightInvertStickX,
@@ -415,11 +415,11 @@ namespace Ryujinx.Ava.UI.Models
Rotate90CW = RightRotate90,
StickButton = (GamepadInputId)(object)RightControllerStickButton,
},
Rumble = new RumbleConfigController()
Rumble = new RumbleConfigController
{
EnableRumble = EnableRumble,
WeakRumble = WeakRumble,
StrongRumble = StrongRumble
StrongRumble = StrongRumble,
},
Version = InputConfig.CurrentVersion,
DeadzoneLeft = DeadzoneLeft,
@@ -428,19 +428,19 @@ namespace Ryujinx.Ava.UI.Models
RangeRight = RangeRight,
TriggerThreshold = TriggerThreshold,
Motion = EnableCemuHookMotion
? new CemuHookMotionConfigController()
? new CemuHookMotionConfigController
{
DsuServerHost = DsuServerHost,
DsuServerPort = DsuServerPort,
Slot = Slot,
AltSlot = AltSlot,
MirrorInput = MirrorInput,
MotionBackend = MotionInputBackendType.CemuHook
MotionBackend = MotionInputBackendType.CemuHook,
}
: new StandardMotionConfigController()
: new StandardMotionConfigController
{
MotionBackend = MotionInputBackendType.GamepadDriver
}
MotionBackend = MotionInputBackendType.GamepadDriver,
},
};
config.Motion.Sensitivity = Sensitivity;

View File

@@ -2,12 +2,12 @@ using LibHac.Fs;
using LibHac.Ncm;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.FileSystem;
using Ryujinx.Ui.App.Common;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Path = System.IO.Path;
namespace Ryujinx.Ava.UI.Models
{
@@ -58,7 +58,7 @@ namespace Ryujinx.Ava.UI.Models
return "0 KiB";
}
public SaveModel(SaveDataInfo info, VirtualFileSystem virtualFileSystem)
public SaveModel(SaveDataInfo info)
{
SaveId = info.SaveDataId;
TitleId = info.ProgramId;
@@ -81,10 +81,11 @@ namespace Ryujinx.Ava.UI.Models
Task.Run(() =>
{
var saveRoot = System.IO.Path.Combine(virtualFileSystem.GetNandPath(), $"user/save/{info.SaveDataId:x16}");
var saveRoot = Path.Combine(MainWindow.MainWindowViewModel.VirtualFileSystem.GetNandPath(), $"user/save/{info.SaveDataId:x16}");
long total_size = GetDirectorySize(saveRoot);
long GetDirectorySize(string path)
long totalSize = GetDirectorySize(saveRoot);
static long GetDirectorySize(string path)
{
long size = 0;
if (Directory.Exists(path))
@@ -105,7 +106,7 @@ namespace Ryujinx.Ava.UI.Models
return size;
}
Size = total_size;
Size = totalSize;
});
}

View File

@@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Models
private string _name = String.Empty;
private UserId _userId;
public uint MaxProfileNameLength => 0x20;
public static uint MaxProfileNameLength => 0x20;
public byte[] Image
{

View File

@@ -1,3 +1,4 @@
using Avalonia;
using Avalonia.Media;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.ViewModels;
@@ -87,7 +88,7 @@ namespace Ryujinx.Ava.UI.Models
private void UpdateBackground()
{
Avalonia.Application.Current.Styles.TryGetResource("ControlFillColorSecondary", out object color);
Application.Current.Styles.TryGetResource("ControlFillColorSecondary", out object color);
if (color is not null)
{

View File

@@ -77,11 +77,13 @@ namespace Ryujinx.Ava.UI.Renderer
{
return CreateLinux(control);
}
else if (OperatingSystem.IsWindows())
if (OperatingSystem.IsWindows())
{
return CreateWin32(control);
}
else if (OperatingSystem.IsMacOS())
if (OperatingSystem.IsMacOS())
{
return CreateMacOS();
}
@@ -141,21 +143,21 @@ namespace Ryujinx.Ava.UI.Renderer
{
if (VisualRoot != null)
{
if (msg == WindowsMessages.LBUTTONDOWN ||
msg == WindowsMessages.RBUTTONDOWN ||
msg == WindowsMessages.LBUTTONUP ||
msg == WindowsMessages.RBUTTONUP ||
msg == WindowsMessages.MOUSEMOVE)
if (msg == WindowsMessages.Lbuttondown ||
msg == WindowsMessages.Rbuttondown ||
msg == WindowsMessages.Lbuttonup ||
msg == WindowsMessages.Rbuttonup ||
msg == WindowsMessages.Mousemove)
{
Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value;
Pointer pointer = new(0, PointerType.Mouse, true);
switch (msg)
{
case WindowsMessages.LBUTTONDOWN:
case WindowsMessages.RBUTTONDOWN:
case WindowsMessages.Lbuttondown:
case WindowsMessages.Rbuttondown:
{
bool isLeft = msg == WindowsMessages.LBUTTONDOWN;
bool isLeft = msg == WindowsMessages.Lbuttondown;
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed);
@@ -172,10 +174,10 @@ namespace Ryujinx.Ava.UI.Renderer
break;
}
case WindowsMessages.LBUTTONUP:
case WindowsMessages.RBUTTONUP:
case WindowsMessages.Lbuttonup:
case WindowsMessages.Rbuttonup:
{
bool isLeft = msg == WindowsMessages.LBUTTONUP;
bool isLeft = msg == WindowsMessages.Lbuttonup;
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased);
@@ -193,7 +195,7 @@ namespace Ryujinx.Ava.UI.Renderer
break;
}
case WindowsMessages.MOUSEMOVE:
case WindowsMessages.Mousemove:
{
var evnt = new PointerEventArgs(
PointerMovedEvent,
@@ -216,19 +218,19 @@ namespace Ryujinx.Ava.UI.Renderer
return DefWindowProc(hWnd, msg, wParam, lParam);
};
WNDCLASSEX wndClassEx = new()
WndClassEx wndClassEx = new()
{
cbSize = Marshal.SizeOf<WNDCLASSEX>(),
cbSize = Marshal.SizeOf<WndClassEx>(),
hInstance = GetModuleHandle(null),
lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
style = ClassStyles.CS_OWNDC,
style = ClassStyles.CsOwndc,
lpszClassName = Marshal.StringToHGlobalUni(_className),
hCursor = CreateArrowCursor()
hCursor = CreateArrowCursor(),
};
RegisterClassEx(ref wndClassEx);
WindowHandle = CreateWindowEx(0, _className, "NativeWindow", WindowStyles.WS_CHILD, 0, 0, 640, 480, control.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
WindowHandle = CreateWindowEx(0, _className, "NativeWindow", WindowStyles.WsChild, 0, 0, 640, 480, control.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
Marshal.FreeHGlobal(wndClassEx.lpszClassName);
@@ -280,9 +282,11 @@ namespace Ryujinx.Ava.UI.Renderer
}
[SupportedOSPlatform("macos")]
#pragma warning disable CA1822 // Mark member as static
void DestroyMacOS()
{
// TODO
}
#pragma warning restore CA1822
}
}

View File

@@ -34,7 +34,7 @@ namespace Ryujinx.Ava.UI.Renderer
return new SurfaceKHR((ulong?)VulkanHelper.CreateWindowSurface(instance.Handle, nativeWindowBase));
}
public SurfaceKHR CreateSurface(Instance instance, Vk api)
public SurfaceKHR CreateSurface(Instance instance, Vk _)
{
return CreateSurface(instance);
}

View File

@@ -1,5 +1,6 @@
using Avalonia;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Common.Utilities;
@@ -87,7 +88,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Version = Program.Version;
var assets = AvaloniaLocator.Current.GetService<Avalonia.Platform.IAssetLoader>();
var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
if (ConfigurationState.Instance.Ui.BaseStyle.Value == "Light")
{

View File

@@ -18,7 +18,6 @@ using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext;
namespace Ryujinx.Ava.UI.ViewModels
{
@@ -44,7 +43,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private bool _useRandomUuid;
private string _usage;
private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
private static readonly AmiiboJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId)
{
@@ -52,7 +51,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(30)
Timeout = TimeSpan.FromSeconds(30),
};
LastScannedAmiiboId = lastScannedAmiiboId;
@@ -185,6 +184,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public void Dispose()
{
GC.SuppressFinalize(this);
_httpClient.Dispose();
}
@@ -196,7 +196,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath);
if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated))
if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).LastUpdated))
{
amiiboJsonString = await DownloadAmiiboJson();
}
@@ -215,7 +215,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
_amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo;
_amiiboList = JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).Amiibo;
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
ParseAmiiboData();
@@ -426,8 +426,8 @@ namespace Ryujinx.Ava.UI.ViewModels
if (response.IsSuccessStatusCode)
{
byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync();
using (MemoryStream memoryStream = new(amiiboPreviewBytes))
{
using MemoryStream memoryStream = new(amiiboPreviewBytes);
Bitmap bitmap = new(memoryStream);
double ratio = Math.Min(AmiiboImageSize / bitmap.Size.Width,
@@ -438,7 +438,6 @@ namespace Ryujinx.Ava.UI.ViewModels
AmiiboImage = bitmap.CreateScaledBitmap(new PixelSize(resizeWidth, resizeHeight));
}
}
else
{
Logger.Error?.Print(LogClass.Application, $"Failed to get amiibo preview. Response status code: {response.StatusCode}");
@@ -447,15 +446,14 @@ namespace Ryujinx.Ava.UI.ViewModels
private void ResetAmiiboPreview()
{
using (MemoryStream memoryStream = new(_amiiboLogoBytes))
{
using MemoryStream memoryStream = new(_amiiboLogoBytes);
Bitmap bitmap = new(memoryStream);
AmiiboImage = bitmap;
}
}
private async void ShowInfoDialog()
private static async void ShowInfoDialog()
{
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiConnectErrorMessage],

View File

@@ -1,363 +0,0 @@
using Avalonia.Media;
using DynamicData;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.UI.Models;
using Ryujinx.HLE.FileSystem;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Color = Avalonia.Media.Color;
namespace Ryujinx.Ava.UI.ViewModels
{
internal class AvatarProfileViewModel : BaseModel, IDisposable
{
private const int MaxImageTasks = 4;
private static readonly Dictionary<string, byte[]> _avatarStore = new();
private static bool _isPreloading;
private static Action _loadCompleteAction;
private ObservableCollection<ProfileImageModel> _images;
private Color _backgroundColor = Colors.White;
private int _selectedIndex;
private int _imagesLoaded;
private bool _isActive;
private byte[] _selectedImage;
private bool _isIndeterminate = true;
public bool IsActive
{
get => _isActive;
set => _isActive = value;
}
public AvatarProfileViewModel()
{
_images = new ObservableCollection<ProfileImageModel>();
}
public AvatarProfileViewModel(Action loadCompleteAction)
{
_images = new ObservableCollection<ProfileImageModel>();
if (_isPreloading)
{
_loadCompleteAction = loadCompleteAction;
}
else
{
ReloadImages();
}
}
public Color BackgroundColor
{
get => _backgroundColor;
set
{
_backgroundColor = value;
IsActive = false;
ReloadImages();
}
}
public ObservableCollection<ProfileImageModel> Images
{
get => _images;
set
{
_images = value;
OnPropertyChanged();
}
}
public bool IsIndeterminate
{
get => _isIndeterminate;
set
{
_isIndeterminate = value;
OnPropertyChanged();
}
}
public int ImageCount => _avatarStore.Count;
public int ImagesLoaded
{
get => _imagesLoaded;
set
{
_imagesLoaded = value;
OnPropertyChanged();
}
}
public int SelectedIndex
{
get => _selectedIndex;
set
{
_selectedIndex = value;
if (_selectedIndex == -1)
{
SelectedImage = null;
}
else
{
SelectedImage = _images[_selectedIndex].Data;
}
OnPropertyChanged();
}
}
public byte[] SelectedImage
{
get => _selectedImage;
private set => _selectedImage = value;
}
public void ReloadImages()
{
if (_isPreloading)
{
IsIndeterminate = false;
return;
}
Task.Run(() =>
{
IsActive = true;
Images.Clear();
int selectedIndex = _selectedIndex;
int index = 0;
ImagesLoaded = 0;
IsIndeterminate = false;
var keys = _avatarStore.Keys.ToList();
var newImages = new List<ProfileImageModel>();
var tasks = new List<Task>();
for (int i = 0; i < MaxImageTasks; i++)
{
var start = i;
tasks.Add(Task.Run(() => ImageTask(start)));
}
Task.WaitAll(tasks.ToArray());
Images.AddRange(newImages);
void ImageTask(int start)
{
for (int i = start; i < keys.Count; i += MaxImageTasks)
{
if (!IsActive)
{
return;
}
var key = keys[i];
var image = _avatarStore[keys[i]];
var data = ProcessImage(image);
newImages.Add(new ProfileImageModel(key, data));
if (index++ == selectedIndex)
{
SelectedImage = data;
}
Interlocked.Increment(ref _imagesLoaded);
OnPropertyChanged(nameof(ImagesLoaded));
}
}
});
}
private byte[] ProcessImage(byte[] data)
{
using (MemoryStream streamJpg = new())
{
Image avatarImage = Image.Load(data, new PngDecoder());
avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(BackgroundColor.R,
BackgroundColor.G,
BackgroundColor.B,
BackgroundColor.A)));
avatarImage.SaveAsJpeg(streamJpg);
return streamJpg.ToArray();
}
}
public static void PreloadAvatars(ContentManager contentManager, VirtualFileSystem virtualFileSystem)
{
try
{
if (_avatarStore.Count > 0)
{
return;
}
_isPreloading = true;
string contentPath =
contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem,
NcaContentType.Data);
string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath);
if (!string.IsNullOrWhiteSpace(avatarPath))
{
using (IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open))
{
Nca nca = new(virtualFileSystem.KeySet, ncaFileStream);
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
foreach (DirectoryEntryEx item in romfs.EnumerateEntries())
{
// TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy.
if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") &&
item.FullPath.Contains("szs"))
{
using var file = new UniqueRef<IFile>();
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read)
.ThrowIfFailure();
using (MemoryStream stream = new())
using (MemoryStream streamPng = new())
{
file.Get.AsStream().CopyTo(stream);
stream.Position = 0;
Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256);
avatarImage.SaveAsPng(streamPng);
_avatarStore.Add(item.FullPath, streamPng.ToArray());
}
}
}
}
}
}
finally
{
_isPreloading = false;
_loadCompleteAction?.Invoke();
}
}
private static byte[] DecompressYaz0(Stream stream)
{
using (BinaryReader reader = new(stream))
{
reader.ReadInt32(); // Magic
uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32());
reader.ReadInt64(); // Padding
byte[] input = new byte[stream.Length - stream.Position];
stream.Read(input, 0, input.Length);
uint inputOffset = 0;
byte[] output = new byte[decodedLength];
uint outputOffset = 0;
ushort mask = 0;
byte header = 0;
while (outputOffset < decodedLength)
{
if ((mask >>= 1) == 0)
{
header = input[inputOffset++];
mask = 0x80;
}
if ((header & mask) != 0)
{
if (outputOffset == output.Length)
{
break;
}
output[outputOffset++] = input[inputOffset++];
}
else
{
byte byte1 = input[inputOffset++];
byte byte2 = input[inputOffset++];
uint dist = (uint)((byte1 & 0xF) << 8) | byte2;
uint position = outputOffset - (dist + 1);
uint length = (uint)byte1 >> 4;
if (length == 0)
{
length = (uint)input[inputOffset++] + 0x12;
}
else
{
length += 2;
}
uint gap = outputOffset - position;
uint nonOverlappingLength = length;
if (nonOverlappingLength > gap)
{
nonOverlappingLength = gap;
}
Buffer.BlockCopy(output, (int)position, output, (int)outputOffset, (int)nonOverlappingLength);
outputOffset += nonOverlappingLength;
position += nonOverlappingLength;
length -= nonOverlappingLength;
while (length-- > 0)
{
output[outputOffset++] = output[position++];
}
}
}
return output;
}
}
public void Dispose()
{
_loadCompleteAction = null;
IsActive = false;
}
}
}

View File

@@ -1,3 +1,4 @@
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
@@ -44,15 +45,14 @@ namespace Ryujinx.Ava.UI.ViewModels
private PlayerIndex _playerId;
private int _controller;
private int _controllerNumber = 0;
private int _controllerNumber;
private string _controllerImage;
private int _device;
private object _configuration;
private string _profileName;
private bool _isLoaded;
private readonly UserControl _owner;
private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public IGamepadDriver AvaloniaKeyboardDriver { get; }
public IGamepad SelectedGamepad { get; private set; }
@@ -176,11 +176,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{
get
{
SvgImage image = new SvgImage();
SvgImage image = new();
if (!string.IsNullOrWhiteSpace(_controllerImage))
{
SvgSource source = new SvgSource();
SvgSource source = new();
source.Load(EmbeddedResources.GetStream(_controllerImage));
@@ -234,22 +234,18 @@ namespace Ryujinx.Ava.UI.ViewModels
public ControllerInputViewModel(UserControl owner) : this()
{
_owner = owner;
if (Program.PreviewerDetached)
{
_mainWindow =
(MainWindow)((IClassicDesktopStyleApplicationLifetime)Avalonia.Application.Current
(MainWindow)((IClassicDesktopStyleApplicationLifetime)Application.Current
.ApplicationLifetime).MainWindow;
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
if (_mainWindow.ViewModel.AppHost != null)
{
_mainWindow.ViewModel.AppHost.NpadManager.BlockInputUpdates();
}
_mainWindow.ViewModel.AppHost?.NpadManager.BlockInputUpdates();
_isLoaded = false;
@@ -351,7 +347,8 @@ namespace Ryujinx.Ava.UI.ViewModels
{
return;
}
else if (type == DeviceType.Keyboard)
if (type == DeviceType.Keyboard)
{
if (_mainWindow.InputManager.KeyboardDriver is AvaloniaKeyboardDriver)
{
@@ -448,7 +445,7 @@ namespace Ryujinx.Ava.UI.ViewModels
const string Hyphen = "-";
const int Offset = 1;
return str.Substring(str.IndexOf(Hyphen) + Offset);
return str[(str.IndexOf(Hyphen) + Offset)..];
}
public void LoadDevices()
@@ -562,7 +559,7 @@ namespace Ryujinx.Ava.UI.ViewModels
ButtonL = Key.E,
ButtonZl = Key.Q,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound
ButtonSr = Key.Unbound,
},
LeftJoyconStick =
new JoyconConfigKeyboardStick<Key>
@@ -571,7 +568,7 @@ namespace Ryujinx.Ava.UI.ViewModels
StickDown = Key.S,
StickLeft = Key.A,
StickRight = Key.D,
StickButton = Key.F
StickButton = Key.F,
},
RightJoycon = new RightJoyconCommonConfig<Key>
{
@@ -583,7 +580,7 @@ namespace Ryujinx.Ava.UI.ViewModels
ButtonR = Key.U,
ButtonZr = Key.O,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound
ButtonSr = Key.Unbound,
},
RightJoyconStick = new JoyconConfigKeyboardStick<Key>
{
@@ -591,8 +588,8 @@ namespace Ryujinx.Ava.UI.ViewModels
StickDown = Key.K,
StickLeft = Key.J,
StickRight = Key.L,
StickButton = Key.H
}
StickButton = Key.H,
},
};
}
else if (activeDevice.Type == DeviceType.Controller)
@@ -622,14 +619,14 @@ namespace Ryujinx.Ava.UI.ViewModels
ButtonL = ConfigGamepadInputId.LeftShoulder,
ButtonZl = ConfigGamepadInputId.LeftTrigger,
ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound
ButtonSr = ConfigGamepadInputId.Unbound,
},
LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{
Joystick = ConfigStickInputId.Left,
StickButton = ConfigGamepadInputId.LeftStick,
InvertStickX = false,
InvertStickY = false
InvertStickY = false,
},
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
{
@@ -641,28 +638,28 @@ namespace Ryujinx.Ava.UI.ViewModels
ButtonR = ConfigGamepadInputId.RightShoulder,
ButtonZr = ConfigGamepadInputId.RightTrigger,
ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound
ButtonSr = ConfigGamepadInputId.Unbound,
},
RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{
Joystick = ConfigStickInputId.Right,
StickButton = ConfigGamepadInputId.RightStick,
InvertStickX = false,
InvertStickY = false
InvertStickY = false,
},
Motion = new StandardMotionConfigController
{
MotionBackend = MotionInputBackendType.GamepadDriver,
EnableMotion = true,
Sensitivity = 100,
GyroDeadzone = 1
GyroDeadzone = 1,
},
Rumble = new RumbleConfigController
{
StrongRumble = 1f,
WeakRumble = 1f,
EnableRumble = false
}
EnableRumble = false,
},
};
}
else
@@ -709,7 +706,7 @@ namespace Ryujinx.Ava.UI.ViewModels
try
{
config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig);
config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig);
}
catch (JsonException) { }
catch (InvalidOperationException)
@@ -754,8 +751,7 @@ namespace Ryujinx.Ava.UI.ViewModels
return;
}
else
{
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
if (validFileName)
@@ -775,7 +771,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.ControllerType = Controllers[_controller].Type;
string jsonString = JsonHelper.Serialize(config, SerializerContext.InputConfig);
string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig);
await File.WriteAllTextAsync(path, jsonString);
@@ -786,7 +782,6 @@ namespace Ryujinx.Ava.UI.ViewModels
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
}
}
}
public async void RemoveProfile()
{
@@ -887,6 +882,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public void Dispose()
{
GC.SuppressFinalize(this);
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;

View File

@@ -22,6 +22,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Application = Avalonia.Application;
using Path = System.IO.Path;
namespace Ryujinx.Ava.UI.ViewModels
@@ -31,16 +32,15 @@ namespace Ryujinx.Ava.UI.ViewModels
private readonly List<DownloadableContentContainer> _downloadableContentContainerList;
private readonly string _downloadableContentJsonPath;
private VirtualFileSystem _virtualFileSystem;
private readonly VirtualFileSystem _virtualFileSystem;
private AvaloniaList<DownloadableContentModel> _downloadableContents = new();
private AvaloniaList<DownloadableContentModel> _views = new();
private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new();
private string _search;
private ulong _titleId;
private string _titleName;
private readonly ulong _titleId;
private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public AvaloniaList<DownloadableContentModel> DownloadableContents
{
@@ -90,18 +90,17 @@ namespace Ryujinx.Ava.UI.ViewModels
get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count);
}
public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId)
{
_virtualFileSystem = virtualFileSystem;
_titleId = titleId;
_titleName = titleName;
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
try
{
_downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, SerializerContext.ListDownloadableContentContainer);
_downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, _serializerContext.ListDownloadableContentContainer);
}
catch
{
@@ -196,19 +195,19 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void Add()
{
OpenFileDialog dialog = new OpenFileDialog()
OpenFileDialog dialog = new()
{
Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle],
AllowMultiple = true
AllowMultiple = true,
};
dialog.Filters.Add(new FileDialogFilter
{
Name = "NSP",
Extensions = { "nsp" }
Extensions = { "nsp" },
});
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
string[] files = await dialog.ShowAsync(desktop.MainWindow);
@@ -314,7 +313,7 @@ namespace Ryujinx.Ava.UI.ViewModels
container = new DownloadableContentContainer
{
ContainerPath = downloadableContent.ContainerPath,
DownloadableContentNcaList = new List<DownloadableContentNca>()
DownloadableContentNcaList = new List<DownloadableContentNca>(),
};
}
@@ -322,7 +321,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Enabled = downloadableContent.Enabled,
TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16),
FullPath = downloadableContent.FullPath
FullPath = downloadableContent.FullPath,
});
}
@@ -331,7 +330,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_downloadableContentContainerList.Add(container);
}
JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, SerializerContext.ListDownloadableContentContainer);
JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, _serializerContext.ListDownloadableContentContainer);
}
}

View File

@@ -1,3 +1,4 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input;
@@ -12,6 +13,7 @@ using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.Models.Generic;
using Ryujinx.Ava.UI.Renderer;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common;
@@ -23,6 +25,7 @@ using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.Ui;
using Ryujinx.Modules;
using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration;
@@ -34,7 +37,10 @@ using System.Collections.ObjectModel;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Path = System.IO.Path;
using Image = SixLabors.ImageSharp.Image;
using InputManager = Ryujinx.Input.HLE.InputManager;
using Key = Ryujinx.Input.Key;
using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
namespace Ryujinx.Ava.UI.ViewModels
@@ -89,7 +95,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private Cursor _cursor;
private string _title;
private string _currentEmulatedGamePath;
private AutoResetEvent _rendererWaitEvent;
private readonly AutoResetEvent _rendererWaitEvent;
private WindowState _windowState;
private double _windowWidth;
private double _windowHeight;
@@ -128,7 +134,7 @@ namespace Ryujinx.Ava.UI.ViewModels
ApplicationLibrary applicationLibrary,
VirtualFileSystem virtualFileSystem,
AccountManager accountManager,
Ryujinx.Input.HLE.InputManager inputManager,
InputManager inputManager,
UserChannelPersistence userChannelPersistence,
LibHacHorizonManager libHacHorizonManager,
IHostUiHandler uiHandler,
@@ -177,7 +183,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool CanUpdate
{
get => _canUpdate && EnableNonGameRunningControls && Modules.Updater.CanUpdate(false);
get => _canUpdate && EnableNonGameRunningControls && Updater.CanUpdate(false);
set
{
_canUpdate = value;
@@ -343,11 +349,11 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public bool OpenUserSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0;
public bool OpenUserSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0;
public bool OpenDeviceSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;
public bool OpenDeviceSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;
public bool OpenBcatSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
public bool OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
public string LoadHeading
{
@@ -888,7 +894,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public ApplicationLibrary ApplicationLibrary { get; private set; }
public VirtualFileSystem VirtualFileSystem { get; private set; }
public AccountManager AccountManager { get; private set; }
public Ryujinx.Input.HLE.InputManager InputManager { get; private set; }
public InputManager InputManager { get; private set; }
public UserChannelPersistence UserChannelPersistence { get; private set; }
public Action<bool> ShowLoading { get; private set; }
public Action<bool> SwitchToGameControl { get; private set; }
@@ -919,7 +925,8 @@ namespace Ryujinx.Ava.UI.ViewModels
{
return SortMode switch
{
ApplicationSort.LastPlayed => new Models.Generic.LastPlayedSortComparer(IsAscending),
#pragma warning disable IDE0055 // Disable formatting
ApplicationSort.LastPlayed => new LastPlayedSortComparer(IsAscending),
ApplicationSort.FileSize => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileSizeBytes)
: SortExpressionComparer<ApplicationData>.Descending(app => app.FileSizeBytes),
ApplicationSort.TotalTimePlayed => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TimePlayedNum)
@@ -935,6 +942,7 @@ namespace Ryujinx.Ava.UI.ViewModels
ApplicationSort.Path => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Path)
: SortExpressionComparer<ApplicationData>.Descending(app => app.Path),
_ => null,
#pragma warning restore IDE0055
};
}
@@ -1023,7 +1031,7 @@ namespace Ryujinx.Ava.UI.ViewModels
// Purge Applet Cache.
DirectoryInfo miiEditorCacheFolder = new DirectoryInfo(Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache"));
DirectoryInfo miiEditorCacheFolder = new(Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache"));
if (miiEditorCacheFolder.Exists)
{
@@ -1044,18 +1052,21 @@ namespace Ryujinx.Ava.UI.ViewModels
{
RefreshFirmwareStatus();
}
}) { Name = "GUI.FirmwareInstallerThread" };
})
{
Name = "GUI.FirmwareInstallerThread",
};
thread.Start();
}
}
catch (LibHac.Common.Keys.MissingKeyException ex)
catch (MissingKeyException ex)
{
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
Logger.Error?.Print(LogClass.Application, ex.ToString());
async void Action() => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, (desktop.MainWindow as MainWindow));
static async void Action() => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys);
Dispatcher.UIThread.Post(Action);
}
@@ -1120,7 +1131,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private void PrepareLoadScreen()
{
using MemoryStream stream = new(SelectedIcon);
using var gameIconBmp = SixLabors.ImageSharp.Image.Load<Bgra32>(stream);
using var gameIconBmp = Image.Load<Bgra32>(stream);
var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp).ToPixel<Bgra32>();
@@ -1175,7 +1186,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Dispatcher.UIThread.InvokeAsync(() =>
{
Avalonia.Application.Current.Styles.TryGetResource(args.VSyncEnabled
Application.Current.Styles.TryGetResource(args.VSyncEnabled
? "VsyncEnabled"
: "VsyncDisabled", out object color);
@@ -1208,7 +1219,7 @@ namespace Ryujinx.Ava.UI.ViewModels
#region PublicMethods
public void SetUIProgressHandlers(Switch emulationContext)
public void SetUiProgressHandlers(Switch emulationContext)
{
if (emulationContext.Processes.ActiveApplication.DiskCacheLoadState != null)
{
@@ -1222,17 +1233,17 @@ namespace Ryujinx.Ava.UI.ViewModels
public void LoadConfigurableHotKeys()
{
if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi, out var showUiKey))
if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi, out var showUiKey))
{
ShowUiKey = new KeyGesture(showUiKey);
}
if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey))
if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey))
{
ScreenshotKey = new KeyGesture(screenshotKey);
}
if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey))
if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey))
{
PauseKey = new KeyGesture(pauseKey);
}
@@ -1260,7 +1271,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void InstallFirmwareFromFile()
{
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
OpenFileDialog dialog = new() { AllowMultiple = false };
dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance[LocaleKeys.FileDialogAllTypes], Extensions = { "xci", "zip" } });
@@ -1278,7 +1289,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void InstallFirmwareFromFolder()
{
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
OpenFolderDialog dialog = new();
@@ -1327,7 +1338,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public void ChangeLanguage(object languageCode)
public static void ChangeLanguage(object languageCode)
{
LocaleManager.Instance.LoadLanguage((string)languageCode);
@@ -1342,6 +1353,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
_ = fileType switch
{
#pragma warning disable IDE0055 // Disable formatting
"NSP" => ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSP,
"PFS0" => ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.PFS0,
"XCI" => ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.XCI,
@@ -1349,6 +1361,7 @@ namespace Ryujinx.Ava.UI.ViewModels
"NRO" => ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NRO,
"NSO" => ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSO,
_ => throw new ArgumentOutOfRangeException(fileType),
#pragma warning restore IDE0055
};
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
@@ -1383,11 +1396,11 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void OpenFile()
{
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
OpenFileDialog dialog = new()
{
Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle]
Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle],
};
dialog.Filters.Add(new FileDialogFilter
@@ -1400,16 +1413,18 @@ namespace Ryujinx.Ava.UI.ViewModels
"xci",
"nca",
"nro",
"nso"
}
"nso",
},
});
#pragma warning disable IDE0055 // Disable formatting
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" } });
#pragma warning restore IDE0055
string[] files = await dialog.ShowAsync(desktop.MainWindow);
@@ -1422,11 +1437,11 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void OpenFolder()
{
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
OpenFolderDialog dialog = new()
{
Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle]
Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle],
};
string folder = await dialog.ShowAsync(desktop.MainWindow);
@@ -1458,10 +1473,7 @@ namespace Ryujinx.Ava.UI.ViewModels
Logger.RestartTime();
if (SelectedIcon == null)
{
SelectedIcon = ApplicationLibrary.GetApplicationIcon(path);
}
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path);
PrepareLoadScreen();
@@ -1521,7 +1533,7 @@ namespace Ryujinx.Ava.UI.ViewModels
});
}
public void UpdateGameMetadata(string titleId)
public static void UpdateGameMetadata(string titleId)
{
ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
{

View File

@@ -18,7 +18,6 @@ using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Configuration.System;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -248,7 +247,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public AvaloniaList<string> NetworkInterfaceList
{
get => new AvaloniaList<string>(_networkInterfaces.Keys);
get => new(_networkInterfaces.Keys);
}
public KeyboardHotkeys KeyboardHotkeys
@@ -561,7 +560,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_directoryChanged = false;
}
public void RevertIfNotSaved()
private static void RevertIfNotSaved()
{
Program.ReloadConfig();
}

View File

@@ -1,3 +1,4 @@
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
@@ -16,7 +17,6 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common;
using System;
using System.Collections.Generic;
@@ -29,17 +29,16 @@ namespace Ryujinx.Ava.UI.ViewModels
{
public class TitleUpdateViewModel : BaseModel
{
public TitleUpdateMetadata _titleUpdateWindowData;
public readonly string _titleUpdateJsonPath;
private VirtualFileSystem _virtualFileSystem { get; }
private ulong _titleId { get; }
private string _titleName { get; }
public TitleUpdateMetadata TitleUpdateWindowData;
public readonly string TitleUpdateJsonPath;
private VirtualFileSystem VirtualFileSystem { get; }
private ulong TitleId { get; }
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
private AvaloniaList<object> _views = new();
private object _selectedUpdate;
private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
private static readonly TitleUpdateMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public AvaloniaList<TitleUpdateModel> TitleUpdates
{
@@ -71,27 +70,26 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId)
{
_virtualFileSystem = virtualFileSystem;
VirtualFileSystem = virtualFileSystem;
_titleId = titleId;
_titleName = titleName;
TitleId = titleId;
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
try
{
_titleUpdateWindowData = JsonHelper.DeserializeFromFile(_titleUpdateJsonPath, SerializerContext.TitleUpdateMetadata);
TitleUpdateWindowData = JsonHelper.DeserializeFromFile(TitleUpdateJsonPath, _serializerContext.TitleUpdateMetadata);
}
catch
{
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}");
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {TitleId} at {TitleUpdateJsonPath}");
_titleUpdateWindowData = new TitleUpdateMetadata
TitleUpdateWindowData = new TitleUpdateMetadata
{
Selected = "",
Paths = new List<string>()
Paths = new List<string>(),
};
Save();
@@ -102,12 +100,12 @@ namespace Ryujinx.Ava.UI.ViewModels
private void LoadUpdates()
{
foreach (string path in _titleUpdateWindowData.Paths)
foreach (string path in TitleUpdateWindowData.Paths)
{
AddUpdate(path);
}
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == TitleUpdateWindowData.Selected, null);
SelectedUpdate = selected;
@@ -126,7 +124,8 @@ namespace Ryujinx.Ava.UI.ViewModels
{
return -1;
}
else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
{
return 1;
}
@@ -163,7 +162,7 @@ namespace Ryujinx.Ava.UI.ViewModels
try
{
(Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0);
(Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(VirtualFileSystem, new PartitionFileSystem(file.AsStorage()), TitleId.ToString("x16"), 0);
if (controlNca != null && patchNca != null)
{
@@ -206,16 +205,16 @@ namespace Ryujinx.Ava.UI.ViewModels
OpenFileDialog dialog = new()
{
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
AllowMultiple = true
AllowMultiple = true,
};
dialog.Filters.Add(new FileDialogFilter
{
Name = "NSP",
Extensions = { "nsp" }
Extensions = { "nsp" },
});
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
string[] files = await dialog.ShowAsync(desktop.MainWindow);
@@ -233,20 +232,20 @@ namespace Ryujinx.Ava.UI.ViewModels
public void Save()
{
_titleUpdateWindowData.Paths.Clear();
_titleUpdateWindowData.Selected = "";
TitleUpdateWindowData.Paths.Clear();
TitleUpdateWindowData.Selected = "";
foreach (TitleUpdateModel update in TitleUpdates)
{
_titleUpdateWindowData.Paths.Add(update.Path);
TitleUpdateWindowData.Paths.Add(update.Path);
if (update == SelectedUpdate)
{
_titleUpdateWindowData.Selected = update.Path;
TitleUpdateWindowData.Selected = update.Path;
}
}
JsonHelper.SerializeToFile(_titleUpdateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata);
JsonHelper.SerializeToFile(TitleUpdateJsonPath, TitleUpdateWindowData, _serializerContext.TitleUpdateMetadata);
}
}
}

View File

@@ -28,7 +28,6 @@ namespace Ryujinx.Ava.UI.ViewModels
private Color _backgroundColor = Colors.White;
private int _selectedIndex;
private byte[] _selectedImage;
public UserFirmwareAvatarSelectorViewModel()
{
@@ -78,11 +77,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public byte[] SelectedImage
{
get => _selectedImage;
private set => _selectedImage = value;
}
public byte[] SelectedImage { get; private set; }
private void LoadImagesFromStore()
{
@@ -114,8 +109,8 @@ namespace Ryujinx.Ava.UI.ViewModels
if (!string.IsNullOrWhiteSpace(avatarPath))
{
using (IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open))
{
using IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open);
Nca nca = new(virtualFileSystem.KeySet, ncaFileStream);
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
@@ -128,9 +123,9 @@ namespace Ryujinx.Ava.UI.ViewModels
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
using (MemoryStream stream = new())
using (MemoryStream streamPng = new())
{
using MemoryStream stream = new();
using MemoryStream streamPng = new();
file.Get.AsStream().CopyTo(stream);
stream.Position = 0;
@@ -144,13 +139,11 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
}
}
}
private static byte[] DecompressYaz0(Stream stream)
{
using (BinaryReader reader = new(stream))
{
using BinaryReader reader = new(stream);
reader.ReadInt32(); // Magic
uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32());
@@ -227,4 +220,3 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
}
}

View File

@@ -1,7 +1,7 @@
using Microsoft.IdentityModel.Tokens;
using Ryujinx.Ava.UI.Models;
using System;
using System.Collections.ObjectModel;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.UI.ViewModels
{
@@ -20,6 +20,9 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsEmpty { get; set; }
public void Dispose() { }
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
}

View File

@@ -15,7 +15,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private string _search;
private ObservableCollection<SaveModel> _saves = new();
private ObservableCollection<SaveModel> _views = new();
private AccountManager _accountManager;
private readonly AccountManager _accountManager;
public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId);
@@ -102,19 +102,16 @@ namespace Ryujinx.Ava.UI.ViewModels
private IComparer<SaveModel> GetComparer()
{
switch (SortIndex)
return SortIndex switch
{
case 0:
return OrderIndex == 0
0 => OrderIndex == 0
? SortExpressionComparer<SaveModel>.Ascending(save => save.Title)
: SortExpressionComparer<SaveModel>.Descending(save => save.Title);
case 1:
return OrderIndex == 0
: SortExpressionComparer<SaveModel>.Descending(save => save.Title),
1 => OrderIndex == 0
? SortExpressionComparer<SaveModel>.Ascending(save => save.Size)
: SortExpressionComparer<SaveModel>.Descending(save => save.Size);
default:
return null;
}
: SortExpressionComparer<SaveModel>.Descending(save => save.Size),
_ => null,
};
}
}
}

View File

@@ -29,7 +29,7 @@ namespace Ryujinx.Ava.UI.Views.Input
foreach (ILogical visual in SettingButtons.GetLogicalDescendants())
{
if (visual is ToggleButton button && !(visual is CheckBox))
if (visual is ToggleButton button && visual is not CheckBox)
{
button.Checked += Button_Checked;
button.Unchecked += Button_Unchecked;

View File

@@ -10,7 +10,7 @@ namespace Ryujinx.Ava.UI.Views.Input
{
public partial class MotionInputView : UserControl
{
private MotionInputViewModel _viewModel;
private readonly MotionInputViewModel _viewModel;
public MotionInputView()
{
@@ -30,7 +30,7 @@ namespace Ryujinx.Ava.UI.Views.Input
MirrorInput = config.MirrorInput,
Sensitivity = config.Sensitivity,
GyroDeadzone = config.GyroDeadzone,
EnableCemuHookMotion = config.EnableCemuHookMotion
EnableCemuHookMotion = config.EnableCemuHookMotion,
};
InitializeComponent();
@@ -47,7 +47,7 @@ namespace Ryujinx.Ava.UI.Views.Input
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
SecondaryButtonText = "",
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
Content = content
Content = content,
};
contentDialog.PrimaryButtonClick += (sender, args) =>
{

View File

@@ -10,7 +10,7 @@ namespace Ryujinx.Ava.UI.Views.Input
{
public partial class RumbleInputView : UserControl
{
private RumbleInputViewModel _viewModel;
private readonly RumbleInputViewModel _viewModel;
public RumbleInputView()
{
@@ -24,7 +24,7 @@ namespace Ryujinx.Ava.UI.Views.Input
_viewModel = new RumbleInputViewModel
{
StrongRumble = config.StrongRumble,
WeakRumble = config.WeakRumble
WeakRumble = config.WeakRumble,
};
InitializeComponent();

View File

@@ -41,18 +41,18 @@ namespace Ryujinx.Ava.UI.Views.Main
foreach (var item in Enum.GetValues(typeof(FileTypes)))
{
string fileName = Enum.GetName(typeof(FileTypes), item);
checkBoxes.Add(new CheckBox()
checkBoxes.Add(new CheckBox
{
Content = $".{fileName}",
IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.Ui.ShownFileTypes),
Command = MiniCommand.Create(() => ViewModel.ToggleFileType(fileName))
Command = MiniCommand.Create(() => ViewModel.ToggleFileType(fileName)),
});
}
return checkBoxes.ToArray();
}
private MenuItem[] GenerateLanguageMenuItems()
private static MenuItem[] GenerateLanguageMenuItems()
{
List<MenuItem> menuItems = new();
@@ -79,8 +79,8 @@ namespace Ryujinx.Ava.UI.Views.Main
Header = languageName,
Command = MiniCommand.Create(() =>
{
ViewModel.ChangeLanguage(languageCode);
})
MainWindowViewModel.ChangeLanguage(languageCode);
}),
};
menuItems.Add(menuItem);

View File

@@ -12,12 +12,12 @@ namespace Ryujinx.Ava.UI.Views.Settings
public partial class SettingsHotkeysView : UserControl
{
private ButtonKeyAssigner _currentAssigner;
private IGamepadDriver AvaloniaKeyboardDriver;
private readonly IGamepadDriver _avaloniaKeyboardDriver;
public SettingsHotkeysView()
{
InitializeComponent();
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this);
_avaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this);
}
private void MouseClick(object sender, PointerPressedEventArgs e)
@@ -46,7 +46,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
PointerPressed += MouseClick;
var keyboard = (IKeyboard)AvaloniaKeyboardDriver.GetGamepad(AvaloniaKeyboardDriver.GamepadsIds[0]);
var keyboard = (IKeyboard)_avaloniaKeyboardDriver.GetGamepad(_avaloniaKeyboardDriver.GamepadsIds[0]);
IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard);
_currentAssigner.GetInputAndAssign(assigner);

View File

@@ -1,5 +1,5 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsUIView"
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsUiView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

View File

@@ -1,4 +1,5 @@
using Avalonia.Controls;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity;
using Ryujinx.Ava.Common.Locale;
@@ -9,11 +10,11 @@ using System.Linq;
namespace Ryujinx.Ava.UI.Views.Settings
{
public partial class SettingsUIView : UserControl
public partial class SettingsUiView : UserControl
{
public SettingsViewModel ViewModel;
public SettingsUIView()
public SettingsUiView()
{
InitializeComponent();
}
@@ -29,7 +30,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
}
else
{
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
path = await new OpenFolderDialog().ShowAsync(desktop.MainWindow);
@@ -60,15 +61,15 @@ namespace Ryujinx.Ava.UI.Views.Settings
public async void BrowseTheme(object sender, RoutedEventArgs e)
{
var dialog = new OpenFileDialog()
var dialog = new OpenFileDialog
{
Title = LocaleManager.Instance[LocaleKeys.SettingsSelectThemeFileDialogTitle],
AllowMultiple = false
AllowMultiple = false,
};
dialog.Filters.Add(new FileDialogFilter() { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] });
dialog.Filters.Add(new FileDialogFilter { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] });
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
var file = await dialog.ShowAsync(desktop.MainWindow);

View File

@@ -20,7 +20,7 @@ namespace Ryujinx.Ava.UI.Views.User
private bool _isNewUser;
public TempProfile TempProfile { get; set; }
public uint MaxProfileNameLength => 0x20;
public static uint MaxProfileNameLength => 0x20;
public bool IsDeletable => _profile.UserId != AccountManager.DefaultUserId;
public UserEditorView()
@@ -39,12 +39,12 @@ namespace Ryujinx.Ava.UI.Views.User
switch (arg.NavigationMode)
{
case NavigationMode.New:
var args = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter;
_isNewUser = args.isNewUser;
_profile = args.profile;
var (parent, profile, isNewUser) = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter;
_isNewUser = isNewUser;
_profile = profile;
TempProfile = new TempProfile(_profile);
_parent = args.parent;
_parent = parent;
break;
}

View File

@@ -11,6 +11,7 @@ using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System.IO;
using Image = SixLabors.ImageSharp.Image;
namespace Ryujinx.Ava.UI.Views.User
{
@@ -70,7 +71,7 @@ namespace Ryujinx.Ava.UI.Views.User
if (ViewModel.SelectedImage != null)
{
MemoryStream streamJpg = new();
SixLabors.ImageSharp.Image avatarImage = SixLabors.ImageSharp.Image.Load(ViewModel.SelectedImage, new PngDecoder());
Image avatarImage = Image.Load(ViewModel.SelectedImage, new PngDecoder());
avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(
ViewModel.BackgroundColor.R,

View File

@@ -67,7 +67,7 @@ namespace Ryujinx.Ava.UI.Views.User
dialog.Filters.Add(new FileDialogFilter
{
Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats],
Extensions = { "jpg", "jpeg", "png", "bmp" }
Extensions = { "jpg", "jpeg", "png", "bmp" },
});
dialog.Filters.Add(new FileDialogFilter { Name = "JPEG", Extensions = { "jpg", "jpeg" } });
dialog.Filters.Add(new FileDialogFilter { Name = "PNG", Extensions = { "png" } });
@@ -108,17 +108,15 @@ namespace Ryujinx.Ava.UI.Views.User
private static byte[] ProcessProfileImage(byte[] buffer)
{
using (Image image = Image.Load(buffer))
{
using Image image = Image.Load(buffer);
image.Mutate(x => x.Resize(256, 256));
using (MemoryStream streamJpg = new())
{
using MemoryStream streamJpg = new();
image.SaveAsJpeg(streamJpg);
return streamJpg.ToArray();
}
}
}
}
}

View File

@@ -18,6 +18,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Button = Avalonia.Controls.Button;
using UserId = LibHac.Fs.UserId;
namespace Ryujinx.Ava.UI.Views.User
@@ -47,12 +48,12 @@ namespace Ryujinx.Ava.UI.Views.User
switch (arg.NavigationMode)
{
case NavigationMode.New:
var args = ((NavigationDialogHost parent, AccountManager accountManager, HorizonClient client, VirtualFileSystem virtualFileSystem))arg.Parameter;
_accountManager = args.accountManager;
_horizonClient = args.client;
_virtualFileSystem = args.virtualFileSystem;
var (parent, accountManager, client, virtualFileSystem) = ((NavigationDialogHost parent, AccountManager accountManager, HorizonClient client, VirtualFileSystem virtualFileSystem))arg.Parameter;
_accountManager = accountManager;
_horizonClient = client;
_virtualFileSystem = virtualFileSystem;
_parent = args.parent;
_parent = parent;
break;
}
@@ -94,7 +95,7 @@ namespace Ryujinx.Ava.UI.Views.User
var save = saveDataInfo[i];
if (save.ProgramId.Value != 0)
{
var saveModel = new SaveModel(save, _virtualFileSystem);
var saveModel = new SaveModel(save);
saves.Add(saveModel);
}
}
@@ -114,7 +115,7 @@ namespace Ryujinx.Ava.UI.Views.User
private void OpenLocation(object sender, RoutedEventArgs e)
{
if (sender is Avalonia.Controls.Button button)
if (sender is Button button)
{
if (button.DataContext is SaveModel saveModel)
{
@@ -125,7 +126,7 @@ namespace Ryujinx.Ava.UI.Views.User
private async void Delete(object sender, RoutedEventArgs e)
{
if (sender is Avalonia.Controls.Button button)
if (sender is Button button)
{
if (button.DataContext is SaveModel saveModel)
{

View File

@@ -5,8 +5,9 @@ using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Navigation;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
using Button = Avalonia.Controls.Button;
namespace Ryujinx.Ava.UI.Views.User
{
@@ -101,7 +102,7 @@ namespace Ryujinx.Ava.UI.Views.User
private void EditUser(object sender, RoutedEventArgs e)
{
if (sender is Avalonia.Controls.Button button)
if (sender is Button button)
{
if (button.DataContext is UserProfile userProfile)
{

View File

@@ -1,6 +1,7 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Styling;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
@@ -28,14 +29,14 @@ namespace Ryujinx.Ava.UI.Windows
PrimaryButtonText = "",
SecondaryButtonText = "",
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
Content = new AboutWindow()
Content = new AboutWindow(),
};
Style closeButton = new(x => x.Name("CloseButton"));
closeButton.Setters.Add(new Setter(WidthProperty, 80d));
Style closeButtonParent = new(x => x.Name("CommandSpace"));
closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty, Avalonia.Layout.HorizontalAlignment.Right));
closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty, HorizontalAlignment.Right));
contentDialog.Styles.Add(closeButton);
contentDialog.Styles.Add(closeButtonParent);

View File

@@ -9,9 +9,10 @@ namespace Ryujinx.Ava.UI.Windows
{
public AmiiboWindow(bool showAll, string lastScannedAmiiboId, string titleId)
{
ViewModel = new AmiiboWindowViewModel(this, lastScannedAmiiboId, titleId);
ViewModel.ShowAllAmiibo = showAll;
ViewModel = new AmiiboWindowViewModel(this, lastScannedAmiiboId, titleId)
{
ShowAllAmiibo = showAll,
};
DataContext = ViewModel;

View File

@@ -1,11 +1,12 @@
using Avalonia;
using Avalonia.Collections;
using Avalonia.Collections;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Models;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -41,11 +42,11 @@ namespace Ryujinx.Ava.UI.Windows
string modsBasePath = ModLoader.GetModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId);
ulong titleIdValue = ulong.Parse(titleId, System.Globalization.NumberStyles.HexNumber);
ulong titleIdValue = ulong.Parse(titleId, NumberStyles.HexNumber);
_enabledCheatsPath = Path.Combine(titleModsPath, "cheats", "enabled.txt");
string[] enabled = { };
string[] enabled = Array.Empty<string>();
if (File.Exists(_enabledCheatsPath))
{
@@ -60,7 +61,6 @@ namespace Ryujinx.Ava.UI.Windows
string currentCheatFile = string.Empty;
string buildId = string.Empty;
string parentPath = string.Empty;
CheatsList currentGroup = null;
@@ -69,7 +69,7 @@ namespace Ryujinx.Ava.UI.Windows
if (cheat.Path.FullName != currentCheatFile)
{
currentCheatFile = cheat.Path.FullName;
parentPath = currentCheatFile.Replace(titleModsPath, "");
string parentPath = currentCheatFile.Replace(titleModsPath, "");
buildId = Path.GetFileNameWithoutExtension(currentCheatFile).ToUpper();
currentGroup = new CheatsList(buildId, parentPath);
@@ -100,7 +100,7 @@ namespace Ryujinx.Ava.UI.Windows
return;
}
List<string> enabledCheats = new List<string>();
List<string> enabledCheats = new();
foreach (var cheats in LoadedCheats)
{

View File

@@ -1,7 +1,8 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
#if DEBUG
using Avalonia;
#endif
namespace Ryujinx.Ava.UI.Windows
{

View File

@@ -89,8 +89,8 @@
<Grid
Grid.Column="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"

View File

@@ -24,9 +24,9 @@ namespace Ryujinx.Ava.UI.Windows
InitializeComponent();
}
public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId)
{
DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, titleId, titleName);
DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, titleId);
InitializeComponent();
}
@@ -38,8 +38,8 @@ namespace Ryujinx.Ava.UI.Windows
PrimaryButtonText = "",
SecondaryButtonText = "",
CloseButtonText = "",
Content = new DownloadableContentManagerWindow(virtualFileSystem, titleId, titleName),
Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], titleName, titleId.ToString("X16"))
Content = new DownloadableContentManagerWindow(virtualFileSystem, titleId),
Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], titleName, titleId.ToString("X16")),
};
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());

View File

@@ -68,8 +68,10 @@ namespace Ryujinx.Ava.UI.Windows
int w8 = w << 8;
int h8 = image.Height << 8;
#pragma warning disable IDE0059 // Unnecessary assignment
int xStep = w8 / ColorsPerLine;
int yStep = h8 / ColorsPerLine;
#pragma warning restore IDE0059
int i = 0;
int maxHitCount = 0;

View File

@@ -323,7 +323,7 @@ namespace Ryujinx.Ava.UI.Windows
ShowKeyErrorOnLoad = false;
Dispatcher.UIThread.Post(async () => await
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, this));
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
}
if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount)
@@ -373,7 +373,7 @@ namespace Ryujinx.Ava.UI.Windows
private void SetWindowSizePosition()
{
PixelPoint SavedPoint = new PixelPoint(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX,
PixelPoint savedPoint = new(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX,
ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY);
ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor;
@@ -381,12 +381,14 @@ namespace Ryujinx.Ava.UI.Windows
ViewModel.WindowState = ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value is true ? WindowState.Maximized : WindowState.Normal;
if (CheckScreenBounds(SavedPoint))
if (CheckScreenBounds(savedPoint))
{
Position = SavedPoint;
Position = savedPoint;
}
else
{
WindowStartupLocation = WindowStartupLocation.CenterScreen;
}
else WindowStartupLocation = WindowStartupLocation.CenterScreen;
}
private bool CheckScreenBounds(PixelPoint configPoint)
@@ -399,7 +401,7 @@ namespace Ryujinx.Ava.UI.Windows
}
}
Logger.Warning?.Print(LogClass.Application, $"Failed to find valid start-up coordinates. Defaulting to primary monitor center.");
Logger.Warning?.Print(LogClass.Application, "Failed to find valid start-up coordinates. Defaulting to primary monitor center.");
return false;
}
@@ -425,10 +427,7 @@ namespace Ryujinx.Ava.UI.Windows
private void SetMainContent(Control content = null)
{
if (content == null)
{
content = GameLibrary;
}
content ??= GameLibrary;
if (MainContent.Content != content)
{
@@ -438,21 +437,25 @@ namespace Ryujinx.Ava.UI.Windows
public static void UpdateGraphicsConfig()
{
#pragma warning disable IDE0055 // Disable formatting
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;
#pragma warning restore IDE0055
}
public void LoadHotKeys()
{
#pragma warning disable IDE0055 // Disable formatting
HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt));
HotKeyManager.SetHotKey(FullscreenHotKey2, new KeyGesture(Key.F11));
HotKeyManager.SetHotKey(FullscreenHotKeyMacOS, new KeyGesture(Key.F, KeyModifiers.Control | KeyModifiers.Meta));
HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9));
HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape));
#pragma warning restore IDE0055
}
private void VolumeStatus_CheckedChanged(object sender, SplitButtonClickEventArgs e)

View File

@@ -34,7 +34,7 @@
IsVisible="False"
KeyboardNavigation.IsTabStop="False"/>
<Grid Name="Pages" IsVisible="False" Grid.Row="2">
<settings:SettingsUIView Name="UiPage" />
<settings:SettingsUiView Name="UiPage" />
<settings:SettingsInputView Name="InputPage" />
<settings:SettingsHotkeysView Name="HotkeysPage" />
<settings:SettingsSystemView Name="SystemPage" />

View File

@@ -2,6 +2,7 @@
using Avalonia.Controls.Primitives;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Ryujinx.Ui.Common.Configuration;
using System;
using System.IO;
using System.Reflection;
@@ -17,7 +18,7 @@ namespace Ryujinx.Ava.UI.Windows
WindowStartupLocation = WindowStartupLocation.CenterOwner;
TransparencyLevelHint = WindowTransparencyLevel.None;
using Stream stream = Assembly.GetAssembly(typeof(Ryujinx.Ui.Common.Configuration.ConfigurationState)).GetManifestResourceStream("Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
using Stream stream = Assembly.GetAssembly(typeof(ConfigurationState)).GetManifestResourceStream("Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
Icon = new WindowIcon(stream);
stream.Position = 0;

View File

@@ -24,9 +24,9 @@ namespace Ryujinx.Ava.UI.Windows
InitializeComponent();
}
public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId)
{
DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, titleId, titleName);
DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, titleId);
InitializeComponent();
}
@@ -38,8 +38,8 @@ namespace Ryujinx.Ava.UI.Windows
PrimaryButtonText = "",
SecondaryButtonText = "",
CloseButtonText = "",
Content = new TitleUpdateWindow(virtualFileSystem, titleId, titleName),
Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, titleName, titleId.ToString("X16"))
Content = new TitleUpdateWindow(virtualFileSystem, titleId),
Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, titleName, titleId.ToString("X16")),
};
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());

View File

@@ -315,7 +315,7 @@ namespace Ryujinx.Graphics.Vulkan
features2.Features.ShaderStorageImageMultisample,
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
features2.Features.MultiViewport,
features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue
featuresRobustness2.NullDescriptor || IsMoltenVk,
_physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName),
featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,

View File

@@ -21,7 +21,6 @@ using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using Path = System.IO.Path;
using RightsId = LibHac.Fs.RightsId;
namespace Ryujinx.HLE.FileSystem
{