mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-03-22 01:55:10 -05:00
Previously "hidden" in the shortcut keys by clicking on the Box tab, this makes it more discoverable. Old hotkeys still retained. fixed hidden 0x200E char in Fashion button text (ancient typo) Update Program.cs
243 lines
8.3 KiB
C#
243 lines
8.3 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using System.Windows.Forms;
|
|
using PKHeX.Core;
|
|
|
|
#if !DEBUG
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Reflection;
|
|
#endif
|
|
|
|
namespace PKHeX.WinForms;
|
|
|
|
internal static class Program
|
|
{
|
|
// Pipelines build can sometimes tack on text to the version code. Strip it out.
|
|
public static readonly Version CurrentVersion = Version.Parse(GetSaneVersionTag(Application.ProductVersion));
|
|
|
|
public static readonly string WorkingDirectory = Path.GetDirectoryName(Environment.ProcessPath) ?? "";
|
|
public const string ConfigFileName = "cfg.json";
|
|
public static string PathConfig => Path.Combine(WorkingDirectory, ConfigFileName);
|
|
|
|
/// <summary>
|
|
/// Global settings instance, loaded before any forms are created.
|
|
/// </summary>
|
|
public static PKHeXSettings Settings { get; }
|
|
|
|
public static bool HaX { get; private set; }
|
|
static Program()
|
|
{
|
|
#if !DEBUG
|
|
Application.ThreadException += UIThreadException;
|
|
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
|
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
|
#endif
|
|
Application.EnableVisualStyles();
|
|
Application.SetCompatibleTextRenderingDefault(false);
|
|
Settings = PKHeXSettings.GetSettings(PathConfig);
|
|
|
|
if (Settings.Startup.DarkMode)
|
|
Application.SetColorMode(SystemColorMode.Dark);
|
|
}
|
|
|
|
[STAThread]
|
|
private static void Main()
|
|
{
|
|
// Load settings first
|
|
var settings = Settings;
|
|
settings.LocalResources.SetLocalPath(WorkingDirectory);
|
|
StartupUtil.ReloadSettings(settings);
|
|
|
|
SplashScreen? splash = null;
|
|
if (!settings.Startup.SkipSplashScreen)
|
|
{
|
|
// Show splash screen on a dedicated STA thread so it can pump its own message loop.
|
|
var splashThread = new Thread(() =>
|
|
{
|
|
splash = new SplashScreen();
|
|
Application.Run(splash);
|
|
})
|
|
{ IsBackground = true };
|
|
splashThread.SetApartmentState(ApartmentState.STA);
|
|
splashThread.Start();
|
|
}
|
|
|
|
var args = Environment.GetCommandLineArgs();
|
|
// Prepare initial values for the main form.
|
|
var startup = StartupUtil.GetStartup(args, settings);
|
|
var init = StartupUtil.FormLoadInitialActions(args, settings, CurrentVersion);
|
|
HaX = init.HaX;
|
|
var main = new Main();
|
|
|
|
// Close splash when Main is ready to display, then perform startup animation.
|
|
main.Shown += async (_, _) =>
|
|
{
|
|
splash?.BeginInvoke(splash.ForceClose);
|
|
main.Activate();
|
|
|
|
// Follow-up: display popups if needed.
|
|
if (init.HaX)
|
|
main.WarnBehavior();
|
|
else if (init.ShowChangelog)
|
|
main.ShowAboutDialog(AboutPage.Changelog);
|
|
else if (init.BackupPrompt)
|
|
main.PromptBackup(settings.LocalResources.GetBackupPath());
|
|
|
|
await main.CheckForUpdates().ConfigureAwait(true);
|
|
};
|
|
|
|
// Setup complete.
|
|
if (Settings.Startup.PluginLoadEnable)
|
|
main.AttachPlugins();
|
|
main.LoadInitialFiles(startup);
|
|
Application.Run(main);
|
|
}
|
|
|
|
private static ReadOnlySpan<char> GetSaneVersionTag(ReadOnlySpan<char> productVersion)
|
|
{
|
|
for (int i = 0; i < productVersion.Length; i++)
|
|
{
|
|
char c = productVersion[i];
|
|
if (c == '.')
|
|
continue;
|
|
if (char.IsNumber(c))
|
|
continue;
|
|
return productVersion[..i];
|
|
}
|
|
return productVersion;
|
|
}
|
|
|
|
#if !DEBUG
|
|
private static void Error(string msg) => MessageBox.Show(msg, "PKHeX Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
|
|
|
|
private static void UIThreadException(object sender, ThreadExceptionEventArgs t)
|
|
{
|
|
DialogResult result = DialogResult.Cancel;
|
|
try
|
|
{
|
|
var e = t.Exception;
|
|
string errorMessage = GetErrorMessage(e);
|
|
result = ErrorWindow.ShowErrorDialog(errorMessage, e, true);
|
|
}
|
|
catch (Exception reportingException)
|
|
{
|
|
HandleReportingException(t.Exception, reportingException);
|
|
}
|
|
|
|
if (result == DialogResult.Abort)
|
|
Application.Exit();
|
|
}
|
|
|
|
private static string GetErrorMessage(Exception e)
|
|
{
|
|
try
|
|
{
|
|
if (IsPluginError<IPlugin>(e, out var pluginName))
|
|
return $"An error occurred in a PKHeX plugin. Please report this error to the plugin author/maintainer.\n{pluginName}";
|
|
}
|
|
catch
|
|
{
|
|
// If we fail to analyze the stack trace, just return the generic message. Don't risk another exception.
|
|
}
|
|
return "An error occurred in PKHeX. Please report this error to the PKHeX author.";
|
|
}
|
|
|
|
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
|
{
|
|
var ex = e.ExceptionObject as Exception;
|
|
try
|
|
{
|
|
if (IsOldPkhexCorePresent(ex))
|
|
{
|
|
Error("You have upgraded PKHeX incorrectly. Please delete PKHeX.Core.dll.");
|
|
}
|
|
else if (IsPkhexCoreMissing(ex))
|
|
{
|
|
Error("You have installed PKHeX incorrectly. Please ensure you have unzipped all files before running.");
|
|
}
|
|
else if (ex is not null)
|
|
{
|
|
var msg = GetErrorMessage(ex);
|
|
ErrorWindow.ShowErrorDialog($"{msg}\nPKHeX must now close.", ex, false);
|
|
}
|
|
else
|
|
{
|
|
Error("A fatal non-UI error has occurred in PKHeX, and the details could not be displayed. Please report this to the author.");
|
|
}
|
|
}
|
|
catch (Exception reportingException)
|
|
{
|
|
HandleReportingException(ex, reportingException);
|
|
}
|
|
}
|
|
|
|
private static bool IsPluginError<T>(Exception exception, out string pluginName)
|
|
{
|
|
pluginName = string.Empty;
|
|
var stackTrace = new System.Diagnostics.StackTrace(exception);
|
|
foreach (var frame in stackTrace.GetFrames())
|
|
{
|
|
var method = frame.GetMethod();
|
|
var type = method?.DeclaringType;
|
|
if (!typeof(T).IsAssignableFrom(type))
|
|
continue;
|
|
pluginName = type.Namespace ?? string.Empty;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static void HandleReportingException(Exception? ex, Exception reportingException)
|
|
{
|
|
try
|
|
{
|
|
EmergencyErrorLog(ex, reportingException);
|
|
}
|
|
catch
|
|
{
|
|
// Do nothing. If we can't log the error, there's not much else we can do, and we don't want to risk another exception.
|
|
}
|
|
if (reportingException is FileNotFoundException x && x.FileName?.StartsWith("PKHeX.Core") == true)
|
|
{
|
|
Error("Could not locate PKHeX.Core.dll. Make sure you're running PKHeX together with its code library. Usually caused when all files are not extracted.");
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
Error("A fatal non-UI error has occurred in PKHeX, and there was a problem displaying the details. Please report this to the author.");
|
|
}
|
|
finally
|
|
{
|
|
Application.Exit();
|
|
}
|
|
}
|
|
|
|
private static bool EmergencyErrorLog(Exception? originalException, Exception errorHandlingException)
|
|
{
|
|
try
|
|
{
|
|
var message = (originalException?.ToString() ?? "null first exception") + Environment.NewLine + errorHandlingException;
|
|
File.WriteAllText($"PKHeX_Error_Report {DateTime.Now:yyyyMMddHHmmss}.txt", message);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static bool IsOldPkhexCorePresent([NotNullWhen(true)] Exception? ex)
|
|
{
|
|
return ex is MissingMethodException or TypeLoadException or TypeInitializationException
|
|
&& File.Exists("PKHeX.Core.dll")
|
|
&& AssemblyName.GetAssemblyName("PKHeX.Core.dll").Version < CurrentVersion;
|
|
}
|
|
|
|
private static bool IsPkhexCoreMissing([NotNullWhen(true)] Exception? ex)
|
|
{
|
|
return ex is FileNotFoundException { FileName: {} n } && n.Contains("PKHeX.Core");
|
|
}
|
|
#endif
|
|
}
|