[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>
This commit is contained in:
parent
8a363b5df2
commit
6c515e1822
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
@ -61,31 +64,31 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
internal class AppHost
|
||||
{
|
||||
private const int CursorHideIdleTime = 5; // Hide Cursor seconds.
|
||||
private const int CursorHideIdleTime = 5; // Hide Cursor seconds.
|
||||
private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping.
|
||||
private const int TargetFps = 60;
|
||||
private const float VolumeDelta = 0.05f;
|
||||
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 long _ticksPerFrame;
|
||||
private readonly Stopwatch _chrono;
|
||||
private long _ticks;
|
||||
private long _ticks;
|
||||
|
||||
private readonly AccountManager _accountManager;
|
||||
private readonly AccountManager _accountManager;
|
||||
private readonly UserChannelPersistence _userChannelPersistence;
|
||||
private readonly InputManager _inputManager;
|
||||
private readonly InputManager _inputManager;
|
||||
|
||||
private readonly MainWindowViewModel _viewModel;
|
||||
private readonly IKeyboard _keyboardInterface;
|
||||
private readonly TopLevel _topLevel;
|
||||
public RendererHost _rendererHost;
|
||||
private readonly IKeyboard _keyboardInterface;
|
||||
private readonly TopLevel _topLevel;
|
||||
public RendererHost RendererHost;
|
||||
|
||||
private readonly GraphicsDebugLevel _glLogLevel;
|
||||
private float _newVolume;
|
||||
private KeyboardHotkeyState _prevHotkeyState;
|
||||
private float _newVolume;
|
||||
private KeyboardHotkeyState _prevHotkeyState;
|
||||
|
||||
private long _lastCursorMoveTime;
|
||||
private bool _isCursorInRenderer = true;
|
||||
@ -94,14 +97,14 @@ namespace Ryujinx.Ava
|
||||
private bool _isActive;
|
||||
private bool _renderingStarted;
|
||||
|
||||
private ManualResetEvent _gpuDoneEvent;
|
||||
private readonly ManualResetEvent _gpuDoneEvent;
|
||||
|
||||
private IRenderer _renderer;
|
||||
private readonly Thread _renderingThread;
|
||||
private IRenderer _renderer;
|
||||
private readonly Thread _renderingThread;
|
||||
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
||||
private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution;
|
||||
|
||||
private bool _dialogShown;
|
||||
private bool _dialogShown;
|
||||
private readonly bool _isFirmwareTitle;
|
||||
|
||||
private readonly object _lockObject = new();
|
||||
@ -109,55 +112,55 @@ namespace Ryujinx.Ava
|
||||
public event EventHandler AppExit;
|
||||
public event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent;
|
||||
|
||||
public VirtualFileSystem VirtualFileSystem { get; }
|
||||
public ContentManager ContentManager { get; }
|
||||
public NpadManager NpadManager { get; }
|
||||
public VirtualFileSystem VirtualFileSystem { get; }
|
||||
public ContentManager ContentManager { get; }
|
||||
public NpadManager NpadManager { get; }
|
||||
public TouchScreenManager TouchScreenManager { get; }
|
||||
public Switch Device { get; set; }
|
||||
public Switch Device { get; set; }
|
||||
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
public string ApplicationPath { get; private set; }
|
||||
public bool ScreenshotRequested { get; set; }
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
public string ApplicationPath { get; private set; }
|
||||
public bool ScreenshotRequested { get; set; }
|
||||
|
||||
public AppHost(
|
||||
RendererHost renderer,
|
||||
InputManager inputManager,
|
||||
string applicationPath,
|
||||
VirtualFileSystem virtualFileSystem,
|
||||
ContentManager contentManager,
|
||||
AccountManager accountManager,
|
||||
RendererHost renderer,
|
||||
InputManager inputManager,
|
||||
string applicationPath,
|
||||
VirtualFileSystem virtualFileSystem,
|
||||
ContentManager contentManager,
|
||||
AccountManager accountManager,
|
||||
UserChannelPersistence userChannelPersistence,
|
||||
MainWindowViewModel viewmodel,
|
||||
TopLevel topLevel)
|
||||
MainWindowViewModel viewmodel,
|
||||
TopLevel topLevel)
|
||||
{
|
||||
_viewModel = viewmodel;
|
||||
_inputManager = inputManager;
|
||||
_accountManager = accountManager;
|
||||
_viewModel = viewmodel;
|
||||
_inputManager = inputManager;
|
||||
_accountManager = accountManager;
|
||||
_userChannelPersistence = userChannelPersistence;
|
||||
_renderingThread = new Thread(RenderLoop) { Name = "GUI.RenderThread" };
|
||||
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
||||
_glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel;
|
||||
_topLevel = topLevel;
|
||||
_renderingThread = new Thread(RenderLoop) { Name = "GUI.RenderThread" };
|
||||
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
||||
_glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel;
|
||||
_topLevel = topLevel;
|
||||
|
||||
_inputManager.SetMouseDriver(new AvaloniaMouseDriver(_topLevel, renderer));
|
||||
|
||||
_keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0");
|
||||
|
||||
NpadManager = _inputManager.CreateNpadManager();
|
||||
NpadManager = _inputManager.CreateNpadManager();
|
||||
TouchScreenManager = _inputManager.CreateTouchScreenManager();
|
||||
ApplicationPath = applicationPath;
|
||||
VirtualFileSystem = virtualFileSystem;
|
||||
ContentManager = contentManager;
|
||||
ApplicationPath = applicationPath;
|
||||
VirtualFileSystem = virtualFileSystem;
|
||||
ContentManager = contentManager;
|
||||
|
||||
_rendererHost = renderer;
|
||||
RendererHost = renderer;
|
||||
|
||||
_chrono = new Stopwatch();
|
||||
_chrono = new Stopwatch();
|
||||
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
|
||||
|
||||
if (ApplicationPath.StartsWith("@SystemContent"))
|
||||
{
|
||||
ApplicationPath = _viewModel.VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath);
|
||||
ApplicationPath = VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath);
|
||||
|
||||
_isFirmwareTitle = true;
|
||||
}
|
||||
@ -170,21 +173,21 @@ namespace Ryujinx.Ava
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
InvisibleCursorWin = CreateEmptyCursor();
|
||||
DefaultCursorWin = CreateArrowCursor();
|
||||
_invisibleCursorWin = CreateEmptyCursor();
|
||||
_defaultCursorWin = CreateArrowCursor();
|
||||
}
|
||||
|
||||
ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState;
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
|
||||
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
|
||||
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
|
||||
ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing;
|
||||
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
|
||||
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
|
||||
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
|
||||
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
|
||||
ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing;
|
||||
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
|
||||
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
|
||||
|
||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
|
||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
|
||||
|
||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||
_gpuDoneEvent = new ManualResetEvent(false);
|
||||
@ -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 point = e.GetCurrentPoint(window).Position;
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -271,12 +274,12 @@ namespace Ryujinx.Ava
|
||||
lock (_lockObject)
|
||||
{
|
||||
DateTime currentTime = DateTime.Now;
|
||||
string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png";
|
||||
string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png";
|
||||
|
||||
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();
|
||||
@ -336,21 +339,21 @@ namespace Ryujinx.Ava
|
||||
|
||||
_viewModel.IsGameRunning = true;
|
||||
|
||||
var activeProcess = Device.Processes.ActiveApplication;
|
||||
var activeProcess = Device.Processes.ActiveApplication;
|
||||
|
||||
string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}";
|
||||
string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}";
|
||||
string titleVersionSection = string.IsNullOrWhiteSpace(activeProcess.DisplayVersion) ? string.Empty : $" v{activeProcess.DisplayVersion}";
|
||||
string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})";
|
||||
string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)";
|
||||
string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})";
|
||||
string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)";
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
_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);
|
||||
}
|
||||
@ -419,7 +422,7 @@ namespace Ryujinx.Ava
|
||||
}
|
||||
|
||||
_isStopped = true;
|
||||
_isActive = false;
|
||||
_isActive = false;
|
||||
}
|
||||
|
||||
public void DisposeContext()
|
||||
@ -448,16 +451,16 @@ 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;
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
|
||||
ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState;
|
||||
ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter;
|
||||
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel;
|
||||
ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing;
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
|
||||
ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState;
|
||||
ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter;
|
||||
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel;
|
||||
ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing;
|
||||
|
||||
_topLevel.PointerMoved -= TopLevel_PointerEnterOrMoved;
|
||||
_topLevel.PointerEnter -= TopLevel_PointerEnterOrMoved;
|
||||
@ -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,12 +809,10 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
return new T();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}.");
|
||||
|
||||
return null;
|
||||
}
|
||||
Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
IHardwareDeviceDriver deviceDriver = null;
|
||||
@ -819,14 +820,14 @@ namespace Ryujinx.Ava
|
||||
for (int i = 0; i < availableBackends.Count; i++)
|
||||
{
|
||||
AudioBackend currentBackend = availableBackends[i];
|
||||
AudioBackend nextBackend = i + 1 < availableBackends.Count ? availableBackends[i + 1] : AudioBackend.Dummy;
|
||||
AudioBackend nextBackend = i + 1 < availableBackends.Count ? availableBackends[i + 1] : AudioBackend.Dummy;
|
||||
|
||||
deviceDriver = currentBackend switch
|
||||
{
|
||||
AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend),
|
||||
AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend),
|
||||
AudioBackend.SoundIo => InitializeAudioBackend<SoundIoHardwareDeviceDriver>(AudioBackend.SoundIo, nextBackend),
|
||||
AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend),
|
||||
_ => new DummyHardwareDeviceDriver()
|
||||
AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend),
|
||||
_ => new DummyHardwareDeviceDriver(),
|
||||
};
|
||||
|
||||
if (deviceDriver != null)
|
||||
@ -843,7 +844,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
private void Window_SizeChanged(object sender, Size e)
|
||||
{
|
||||
Width = (int)e.Width;
|
||||
Width = (int)e.Width;
|
||||
Height = (int)e.Height;
|
||||
|
||||
SetRendererWindowSize(e);
|
||||
@ -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()
|
||||
|
@ -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,11 +147,11 @@ namespace Ryujinx.Ava.Common
|
||||
{
|
||||
OpenFolderDialog folderDialog = new()
|
||||
{
|
||||
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle]
|
||||
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle],
|
||||
};
|
||||
|
||||
string destination = await folderDialog.ShowAsync(_owner);
|
||||
var cancellationToken = new CancellationTokenSource();
|
||||
string destination = await folderDialog.ShowAsync(_owner);
|
||||
var cancellationToken = new CancellationTokenSource();
|
||||
|
||||
UpdateWaitWindow waitingDialog = new(
|
||||
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
|
||||
@ -166,7 +166,7 @@ namespace Ryujinx.Ava.Common
|
||||
|
||||
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
|
||||
|
||||
Nca mainNca = null;
|
||||
Nca mainNca = null;
|
||||
Nca patchNca = null;
|
||||
|
||||
string extension = Path.GetExtension(titleFilePath).ToLower();
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,6 @@ namespace Ryujinx.Ava.Common
|
||||
FileType,
|
||||
FileSize,
|
||||
Path,
|
||||
Favorite
|
||||
Favorite,
|
||||
}
|
||||
}
|
@ -11,6 +11,6 @@
|
||||
ResScaleUp,
|
||||
ResScaleDown,
|
||||
VolumeUp,
|
||||
VolumeDown
|
||||
VolumeDown,
|
||||
}
|
||||
}
|
@ -20,8 +20,8 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
|
||||
ReflectionBindingExtension binding = new($"[{keyToUse}]")
|
||||
{
|
||||
Mode = BindingMode.OneWay,
|
||||
Source = LocaleManager.Instance
|
||||
Mode = BindingMode.OneWay,
|
||||
Source = LocaleManager.Instance,
|
||||
};
|
||||
|
||||
return binding.ProvideValue(serviceProvider);
|
||||
|
@ -13,17 +13,17 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
{
|
||||
private const string DefaultLanguageCode = "en_US";
|
||||
|
||||
private Dictionary<LocaleKeys, string> _localeStrings;
|
||||
private Dictionary<LocaleKeys, string> _localeDefaultStrings;
|
||||
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()
|
||||
{
|
||||
_localeStrings = new Dictionary<LocaleKeys, string>();
|
||||
_localeStrings = new Dictionary<LocaleKeys, string>();
|
||||
_localeDefaultStrings = new Dictionary<LocaleKeys, string>();
|
||||
_dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>();
|
||||
_dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>();
|
||||
|
||||
Load();
|
||||
}
|
||||
@ -126,11 +126,11 @@ 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");
|
||||
var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
|
||||
var localeStrings = new Dictionary<LocaleKeys, string>();
|
||||
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");
|
||||
var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
|
||||
|
||||
foreach (var item in strings)
|
||||
{
|
||||
|
@ -12,25 +12,25 @@ namespace Ryujinx.Ava.Input
|
||||
internal class AvaloniaKeyboard : IKeyboard
|
||||
{
|
||||
private readonly List<ButtonMappingEntry> _buttonsUserMapping;
|
||||
private readonly AvaloniaKeyboardDriver _driver;
|
||||
private StandardKeyboardInputConfig _configuration;
|
||||
private readonly AvaloniaKeyboardDriver _driver;
|
||||
private StandardKeyboardInputConfig _configuration;
|
||||
|
||||
private readonly object _userMappingLock = new();
|
||||
|
||||
public string Id { get; }
|
||||
public string Id { get; }
|
||||
public string Name { get; }
|
||||
|
||||
public bool IsConnected => true;
|
||||
public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None;
|
||||
public bool IsConnected => true;
|
||||
public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None;
|
||||
|
||||
private class ButtonMappingEntry
|
||||
{
|
||||
public readonly Key From;
|
||||
public readonly Key From;
|
||||
public readonly GamepadButtonInputId To;
|
||||
|
||||
public ButtonMappingEntry(GamepadButtonInputId to, Key from)
|
||||
{
|
||||
To = to;
|
||||
To = to;
|
||||
From = from;
|
||||
}
|
||||
}
|
||||
@ -40,8 +40,8 @@ namespace Ryujinx.Ava.Input
|
||||
_buttonsUserMapping = new List<ButtonMappingEntry>();
|
||||
|
||||
_driver = driver;
|
||||
Id = id;
|
||||
Name = name;
|
||||
Id = id;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public KeyboardStateSnapshot GetKeyboardStateSnapshot()
|
||||
@ -52,7 +52,7 @@ namespace Ryujinx.Ava.Input
|
||||
public GamepadStateSnapshot GetMappedStateSnapshot()
|
||||
{
|
||||
KeyboardStateSnapshot rawState = GetKeyboardStateSnapshot();
|
||||
GamepadStateSnapshot result = default;
|
||||
GamepadStateSnapshot result = default;
|
||||
|
||||
lock (_userMappingLock)
|
||||
{
|
||||
@ -75,10 +75,10 @@ namespace Ryujinx.Ava.Input
|
||||
}
|
||||
}
|
||||
|
||||
(short leftStickX, short leftStickY) = GetStickValues(ref rawState, _configuration.LeftJoyconStick);
|
||||
(short leftStickX, short leftStickY) = GetStickValues(ref rawState, _configuration.LeftJoyconStick);
|
||||
(short rightStickX, short rightStickY) = GetStickValues(ref rawState, _configuration.RightJoyconStick);
|
||||
|
||||
result.SetStick(StickInputId.Left, ConvertRawStickValue(leftStickX), ConvertRawStickValue(leftStickY));
|
||||
result.SetStick(StickInputId.Left, ConvertRawStickValue(leftStickX), ConvertRawStickValue(leftStickY));
|
||||
result.SetStick(StickInputId.Right, ConvertRawStickValue(rightStickX), ConvertRawStickValue(rightStickY));
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,23 +13,23 @@ namespace Ryujinx.Ava.Input
|
||||
internal class AvaloniaKeyboardDriver : IGamepadDriver
|
||||
{
|
||||
private static readonly string[] _keyboardIdentifers = new string[1] { "0" };
|
||||
private readonly Control _control;
|
||||
private readonly Control _control;
|
||||
private readonly HashSet<AvaKey> _pressedKeys;
|
||||
|
||||
public event EventHandler<KeyEventArgs> KeyPressed;
|
||||
public event EventHandler<KeyEventArgs> KeyRelease;
|
||||
public event EventHandler<string> TextInput;
|
||||
public event EventHandler<string> TextInput;
|
||||
|
||||
public string DriverName => "AvaloniaKeyboardDriver";
|
||||
public string DriverName => "AvaloniaKeyboardDriver";
|
||||
public ReadOnlySpan<string> GamepadsIds => _keyboardIdentifers;
|
||||
|
||||
public AvaloniaKeyboardDriver(Control control)
|
||||
{
|
||||
_control = control;
|
||||
_control = control;
|
||||
_pressedKeys = new HashSet<AvaKey>();
|
||||
|
||||
_control.KeyDown += OnKeyPress;
|
||||
_control.KeyUp += OnKeyRelease;
|
||||
_control.KeyDown += OnKeyPress;
|
||||
_control.KeyUp += OnKeyRelease;
|
||||
_control.TextInput += Control_TextInput;
|
||||
_control.AddHandler(InputElement.TextInputEvent, Control_LastChanceTextInput, RoutingStrategies.Bubble);
|
||||
}
|
||||
@ -47,13 +47,13 @@ namespace Ryujinx.Ava.Input
|
||||
|
||||
public event Action<string> OnGamepadConnected
|
||||
{
|
||||
add { }
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public event Action<string> OnGamepadDisconnected
|
||||
{
|
||||
add { }
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ namespace Ryujinx.Ava.Input
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_control.KeyUp -= OnKeyPress;
|
||||
_control.KeyUp -= OnKeyPress;
|
||||
_control.KeyDown -= OnKeyRelease;
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ namespace Ryujinx.Ava.Input
|
||||
AvaKey.OemBackslash,
|
||||
|
||||
// NOTE: invalid
|
||||
AvaKey.None
|
||||
AvaKey.None,
|
||||
};
|
||||
|
||||
private static readonly Dictionary<AvaKey, Key> _avaKeyMapping;
|
||||
|
@ -10,12 +10,12 @@ namespace Ryujinx.Ava.Input
|
||||
{
|
||||
private AvaloniaMouseDriver _driver;
|
||||
|
||||
public string Id => "0";
|
||||
public string Id => "0";
|
||||
public string Name => "AvaloniaMouse";
|
||||
|
||||
public bool IsConnected => true;
|
||||
public GamepadFeaturesFlag Features => throw new NotImplementedException();
|
||||
public bool[] Buttons => _driver.PressedButtons;
|
||||
public bool IsConnected => true;
|
||||
public GamepadFeaturesFlag Features => throw new NotImplementedException();
|
||||
public bool[] Buttons => _driver.PressedButtons;
|
||||
|
||||
public AvaloniaMouse(AvaloniaMouseDriver driver)
|
||||
{
|
||||
|
@ -11,16 +11,16 @@ namespace Ryujinx.Ava.Input
|
||||
{
|
||||
internal class AvaloniaMouseDriver : IGamepadDriver
|
||||
{
|
||||
private Control _widget;
|
||||
private bool _isDisposed;
|
||||
private Size _size;
|
||||
private Control _widget;
|
||||
private bool _isDisposed;
|
||||
private Size _size;
|
||||
private readonly TopLevel _window;
|
||||
|
||||
public bool[] PressedButtons { get; }
|
||||
public bool[] PressedButtons { get; }
|
||||
public Vector2 CurrentPosition { get; private set; }
|
||||
public Vector2 Scroll { get; private set; }
|
||||
public Vector2 Scroll { get; private set; }
|
||||
|
||||
public string DriverName => "AvaloniaMouseDriver";
|
||||
public string DriverName => "AvaloniaMouseDriver";
|
||||
public ReadOnlySpan<string> GamepadsIds => new[] { "0" };
|
||||
|
||||
public AvaloniaMouseDriver(TopLevel window, Control parent)
|
||||
@ -28,14 +28,14 @@ namespace Ryujinx.Ava.Input
|
||||
_widget = parent;
|
||||
_window = window;
|
||||
|
||||
_widget.PointerMoved += Parent_PointerMovedEvent;
|
||||
_widget.PointerPressed += Parent_PointerPressedEvent;
|
||||
_widget.PointerReleased += Parent_PointerReleasedEvent;
|
||||
_widget.PointerMoved += Parent_PointerMovedEvent;
|
||||
_widget.PointerPressed += Parent_PointerPressedEvent;
|
||||
_widget.PointerReleased += Parent_PointerReleasedEvent;
|
||||
_widget.PointerWheelChanged += Parent_PointerWheelChanged;
|
||||
|
||||
_window.PointerMoved += Parent_PointerMovedEvent;
|
||||
_window.PointerPressed += Parent_PointerPressedEvent;
|
||||
_window.PointerReleased += Parent_PointerReleasedEvent;
|
||||
_window.PointerMoved += Parent_PointerMovedEvent;
|
||||
_window.PointerPressed += Parent_PointerPressedEvent;
|
||||
_window.PointerReleased += Parent_PointerReleasedEvent;
|
||||
_window.PointerWheelChanged += Parent_PointerWheelChanged;
|
||||
|
||||
PressedButtons = new bool[(int)MouseButton.Count];
|
||||
@ -47,13 +47,13 @@ namespace Ryujinx.Ava.Input
|
||||
|
||||
public event Action<string> OnGamepadConnected
|
||||
{
|
||||
add { }
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public event Action<string> OnGamepadDisconnected
|
||||
{
|
||||
add { }
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
@ -143,14 +143,14 @@ namespace Ryujinx.Ava.Input
|
||||
|
||||
_isDisposed = true;
|
||||
|
||||
_widget.PointerMoved -= Parent_PointerMovedEvent;
|
||||
_widget.PointerPressed -= Parent_PointerPressedEvent;
|
||||
_widget.PointerReleased -= Parent_PointerReleasedEvent;
|
||||
_widget.PointerMoved -= Parent_PointerMovedEvent;
|
||||
_widget.PointerPressed -= Parent_PointerPressedEvent;
|
||||
_widget.PointerReleased -= Parent_PointerReleasedEvent;
|
||||
_widget.PointerWheelChanged -= Parent_PointerWheelChanged;
|
||||
|
||||
_window.PointerMoved -= Parent_PointerMovedEvent;
|
||||
_window.PointerPressed -= Parent_PointerPressedEvent;
|
||||
_window.PointerReleased -= Parent_PointerReleasedEvent;
|
||||
_window.PointerMoved -= Parent_PointerMovedEvent;
|
||||
_window.PointerPressed -= Parent_PointerPressedEvent;
|
||||
_window.PointerReleased -= Parent_PointerReleasedEvent;
|
||||
_window.PointerWheelChanged -= Parent_PointerWheelChanged;
|
||||
|
||||
_widget = null;
|
||||
|
@ -31,22 +31,22 @@ 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;
|
||||
private static string _buildUrl;
|
||||
private static long _buildSize;
|
||||
private static bool _updateSuccessful;
|
||||
private static bool _running;
|
||||
private static long _buildSize;
|
||||
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,23 +195,21 @@ namespace Ryujinx.Modules
|
||||
}
|
||||
|
||||
// Fetch build size information to learn chunk sizes.
|
||||
using (HttpClient buildSizeClient = ConstructHttpClient())
|
||||
using HttpClient buildSizeClient = ConstructHttpClient();
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
|
||||
buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
|
||||
|
||||
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead);
|
||||
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead);
|
||||
|
||||
_buildSize = message.Content.Headers.ContentRange.Length.Value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, ex.Message);
|
||||
Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater");
|
||||
_buildSize = message.Content.Headers.ContentRange.Length.Value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, ex.Message);
|
||||
Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater");
|
||||
|
||||
_buildSize = -1;
|
||||
}
|
||||
_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 = { },
|
||||
Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
||||
SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading],
|
||||
IconSource = new SymbolIconSource { Symbol = Symbol.Download },
|
||||
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 completedRequests = 0;
|
||||
int totalProgressPercentage = 0;
|
||||
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,10 +418,9 @@ 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 }))
|
||||
{
|
||||
xattrProcess.WaitForExit();
|
||||
}
|
||||
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,31 +464,29 @@ 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;
|
||||
long byteWritten = 0;
|
||||
|
||||
byte[] buffer = new byte[32 * 1024];
|
||||
|
||||
while (true)
|
||||
{
|
||||
using Stream updateFileStream = File.Open(updateFile, FileMode.Create);
|
||||
int readSize = remoteFileStream.Read(buffer);
|
||||
|
||||
long totalBytes = response.Content.Headers.ContentLength.Value;
|
||||
long byteWritten = 0;
|
||||
|
||||
byte[] buffer = new byte[32 * 1024];
|
||||
|
||||
while (true)
|
||||
if (readSize == 0)
|
||||
{
|
||||
int readSize = remoteFileStream.Read(buffer);
|
||||
|
||||
if (readSize == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
byteWritten += readSize;
|
||||
|
||||
taskDialog.SetProgressBarState(GetPercentage(byteWritten, totalBytes), TaskDialogProgressState.Normal);
|
||||
|
||||
updateFileStream.Write(buffer, 0, readSize);
|
||||
break;
|
||||
}
|
||||
|
||||
byteWritten += readSize;
|
||||
|
||||
taskDialog.SetProgressBarState(GetPercentage(byteWritten, totalBytes), TaskDialogProgressState.Normal);
|
||||
|
||||
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();
|
||||
@ -520,9 +512,9 @@ namespace Ryujinx.Modules
|
||||
[SupportedOSPlatform("macos")]
|
||||
private static void ExtractTarGzipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath)
|
||||
{
|
||||
using Stream inStream = File.OpenRead(archivePath);
|
||||
using Stream inStream = File.OpenRead(archivePath);
|
||||
using GZipInputStream gzipStream = new(inStream);
|
||||
using TarInputStream tarStream = new(gzipStream, Encoding.ASCII);
|
||||
using TarInputStream tarStream = new(gzipStream, Encoding.ASCII);
|
||||
|
||||
TarEntry tarEntry;
|
||||
|
||||
@ -537,10 +529,8 @@ namespace Ryujinx.Modules
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
||||
|
||||
using (FileStream outStream = File.OpenWrite(outPath))
|
||||
{
|
||||
tarStream.CopyEntryContents(outStream);
|
||||
}
|
||||
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));
|
||||
@ -559,24 +549,26 @@ namespace Ryujinx.Modules
|
||||
|
||||
private static void ExtractZipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath)
|
||||
{
|
||||
using Stream inStream = File.OpenRead(archivePath);
|
||||
using ZipFile zipFile = new(inStream);
|
||||
using Stream inStream = File.OpenRead(archivePath);
|
||||
using ZipFile zipFile = new(inStream);
|
||||
|
||||
double count = 0;
|
||||
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))
|
||||
{
|
||||
zipStream.CopyTo(outStream);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
@ -22,16 +22,16 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
internal partial class Program
|
||||
{
|
||||
public static double WindowScaleFactor { get; set; }
|
||||
public static double WindowScaleFactor { get; set; }
|
||||
public static double DesktopScaleFactor { get; set; } = 1.0;
|
||||
public static string Version { get; private set; }
|
||||
public static string ConfigurationPath { get; private set; }
|
||||
public static bool PreviewerDetached { get; private set; }
|
||||
public static string Version { get; private set; }
|
||||
public static string ConfigurationPath { get; private set; }
|
||||
public static bool PreviewerDetached { get; private set; }
|
||||
|
||||
[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;
|
||||
@ -58,15 +58,15 @@ namespace Ryujinx.Ava
|
||||
.With(new X11PlatformOptions
|
||||
{
|
||||
EnableMultiTouch = true,
|
||||
EnableIme = true,
|
||||
UseEGL = false,
|
||||
UseGpu = true
|
||||
EnableIme = true,
|
||||
UseEGL = false,
|
||||
UseGpu = true,
|
||||
})
|
||||
.With(new Win32PlatformOptions
|
||||
{
|
||||
EnableMultitouch = true,
|
||||
UseWgl = false,
|
||||
AllowEglInitialization = false,
|
||||
EnableMultitouch = true,
|
||||
UseWgl = false,
|
||||
AllowEglInitialization = false,
|
||||
CompositionBackdropCornerRadius = 8.0f,
|
||||
})
|
||||
.UseSkia();
|
||||
@ -84,7 +84,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
// Hook unhandled exception and process exit events.
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
|
||||
AppDomain.CurrentDomain.ProcessExit += (sender, e) => Exit();
|
||||
AppDomain.CurrentDomain.ProcessExit += (sender, e) => Exit();
|
||||
|
||||
// Setup base data directory.
|
||||
AppDataManager.Initialize(CommandLineState.BaseDirPathArg);
|
||||
@ -130,7 +130,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
public static void ReloadConfig()
|
||||
{
|
||||
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
|
||||
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
|
||||
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json");
|
||||
|
||||
// Now load the configuration as the other subsystems are now registered
|
||||
@ -192,7 +192,7 @@ namespace Ryujinx.Ava
|
||||
"never" => HideCursorMode.Never,
|
||||
"onidle" => HideCursorMode.OnIdle,
|
||||
"always" => HideCursorMode.Always,
|
||||
_ => ConfigurationState.Instance.HideCursor.Value
|
||||
_ => ConfigurationState.Instance.HideCursor.Value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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(() =>
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
return new ThemeColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
|
||||
public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened
|
||||
{
|
||||
add { AddHandler(ApplicationOpenedEvent, value); }
|
||||
add { AddHandler(ApplicationOpenedEvent, value); }
|
||||
remove { RemoveHandler(ApplicationOpenedEvent, value); }
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
|
||||
public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened
|
||||
{
|
||||
add { AddHandler(ApplicationOpenedEvent, value); }
|
||||
add { AddHandler(ApplicationOpenedEvent, value); }
|
||||
remove { RemoveHandler(ApplicationOpenedEvent, value); }
|
||||
}
|
||||
|
||||
|
@ -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,15 +138,15 @@ 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);
|
||||
if (ViewModel.Profiles.Cast<UserProfile>().FirstOrDefault( x=> x.UserId == id) == null)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach(var account in lostAccounts)
|
||||
foreach (var account in lostAccounts)
|
||||
{
|
||||
ViewModel.LostProfiles.Add(new UserProfile(new HLE.HOS.Services.Account.Acc.UserProfile(account, "", null), this));
|
||||
}
|
||||
@ -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]);
|
||||
}
|
||||
|
@ -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;
|
||||
@ -32,18 +33,17 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
Title = title,
|
||||
PrimaryButtonText = primaryButton,
|
||||
Title = title,
|
||||
PrimaryButtonText = primaryButton,
|
||||
SecondaryButtonText = secondaryButton,
|
||||
CloseButtonText = closeButton,
|
||||
Content = content
|
||||
CloseButtonText = closeButton,
|
||||
Content = content,
|
||||
PrimaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = primaryButtonResult;
|
||||
}),
|
||||
};
|
||||
|
||||
contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = primaryButtonResult;
|
||||
});
|
||||
|
||||
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
|
||||
{
|
||||
result = UserResult.No;
|
||||
@ -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,18 +147,18 @@ 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()
|
||||
{
|
||||
Symbol = (Symbol)symbol,
|
||||
Margin = new Thickness(10),
|
||||
FontSize = 40,
|
||||
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
|
||||
Symbol = (Symbol)symbol,
|
||||
Margin = new Thickness(10),
|
||||
FontSize = 40,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
};
|
||||
|
||||
Grid.SetColumn(icon, 0);
|
||||
@ -170,18 +167,18 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
TextBlock primaryLabel = new()
|
||||
{
|
||||
Text = primaryText,
|
||||
Margin = new Thickness(5),
|
||||
Text = primaryText,
|
||||
Margin = new Thickness(5),
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
MaxWidth = 450
|
||||
MaxWidth = 450,
|
||||
};
|
||||
|
||||
TextBlock secondaryLabel = new()
|
||||
{
|
||||
Text = secondaryText,
|
||||
Margin = new Thickness(5),
|
||||
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
|
||||
Height = parent.Bounds.Height,
|
||||
Width = parent.Bounds.Width,
|
||||
Position = parent.PointToScreen(new Point()),
|
||||
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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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 RyuLogger = Ryujinx.Common.Logging.Logger;
|
||||
using AvaLogger = Avalonia.Logging.Logger;
|
||||
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()
|
||||
{
|
||||
@ -20,13 +22,13 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
return level switch
|
||||
{
|
||||
AvaLogLevel.Verbose => RyuLogger.Debug,
|
||||
AvaLogLevel.Debug => RyuLogger.Debug,
|
||||
AvaLogLevel.Verbose => RyuLogger.Debug,
|
||||
AvaLogLevel.Debug => RyuLogger.Debug,
|
||||
AvaLogLevel.Information => RyuLogger.Debug,
|
||||
AvaLogLevel.Warning => RyuLogger.Debug,
|
||||
AvaLogLevel.Error => RyuLogger.Error,
|
||||
AvaLogLevel.Fatal => RyuLogger.Error,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null)
|
||||
AvaLogLevel.Warning => RyuLogger.Debug,
|
||||
AvaLogLevel.Error => RyuLogger.Error,
|
||||
AvaLogLevel.Fatal => RyuLogger.Error,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null),
|
||||
};
|
||||
}
|
||||
|
||||
@ -45,7 +47,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, new object[] { propertyValue0 }));
|
||||
}
|
||||
|
||||
public void Log<T0, T1>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
|
||||
public void Log<T0, T1>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
|
||||
{
|
||||
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, new object[] { propertyValue0, propertyValue1 }));
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -12,12 +12,12 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
public static class NotificationHelper
|
||||
{
|
||||
private const int MaxNotifications = 4;
|
||||
private const int MaxNotifications = 4;
|
||||
private const int NotificationDelayInMs = 5000;
|
||||
|
||||
private static WindowNotificationManager _notificationManager;
|
||||
|
||||
private static readonly BlockingCollection<Notification> _notifications = new();
|
||||
private static readonly BlockingCollection<Notification> _notifications = new();
|
||||
|
||||
public static void SetNotificationManager(Window host)
|
||||
{
|
||||
@ -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>>(
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -11,8 +11,8 @@ namespace Ryujinx.Ava.UI.Models
|
||||
|
||||
public CheatModel(string name, string buildId, bool isEnabled)
|
||||
{
|
||||
Name = name;
|
||||
BuildId = buildId;
|
||||
Name = name;
|
||||
BuildId = buildId;
|
||||
IsEnabled = isEnabled;
|
||||
}
|
||||
|
||||
@ -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];
|
||||
}
|
||||
}
|
@ -10,13 +10,13 @@ namespace Ryujinx.Ava.UI.Models
|
||||
public CheatsList(string buildId, string path)
|
||||
{
|
||||
BuildId = buildId;
|
||||
Path = path;
|
||||
Path = path;
|
||||
|
||||
CollectionChanged += CheatsList_CollectionChanged;
|
||||
}
|
||||
|
||||
public string BuildId { get; }
|
||||
public string Path { get; }
|
||||
public string Path { get; }
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
|
@ -4,6 +4,6 @@ namespace Ryujinx.Ava.UI.Models
|
||||
{
|
||||
None,
|
||||
Keyboard,
|
||||
Controller
|
||||
Controller,
|
||||
}
|
||||
}
|
@ -18,18 +18,18 @@ namespace Ryujinx.Ava.UI.Models
|
||||
}
|
||||
}
|
||||
|
||||
public string TitleId { get; }
|
||||
public string TitleId { get; }
|
||||
public string ContainerPath { get; }
|
||||
public string FullPath { get; }
|
||||
public string FullPath { get; }
|
||||
|
||||
public string FileName => Path.GetFileName(ContainerPath);
|
||||
|
||||
public DownloadableContentModel(string titleId, string containerPath, string fullPath, bool enabled)
|
||||
{
|
||||
TitleId = titleId;
|
||||
TitleId = titleId;
|
||||
ContainerPath = containerPath;
|
||||
FullPath = fullPath;
|
||||
Enabled = enabled;
|
||||
FullPath = fullPath;
|
||||
Enabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
{
|
||||
DsuServerHost = DsuServerHost,
|
||||
DsuServerPort = DsuServerPort,
|
||||
Slot = Slot,
|
||||
AltSlot = AltSlot,
|
||||
MirrorInput = MirrorInput,
|
||||
MotionBackend = MotionInputBackendType.CemuHook
|
||||
}
|
||||
: new StandardMotionConfigController()
|
||||
{
|
||||
MotionBackend = MotionInputBackendType.GamepadDriver
|
||||
}
|
||||
? new CemuHookMotionConfigController
|
||||
{
|
||||
DsuServerHost = DsuServerHost,
|
||||
DsuServerPort = DsuServerPort,
|
||||
Slot = Slot,
|
||||
AltSlot = AltSlot,
|
||||
MirrorInput = MirrorInput,
|
||||
MotionBackend = MotionInputBackendType.CemuHook,
|
||||
}
|
||||
: new StandardMotionConfigController
|
||||
{
|
||||
MotionBackend = MotionInputBackendType.GamepadDriver,
|
||||
},
|
||||
};
|
||||
|
||||
config.Motion.Sensitivity = Sensitivity;
|
||||
|
@ -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;
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -21,20 +21,20 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
public class EmbeddedWindow : NativeControlHost
|
||||
{
|
||||
private WindowProc _wndProcDelegate;
|
||||
private string _className;
|
||||
private string _className;
|
||||
|
||||
protected GLXWindow X11Window { get; set; }
|
||||
|
||||
protected IntPtr WindowHandle { get; set; }
|
||||
protected IntPtr X11Display { get; set; }
|
||||
protected IntPtr NsView { get; set; }
|
||||
protected IntPtr MetalLayer { get; set; }
|
||||
protected IntPtr X11Display { get; set; }
|
||||
protected IntPtr NsView { get; set; }
|
||||
protected IntPtr MetalLayer { get; set; }
|
||||
|
||||
public delegate void UpdateBoundsCallbackDelegate(Rect rect);
|
||||
private UpdateBoundsCallbackDelegate _updateBoundsCallback;
|
||||
|
||||
public event EventHandler<IntPtr> WindowCreated;
|
||||
public event EventHandler<Size> SizeChanged;
|
||||
public event EventHandler<Size> SizeChanged;
|
||||
|
||||
public EmbeddedWindow()
|
||||
{
|
||||
@ -50,9 +50,9 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
protected virtual void OnWindowDestroying()
|
||||
{
|
||||
WindowHandle = IntPtr.Zero;
|
||||
X11Display = IntPtr.Zero;
|
||||
NsView = IntPtr.Zero;
|
||||
MetalLayer = IntPtr.Zero;
|
||||
X11Display = IntPtr.Zero;
|
||||
NsView = IntPtr.Zero;
|
||||
MetalLayer = IntPtr.Zero;
|
||||
}
|
||||
|
||||
private void OnNativeEmbeddedWindowCreated(object sender, EventArgs e)
|
||||
@ -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();
|
||||
}
|
||||
@ -127,7 +129,7 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
}
|
||||
|
||||
WindowHandle = X11Window.WindowHandle.RawHandle;
|
||||
X11Display = X11Window.DisplayHandle.RawHandle;
|
||||
X11Display = X11Window.DisplayHandle.RawHandle;
|
||||
|
||||
return new PlatformHandle(WindowHandle, "X11");
|
||||
}
|
||||
@ -141,23 +143,23 @@ 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);
|
||||
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;
|
||||
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
|
||||
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed);
|
||||
bool isLeft = msg == WindowsMessages.Lbuttondown;
|
||||
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
|
||||
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed);
|
||||
|
||||
var evnt = new PointerPressedEventArgs(
|
||||
this,
|
||||
@ -172,12 +174,12 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
|
||||
break;
|
||||
}
|
||||
case WindowsMessages.LBUTTONUP:
|
||||
case WindowsMessages.RBUTTONUP:
|
||||
case WindowsMessages.Lbuttonup:
|
||||
case WindowsMessages.Rbuttonup:
|
||||
{
|
||||
bool isLeft = msg == WindowsMessages.LBUTTONUP;
|
||||
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
|
||||
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased);
|
||||
bool isLeft = msg == WindowsMessages.Lbuttonup;
|
||||
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
|
||||
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased);
|
||||
|
||||
var evnt = new PointerReleasedEventArgs(
|
||||
this,
|
||||
@ -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>(),
|
||||
hInstance = GetModuleHandle(null),
|
||||
lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
|
||||
style = ClassStyles.CS_OWNDC,
|
||||
cbSize = Marshal.SizeOf<WndClassEx>(),
|
||||
hInstance = GetModuleHandle(null),
|
||||
lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
|
||||
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
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
public readonly EmbeddedWindow EmbeddedWindow;
|
||||
|
||||
public event EventHandler<EventArgs> WindowCreated;
|
||||
public event Action<object, Size> SizeChanged;
|
||||
public event Action<object, Size> SizeChanged;
|
||||
|
||||
public RendererHost()
|
||||
{
|
||||
@ -32,7 +32,7 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
private void Initialize()
|
||||
{
|
||||
EmbeddedWindow.WindowCreated += CurrentWindow_WindowCreated;
|
||||
EmbeddedWindow.SizeChanged += CurrentWindow_SizeChanged;
|
||||
EmbeddedWindow.SizeChanged += CurrentWindow_SizeChanged;
|
||||
|
||||
Content = EmbeddedWindow;
|
||||
}
|
||||
@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
if (EmbeddedWindow != null)
|
||||
{
|
||||
EmbeddedWindow.WindowCreated -= CurrentWindow_WindowCreated;
|
||||
EmbeddedWindow.SizeChanged -= CurrentWindow_SizeChanged;
|
||||
EmbeddedWindow.SizeChanged -= CurrentWindow_SizeChanged;
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
|
@ -10,12 +10,12 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
class SPBOpenGLContext : IOpenGLContext
|
||||
{
|
||||
private readonly OpenGLContextBase _context;
|
||||
private readonly NativeWindowBase _window;
|
||||
private readonly NativeWindowBase _window;
|
||||
|
||||
private SPBOpenGLContext(OpenGLContextBase context, NativeWindowBase window)
|
||||
{
|
||||
_context = context;
|
||||
_window = window;
|
||||
_window = window;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@ -32,7 +32,7 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
public static SPBOpenGLContext CreateBackgroundContext(OpenGLContextBase sharedContext)
|
||||
{
|
||||
OpenGLContextBase context = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, 3, 3, OpenGLContextFlags.Compat, true, sharedContext);
|
||||
NativeWindowBase window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100);
|
||||
NativeWindowBase window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100);
|
||||
|
||||
context.Initialize(window);
|
||||
context.MakeCurrent(window);
|
||||
|
@ -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")
|
||||
{
|
||||
|
@ -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,18 +426,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync();
|
||||
using (MemoryStream memoryStream = new(amiiboPreviewBytes))
|
||||
{
|
||||
Bitmap bitmap = new(memoryStream);
|
||||
using MemoryStream memoryStream = new(amiiboPreviewBytes);
|
||||
|
||||
double ratio = Math.Min(AmiiboImageSize / bitmap.Size.Width,
|
||||
Bitmap bitmap = new(memoryStream);
|
||||
|
||||
double ratio = Math.Min(AmiiboImageSize / bitmap.Size.Width,
|
||||
AmiiboImageSize / bitmap.Size.Height);
|
||||
|
||||
int resizeHeight = (int)(bitmap.Size.Height * ratio);
|
||||
int resizeWidth = (int)(bitmap.Size.Width * ratio);
|
||||
int resizeHeight = (int)(bitmap.Size.Height * ratio);
|
||||
int resizeWidth = (int)(bitmap.Size.Width * ratio);
|
||||
|
||||
AmiiboImage = bitmap.CreateScaledBitmap(new PixelSize(resizeWidth, resizeHeight));
|
||||
}
|
||||
AmiiboImage = bitmap.CreateScaledBitmap(new PixelSize(resizeWidth, resizeHeight));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -447,15 +446,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
private void ResetAmiiboPreview()
|
||||
{
|
||||
using (MemoryStream memoryStream = new(_amiiboLogoBytes))
|
||||
{
|
||||
Bitmap bitmap = new(memoryStream);
|
||||
using MemoryStream memoryStream = new(_amiiboLogoBytes);
|
||||
|
||||
AmiiboImage = bitmap;
|
||||
}
|
||||
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],
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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,37 +751,35 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
|
||||
|
||||
if (validFileName)
|
||||
{
|
||||
string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
|
||||
|
||||
InputConfig config = null;
|
||||
|
||||
if (IsKeyboard)
|
||||
{
|
||||
config = (Configuration as InputConfiguration<Key, ConfigStickInputId>).GetConfig();
|
||||
}
|
||||
else if (IsController)
|
||||
{
|
||||
config = (Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>).GetConfig();
|
||||
}
|
||||
|
||||
config.ControllerType = Controllers[_controller].Type;
|
||||
|
||||
string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig);
|
||||
|
||||
await File.WriteAllTextAsync(path, jsonString);
|
||||
|
||||
LoadProfiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
|
||||
|
||||
if (validFileName)
|
||||
{
|
||||
string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
|
||||
|
||||
InputConfig config = null;
|
||||
|
||||
if (IsKeyboard)
|
||||
{
|
||||
config = (Configuration as InputConfiguration<Key, ConfigStickInputId>).GetConfig();
|
||||
}
|
||||
else if (IsController)
|
||||
{
|
||||
config = (Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>).GetConfig();
|
||||
}
|
||||
|
||||
config.ControllerType = Controllers[_controller].Type;
|
||||
|
||||
string jsonString = JsonHelper.Serialize(config, SerializerContext.InputConfig);
|
||||
|
||||
await File.WriteAllTextAsync(path, jsonString);
|
||||
|
||||
LoadProfiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
|
||||
}
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
@ -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
|
||||
@ -29,18 +30,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public class DownloadableContentManagerViewModel : BaseModel
|
||||
{
|
||||
private readonly List<DownloadableContentContainer> _downloadableContentContainerList;
|
||||
private readonly string _downloadableContentJsonPath;
|
||||
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;
|
||||
_titleId = titleId;
|
||||
|
||||
_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
|
||||
Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle],
|
||||
AllowMultiple = true,
|
||||
};
|
||||
|
||||
dialog.Filters.Add(new FileDialogFilter
|
||||
{
|
||||
Name = "NSP",
|
||||
Extensions = { "nsp" }
|
||||
Name = "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);
|
||||
|
||||
@ -231,8 +230,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
using FileStream containerFile = File.OpenRead(path);
|
||||
|
||||
PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage());
|
||||
bool containsDownloadableContent = false;
|
||||
PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage());
|
||||
bool containsDownloadableContent = false;
|
||||
|
||||
_virtualFileSystem.ImportTickets(partitionFileSystem);
|
||||
|
||||
@ -313,16 +312,16 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
container = new DownloadableContentContainer
|
||||
{
|
||||
ContainerPath = downloadableContent.ContainerPath,
|
||||
DownloadableContentNcaList = new List<DownloadableContentNca>()
|
||||
ContainerPath = downloadableContent.ContainerPath,
|
||||
DownloadableContentNcaList = new List<DownloadableContentNca>(),
|
||||
};
|
||||
}
|
||||
|
||||
container.DownloadableContentNcaList.Add(new DownloadableContentNca
|
||||
{
|
||||
Enabled = downloadableContent.Enabled,
|
||||
TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16),
|
||||
FullPath = downloadableContent.FullPath
|
||||
Enabled = downloadableContent.Enabled,
|
||||
TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16),
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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,
|
||||
@ -152,7 +158,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
TopLevel = topLevel;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
#region Properties
|
||||
|
||||
public string SearchText
|
||||
{
|
||||
@ -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; }
|
||||
@ -911,15 +917,16 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool IsGridLarge => ConfigurationState.Instance.Ui.GridSize == 3;
|
||||
public bool IsGridHuge => ConfigurationState.Instance.Ui.GridSize == 4;
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region PrivateMethods
|
||||
#region PrivateMethods
|
||||
|
||||
private IComparer<ApplicationData> GetComparer()
|
||||
{
|
||||
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);
|
||||
|
||||
@ -1204,11 +1215,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
_rendererWaitEvent.Set();
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region PublicMethods
|
||||
#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,12 +1271,12 @@ 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" } });
|
||||
dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } });
|
||||
dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } });
|
||||
dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } });
|
||||
dialog.Filters.Add(new FileDialogFilter { Name = "ZIP", Extensions = { "zip" } });
|
||||
|
||||
string[] file = await dialog.ShowAsync(desktop.MainWindow);
|
||||
|
||||
@ -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);
|
||||
@ -1371,9 +1384,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
Applications.Clear();
|
||||
|
||||
StatusBarVisible = true;
|
||||
StatusBarVisible = true;
|
||||
StatusBarProgressMaximum = 0;
|
||||
StatusBarProgressValue = 0;
|
||||
StatusBarProgressValue = 0;
|
||||
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
||||
});
|
||||
@ -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();
|
||||
|
||||
@ -1495,7 +1507,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
if (string.IsNullOrWhiteSpace(titleName))
|
||||
{
|
||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name);
|
||||
TitleName = AppHost.Device.Processes.ActiveApplication.Name;
|
||||
TitleName = AppHost.Device.Processes.ActiveApplication.Name;
|
||||
}
|
||||
|
||||
SwitchToRenderer(startFullscreen);
|
||||
@ -1521,7 +1533,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
});
|
||||
}
|
||||
|
||||
public void UpdateGameMetadata(string titleId)
|
||||
public static void UpdateGameMetadata(string titleId)
|
||||
{
|
||||
ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
|
||||
{
|
||||
@ -1675,6 +1687,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
@ -205,17 +204,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
OpenFileDialog dialog = new()
|
||||
{
|
||||
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
|
||||
AllowMultiple = true
|
||||
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
|
||||
AllowMultiple = true,
|
||||
};
|
||||
|
||||
dialog.Filters.Add(new FileDialogFilter
|
||||
{
|
||||
Name = "NSP",
|
||||
Extensions = { "nsp" }
|
||||
Name = "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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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,34 +109,32 @@ 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);
|
||||
|
||||
foreach (DirectoryEntryEx item in romfs.EnumerateEntries())
|
||||
{
|
||||
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"))
|
||||
{
|
||||
// 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>();
|
||||
using var file = new UniqueRef<IFile>();
|
||||
|
||||
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
using (MemoryStream stream = new())
|
||||
using (MemoryStream streamPng = new())
|
||||
{
|
||||
file.Get.AsStream().CopyTo(stream);
|
||||
using MemoryStream stream = new();
|
||||
using MemoryStream streamPng = new();
|
||||
|
||||
stream.Position = 0;
|
||||
file.Get.AsStream().CopyTo(stream);
|
||||
|
||||
Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256);
|
||||
stream.Position = 0;
|
||||
|
||||
avatarImage.SaveAsPng(streamPng);
|
||||
Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256);
|
||||
|
||||
_avatarStore.Add(item.FullPath, streamPng.ToArray());
|
||||
}
|
||||
}
|
||||
avatarImage.SaveAsPng(streamPng);
|
||||
|
||||
_avatarStore.Add(item.FullPath, streamPng.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,82 +142,81 @@ 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());
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if ((mask >>= 1) == 0)
|
||||
header = input[inputOffset++];
|
||||
mask = 0x80;
|
||||
}
|
||||
|
||||
if ((header & mask) != 0)
|
||||
{
|
||||
if (outputOffset == output.Length)
|
||||
{
|
||||
header = input[inputOffset++];
|
||||
mask = 0x80;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((header & mask) != 0)
|
||||
{
|
||||
if (outputOffset == output.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
output[outputOffset++] = input[inputOffset++];
|
||||
}
|
||||
else
|
||||
{
|
||||
byte byte1 = input[inputOffset++];
|
||||
byte byte2 = input[inputOffset++];
|
||||
|
||||
output[outputOffset++] = 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
|
||||
{
|
||||
byte byte1 = input[inputOffset++];
|
||||
byte byte2 = input[inputOffset++];
|
||||
length += 2;
|
||||
}
|
||||
|
||||
uint dist = (uint)((byte1 & 0xF) << 8) | byte2;
|
||||
uint position = outputOffset - (dist + 1);
|
||||
uint gap = outputOffset - position;
|
||||
uint nonOverlappingLength = length;
|
||||
|
||||
uint length = (uint)byte1 >> 4;
|
||||
if (length == 0)
|
||||
{
|
||||
length = (uint)input[inputOffset++] + 0x12;
|
||||
}
|
||||
else
|
||||
{
|
||||
length += 2;
|
||||
}
|
||||
if (nonOverlappingLength > gap)
|
||||
{
|
||||
nonOverlappingLength = gap;
|
||||
}
|
||||
|
||||
uint gap = outputOffset - position;
|
||||
uint nonOverlappingLength = length;
|
||||
Buffer.BlockCopy(output, (int)position, output, (int)outputOffset, (int)nonOverlappingLength);
|
||||
outputOffset += nonOverlappingLength;
|
||||
position += nonOverlappingLength;
|
||||
length -= nonOverlappingLength;
|
||||
|
||||
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++];
|
||||
}
|
||||
while (length-- > 0)
|
||||
{
|
||||
output[outputOffset++] = output[position++];
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
? SortExpressionComparer<SaveModel>.Ascending(save => save.Title)
|
||||
: SortExpressionComparer<SaveModel>.Descending(save => save.Title);
|
||||
case 1:
|
||||
return OrderIndex == 0
|
||||
? SortExpressionComparer<SaveModel>.Ascending(save => save.Size)
|
||||
: SortExpressionComparer<SaveModel>.Descending(save => save.Size);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
0 => OrderIndex == 0
|
||||
? SortExpressionComparer<SaveModel>.Ascending(save => save.Title)
|
||||
: SortExpressionComparer<SaveModel>.Descending(save => save.Title),
|
||||
1 => OrderIndex == 0
|
||||
? SortExpressionComparer<SaveModel>.Ascending(save => save.Size)
|
||||
: SortExpressionComparer<SaveModel>.Descending(save => save.Size),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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) =>
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -38,26 +38,26 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
List<CheckBox> checkBoxes = new();
|
||||
|
||||
foreach (var item in Enum.GetValues(typeof (FileTypes)))
|
||||
foreach (var item in Enum.GetValues(typeof(FileTypes)))
|
||||
{
|
||||
string fileName = Enum.GetName(typeof (FileTypes), item);
|
||||
checkBoxes.Add(new CheckBox()
|
||||
string fileName = Enum.GetName(typeof(FileTypes), item);
|
||||
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();
|
||||
|
||||
string localePath = "Ryujinx.Ava/Assets/Locales";
|
||||
string localeExt = ".json";
|
||||
string localeExt = ".json";
|
||||
|
||||
string[] localesPath = EmbeddedResources.GetAllAvailableResources(localePath, localeExt);
|
||||
|
||||
@ -67,7 +67,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
{
|
||||
string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last();
|
||||
string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}");
|
||||
var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
|
||||
var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
|
||||
|
||||
if (!strings.TryGetValue("Language", out string languageName))
|
||||
{
|
||||
@ -76,11 +76,11 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
|
||||
MenuItem menuItem = new()
|
||||
{
|
||||
Header = languageName,
|
||||
Header = languageName,
|
||||
Command = MiniCommand.Create(() =>
|
||||
{
|
||||
ViewModel.ChangeLanguage(languageCode);
|
||||
})
|
||||
MainWindowViewModel.ChangeLanguage(languageCode);
|
||||
}),
|
||||
};
|
||||
|
||||
menuItems.Add(menuItem);
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,17 +39,17 @@ 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;
|
||||
}
|
||||
|
||||
((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - " +
|
||||
$"{ (_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}";
|
||||
$"{(_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}";
|
||||
|
||||
DataContext = TempProfile;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
{
|
||||
image.Mutate(x => x.Resize(256, 256));
|
||||
using Image image = Image.Load(buffer);
|
||||
|
||||
using (MemoryStream streamJpg = new())
|
||||
{
|
||||
image.SaveAsJpeg(streamJpg);
|
||||
image.Mutate(x => x.Resize(256, 256));
|
||||
|
||||
return streamJpg.ToArray();
|
||||
}
|
||||
}
|
||||
using MemoryStream streamJpg = new();
|
||||
|
||||
image.SaveAsJpeg(streamJpg);
|
||||
|
||||
return streamJpg.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
@ -25,17 +26,17 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
PrimaryButtonText = "",
|
||||
PrimaryButtonText = "",
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
|
||||
Content = new AboutWindow()
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
|
||||
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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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"
|
||||
|
@ -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();
|
||||
}
|
||||
@ -35,11 +35,11 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
PrimaryButtonText = "",
|
||||
PrimaryButtonText = "",
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = "",
|
||||
Content = new DownloadableContentManagerWindow(virtualFileSystem, titleId, titleName),
|
||||
Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], titleName, titleId.ToString("X16"))
|
||||
CloseButtonText = "",
|
||||
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>());
|
||||
|
@ -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;
|
||||
|
@ -155,7 +155,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
ViewModel.StatusBarProgressValue = e.NumAppsLoaded;
|
||||
ViewModel.StatusBarProgressValue = e.NumAppsLoaded;
|
||||
ViewModel.StatusBarProgressMaximum = e.NumAppsFound;
|
||||
|
||||
if (e.NumAppsFound == 0)
|
||||
@ -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)
|
||||
@ -536,8 +539,8 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
ViewModel.Applications.Clear();
|
||||
|
||||
StatusBarView.LoadProgressBar.IsVisible = true;
|
||||
ViewModel.StatusBarProgressMaximum = 0;
|
||||
ViewModel.StatusBarProgressValue = 0;
|
||||
ViewModel.StatusBarProgressMaximum = 0;
|
||||
ViewModel.StatusBarProgressValue = 0;
|
||||
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
||||
});
|
||||
|
@ -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" />
|
||||
|
@ -16,7 +16,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.Settings]}";
|
||||
|
||||
ViewModel = new SettingsViewModel(virtualFileSystem, contentManager);
|
||||
ViewModel = new SettingsViewModel(virtualFileSystem, contentManager);
|
||||
DataContext = ViewModel;
|
||||
|
||||
ViewModel.CloseWindow += Close;
|
||||
@ -28,7 +28,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
public SettingsWindow()
|
||||
{
|
||||
ViewModel = new SettingsViewModel();
|
||||
ViewModel = new SettingsViewModel();
|
||||
DataContext = ViewModel;
|
||||
|
||||
InitializeComponent();
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
@ -35,11 +35,11 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
PrimaryButtonText = "",
|
||||
PrimaryButtonText = "",
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = "",
|
||||
Content = new TitleUpdateWindow(virtualFileSystem, titleId, titleName),
|
||||
Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, titleName, titleId.ToString("X16"))
|
||||
CloseButtonText = "",
|
||||
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>());
|
||||
|
@ -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
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user