Last active
May 31, 2021 18:36
-
-
Save pardeike/ef35eaa4cf297e478dee005d9ace3d50 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
Patches.FieldReplace(typeof(TargetClass), "foo"); | |
// Test code | |
var tc = new TargetClass(); | |
tc.Test(); | |
tc.Test(); | |
} | |
static class Patches | |
{ | |
public static Harmony harmony = new Harmony("test"); | |
public static Dictionary<Type, string> fields = new Dictionary<Type, string>(); | |
public static MethodInfo transpiler = SymbolExtensions.GetMethodInfo(() => Patches.Transpiler(default)); | |
public static MethodInfo constructorPostfix = SymbolExtensions.GetMethodInfo(() => Patches.ConstructorPostfix(default)); | |
public static void FieldReplace(Type type, string fieldName) | |
{ | |
fields[type] = fieldName; | |
AccessTools.GetDeclaredMethods(type).Do(method => harmony.Patch(method, transpiler: new HarmonyMethod(transpiler))); | |
harmony.Patch(AccessTools.Constructor(type, new Type[0]), postfix: new HarmonyMethod(constructorPostfix)); | |
} | |
public static void ConstructorPostfix(object __instance) | |
{ | |
Traverse.Create(__instance).Field(fields[__instance.GetType()]).SetValue(new MyList<Foo>()); | |
} | |
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) | |
{ | |
var add1 = AccessTools.Method(typeof(List<Foo>), "Add"); | |
var add2 = SymbolExtensions.GetMethodInfo(()=> Add(default, default)); | |
var count1 = AccessTools.PropertyGetter(typeof(List<Foo>), "Count"); | |
var count2 = SymbolExtensions.GetMethodInfo(() => Count(default)); | |
foreach(var instruction in instructions) | |
{ | |
if (instruction.operand is MethodInfo m1 && m1 == add1) | |
{ | |
Console.WriteLine($"Call to {instruction.operand} replaced"); | |
instruction.opcode = OpCodes.Call; | |
instruction.operand = add2; | |
} | |
if (instruction.operand is MethodInfo m2 && m2 == count1) | |
{ | |
Console.WriteLine($"Call to {instruction.operand} replaced"); | |
instruction.opcode = OpCodes.Call; | |
instruction.operand = count2; | |
} | |
yield return instruction; | |
} | |
} | |
public static void Add(IList list, Foo f) | |
{ | |
Console.WriteLine($"Add {f} to {list}"); | |
list.Add(f); | |
} | |
public static int Count(IList list) | |
{ | |
Console.WriteLine($"Count {list}"); | |
return list.Count; | |
} | |
} | |
public class MyList<Foo> : List<Foo> | |
{ | |
} | |
// | |
public class Foo { } | |
public class TargetClass | |
{ | |
public List<Foo> foo = new List<Foo>(); | |
public void Test() | |
{ | |
foo.Add(new Foo()); | |
Console.WriteLine("foo #" + foo.Count); | |
} | |
} | |
// Prints: | |
/* | |
Call to Void Add(Foo) replaced | |
Call to Int32 get_Count() replaced | |
Add UserQuery+Foo to UserQuery+MyList`1[UserQuery+Foo] | |
Count UserQuery+MyList`1[UserQuery+Foo] | |
foo #1 | |
Add UserQuery+Foo to UserQuery+MyList`1[UserQuery+Foo] | |
Count UserQuery+MyList`1[UserQuery+Foo] | |
foo #2 | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment