255 lines
8.7 KiB
C#
255 lines
8.7 KiB
C#
using System.Diagnostics;
|
|
using System.Reflection;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using Windows.UI.Popups;
|
|
using HarmonyLib;
|
|
using PluginLoader.SEPM;
|
|
using Sandbox;
|
|
using Sandbox.Game.World;
|
|
using Sandbox.Graphics.GUI;
|
|
using VRage.FileSystem;
|
|
using VRage.Input;
|
|
using VRage.Plugins;
|
|
|
|
namespace PluginLoader;
|
|
|
|
public static class LoaderTools
|
|
{
|
|
public static string PluginsDir => Path.GetFullPath(Path.Combine(MyFileSystem.ExePath, "Plugins"));
|
|
|
|
public static DialogResult ShowMessageBox(string message, string title, MessageBoxButtons buttons, MessageBoxIcon icon)
|
|
{
|
|
var dialog = new MessageDialog(message, title);
|
|
|
|
switch (buttons)
|
|
{
|
|
case MessageBoxButtons.OK:
|
|
dialog.Commands.Add(new UICommand("Ok"));
|
|
break;
|
|
case MessageBoxButtons.OKCancel:
|
|
dialog.Commands.Add(new UICommand("Ok"));
|
|
dialog.Commands.Add(new UICommand("Cancel"));
|
|
break;
|
|
case MessageBoxButtons.AbortRetryIgnore:
|
|
break;
|
|
case MessageBoxButtons.YesNoCancel:
|
|
dialog.Commands.Add(new UICommand("Yes"));
|
|
dialog.Commands.Add(new UICommand("No"));
|
|
dialog.Commands.Add(new UICommand("Cancel"));
|
|
break;
|
|
case MessageBoxButtons.YesNo:
|
|
dialog.Commands.Add(new UICommand("Yes"));
|
|
dialog.Commands.Add(new UICommand("No"));
|
|
break;
|
|
case MessageBoxButtons.RetryCancel:
|
|
dialog.Commands.Add(new UICommand("Retry"));
|
|
dialog.Commands.Add(new UICommand("Cancel"));
|
|
break;
|
|
case MessageBoxButtons.CancelTryContinue:
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(buttons), buttons, null);
|
|
}
|
|
|
|
WinRT.Interop.InitializeWithWindow.Initialize(dialog, Process.GetCurrentProcess().MainWindowHandle);
|
|
|
|
var result = dialog.ShowAsync().AsTask().Result;
|
|
|
|
return buttons switch
|
|
{
|
|
MessageBoxButtons.OK => DialogResult.OK,
|
|
MessageBoxButtons.OKCancel => result.Label == "Ok" ? DialogResult.OK : DialogResult.Cancel,
|
|
MessageBoxButtons.AbortRetryIgnore => DialogResult.Ignore,
|
|
MessageBoxButtons.YesNoCancel => result.Label switch
|
|
{
|
|
"Yes" => DialogResult.Yes,
|
|
"No" => DialogResult.No,
|
|
_ => DialogResult.Cancel
|
|
},
|
|
MessageBoxButtons.YesNo => result.Label switch
|
|
{
|
|
"Yes" => DialogResult.Yes,
|
|
_ => DialogResult.No
|
|
},
|
|
MessageBoxButtons.RetryCancel => result.Label switch
|
|
{
|
|
"Retry" => DialogResult.Retry,
|
|
_ => DialogResult.Cancel
|
|
},
|
|
MessageBoxButtons.CancelTryContinue => DialogResult.Cancel,
|
|
_ => throw new ArgumentOutOfRangeException(nameof(buttons), buttons, null)
|
|
};
|
|
}
|
|
|
|
|
|
public static void UnloadAndRestart()
|
|
{
|
|
LogFile.Dispose();
|
|
MySessionLoader.Unload();
|
|
MySandboxGame.Config.ControllerDefaultOnStart = MyInput.Static.IsJoystickLastUsed;
|
|
MySandboxGame.Config.Save();
|
|
MyScreenManager.CloseAllScreensNowExcept(null);
|
|
MyPlugins.Unload();
|
|
Restart();
|
|
}
|
|
|
|
public static void Restart()
|
|
{
|
|
Application.Restart();
|
|
Process.GetCurrentProcess().Kill();
|
|
}
|
|
|
|
public static void ExecuteMain(SEPMPlugin plugin)
|
|
{
|
|
var name = plugin.GetType().ToString();
|
|
plugin.Main(new(name), new());
|
|
}
|
|
|
|
public static string GetHash1(string file)
|
|
{
|
|
using (var sha = new SHA1Managed())
|
|
{
|
|
return GetHash(file, sha);
|
|
}
|
|
}
|
|
|
|
public static string GetHash256(string file)
|
|
{
|
|
using (var sha = new SHA256CryptoServiceProvider())
|
|
{
|
|
return GetHash(file, sha);
|
|
}
|
|
}
|
|
|
|
public static string GetHash(string file, HashAlgorithm hash)
|
|
{
|
|
using (var fileStream = new FileStream(file, FileMode.Open))
|
|
{
|
|
using (var bufferedStream = new BufferedStream(fileStream))
|
|
{
|
|
var data = hash.ComputeHash(bufferedStream);
|
|
var sb = new StringBuilder(2 * data.Length);
|
|
foreach (var b in data)
|
|
sb.AppendFormat("{0:x2}", b);
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method attempts to disable JIT compiling for the assembly.
|
|
/// This method will force any member access exceptions by methods to be thrown now instead of later.
|
|
/// </summary>
|
|
public static void Precompile(Assembly a)
|
|
{
|
|
Type[] types;
|
|
try
|
|
{
|
|
types = a.GetTypes();
|
|
}
|
|
catch (ReflectionTypeLoadException e)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine("LoaderExceptions: ");
|
|
foreach (var e2 in e.LoaderExceptions)
|
|
sb.Append(e2).AppendLine();
|
|
LogFile.WriteLine(sb.ToString());
|
|
throw;
|
|
}
|
|
|
|
foreach (var t in types)
|
|
{
|
|
// Static constructors allow for early code execution which can cause issues later in the game
|
|
if (HasStaticConstructor(t))
|
|
continue;
|
|
|
|
foreach (var m in t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public |
|
|
BindingFlags.Instance | BindingFlags.Static))
|
|
{
|
|
if (m.HasAttribute<HarmonyReversePatch>())
|
|
throw new("Harmony attribute 'HarmonyReversePatch' found on the method '" + m.Name +
|
|
"' is not compatible with Plugin Loader!");
|
|
Precompile(m);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void Precompile(MethodInfo m)
|
|
{
|
|
if (!m.IsAbstract && !m.ContainsGenericParameters)
|
|
RuntimeHelpers.PrepareMethod(m.MethodHandle);
|
|
}
|
|
|
|
private static bool HasStaticConstructor(Type t)
|
|
{
|
|
return t.GetConstructors(BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic |
|
|
BindingFlags.Instance).Any(c => c.IsStatic);
|
|
}
|
|
|
|
|
|
public static void OpenFileDialog(string title, string directory, string filter, Action<string> onOk)
|
|
{
|
|
var t = new Thread(() => OpenFileDialogThread(title, directory, filter, onOk));
|
|
t.SetApartmentState(ApartmentState.STA);
|
|
t.Start();
|
|
}
|
|
|
|
private static void OpenFileDialogThread(string title, string directory, string filter, Action<string> onOk)
|
|
{
|
|
try
|
|
{
|
|
// Get the file path via prompt
|
|
using (var openFileDialog = new OpenFileDialog())
|
|
{
|
|
if (Directory.Exists(directory))
|
|
openFileDialog.InitialDirectory = directory;
|
|
openFileDialog.Title = title;
|
|
openFileDialog.Filter = filter;
|
|
openFileDialog.RestoreDirectory = true;
|
|
|
|
if (openFileDialog.ShowDialog() == DialogResult.OK)
|
|
// Move back to the main thread so that we can interact with keen code again
|
|
MySandboxGame.Static.Invoke(
|
|
() => onOk(openFileDialog.FileName),
|
|
"PluginLoader");
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
LogFile.WriteGameLog("Error while opening file dialog: " + e);
|
|
}
|
|
}
|
|
|
|
public static void OpenFolderDialog(string title, string directory, Action<string> onOk)
|
|
{
|
|
var t = new Thread(() => OpenFolderDialogThread(title, directory, onOk));
|
|
t.SetApartmentState(ApartmentState.STA);
|
|
t.Start();
|
|
}
|
|
|
|
private static void OpenFolderDialogThread(string title, string directory, Action<string> onOk)
|
|
{
|
|
try
|
|
{
|
|
// Get the file path via prompt
|
|
using (var openFileDialog = new FolderBrowserDialog())
|
|
{
|
|
if (Directory.Exists(directory))
|
|
openFileDialog.SelectedPath = directory;
|
|
openFileDialog.Description = title;
|
|
|
|
if (openFileDialog.ShowDialog() == DialogResult.OK)
|
|
// Move back to the main thread so that we can interact with keen code again
|
|
MySandboxGame.Static.Invoke(
|
|
() => onOk(openFileDialog.SelectedPath),
|
|
"PluginLoader");
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
LogFile.WriteGameLog("Error while opening file dialog: " + e);
|
|
}
|
|
}
|
|
} |