123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- using System;
- using System.Linq;
- using System.Reflection;
- using Mono.Cecil;
- using Mono.Cecil.Cil;
- namespace NeighPatcher
- {
- [AttributeUsage(AttributeTargets.Method)]
- internal class NeighPatchAttribute : Attribute
- {
- public string[] ArgTypes;
- public string AssemblyName;
- public string MethodName;
- public string TypeName;
- public NeighPatchAttribute(string assemblyName, string typeName, string methodName, params string[] argTypes)
- {
- AssemblyName = assemblyName;
- TypeName = typeName;
- MethodName = methodName;
- ArgTypes = argTypes;
- }
- }
- internal static class NeighPatcher
- {
- public static void PatchAll(AssemblyDefinition targetAssembly, string patchTypeName)
- {
- var hookAd = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location);
- var patchType = hookAd.MainModule.GetType(patchTypeName);
- Console.WriteLine($"[NeighPatcher] Patching {targetAssembly.Name.Name} with {patchType.FullName}");
- foreach (var methodDefinition in patchType.Methods.Where(m => m.HasCustomAttributes))
- {
- var patchAttributes = methodDefinition
- .CustomAttributes
- .Where(c => c.HasConstructorArguments &&
- c.Constructor.DeclaringType.Name == nameof(NeighPatchAttribute))
- .ToList();
- if (patchAttributes.Count == 0)
- continue;
- Console.WriteLine(
- $"[NeighPatcher] Applying {methodDefinition.FullName} with {patchAttributes.Count} patch attributes");
- var patchAttrs = patchAttributes.Select(p => new NeighPatchAttribute(
- p.ConstructorArguments[0].Value as string,
- p.ConstructorArguments[1].Value as string,
- p.ConstructorArguments[2].Value as string,
- ((CustomAttributeArgument[])p.ConstructorArguments[3].Value)
- .Select(c => c.Value as string).ToArray())).ToList();
- foreach (var neighPatchAttribute in patchAttrs)
- {
- Console.WriteLine(
- $"[NeighPatcher] Patching [{neighPatchAttribute.AssemblyName}]{neighPatchAttribute.TypeName}::{neighPatchAttribute.MethodName} with {neighPatchAttribute.ArgTypes.Length} arg types");
- if (targetAssembly.Name.Name != neighPatchAttribute.AssemblyName)
- continue;
- var t = targetAssembly.MainModule.GetType(neighPatchAttribute.TypeName);
- Console.WriteLine($"[NeighPatcher] Found type {t}");
- var method = t?.Methods.FirstOrDefault(m => m.Name == neighPatchAttribute.MethodName &&
- (neighPatchAttribute.ArgTypes.Length == 0 ||
- neighPatchAttribute.ArgTypes.Length != 0 &&
- neighPatchAttribute.ArgTypes.Length ==
- m.Parameters.Count &&
- m.Parameters.Select(p => p.ParameterType.FullName)
- .SequenceEqual(neighPatchAttribute.ArgTypes)));
- Console.WriteLine($"[NeighPatcher] Found method {method}");
- if (method == null)
- continue;
- var il = method.Body.GetILProcessor();
- var ins = method.Body.Instructions.First();
- foreach (var mParam in methodDefinition.Parameters)
- if (mParam.Name == "__instance")
- il.InsertBefore(ins, il.Create(OpCodes.Ldarg_0));
- else if (mParam.Name.StartsWith("___"))
- {
- string realName = mParam.Name.Substring(3);
- var targetField = t.Fields.FirstOrDefault(ff => ff.Name == realName);
- if (targetField == null)
- throw new Exception($"Field {realName} not found from type {t.FullName}!");
- if (!method.IsStatic)
- {
- il.InsertBefore(ins, il.Create(OpCodes.Ldarg_0));
- il.InsertBefore(ins, il.Create(OpCodes.Ldflda, targetField));
- }
- else
- il.InsertBefore(ins, il.Create(OpCodes.Ldsflda, targetField));
- }
- else
- {
- var pIndex = method.Parameters.FirstOrDefault(p => p.Name == mParam.Name);
- if (pIndex == null)
- throw new Exception(
- $"Parameter with name {mParam.Name} does not exist in {method.FullName}");
- il.InsertBefore(ins, il.Create(OpCodes.Ldarg, pIndex));
- }
- il.InsertBefore(
- ins, il.Create(OpCodes.Call, targetAssembly.MainModule.ImportReference(methodDefinition)));
- }
- }
- }
- }
- }
|