// SPDX-License-Identifier: GPL-3.0-only using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.InteropServices; namespace BlockModLoader { [Flags] public enum WhiteListLoader { None = 0, MelonLoader = 1, BepInEx = 2, UMM = 4, All = MelonLoader | BepInEx | UMM } public static class LoaderGuard { public static void EnsureAllowed(string modName) { EnsureAllowed(modName, null); } public static void EnsureAllowed(string modName, string recommendedLoader) { EnsureAllowed(modName, WhiteListLoader.None, recommendedLoader); } public static void EnsureAllowed(string modName, WhiteListLoader whiteListLoader, string recommendedLoader = null) { EnsureAllowed(modName, AppDomain.CurrentDomain.GetAssemblies(), ShowAlert, Environment.FailFast, recommendedLoader, whiteListLoader); } internal static void EnsureAllowed(string modName, IEnumerable assemblies, Action showAlert, Action terminate, string recommendedLoader = null, WhiteListLoader whiteListLoader = WhiteListLoader.None) { if ((whiteListLoader & ~WhiteListLoader.All) != 0) throw new ArgumentOutOfRangeException(nameof(whiteListLoader)); foreach (var assembly in assemblies) { var loader = GetLoader(assembly.GetName().Name); if (loader == WhiteListLoader.None || (whiteListLoader & loader) != 0) continue; var loaderName = loader == WhiteListLoader.UMM ? "Unity Mod Manager (UMM)" : loader.ToString(); var message = modName + " cannot run while " + loaderName + " is active.\n\n" + "Remove the unsupported mod loader and follow this mod's installation instructions.\n\n" + (string.IsNullOrWhiteSpace(recommendedLoader) ? string.Empty : "Recommended mod loader: " + recommendedLoader.Trim() + ".\n\n") + "The game will close after this message."; // Loader exception handlers can swallow a thrown exception, so termination // must happen even if the native dialog fails. try { showAlert(message); } finally { terminate(message); } return; } } private static WhiteListLoader GetLoader(string assemblyName) { if (string.Equals(assemblyName, "MelonLoader", StringComparison.OrdinalIgnoreCase)) return WhiteListLoader.MelonLoader; if (string.Equals(assemblyName, "BepInEx", StringComparison.OrdinalIgnoreCase) || string.Equals(assemblyName, "BepInEx.Core", StringComparison.OrdinalIgnoreCase)) return WhiteListLoader.BepInEx; if (string.Equals(assemblyName, "UnityModManager", StringComparison.OrdinalIgnoreCase)) return WhiteListLoader.UMM; return WhiteListLoader.None; } private static void ShowAlert(string message) { Console.Error.WriteLine(message); if (Environment.OSVersion.Platform == PlatformID.Win32NT) MessageBoxW(IntPtr.Zero, message, "Unsupported mod loader", 0x10 | 0x10000); } [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] private static extern int MessageBoxW(IntPtr window, string text, string caption, uint type); } }