123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- using System;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Diagnostics;
- using System.Linq;
- using System.Reflection;
- using System.Reflection.Emit;
- namespace Harmony
- {
- public class Patches
- {
- public readonly ReadOnlyCollection<Patch> Prefixes;
- public readonly ReadOnlyCollection<Patch> Postfixes;
- public readonly ReadOnlyCollection<Patch> Transpilers;
- public ReadOnlyCollection<string> Owners
- {
- get
- {
- var result = new HashSet<string>();
- result.UnionWith(Prefixes.Select(p => p.owner));
- result.UnionWith(Postfixes.Select(p => p.owner));
- result.UnionWith(Transpilers.Select(p => p.owner));
- return result.ToList().AsReadOnly();
- }
- }
- public Patches(Patch[] prefixes, Patch[] postfixes, Patch[] transpilers)
- {
- if (prefixes == null) prefixes = new Patch[0];
- if (postfixes == null) postfixes = new Patch[0];
- if (transpilers == null) transpilers = new Patch[0];
- Prefixes = prefixes.ToList().AsReadOnly();
- Postfixes = postfixes.ToList().AsReadOnly();
- Transpilers = transpilers.ToList().AsReadOnly();
- }
- }
- public class HarmonyInstance
- {
- readonly string id;
- public string Id => id;
- public static bool DEBUG = false;
- private static bool selfPatchingDone = false;
- HarmonyInstance(string id)
- {
- if (DEBUG)
- {
- var assembly = typeof(HarmonyInstance).Assembly;
- var version = assembly.GetName().Version;
- var location = assembly.Location;
- if (location == null || location == "") location = new Uri(assembly.CodeBase).LocalPath;
- FileLog.Log("### Harmony id=" + id + ", version=" + version + ", location=" + location);
- var callingMethod = GetOutsideCaller();
- var callingAssembly = callingMethod.DeclaringType.Assembly;
- location = callingAssembly.Location;
- if (location == null || location == "") location = new Uri(callingAssembly.CodeBase).LocalPath;
- FileLog.Log("### Started from " + callingMethod.FullDescription() + ", location " + location);
- FileLog.Log("### At " + DateTime.Now.ToString("yyyy-MM-dd hh.mm.ss"));
- }
- this.id = id;
- if (!selfPatchingDone)
- {
- selfPatchingDone = true;
- }
- }
- public static HarmonyInstance Create(string id)
- {
- if (id == null) throw new Exception("id cannot be null");
- return new HarmonyInstance(id);
- }
- private MethodBase GetOutsideCaller()
- {
- var trace = new StackTrace(true);
- foreach (var frame in trace.GetFrames())
- {
- var method = frame.GetMethod();
- if (method.DeclaringType.Namespace != typeof(HarmonyInstance).Namespace)
- return method;
- }
- throw new Exception("Unexpected end of stack trace");
- }
- //
- public void PatchAll()
- {
- var method = new StackTrace().GetFrame(1).GetMethod();
- var assembly = method.ReflectedType.Assembly;
- PatchAll(assembly);
- }
- public void PatchAll(Assembly assembly)
- {
- assembly.GetTypes().Do(type =>
- {
- var parentMethodInfos = type.GetHarmonyMethods();
- if (parentMethodInfos != null && parentMethodInfos.Count() > 0)
- {
- var info = HarmonyMethod.Merge(parentMethodInfos);
- var processor = new PatchProcessor(this, type, info);
- processor.Patch();
- }
- });
- }
- public DynamicMethod Patch(MethodBase original, HarmonyMethod prefix = null, HarmonyMethod postfix = null, HarmonyMethod transpiler = null)
- {
- var processor = new PatchProcessor(this, new List<MethodBase> { original }, prefix, postfix, transpiler);
- return processor.Patch().FirstOrDefault();
- }
- public void UnpatchAll(string harmonyID = null)
- {
- bool IDCheck(Patch patchInfo) => harmonyID == null || patchInfo.owner == harmonyID;
- var originals = GetPatchedMethods().ToList();
- foreach (var original in originals)
- {
- var info = GetPatchInfo(original);
- info.Prefixes.DoIf(IDCheck, patchInfo => Unpatch(original, patchInfo.patch));
- info.Postfixes.DoIf(IDCheck, patchInfo => Unpatch(original, patchInfo.patch));
- info.Transpilers.DoIf(IDCheck, patchInfo => Unpatch(original, patchInfo.patch));
- }
- }
- public void Unpatch(MethodBase original, HarmonyPatchType type, string harmonyID = null)
- {
- var processor = new PatchProcessor(this, new List<MethodBase> { original });
- processor.Unpatch(type, harmonyID);
- }
- public void Unpatch(MethodBase original, MethodInfo patch)
- {
- var processor = new PatchProcessor(this, new List<MethodBase> { original });
- processor.Unpatch(patch);
- }
- //
- public bool HasAnyPatches(string harmonyID)
- {
- return GetPatchedMethods()
- .Select(original => GetPatchInfo(original))
- .Any(info => info.Owners.Contains(harmonyID));
- }
- public Patches GetPatchInfo(MethodBase method)
- {
- return PatchProcessor.GetPatchInfo(method);
- }
- public IEnumerable<MethodBase> GetPatchedMethods()
- {
- return HarmonySharedState.GetPatchedMethods();
- }
- public Dictionary<string, Version> VersionInfo(out Version currentVersion)
- {
- currentVersion = typeof(HarmonyInstance).Assembly.GetName().Version;
- var assemblies = new Dictionary<string, Assembly>();
- GetPatchedMethods().Do(method =>
- {
- var info = HarmonySharedState.GetPatchInfo(method);
- info.prefixes.Do(fix => assemblies[fix.owner] = fix.patch.DeclaringType.Assembly);
- info.postfixes.Do(fix => assemblies[fix.owner] = fix.patch.DeclaringType.Assembly);
- info.transpilers.Do(fix => assemblies[fix.owner] = fix.patch.DeclaringType.Assembly);
- });
- var result = new Dictionary<string, Version>();
- assemblies.Do(info =>
- {
- var assemblyName = info.Value.GetReferencedAssemblies().FirstOrDefault(a => a.FullName.StartsWith("0Harmony, Version"));
- if (assemblyName != null)
- result[info.Key] = assemblyName.Version;
- });
- return result;
- }
- }
- }
|