mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-03-21 17:48:28 -05:00
Only show Square shiny in Gen8 context (sw/sh only) to avoid confusion Fix method name typo Close subforms in reverse to avoid allocating a temp list Close splash screen entirely async rather than dual Task.Run Translate the entirety of the EntitySearchSetup (comparator button/menu now translates) Launch box popup to the right of the main form, like the Search behavior Fix dark mode coloring of popup box editor/group viewer images Fix dark mode RichTextBox retaining border when it should be removed (white was annoying); was early-returning due to satisfying TextBoxBase
240 lines
8.0 KiB
C#
240 lines
8.0 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
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 { }
|
|
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
|
|
{
|
|
}
|
|
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
|
|
}
|