Created
July 26, 2019 21:51
-
-
Save pardeike/3121b63859e5cf526bb191302bd21c06 to your computer and use it in GitHub Desktop.
Transpiler Helper for Harmony
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
public class MultiPatchInfo | |
{ | |
public MethodBase original; | |
public MethodInfo replaceFrom; | |
public MethodInfo replaceTo; | |
public Func<IEnumerable<CodeInstruction>, IEnumerable<CodeInstruction>> argumentCodes; | |
public MultiPatchInfo(MethodBase original, MethodInfo replaceFrom, MethodInfo replaceTo, params CodeInstruction[] instructions) | |
{ | |
this.original = original; | |
this.replaceFrom = replaceFrom; | |
this.replaceTo = replaceTo; | |
argumentCodes = (_) => instructions.AsEnumerable(); | |
} | |
public MultiPatchInfo(MethodBase original, MethodInfo replaceFrom, MethodInfo replaceTo, Func<IEnumerable<CodeInstruction>, IEnumerable<CodeInstruction>> argumentCodes) | |
{ | |
this.original = original; | |
this.replaceFrom = replaceFrom; | |
this.replaceTo = replaceTo; | |
this.argumentCodes = argumentCodes; | |
} | |
} | |
public class MultiPatches | |
{ | |
readonly Type patchClass; | |
readonly MultiPatchInfo[] patchInfos; | |
public MultiPatches(Type patchClass, params MultiPatchInfo[] patchInfos) | |
{ | |
this.patchClass = patchClass; | |
this.patchInfos = patchInfos; | |
} | |
public IEnumerable<MethodBase> TargetMethods() | |
{ | |
var i = 1; | |
var originals = new HashSet<MethodBase>(); | |
foreach (var patchInfo in patchInfos) | |
{ | |
if (patchInfo.original == null) | |
Log.Error($"In {patchClass.FullName} original #{i} was not defined"); | |
if (patchInfo.replaceFrom == null || patchInfo.replaceFrom.IsStatic == false) | |
Log.Error($"In {patchClass.FullName} replaceFrom #{i} was not defined"); | |
if (patchInfo.replaceTo == null || patchInfo.replaceTo.IsStatic == false) | |
Log.Error($"In {patchClass.FullName} replaceTo #{i} was not defined"); | |
if (patchInfo.original != null && originals.Contains(patchInfo.original) == false) | |
{ | |
originals.Add(patchInfo.original); | |
yield return patchInfo.original; | |
} | |
i++; | |
} | |
} | |
public IEnumerable<CodeInstruction> Transpile(MethodBase original, IEnumerable<CodeInstruction> instructions) | |
{ | |
var codes = instructions.ToList(); | |
var multiPatches = patchInfos.Where(info => info.original == original); | |
foreach (var multiPatch in multiPatches) | |
{ | |
if (multiPatch.replaceFrom == multiPatch.replaceTo) | |
Log.Error($"Replacement methods are the same in {original.FullDescription()}"); | |
var fromTypes = multiPatch.replaceFrom.GetParameters().Types(); | |
var toTypes = multiPatch.replaceTo.GetParameters().Types(); | |
if (toTypes.Length < fromTypes.Length) | |
{ | |
var info = $"{multiPatch.replaceFrom.FullDescription()}/{multiPatch.replaceTo.FullDescription()}"; | |
Log.Error($"Replacement methods have mismatching arguments ({info}) in {original.FullDescription()}"); | |
} | |
for (var i = 0; i < fromTypes.Length; i++) | |
if (fromTypes[i] != toTypes[i]) | |
Log.Error($"Replacement methods have mismatching argument #{i + 1} (should be {fromTypes[i]}) in {original.FullDescription()}"); | |
var found = false; | |
for (var i = 0; i < codes.Count; i++) | |
{ | |
var code = codes[i]; | |
if (code.opcode == OpCodes.Call || code.opcode == OpCodes.Callvirt) | |
if (code.operand == multiPatch.replaceFrom) | |
{ | |
var argumentCodes = multiPatch.argumentCodes(instructions).ToList(); | |
codes.InsertRange(i, argumentCodes); | |
i += argumentCodes.Count; | |
code.operand = multiPatch.replaceTo; | |
found = true; | |
} | |
} | |
if (found == false) | |
Log.Error($"Cannot find instruction CALL {multiPatch.replaceFrom.FullDescription()} in {original.FullDescription()}"); | |
} | |
return codes.AsEnumerable(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment