191 lines
5.7 KiB
C#
191 lines
5.7 KiB
C#
using Harmony.Tools;
|
|
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;
|
|
SelfPatching.PatchOldHarmonyMethods();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|