diff --git a/BepInEx.MelonLoader.Loader.sln b/BepInEx.MelonLoader.Loader.sln new file mode 100644 index 0000000..cc67765 --- /dev/null +++ b/BepInEx.MelonLoader.Loader.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30413.136 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BepInEx.MelonLoader.Loader", "BepInEx.MelonLoader.Loader\BepInEx.MelonLoader.Loader.csproj", "{7D64B55B-887B-4A70-A5FD-5E01B849B9BC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7D64B55B-887B-4A70-A5FD-5E01B849B9BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D64B55B-887B-4A70-A5FD-5E01B849B9BC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D64B55B-887B-4A70-A5FD-5E01B849B9BC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D64B55B-887B-4A70-A5FD-5E01B849B9BC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {22610235-9D7E-4B20-B180-26071ACC246C} + EndGlobalSection +EndGlobal diff --git a/BepInEx.MelonLoader.Loader/BepInEx.MelonLoader.Loader.csproj b/BepInEx.MelonLoader.Loader/BepInEx.MelonLoader.Loader.csproj new file mode 100644 index 0000000..fc069e6 --- /dev/null +++ b/BepInEx.MelonLoader.Loader/BepInEx.MelonLoader.Loader.csproj @@ -0,0 +1,211 @@ + + + + + Debug + AnyCPU + {7D64B55B-887B-4A70-A5FD-5E01B849B9BC} + Library + Properties + BepInEx.MelonLoader.Loader + MelonLoader.ModHandler + v4.7.2 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + + ..\..\BepInEx\bin\il2cpp\0Harmony.dll + False + + + ..\..\BepInEx\bin\il2cpp\BepInEx.Core.dll + False + + + ..\..\BepInEx\bin\il2cpp\BepInEx.IL2CPP.dll + False + + + ..\Libs\Il2Cppmscorlib.dll + False + + + ..\Libs\Il2CppSystem.dll + False + + + G:\SteamLibrary\steamapps\common\VRChat\BepInEx\core\MonoMod.RuntimeDetour.dll + False + + + G:\SteamLibrary\steamapps\common\VRChat\BepInEx\core\MonoMod.Utils.dll + False + + + + + + ..\..\BepInEx\bin\il2cpp\UnhollowerBaseLib.dll + False + + + ..\..\BepInEx\bin\il2cpp\UnhollowerRuntimeLib.dll + False + + + ..\Libs\UnityEngine.AssetBundleModule.dll + False + + + ..\Libs\UnityEngine.CoreModule.dll + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BepInEx.MelonLoader.Loader/MLLoaderPlugin.cs b/BepInEx.MelonLoader.Loader/MLLoaderPlugin.cs new file mode 100644 index 0000000..908b4fb --- /dev/null +++ b/BepInEx.MelonLoader.Loader/MLLoaderPlugin.cs @@ -0,0 +1,15 @@ +using BepInEx.IL2CPP; +using MelonLoader; + +namespace BepInEx.MelonLoader.Loader +{ + [BepInPlugin("io.bepis.mlloaderplugin", "MelonLoader Plugin Loader", "1.0")] + public class MLLoaderPlugin : BasePlugin + { + public override void Load() + { + MelonLoaderBase.Initialize(); + MelonLoaderBase.Startup(); + } + } +} \ No newline at end of file diff --git a/BepInEx.MelonLoader.Loader/MelonLoader/BuildInfo.cs b/BepInEx.MelonLoader.Loader/MelonLoader/BuildInfo.cs new file mode 100644 index 0000000..fa0784d --- /dev/null +++ b/BepInEx.MelonLoader.Loader/MelonLoader/BuildInfo.cs @@ -0,0 +1,11 @@ +namespace MelonLoader +{ + public static class BuildInfo + { + public const string Name = "BepInEx"; + public const string Description = "MelonLoader wrapper for BepInEx"; + public const string Author = "Bepis"; + public const string Company = "https://discord.gg/MpFEDAg"; + public const string Version = "6.0.0.0"; + } +} \ No newline at end of file diff --git a/BepInEx.MelonLoader.Loader/MelonLoader/DependencyGraph.cs b/BepInEx.MelonLoader.Loader/MelonLoader/DependencyGraph.cs new file mode 100644 index 0000000..42de727 --- /dev/null +++ b/BepInEx.MelonLoader.Loader/MelonLoader/DependencyGraph.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection; +using System.IO; + +namespace MelonLoader { + internal class DependencyGraph where T : MelonBase { + + public static void TopologicalSort(IList modsOrPlugins, Func modNameGetter) { + DependencyGraph dependencyGraph = new DependencyGraph(modsOrPlugins, modNameGetter); + modsOrPlugins.Clear(); + dependencyGraph.TopologicalSortInto(modsOrPlugins); + } + + private readonly Vertex[] vertices; + + private DependencyGraph(IList mods, Func modNameGetter) { + int size = mods.Count; + vertices = new Vertex[size]; + IDictionary nameLookup = new Dictionary(size); + + // Create a vertex in the dependency graph for each mod to load + for (int i = 0; i < size; ++i) { + Assembly modAssembly = mods[i].Assembly; + string modName = modNameGetter(mods[i]); + + Vertex modVertex = new Vertex(i, mods[i], modName); + vertices[i] = modVertex; + nameLookup[modAssembly.GetName().Name] = modVertex; + } + + // Add an edge for each dependency between mods + IDictionary> modsWithMissingDeps = new SortedDictionary>(); + List missingDependencies = new List(); + HashSet optionalDependencies = new HashSet(); + + foreach (Vertex modVertex in vertices) { + Assembly modAssembly = modVertex.mod.Assembly; + missingDependencies.Clear(); + optionalDependencies.Clear(); + + MelonOptionalDependenciesAttribute optionals = (MelonOptionalDependenciesAttribute) Attribute.GetCustomAttribute(modAssembly, typeof(MelonOptionalDependenciesAttribute)); + if (optionals != null && optionals.AssemblyNames != null) { + optionalDependencies.UnionWith(optionals.AssemblyNames); + } + + foreach (AssemblyName dependency in modAssembly.GetReferencedAssemblies()) { + if (nameLookup.TryGetValue(dependency.Name, out Vertex dependencyVertex)) { + modVertex.dependencies.Add(dependencyVertex); + dependencyVertex.dependents.Add(modVertex); + } else if (!TryLoad(dependency) && !optionalDependencies.Contains(dependency.Name)) { + missingDependencies.Add(dependency); + } + } + + if (missingDependencies.Count > 0) { + // modVertex.skipLoading = true; + modsWithMissingDeps.Add(modNameGetter(modVertex.mod), missingDependencies.ToArray()); + } + } + + if (modsWithMissingDeps.Count > 0) { + // Some mods are missing dependencies. Don't load these mods and show an error message + MelonLogger.LogWarning(BuildMissingDependencyMessage(modsWithMissingDeps)); + } + } + + // Returns true if 'assembly' was already loaded or could be loaded, false if the required assembly was missing. + private static bool TryLoad(AssemblyName assembly) { + try { + Assembly.Load(assembly); + return true; + } catch (FileNotFoundException) { + return false; + } catch (Exception ex) { + MelonLogger.LogError("Loading mod dependency failed: " + ex); + return false; + } + } + + private static string BuildMissingDependencyMessage(IDictionary> modsWithMissingDeps) { + StringBuilder messageBuilder = new StringBuilder("Some mods are missing dependencies, which you may have to install.\n" + + "If these are optional dependencies, mark them as optional using the MelonOptionalDependencies attribute.\n" + + "This warning will turn into an error and mods with missing dependencies will not be loaded in the next version of MelonLoader.\n"); + foreach (string modName in modsWithMissingDeps.Keys) { + messageBuilder.Append($"- '{modName}' is missing the following dependencies:\n"); + foreach (AssemblyName dependency in modsWithMissingDeps[modName]) { + messageBuilder.Append($" - '{dependency.Name}' v{dependency.Version}\n"); + } + } + messageBuilder.Length -= 1; // Remove trailing newline + return messageBuilder.ToString(); + } + + private void TopologicalSortInto(IList loadedMods) { + int[] unloadedDependencies = new int[vertices.Length]; + SortedList loadableMods = new SortedList(); + int skippedMods = 0; + + // Find all sinks in the dependency graph, i.e. mods without any dependencies on other mods + for (int i = 0; i < vertices.Length; ++i) { + Vertex vertex = vertices[i]; + int dependencyCount = vertex.dependencies.Count; + + unloadedDependencies[i] = dependencyCount; + if (dependencyCount == 0) { + loadableMods.Add(vertex.name, vertex); + } + } + + // Perform the (reverse) topological sorting + while (loadableMods.Count > 0) { + Vertex mod = loadableMods.Values[0]; + loadableMods.RemoveAt(0); + + if (!mod.skipLoading) { + loadedMods.Add(mod.mod); + } else { + ++skippedMods; + } + + foreach (Vertex dependent in mod.dependents) { + unloadedDependencies[dependent.index] -= 1; + dependent.skipLoading |= mod.skipLoading; + + if (unloadedDependencies[dependent.index] == 0) { + loadableMods.Add(dependent.name, dependent); + } + } + } + + // Check if all mods were either loaded or skipped. If this is not the case, there is a cycle in the dependency graph + if (loadedMods.Count + skippedMods < vertices.Length) { + StringBuilder errorMessage = new StringBuilder("Some mods could not be loaded due to a cyclic dependency:\n"); + for (int i = 0; i < vertices.Length; ++i) { + if (unloadedDependencies[i] > 0) + errorMessage.Append($"- '{vertices[i].name}'\n"); + } + errorMessage.Length -= 1; // Remove trailing newline + MelonLogger.LogError(errorMessage.ToString()); + } + } + + private class Vertex { + + internal readonly int index; + internal readonly T mod; + internal readonly string name; + + internal readonly IList dependencies; + internal readonly IList dependents; + internal bool skipLoading; + + internal Vertex(int index, T mod, string name) { + this.index = index; + this.mod = mod; + this.name = name; + + dependencies = new List(); + dependents = new List(); + skipLoading = false; + } + } + } +} diff --git a/BepInEx.MelonLoader.Loader/MelonLoader/Deprecated.cs b/BepInEx.MelonLoader.Loader/MelonLoader/Deprecated.cs new file mode 100644 index 0000000..fd436ac --- /dev/null +++ b/BepInEx.MelonLoader.Loader/MelonLoader/Deprecated.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MelonLoader +{ + [Obsolete("Main is obsolete. Please use MelonLoaderBase or MelonHandler instead.")] + public static class Main + { + public static List Mods = null; + public static List Plugins = null; + public static bool IsVRChat = false; + public static bool IsBoneworks = false; + public static string GetUnityVersion() => MelonLoaderBase.UnityVersion; + public static string GetUserDataPath() => MelonLoaderBase.UserDataPath; + internal static void LegacySupport(List mods, List plugins, bool isVRChat, bool isBoneworks) + { + Mods = mods; + Plugins = plugins; + IsVRChat = isVRChat; + IsBoneworks = isBoneworks; + } + } + [Obsolete("MelonModGame is obsolete. Please use MelonGame instead.")] + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public class MelonModGameAttribute : Attribute + { + public string Developer { get; } + public string GameName { get; } + public MelonModGameAttribute(string developer = null, string gameName = null) + { + Developer = developer; + GameName = gameName; + } + internal MelonGameAttribute Convert() => new MelonGameAttribute(Developer, GameName); + } + [Obsolete("MelonModInfo is obsolete. Please use MelonInfo instead.")] + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] + public class MelonModInfoAttribute : Attribute + { + public Type SystemType { get; } + public string Name { get; } + public string Version { get; } + public string Author { get; } + public string DownloadLink { get; } + + public MelonModInfoAttribute(Type type, string name, string version, string author, string downloadLink = null) + { + SystemType = type; + Name = name; + Version = version; + Author = author; + DownloadLink = downloadLink; + } + internal MelonInfoAttribute Convert() => new MelonInfoAttribute(SystemType, Name, Version, Author, DownloadLink); + } + [Obsolete("MelonPluginGame is obsolete. Please use MelonGame instead.")] + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public class MelonPluginGameAttribute : Attribute + { + public string Developer { get; } + public string GameName { get; } + public MelonPluginGameAttribute(string developer = null, string gameName = null) + { + Developer = developer; + GameName = gameName; + } + public MelonGameAttribute Convert() => new MelonGameAttribute(Developer, GameName); + } + [Obsolete("MelonPluginInfo is obsolete. Please use MelonInfo instead.")] + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] + public class MelonPluginInfoAttribute : Attribute + { + public Type SystemType { get; } + public string Name { get; } + public string Version { get; } + public string Author { get; } + public string DownloadLink { get; } + + public MelonPluginInfoAttribute(Type type, string name, string version, string author, string downloadLink = null) + { + SystemType = type; + Name = name; + Version = version; + Author = author; + DownloadLink = downloadLink; + } + public MelonInfoAttribute Convert() => new MelonInfoAttribute(SystemType, Name, Version, Author, DownloadLink); + } + [Obsolete("MelonModLogger is obsolete. Please use MelonLogger instead.")] + public class MelonModLogger : MelonLogger {} + [Obsolete("ModPrefs is obsolete. Please use MelonPrefs instead.")] + public class ModPrefs : MelonPrefs + { + public static Dictionary> GetPrefs() + { + Dictionary> output = new Dictionary>(); + Dictionary> prefs = GetPreferences(); + for (int i = 0; i < prefs.Values.Count; i++) + { + Dictionary prefsdict = prefs.Values.ElementAt(i); + Dictionary newprefsdict = new Dictionary(); + for (int j = 0; j < prefsdict.Values.Count; j++) + { + MelonPreference pref = prefsdict.Values.ElementAt(j); + PrefDesc newpref = new PrefDesc(pref.Value, (PrefType)pref.Type, pref.Hidden, pref.DisplayText); + newpref.ValueEdited = pref.ValueEdited; + newprefsdict.Add(prefsdict.Keys.ElementAt(j), newpref); + } + output.Add(prefs.Keys.ElementAt(i), newprefsdict); + } + return output; + } + public static void RegisterPrefString(string section, string name, string defaultValue, string displayText = null, bool hideFromList = false) => RegisterString(section, name, defaultValue, displayText, hideFromList); + public static void RegisterPrefBool(string section, string name, bool defaultValue, string displayText = null, bool hideFromList = false) => RegisterBool(section, name, defaultValue, displayText, hideFromList); + public static void RegisterPrefInt(string section, string name, int defaultValue, string displayText = null, bool hideFromList = false) => RegisterInt(section, name, defaultValue, displayText, hideFromList); + public static void RegisterPrefFloat(string section, string name, float defaultValue, string displayText = null, bool hideFromList = false) => RegisterFloat(section, name, defaultValue, displayText, hideFromList); + public enum PrefType + { + STRING, + BOOL, + INT, + FLOAT + } + public class PrefDesc : MelonPreference + { + public PrefType Type { get => (PrefType)base.Type; } + public PrefDesc(string value, PrefType type, bool hidden, string displayText) : base(value, type, hidden, displayText) + { + Value = value; + ValueEdited = value; + base.Type = (MelonPreferenceType)type; + Hidden = hidden; + DisplayText = displayText; + } + } + } + } \ No newline at end of file diff --git a/BepInEx.MelonLoader.Loader/MelonLoader/Harmony/Attributes.cs b/BepInEx.MelonLoader.Loader/MelonLoader/Harmony/Attributes.cs new file mode 100644 index 0000000..14e4f51 --- /dev/null +++ b/BepInEx.MelonLoader.Loader/MelonLoader/Harmony/Attributes.cs @@ -0,0 +1,325 @@ +using System; +using System.Collections.Generic; + +namespace Harmony +{ + public enum MethodType + { + Normal, + Getter, + Setter, + Constructor, + StaticConstructor + } + + [Obsolete("This enum will be removed in the next major version. To define special methods, use MethodType")] + public enum PropertyMethod + { + Getter, + Setter + } + + public enum ArgumentType + { + Normal, + Ref, + Out, + Pointer + } + + public enum HarmonyPatchType + { + All, + Prefix, + Postfix, + Transpiler + } + + public class HarmonyAttribute : Attribute + { + public HarmonyMethod info = new HarmonyMethod(); + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] + public class HarmonyPatch : HarmonyAttribute + { + // no argument (for use with TargetMethod) + + public HarmonyPatch() + { + } + + // starting with 'Type' + + public HarmonyPatch(Type declaringType) + { + info.declaringType = declaringType; + } + + public HarmonyPatch(Type declaringType, Type[] argumentTypes) + { + info.declaringType = declaringType; + info.argumentTypes = argumentTypes; + } + + public HarmonyPatch(Type declaringType, string methodName) + { + info.declaringType = declaringType; + info.methodName = methodName; + } + + public HarmonyPatch(Type declaringType, string methodName, params Type[] argumentTypes) + { + info.declaringType = declaringType; + info.methodName = methodName; + info.argumentTypes = argumentTypes; + } + + public HarmonyPatch(Type declaringType, string methodName, Type[] argumentTypes, ArgumentType[] argumentVariations) + { + info.declaringType = declaringType; + info.methodName = methodName; + ParseSpecialArguments(argumentTypes, argumentVariations); + } + + public HarmonyPatch(Type declaringType, MethodType methodType) + { + info.declaringType = declaringType; + info.methodType = methodType; + } + + public HarmonyPatch(Type declaringType, MethodType methodType, params Type[] argumentTypes) + { + info.declaringType = declaringType; + info.methodType = methodType; + info.argumentTypes = argumentTypes; + } + + public HarmonyPatch(Type declaringType, MethodType methodType, Type[] argumentTypes, ArgumentType[] argumentVariations) + { + info.declaringType = declaringType; + info.methodType = methodType; + ParseSpecialArguments(argumentTypes, argumentVariations); + } + + public HarmonyPatch(Type declaringType, string propertyName, MethodType methodType) + { + info.declaringType = declaringType; + info.methodName = propertyName; + info.methodType = methodType; + } + + // starting with 'string' + + public HarmonyPatch(string methodName) + { + info.methodName = methodName; + } + + public HarmonyPatch(string methodName, params Type[] argumentTypes) + { + info.methodName = methodName; + info.argumentTypes = argumentTypes; + } + + public HarmonyPatch(string methodName, Type[] argumentTypes, ArgumentType[] argumentVariations) + { + info.methodName = methodName; + ParseSpecialArguments(argumentTypes, argumentVariations); + } + + public HarmonyPatch(string propertyName, MethodType methodType) + { + info.methodName = propertyName; + info.methodType = methodType; + } + + // starting with 'MethodType' + + public HarmonyPatch(MethodType methodType) + { + info.methodType = methodType; + } + + public HarmonyPatch(MethodType methodType, params Type[] argumentTypes) + { + info.methodType = methodType; + info.argumentTypes = argumentTypes; + } + + public HarmonyPatch(MethodType methodType, Type[] argumentTypes, ArgumentType[] argumentVariations) + { + info.methodType = methodType; + ParseSpecialArguments(argumentTypes, argumentVariations); + } + + // starting with 'Type[]' + + public HarmonyPatch(Type[] argumentTypes) + { + info.argumentTypes = argumentTypes; + } + + public HarmonyPatch(Type[] argumentTypes, ArgumentType[] argumentVariations) + { + ParseSpecialArguments(argumentTypes, argumentVariations); + } + + // Obsolete attributes + + [Obsolete("This attribute will be removed in the next major version. Use HarmonyPatch together with MethodType.Getter or MethodType.Setter instead")] + public HarmonyPatch(string propertyName, PropertyMethod type) + { + info.methodName = propertyName; + info.methodType = type == PropertyMethod.Getter ? MethodType.Getter : MethodType.Setter; + } + + // + + private void ParseSpecialArguments(Type[] argumentTypes, ArgumentType[] argumentVariations) + { + if (argumentVariations == null || argumentVariations.Length == 0) + { + info.argumentTypes = argumentTypes; + return; + } + + if (argumentTypes.Length < argumentVariations.Length) + throw new ArgumentException("argumentVariations contains more elements than argumentTypes", nameof(argumentVariations)); + + var types = new List(); + for (var i = 0; i < argumentTypes.Length; i++) + { + var type = argumentTypes[i]; + switch (argumentVariations[i]) + { + case ArgumentType.Ref: + case ArgumentType.Out: + type = type.MakeByRefType(); + break; + case ArgumentType.Pointer: + type = type.MakePointerType(); + break; + } + types.Add(type); + } + info.argumentTypes = types.ToArray(); + } + } + + [AttributeUsage(AttributeTargets.Class)] + public class HarmonyPatchAll : HarmonyAttribute + { + public HarmonyPatchAll() + { + } + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class HarmonyPriority : HarmonyAttribute + { + public HarmonyPriority(int prioritiy) + { + info.prioritiy = prioritiy; + } + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class HarmonyBefore : HarmonyAttribute + { + public HarmonyBefore(params string[] before) + { + info.before = before; + } + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class HarmonyAfter : HarmonyAttribute + { + public HarmonyAfter(params string[] after) + { + info.after = after; + } + } + + // If you don't want to use the special method names you can annotate + // using the following attributes: + + [AttributeUsage(AttributeTargets.Method)] + public class HarmonyPrepare : Attribute + { + } + + [AttributeUsage(AttributeTargets.Method)] + public class HarmonyCleanup : Attribute + { + } + + [AttributeUsage(AttributeTargets.Method)] + public class HarmonyTargetMethod : Attribute + { + } + + [AttributeUsage(AttributeTargets.Method)] + public class HarmonyTargetMethods : Attribute + { + } + + [AttributeUsage(AttributeTargets.Method)] + public class HarmonyPrefix : Attribute + { + } + + [AttributeUsage(AttributeTargets.Method)] + public class HarmonyPostfix : Attribute + { + } + + [AttributeUsage(AttributeTargets.Method)] + public class HarmonyTranspiler : Attribute + { + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] + public class HarmonyArgument : Attribute + { + public string OriginalName { get; private set; } + public int Index { get; private set; } + public string NewName { get; private set; } + + public HarmonyArgument(string originalName) : this(originalName, null) + { + } + + public HarmonyArgument(int index) : this(index, null) + { + } + + public HarmonyArgument(string originalName, string newName) + { + OriginalName = originalName; + Index = -1; + NewName = newName; + } + + public HarmonyArgument(int index, string name) + { + OriginalName = null; + Index = index; + NewName = name; + } + } + + // This attribute is for Harmony patching itself to the latest + // + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor)] + internal class UpgradeToLatestVersion : Attribute + { + public int version; + + public UpgradeToLatestVersion(int version) + { + this.version = version; + } + } +} \ No newline at end of file diff --git a/BepInEx.MelonLoader.Loader/MelonLoader/Harmony/CodeInstruction.cs b/BepInEx.MelonLoader.Loader/MelonLoader/Harmony/CodeInstruction.cs new file mode 100644 index 0000000..a6d8972 --- /dev/null +++ b/BepInEx.MelonLoader.Loader/MelonLoader/Harmony/CodeInstruction.cs @@ -0,0 +1,62 @@ +using Harmony.ILCopying; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Emit; + +namespace Harmony +{ + public class CodeInstruction + { + public OpCode opcode; + public object operand; + public List