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(); } } } /// /// 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. /// 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()) 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 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 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 onOk) { var t = new Thread(() => OpenFolderDialogThread(title, directory, onOk)); t.SetApartmentState(ApartmentState.STA); t.Start(); } private static void OpenFolderDialogThread(string title, string directory, Action 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); } } }