mirror of
https://git.743378673.xyz/MeloNX/MeloNX.git
synced 2025-05-14 00:35:16 +00:00
* Impl first attempt to LDN * Make this work. - Endianness swap on all IPs. - Use local network IP for connections, rather than 127.0.0.1. This is to be changed when tunnelling or whatever. - Mac addresses are now randomly assigned on the server. (fixes joining lobbies) - Fixed the "connected" handler for stations to actually find a - Added info retrieval when connected to a station. - Users that disconnect are now removed from rooms they were in. (still need to broadcast tho) - The communication service does a bit better with being closed now. - Some locking around the game instance dictionary. * We may just be "initialized". Ignore this for now. * Lots of WIP * Add Disconnect packet * Improve signalling of internal events. * Fix scan. * Fix some more stupid things. * Enable NoDelay on all sockets. * Add station accept policy, disconnect function. * Limit max number of games. * Split out networking stuff from HLE, so it can be swapped. * Update logging calls. * Missed a spot. * Call SignalDisconnect instead of SetState * Add comment to GetNetworkInfo * Update configuration + UI Now has its own tab, more options. * Refactoring IUserLocalCommunicationService ( Expected new issues :'( ) * some cleanup * More fix * Correctly handle errors when connecting. * Disable *Private call and clean symbols * Structs cleanup * Big cleanup * Fix InvalidHandle (in MK8D and other games) * Add Reject and Private Network support (v1) RyuLdn Version bumped to 1. * Add Initialize Packet Allows users to keep Mac Addresses assigned by the server. * Add SetWirelessControllerRestriction and some cleanup * LDN-2 Initial Rebase Make this work. - Endianness swap on all IPs. - Use local network IP for connections, rather than 127.0.0.1. This is to be changed when tunnelling or whatever. - Fixed the "connected" handler for stations to actually find a - The communication service does a bit better with being closed now. - Some locking around the game instance dictionary. We may just be "initialized". Ignore this for now. Lots of WIP Implement scan filter. Improve signalling of internal events. Fix scan. Fix 0 width data, scan reply end delay removed. Fix some more stupid things. Enable NoDelay on all sockets. Add station accept policy, disconnect function. Limit max number of games. Split out networking stuff from HLE, so it can be swapped. Update logging calls. Missed a spot. SetAdvertiseData when open, don't return games that have accept policy 1 Update configuration + UI Now has its own tab, more options. Don't Keepalive, it causes problems. Refactoring IUserLocalCommunicationService ( Expected new issues :'( ) some cleanup More fix Correctly handle errors when connecting. Disable *Private call and clean symbols Structs cleanup Big cleanup Fix InvalidHandle (in MK8D and other games) Add Reject and Private Network support (v1) Disable TcpNoDelay option on linux. Add SetWirelessControllerRestriction and some cleanup Misc cleanup, implement broadcast flag. * Misc Changes * Fix GetNetworkInfo * Fix some small issues * Implement GetNetworkInfoLatestUpdate * Hotfix when LocalCommunicationId = 0xFFFFFFFFFFFFFFFF * Fix ARMS Scan (and other games using wrong LocalCommunicationId * Fix latest update when host leaves * Revert "Fix ARMS Scan (and other games using wrong LocalCommunicationId" This reverts commit 519c283d3993e2fdfafb8ac6b4e0a98231f6fb75. * Fix the localCommunicationId = -1 * Don't set Connect flag for nodes already in the room before joining. * Make IUserLocalCommunicationService disposable * Don't dispose if there's no client. * LDN-2-2 Rebase Make this work. - Endianness swap on all IPs. - Use local network IP for connections, rather than 127.0.0.1. This is to be changed when tunnelling or whatever. - Fixed the "connected" handler for stations to actually find a - The communication service does a bit better with being closed now. - Some locking around the game instance dictionary. We may just be "initialized". Ignore this for now. Put sockets behind an interface, so that they can be swapped for something proxyable Lots of WIP Implement scan filter. Improve signalling of internal events. Fix scan. Fix 0 width data, scan reply end delay removed. Fix some more stupid things. Enable NoDelay on all sockets. Add station accept policy, disconnect function. Limit max number of games. Split out networking stuff from HLE, so it can be swapped. Update logging calls. Missed a spot. SetAdvertiseData when open, don't return games that have accept policy 1 Update configuration + UI Now has its own tab, more options. Don't Keepalive, it causes problems. Refactoring IUserLocalCommunicationService ( Expected new issues :'( ) some cleanup More fix Correctly handle errors when connecting. Disable *Private call and clean symbols Structs cleanup Big cleanup Fix InvalidHandle (in MK8D and other games) Add Reject and Private Network support (v1) Disable TcpNoDelay option on linux. Add SetWirelessControllerRestriction and some cleanup Misc cleanup, implement broadcast flag. Misc Changes Fix GetNetworkInfo Fix some small issues Disable LAN by default til the config is added. Fix Splatoon 2 - Stub nfp IUser::StartDetection / IUser::StopDetection. - Stub ntc IEnsureNetworkClockAvailabilityService and needed calls. Cleanup previous fixes Stub IAudioInManager/IAudioIn for Splatoon 2 LAN Add LAN settings to multiplayer tab LAN Play > LAN Mode Implement GetNetworkInfoLatestUpdate Hotfix when LocalCommunicationId = 0xFFFFFFFFFFFFFFFF Fix ARMS Scan (and other games using wrong LocalCommunicationId Fix latest update when host leaves Revert "Fix ARMS Scan (and other games using wrong LocalCommunicationId" This reverts commit 519c283d3993e2fdfafb8ac6b4e0a98231f6fb75. Fix the localCommunicationId = -1 Don't set Connect flag for nodes already in the room before joining. Make IUserLocalCommunicationService disposable Fix crash when using LAN mode on linux. Actually use that call Don't dispose if there's no client. Fix the settings window crash Fix configurationFileUpdated * Make LDN compatible with Ryujinx/Ryujinx#3805 * Ava: Add Ldn options to SettingsNetworkTab * Ava: Add update events for multiplayer options * Apply formatting * Remove LdnHelper * ldn: Fix hardcoded /24 subnet mask * Fix naming rule violations * Add missing summary doc tag * Remove NetCoreServer dependency * Address code style issues and typos Co-authored-by: gdkchan <gab.dark.100@gmail.com> * Call CloseStation/CloseAccessPoint to reduce code duplication * Fix typo Co-authored-by: gdkchan <gab.dark.100@gmail.com> * Fix missing trailing commas * Extract AddressList from AddressEntry * Use AcceptPolicy as a type for LdnNetworkInfo.StationAcceptPolicy * Add Flags attribute to ScanFilterFlag * Rename struct members for LdnNetworkInfo * Remove extra line Co-authored-by: Ac_K <Acoustik666@gmail.com> * Extract NetworkErrorMessage from NetworkError * Fix missing trailing commas --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> Co-authored-by: riperiperi <rhy3756547@hotmail.com> Co-authored-by: gdkchan <gab.dark.100@gmail.com>
567 lines
20 KiB
C#
567 lines
20 KiB
C#
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls.Primitives;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.Threading;
|
|
using FluentAvalonia.UI.Controls;
|
|
using Ryujinx.Ava.Common;
|
|
using Ryujinx.Ava.Common.Locale;
|
|
using Ryujinx.Ava.Input;
|
|
using Ryujinx.Ava.UI.Applet;
|
|
using Ryujinx.Ava.UI.Helpers;
|
|
using Ryujinx.Ava.UI.ViewModels;
|
|
using Ryujinx.Common.Logging;
|
|
using Ryujinx.Graphics.Gpu;
|
|
using Ryujinx.HLE.FileSystem;
|
|
using Ryujinx.HLE.HOS;
|
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
|
using Ryujinx.Input.HLE;
|
|
using Ryujinx.Input.SDL2;
|
|
using Ryujinx.Modules;
|
|
using Ryujinx.Ui.App.Common;
|
|
using Ryujinx.Ui.Common;
|
|
using Ryujinx.Ui.Common.Configuration;
|
|
using Ryujinx.Ui.Common.Helper;
|
|
using System;
|
|
using System.IO;
|
|
using System.Runtime.Versioning;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Ryujinx.Ava.UI.Windows
|
|
{
|
|
public partial class MainWindow : StyleableWindow
|
|
{
|
|
internal static MainWindowViewModel MainWindowViewModel { get; private set; }
|
|
|
|
private bool _isLoading;
|
|
|
|
private UserChannelPersistence _userChannelPersistence;
|
|
private static bool _deferLoad;
|
|
private static string _launchPath;
|
|
private static bool _startFullscreen;
|
|
internal readonly AvaHostUiHandler UiHandler;
|
|
|
|
public VirtualFileSystem VirtualFileSystem { get; private set; }
|
|
public ContentManager ContentManager { get; private set; }
|
|
public AccountManager AccountManager { get; private set; }
|
|
|
|
public LibHacHorizonManager LibHacHorizonManager { get; private set; }
|
|
|
|
public InputManager InputManager { get; private set; }
|
|
|
|
internal MainWindowViewModel ViewModel { get; private set; }
|
|
public SettingsWindow SettingsWindow { get; set; }
|
|
|
|
public static bool ShowKeyErrorOnLoad { get; set; }
|
|
public ApplicationLibrary ApplicationLibrary { get; set; }
|
|
|
|
public MainWindow()
|
|
{
|
|
ViewModel = new MainWindowViewModel();
|
|
|
|
MainWindowViewModel = ViewModel;
|
|
|
|
DataContext = ViewModel;
|
|
|
|
SetWindowSizePosition();
|
|
|
|
InitializeComponent();
|
|
Load();
|
|
|
|
UiHandler = new AvaHostUiHandler(this);
|
|
|
|
ViewModel.Title = $"Ryujinx {Program.Version}";
|
|
|
|
// NOTE: Height of MenuBar and StatusBar is not usable here, since it would still be 0 at this point.
|
|
double barHeight = MenuBar.MinHeight + StatusBarView.StatusBar.MinHeight;
|
|
Height = ((Height - barHeight) / Program.WindowScaleFactor) + barHeight;
|
|
Width /= Program.WindowScaleFactor;
|
|
|
|
if (Program.PreviewerDetached)
|
|
{
|
|
Initialize();
|
|
|
|
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
|
|
|
|
ViewModel.Initialize(
|
|
ContentManager,
|
|
StorageProvider,
|
|
ApplicationLibrary,
|
|
VirtualFileSystem,
|
|
AccountManager,
|
|
InputManager,
|
|
_userChannelPersistence,
|
|
LibHacHorizonManager,
|
|
UiHandler,
|
|
ShowLoading,
|
|
SwitchToGameControl,
|
|
SetMainContent,
|
|
this);
|
|
|
|
ViewModel.RefreshFirmwareStatus();
|
|
|
|
LoadGameList();
|
|
|
|
this.GetObservable(IsActiveProperty).Subscribe(IsActiveChanged);
|
|
this.ScalingChanged += OnScalingChanged;
|
|
}
|
|
|
|
ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
|
|
ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
|
|
}
|
|
|
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
|
{
|
|
base.OnApplyTemplate(e);
|
|
|
|
NotificationHelper.SetNotificationManager(this);
|
|
}
|
|
|
|
private void IsActiveChanged(bool obj)
|
|
{
|
|
ViewModel.IsActive = obj;
|
|
}
|
|
|
|
public void LoadGameList()
|
|
{
|
|
if (_isLoading)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_isLoading = true;
|
|
|
|
LoadApplications();
|
|
|
|
_isLoading = false;
|
|
}
|
|
|
|
private void OnScalingChanged(object sender, EventArgs e)
|
|
{
|
|
Program.DesktopScaleFactor = this.RenderScaling;
|
|
}
|
|
|
|
public void AddApplication(ApplicationData applicationData)
|
|
{
|
|
Dispatcher.UIThread.InvokeAsync(() =>
|
|
{
|
|
ViewModel.Applications.Add(applicationData);
|
|
});
|
|
}
|
|
|
|
private void ApplicationLibrary_ApplicationAdded(object sender, ApplicationAddedEventArgs e)
|
|
{
|
|
AddApplication(e.AppData);
|
|
}
|
|
|
|
private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e)
|
|
{
|
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, e.NumAppsLoaded, e.NumAppsFound);
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
ViewModel.StatusBarProgressValue = e.NumAppsLoaded;
|
|
ViewModel.StatusBarProgressMaximum = e.NumAppsFound;
|
|
|
|
if (e.NumAppsFound == 0)
|
|
{
|
|
StatusBarView.LoadProgressBar.IsVisible = false;
|
|
}
|
|
|
|
if (e.NumAppsLoaded == e.NumAppsFound)
|
|
{
|
|
StatusBarView.LoadProgressBar.IsVisible = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
public void Application_Opened(object sender, ApplicationOpenedEventArgs args)
|
|
{
|
|
if (args.Application != null)
|
|
{
|
|
ViewModel.SelectedIcon = args.Application.Icon;
|
|
|
|
string path = new FileInfo(args.Application.Path).FullName;
|
|
|
|
ViewModel.LoadApplication(path);
|
|
}
|
|
|
|
args.Handled = true;
|
|
}
|
|
|
|
internal static void DeferLoadApplication(string launchPathArg, bool startFullscreenArg)
|
|
{
|
|
_deferLoad = true;
|
|
_launchPath = launchPathArg;
|
|
_startFullscreen = startFullscreenArg;
|
|
}
|
|
|
|
public void SwitchToGameControl(bool startFullscreen = false)
|
|
{
|
|
ViewModel.ShowLoadProgress = false;
|
|
ViewModel.ShowContent = true;
|
|
ViewModel.IsLoadingIndeterminate = false;
|
|
|
|
Dispatcher.UIThread.InvokeAsync(() =>
|
|
{
|
|
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
|
|
{
|
|
ViewModel.ToggleFullscreen();
|
|
}
|
|
});
|
|
}
|
|
|
|
public void ShowLoading(bool startFullscreen = false)
|
|
{
|
|
ViewModel.ShowContent = false;
|
|
ViewModel.ShowLoadProgress = true;
|
|
ViewModel.IsLoadingIndeterminate = true;
|
|
|
|
Dispatcher.UIThread.InvokeAsync(() =>
|
|
{
|
|
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
|
|
{
|
|
ViewModel.ToggleFullscreen();
|
|
}
|
|
});
|
|
}
|
|
|
|
private void Initialize()
|
|
{
|
|
_userChannelPersistence = new UserChannelPersistence();
|
|
VirtualFileSystem = VirtualFileSystem.CreateInstance();
|
|
LibHacHorizonManager = new LibHacHorizonManager();
|
|
ContentManager = new ContentManager(VirtualFileSystem);
|
|
|
|
LibHacHorizonManager.InitializeFsServer(VirtualFileSystem);
|
|
LibHacHorizonManager.InitializeArpServer();
|
|
LibHacHorizonManager.InitializeBcatServer();
|
|
LibHacHorizonManager.InitializeSystemClients();
|
|
|
|
ApplicationLibrary = new ApplicationLibrary(VirtualFileSystem);
|
|
|
|
// Save data created before we supported extra data in directory save data will not work properly if
|
|
// given empty extra data. Luckily some of that extra data can be created using the data from the
|
|
// save data indexer, which should be enough to check access permissions for user saves.
|
|
// Every single save data's extra data will be checked and fixed if needed each time the emulator is opened.
|
|
// Consider removing this at some point in the future when we don't need to worry about old saves.
|
|
VirtualFileSystem.FixExtraData(LibHacHorizonManager.RyujinxClient);
|
|
|
|
AccountManager = new AccountManager(LibHacHorizonManager.RyujinxClient, CommandLineState.Profile);
|
|
|
|
VirtualFileSystem.ReloadKeySet();
|
|
|
|
ApplicationHelper.Initialize(VirtualFileSystem, AccountManager, LibHacHorizonManager.RyujinxClient);
|
|
}
|
|
|
|
[SupportedOSPlatform("linux")]
|
|
private static async void ShowVmMaxMapCountWarning()
|
|
{
|
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountWarningTextSecondary,
|
|
LinuxHelper.VmMaxMapCount, LinuxHelper.RecommendedVmMaxMapCount);
|
|
|
|
await ContentDialogHelper.CreateWarningDialog(
|
|
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountWarningTextPrimary],
|
|
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountWarningTextSecondary]
|
|
);
|
|
}
|
|
|
|
[SupportedOSPlatform("linux")]
|
|
private static async void ShowVmMaxMapCountDialog()
|
|
{
|
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary,
|
|
LinuxHelper.RecommendedVmMaxMapCount);
|
|
|
|
UserResult response = await ContentDialogHelper.ShowTextDialog(
|
|
$"Ryujinx - {LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTitle]}",
|
|
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary],
|
|
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTextSecondary],
|
|
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogButtonUntilRestart],
|
|
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogButtonPersistent],
|
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
|
(int)Symbol.Help
|
|
);
|
|
|
|
int rc;
|
|
|
|
switch (response)
|
|
{
|
|
case UserResult.Ok:
|
|
rc = LinuxHelper.RunPkExec($"echo {LinuxHelper.RecommendedVmMaxMapCount} > {LinuxHelper.VmMaxMapCountPath}");
|
|
if (rc == 0)
|
|
{
|
|
Logger.Info?.Print(LogClass.Application, $"vm.max_map_count set to {LinuxHelper.VmMaxMapCount} until the next restart.");
|
|
}
|
|
else
|
|
{
|
|
Logger.Error?.Print(LogClass.Application, $"Unable to change vm.max_map_count. Process exited with code: {rc}");
|
|
}
|
|
break;
|
|
case UserResult.No:
|
|
rc = LinuxHelper.RunPkExec($"echo \"vm.max_map_count = {LinuxHelper.RecommendedVmMaxMapCount}\" > {LinuxHelper.SysCtlConfigPath} && sysctl -p {LinuxHelper.SysCtlConfigPath}");
|
|
if (rc == 0)
|
|
{
|
|
Logger.Info?.Print(LogClass.Application, $"vm.max_map_count set to {LinuxHelper.VmMaxMapCount}. Written to config: {LinuxHelper.SysCtlConfigPath}");
|
|
}
|
|
else
|
|
{
|
|
Logger.Error?.Print(LogClass.Application, $"Unable to write new value for vm.max_map_count to config. Process exited with code: {rc}");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void CheckLaunchState()
|
|
{
|
|
if (ShowKeyErrorOnLoad)
|
|
{
|
|
ShowKeyErrorOnLoad = false;
|
|
|
|
Dispatcher.UIThread.Post(async () => await
|
|
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
|
|
}
|
|
|
|
if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount)
|
|
{
|
|
Logger.Warning?.Print(LogClass.Application, $"The value of vm.max_map_count is lower than {LinuxHelper.RecommendedVmMaxMapCount}. ({LinuxHelper.VmMaxMapCount})");
|
|
|
|
if (LinuxHelper.PkExecPath is not null)
|
|
{
|
|
Dispatcher.UIThread.Post(ShowVmMaxMapCountDialog);
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(ShowVmMaxMapCountWarning);
|
|
}
|
|
}
|
|
|
|
if (_deferLoad)
|
|
{
|
|
_deferLoad = false;
|
|
|
|
ViewModel.LoadApplication(_launchPath, _startFullscreen);
|
|
}
|
|
|
|
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
|
|
{
|
|
Updater.BeginParse(this, false).ContinueWith(task =>
|
|
{
|
|
Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}");
|
|
}, TaskContinuationOptions.OnlyOnFaulted);
|
|
}
|
|
}
|
|
|
|
private void Load()
|
|
{
|
|
StatusBarView.VolumeStatus.Click += VolumeStatus_CheckedChanged;
|
|
|
|
ApplicationGrid.ApplicationOpened += Application_Opened;
|
|
|
|
ApplicationGrid.DataContext = ViewModel;
|
|
|
|
ApplicationList.ApplicationOpened += Application_Opened;
|
|
|
|
ApplicationList.DataContext = ViewModel;
|
|
}
|
|
|
|
private void SetWindowSizePosition()
|
|
{
|
|
PixelPoint savedPoint = new(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX,
|
|
ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY);
|
|
|
|
ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor;
|
|
ViewModel.WindowWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth * Program.WindowScaleFactor;
|
|
|
|
ViewModel.WindowState = ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value is true ? WindowState.Maximized : WindowState.Normal;
|
|
|
|
if (CheckScreenBounds(savedPoint))
|
|
{
|
|
Position = savedPoint;
|
|
}
|
|
else
|
|
{
|
|
WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
|
}
|
|
}
|
|
|
|
private bool CheckScreenBounds(PixelPoint configPoint)
|
|
{
|
|
for (int i = 0; i < Screens.ScreenCount; i++)
|
|
{
|
|
if (Screens.All[i].Bounds.Contains(configPoint))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Logger.Warning?.Print(LogClass.Application, "Failed to find valid start-up coordinates. Defaulting to primary monitor center.");
|
|
return false;
|
|
}
|
|
|
|
private void SaveWindowSizePosition()
|
|
{
|
|
ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight.Value = (int)Height;
|
|
ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth.Value = (int)Width;
|
|
|
|
ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX.Value = Position.X;
|
|
ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY.Value = Position.Y;
|
|
|
|
ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value = WindowState == WindowState.Maximized;
|
|
|
|
MainWindowViewModel.SaveConfig();
|
|
}
|
|
|
|
protected override void OnOpened(EventArgs e)
|
|
{
|
|
base.OnOpened(e);
|
|
|
|
CheckLaunchState();
|
|
}
|
|
|
|
private void SetMainContent(Control content = null)
|
|
{
|
|
content ??= GameLibrary;
|
|
|
|
if (MainContent.Content != content)
|
|
{
|
|
MainContent.Content = content;
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
private void VolumeStatus_CheckedChanged(object sender, RoutedEventArgs e)
|
|
{
|
|
var volumeSplitButton = sender as ToggleSplitButton;
|
|
if (ViewModel.IsGameRunning)
|
|
{
|
|
if (!volumeSplitButton.IsChecked)
|
|
{
|
|
ViewModel.AppHost.Device.SetVolume(ConfigurationState.Instance.System.AudioVolume);
|
|
}
|
|
else
|
|
{
|
|
ViewModel.AppHost.Device.SetVolume(0);
|
|
}
|
|
|
|
ViewModel.Volume = ViewModel.AppHost.Device.GetVolume();
|
|
}
|
|
}
|
|
|
|
protected override void OnClosing(WindowClosingEventArgs e)
|
|
{
|
|
if (!ViewModel.IsClosing && ViewModel.AppHost != null && ConfigurationState.Instance.ShowConfirmExit)
|
|
{
|
|
e.Cancel = true;
|
|
|
|
ConfirmExit();
|
|
|
|
return;
|
|
}
|
|
|
|
ViewModel.IsClosing = true;
|
|
|
|
if (ViewModel.AppHost != null)
|
|
{
|
|
ViewModel.AppHost.AppExit -= ViewModel.AppHost_AppExit;
|
|
ViewModel.AppHost.AppExit += (sender, e) =>
|
|
{
|
|
ViewModel.AppHost = null;
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
MainContent = null;
|
|
|
|
Close();
|
|
});
|
|
};
|
|
ViewModel.AppHost?.Stop();
|
|
|
|
e.Cancel = true;
|
|
|
|
return;
|
|
}
|
|
|
|
SaveWindowSizePosition();
|
|
|
|
ApplicationLibrary.CancelLoading();
|
|
InputManager.Dispose();
|
|
Program.Exit();
|
|
|
|
base.OnClosing(e);
|
|
}
|
|
|
|
private void ConfirmExit()
|
|
{
|
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
|
{
|
|
ViewModel.IsClosing = await ContentDialogHelper.CreateExitDialog();
|
|
|
|
if (ViewModel.IsClosing)
|
|
{
|
|
Close();
|
|
}
|
|
});
|
|
}
|
|
|
|
public async void LoadApplications()
|
|
{
|
|
await Dispatcher.UIThread.InvokeAsync(() =>
|
|
{
|
|
ViewModel.Applications.Clear();
|
|
|
|
StatusBarView.LoadProgressBar.IsVisible = true;
|
|
ViewModel.StatusBarProgressMaximum = 0;
|
|
ViewModel.StatusBarProgressValue = 0;
|
|
|
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
|
});
|
|
|
|
ReloadGameList();
|
|
}
|
|
|
|
public void ToggleFileType(string fileType)
|
|
{
|
|
_ = 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,
|
|
"NCA" => ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NCA,
|
|
"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);
|
|
LoadApplications();
|
|
}
|
|
|
|
private void ReloadGameList()
|
|
{
|
|
if (_isLoading)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_isLoading = true;
|
|
|
|
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs.Value, ConfigurationState.Instance.System.Language);
|
|
|
|
_isLoading = false;
|
|
}
|
|
}
|
|
}
|