diff options
Diffstat (limited to 'StardewInjector/CecilUtils.cs')
-rw-r--r-- | StardewInjector/CecilUtils.cs | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/StardewInjector/CecilUtils.cs b/StardewInjector/CecilUtils.cs new file mode 100644 index 00000000..acdf5198 --- /dev/null +++ b/StardewInjector/CecilUtils.cs @@ -0,0 +1,173 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace StardewInjector +{ + public struct ScannerState + { + public ILProcessor ILProcessor; + public Instruction Instruction; + + public ScannerState(ILProcessor ilProc, Instruction ins) + { + ILProcessor = ilProc; + Instruction = ins; + } + + public ScannerState Previous(Func<Instruction, bool> until = null) + { + if (until != null) + { + Instruction cur = this.Instruction; + do + { + cur = cur.Previous; + } while (!until(cur)); + return new ScannerState(this.ILProcessor, cur); + } + return new ScannerState(this.ILProcessor, Instruction.Previous); + } + + public ScannerState Next(Func<Instruction, bool> until = null) + { + if (until != null) + { + Instruction cur = this.Instruction; + do + { + cur = cur.Next; + } while (!until(cur)); + return new ScannerState(this.ILProcessor, cur); + } + return new ScannerState(this.ILProcessor, Instruction.Next); + } + + public ScannerState Last() + { + var instructions = this.ILProcessor.Body.Instructions; + return new ScannerState(this.ILProcessor, instructions[instructions.Count - 1]); + } + + public ScannerState First() + { + var instructions = this.ILProcessor.Body.Instructions; + return new ScannerState(this.ILProcessor, instructions[0]); + } + + public ScannerState ReplaceCreate(OpCode opcode) + { + Instruction ins = this.ILProcessor.Create(opcode); + this.ILProcessor.Replace(this.Instruction, ins); + return new ScannerState(this.ILProcessor, ins); + } + + public ScannerState ReplaceCreate(OpCode opcode, object arg) + { + Instruction ins = this.ILProcessor.Create(opcode, arg as dynamic); + this.ILProcessor.Replace(this.Instruction, ins); + return new ScannerState(this.ILProcessor, ins); + } + + public ScannerState CreateBefore(OpCode opcode) + { + Instruction ins = this.ILProcessor.Create(opcode); + this.ILProcessor.InsertBefore(this.Instruction, ins); + return new ScannerState(this.ILProcessor, ins); + } + + public ScannerState CreateBefore(OpCode opcode, object arg) + { + Instruction ins = this.ILProcessor.Create(opcode, arg as dynamic); + this.ILProcessor.InsertBefore(this.Instruction, ins); + return new ScannerState(this.ILProcessor, ins); + } + + public ScannerState CreateAfter(OpCode opcode) + { + Instruction ins = this.ILProcessor.Create(opcode); + this.ILProcessor.InsertAfter(this.Instruction, ins); + return new ScannerState(this.ILProcessor, ins); + } + + public ScannerState CreateAfter(OpCode opcode, object arg) + { + Instruction ins = this.ILProcessor.Create(opcode, arg as dynamic); + this.ILProcessor.InsertAfter(this.Instruction, ins); + return new ScannerState(this.ILProcessor, ins); + } + } + + public static class CecilUtils + { + public static ScannerState Scanner(this MethodDefinition me) + { + return new ScannerState(me.Body.GetILProcessor(), me.Body.Instructions[0]); + } + + public static ScannerState FindSetField(this MethodDefinition me, string fieldName) + { + var instruction = me.Body.Instructions + .FirstOrDefault(i => i.OpCode == OpCodes.Stsfld && (i.Operand as FieldDefinition).Name == fieldName); + return new ScannerState(me.Body.GetILProcessor(), instruction); + } + + public static ScannerState FindLoadField(this MethodDefinition me, string fieldName) + { + var instruction = me.Body.Instructions + .FirstOrDefault(i => { + if (i.OpCode != OpCodes.Ldfld && i.OpCode != OpCodes.Ldsfld) + return false; + if (i.Operand is FieldDefinition && (i.Operand as FieldDefinition).Name == fieldName) + return true; + if (i.Operand is FieldReference && (i.Operand as FieldReference).Name == fieldName) + return true; + return false; + }); + return new ScannerState(me.Body.GetILProcessor(), instruction); + } + + public static ScannerState FindLoadConstant(this MethodDefinition me, int val) + { + var instruction = me.Body.Instructions + .FirstOrDefault(i => i.OpCode == OpCodes.Ldc_I4 && (int)i.Operand == val); + return new ScannerState(me.Body.GetILProcessor(), instruction); + } + + public static ScannerState FindLoadConstant(this MethodDefinition me, float val) + { + var instruction = me.Body.Instructions + .FirstOrDefault(i => i.OpCode == OpCodes.Ldc_R4 && (float)i.Operand == val); + return new ScannerState(me.Body.GetILProcessor(), instruction); + } + + public static MethodDefinition FindMethod(this ModuleDefinition me, string name) + { + var nameSplit = name.Split(new string[] { "::" }, StringSplitOptions.RemoveEmptyEntries); + if (nameSplit.Length < 2) + throw new ArgumentException("Invalid method full name", "name"); + + var currentType = me.Types.FirstOrDefault(t => t.FullName == nameSplit[0]); + if (currentType == null) + return null; + + return currentType.Methods.FirstOrDefault(m => m.Name == nameSplit[1]); + } + + public static FieldDefinition FindField(this ModuleDefinition me, string name) + { + var nameSplit = name.Split(new string[] { "::" }, StringSplitOptions.RemoveEmptyEntries); + if (nameSplit.Length < 2) + throw new ArgumentException("Invalid field full name", "name"); + + var currentType = me.Types.FirstOrDefault(t => t.FullName == nameSplit[0]); + if (currentType == null) + return null; + + return currentType.Fields.FirstOrDefault(m => m.Name == nameSplit[1]); + } + } +} |