623604c391
* Fixed formatting/parsing issues with ApplicationData properties TimePlayed, LastPlayed, and FileSize Replaced double-based TimePlayed property with TimeSpan?-based one in ApplicationData and ApplicationMetadata Added a migration for TimePlayed, just like in #4861 Consolidated ApplicationData's FileSize* properties into one FileSize property Added a formatting/parsing helper class ValueFormatUtils for TimeSpans, DateTimes, and file sizes Added new value converters for TimeSpans and file sizes for the Avalonia UI Added TimePlayedSortComparer Fixed sort order in LastPlayedSortComparer Fixed sort order for ApplicationData fields TimePlayed, LastPlayed, and FileSize Fixed crashes caused by SortHelper Replaced SystemInfo.ToMiBString with ToGiBString backed by ValueFormatUtils Replaced SaveModel.GetSizeString() with ValueFormatUtils * Additional ApplicationLibrary changes that got lost in the last commit * Removed unneeded usings * Removed converters as they are no longer needed * Updated comment on FormatDateTime * Removed base10 parameter from ValueFormatUtils FormatFileSize now always returns base 2 values with base 10 units Made ParseFileSize capable of parsing both base 2 and base 10 units * Removed nullable attribute from TimePlayed property Centralized TimePlayed update code into ApplicationMetadata * Changed UpdateTimePlayed() to use TimeSpan logic * Removed JsonIgnore attributes from ApplicationData * Implemented requested format changes * Fixed mistakes in method documentation comments * Made it so the Last Played value "Never" is localized in the Avalonia UI * Implemented suggestions * Remove unused import * Did a comment refinement pass in ValueFormatUtils.cs * Reordered ValueFormatUtils methods and sorted them into #regions * Integrated functionality from #5056 Also removed Logger print from last_played migration code * Implemented suggestions * Moved ValueFormatUtils and SystemInfo to namespace Ryujinx.Ui.Common * common: Respect proper value format convention and use base10 by default This could be discuss again in another issue/PR, for now revert to the previous behavior. Signed-off-by: Mary Guillemard <mary@mary.zone> --------- Signed-off-by: Mary Guillemard <mary@mary.zone> Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com> Co-authored-by: Mary Guillemard <mary@mary.zone>
236 lines
8.8 KiB
C#
236 lines
8.8 KiB
C#
using Avalonia;
|
|
using Avalonia.Threading;
|
|
using Ryujinx.Ava.UI.Helpers;
|
|
using Ryujinx.Ava.UI.Windows;
|
|
using Ryujinx.Common;
|
|
using Ryujinx.Common.Configuration;
|
|
using Ryujinx.Common.GraphicsDriver;
|
|
using Ryujinx.Common.Logging;
|
|
using Ryujinx.Common.SystemInterop;
|
|
using Ryujinx.Modules;
|
|
using Ryujinx.SDL2.Common;
|
|
using Ryujinx.Ui.Common;
|
|
using Ryujinx.Ui.Common.Configuration;
|
|
using Ryujinx.Ui.Common.Helper;
|
|
using Ryujinx.Ui.Common.SystemInfo;
|
|
using System;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Ryujinx.Ava
|
|
{
|
|
internal partial class Program
|
|
{
|
|
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; }
|
|
|
|
[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 MbIconwarning = 0x30;
|
|
|
|
public static void Main(string[] args)
|
|
{
|
|
Version = ReleaseInformation.GetVersion();
|
|
|
|
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}", MbIconwarning);
|
|
}
|
|
|
|
PreviewerDetached = true;
|
|
|
|
Initialize(args);
|
|
|
|
LoggerAdapter.Register();
|
|
|
|
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
|
}
|
|
|
|
public static AppBuilder BuildAvaloniaApp()
|
|
{
|
|
return AppBuilder.Configure<App>()
|
|
.UsePlatformDetect()
|
|
.With(new X11PlatformOptions
|
|
{
|
|
EnableMultiTouch = true,
|
|
EnableIme = true,
|
|
RenderingMode = new[] { X11RenderingMode.Glx, X11RenderingMode.Software },
|
|
})
|
|
.With(new Win32PlatformOptions
|
|
{
|
|
WinUICompositionBackdropCornerRadius = 8.0f,
|
|
RenderingMode = new[] { Win32RenderingMode.AngleEgl, Win32RenderingMode.Software },
|
|
})
|
|
.UseSkia();
|
|
}
|
|
|
|
private static void Initialize(string[] args)
|
|
{
|
|
// Parse arguments
|
|
CommandLineState.ParseArguments(args);
|
|
|
|
// Delete backup files after updating.
|
|
Task.Run(Updater.CleanupUpdate);
|
|
|
|
Console.Title = $"Ryujinx Console {Version}";
|
|
|
|
// 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();
|
|
|
|
// Setup base data directory.
|
|
AppDataManager.Initialize(CommandLineState.BaseDirPathArg);
|
|
|
|
// Initialize the configuration.
|
|
ConfigurationState.Initialize();
|
|
|
|
// Initialize the logger system.
|
|
LoggerModule.Initialize();
|
|
|
|
// Initialize Discord integration.
|
|
DiscordIntegrationModule.Initialize();
|
|
|
|
// Initialize SDL2 driver
|
|
SDL2Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input);
|
|
|
|
ReloadConfig();
|
|
|
|
WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor();
|
|
|
|
// Logging system information.
|
|
PrintSystemInfo();
|
|
|
|
// Enable OGL multithreading on the driver, when available.
|
|
DriverUtilities.ToggleOGLThreading(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
|
|
|
|
// Check if keys exists.
|
|
if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))
|
|
{
|
|
if (!(AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys"))))
|
|
{
|
|
MainWindow.ShowKeyErrorOnLoad = true;
|
|
}
|
|
}
|
|
|
|
if (CommandLineState.LaunchPathArg != null)
|
|
{
|
|
MainWindow.DeferLoadApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg);
|
|
}
|
|
}
|
|
|
|
public static void ReloadConfig()
|
|
{
|
|
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
|
|
if (File.Exists(localConfigurationPath))
|
|
{
|
|
ConfigurationPath = localConfigurationPath;
|
|
}
|
|
else if (File.Exists(appDataConfigurationPath))
|
|
{
|
|
ConfigurationPath = appDataConfigurationPath;
|
|
}
|
|
|
|
if (ConfigurationPath == null)
|
|
{
|
|
// No configuration, we load the default values and save it to disk
|
|
ConfigurationPath = appDataConfigurationPath;
|
|
|
|
ConfigurationState.Instance.LoadDefault();
|
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(ConfigurationPath);
|
|
}
|
|
else
|
|
{
|
|
if (ConfigurationFileFormat.TryLoad(ConfigurationPath, out ConfigurationFileFormat configurationFileFormat))
|
|
{
|
|
ConfigurationState.Instance.Load(configurationFileFormat, ConfigurationPath);
|
|
}
|
|
else
|
|
{
|
|
ConfigurationState.Instance.LoadDefault();
|
|
|
|
Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location {ConfigurationPath}");
|
|
}
|
|
}
|
|
|
|
// Check if graphics backend was overridden
|
|
if (CommandLineState.OverrideGraphicsBackend != null)
|
|
{
|
|
if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl")
|
|
{
|
|
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl;
|
|
}
|
|
else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan")
|
|
{
|
|
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan;
|
|
}
|
|
}
|
|
|
|
// Check if docked mode was overriden.
|
|
if (CommandLineState.OverrideDockedMode.HasValue)
|
|
{
|
|
ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value;
|
|
}
|
|
|
|
// Check if HideCursor was overridden.
|
|
if (CommandLineState.OverrideHideCursor is not null)
|
|
{
|
|
ConfigurationState.Instance.HideCursor.Value = CommandLineState.OverrideHideCursor!.ToLower() switch
|
|
{
|
|
"never" => HideCursorMode.Never,
|
|
"onidle" => HideCursorMode.OnIdle,
|
|
"always" => HideCursorMode.Always,
|
|
_ => ConfigurationState.Instance.HideCursor.Value,
|
|
};
|
|
}
|
|
}
|
|
|
|
private static void PrintSystemInfo()
|
|
{
|
|
Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}");
|
|
SystemInfo.Gather().Print();
|
|
|
|
Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(Logger.GetEnabledLevels().Count == 0 ? "<None>" : string.Join(", ", Logger.GetEnabledLevels()))}");
|
|
|
|
if (AppDataManager.Mode == AppDataManager.LaunchMode.Custom)
|
|
{
|
|
Logger.Notice.Print(LogClass.Application, $"Launch Mode: Custom Path {AppDataManager.BaseDirPath}");
|
|
}
|
|
else
|
|
{
|
|
Logger.Notice.Print(LogClass.Application, $"Launch Mode: {AppDataManager.Mode}");
|
|
}
|
|
}
|
|
|
|
private static void ProcessUnhandledException(Exception ex, bool isTerminating)
|
|
{
|
|
string message = $"Unhandled exception caught: {ex}";
|
|
|
|
Logger.Error?.PrintMsg(LogClass.Application, message);
|
|
|
|
if (Logger.Error == null)
|
|
{
|
|
Logger.Notice.PrintMsg(LogClass.Application, message);
|
|
}
|
|
|
|
if (isTerminating)
|
|
{
|
|
Exit();
|
|
}
|
|
}
|
|
|
|
public static void Exit()
|
|
{
|
|
DiscordIntegrationModule.Exit();
|
|
|
|
Logger.Shutdown();
|
|
}
|
|
}
|
|
}
|