Skip to content

Instantly share code, notes, and snippets.

@pardeike
Last active May 31, 2021 18:36
Show Gist options
  • Save pardeike/ef35eaa4cf297e478dee005d9ace3d50 to your computer and use it in GitHub Desktop.
Save pardeike/ef35eaa4cf297e478dee005d9ace3d50 to your computer and use it in GitHub Desktop.
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