diff --git a/Harmony/Public/CodeInstruction.cs b/Harmony/Public/CodeInstruction.cs index 7e782d86..6791235e 100644 --- a/Harmony/Public/CodeInstruction.cs +++ b/Harmony/Public/CodeInstruction.cs @@ -229,6 +229,80 @@ public static CodeInstruction StoreField(Type type, string name) return new CodeInstruction(field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, field); } + // --- LOCALS + + /// Creates a CodeInstruction loading a local with the given index, using the shorter forms when possible + /// The index where the local is stored + /// Use address of local + /// + /// + public static CodeInstruction LoadLocal(int index, bool useAddress = false) + { + if (useAddress) + { + if (index < 256) return new CodeInstruction(OpCodes.Ldloca_S, Convert.ToByte(index)); + else return new CodeInstruction(OpCodes.Ldloca, index); + } + else + { + if (index == 0) return new CodeInstruction(OpCodes.Ldloc_0); + else if (index == 1) return new CodeInstruction(OpCodes.Ldloc_1); + else if (index == 2) return new CodeInstruction(OpCodes.Ldloc_2); + else if (index == 3) return new CodeInstruction(OpCodes.Ldloc_3); + else if (index < 256) return new CodeInstruction(OpCodes.Ldloc_S, Convert.ToByte(index)); + else return new CodeInstruction(OpCodes.Ldloc, index); + } + } + + /// Creates a CodeInstruction storing to a local with the given index, using the shorter forms when possible + /// The index where the local is stored + /// + /// + public static CodeInstruction StoreLocal(int index) + { + if (index == 0) return new CodeInstruction(OpCodes.Stloc_0); + else if (index == 1) return new CodeInstruction(OpCodes.Stloc_1); + else if (index == 2) return new CodeInstruction(OpCodes.Stloc_2); + else if (index == 3) return new CodeInstruction(OpCodes.Stloc_3); + else if (index < 256) return new CodeInstruction(OpCodes.Stloc_S, Convert.ToByte(index)); + else return new CodeInstruction(OpCodes.Stloc, index); + } + + // --- ARGUMENTS + + /// Creates a CodeInstruction loading an argument with the given index, using the shorter forms when possible + /// The index of the argument + /// Use address of argument + /// + /// + public static CodeInstruction LoadArgument(int index, bool useAddress = false) + { + if (useAddress) + { + if (index < 256) return new CodeInstruction(OpCodes.Ldarga_S, Convert.ToByte(index)); + else return new CodeInstruction(OpCodes.Ldarga, index); + } + else + { + if (index == 0) return new CodeInstruction(OpCodes.Ldarg_0); + else if (index == 1) return new CodeInstruction(OpCodes.Ldarg_1); + else if (index == 2) return new CodeInstruction(OpCodes.Ldarg_2); + else if (index == 3) return new CodeInstruction(OpCodes.Ldarg_3); + else if (index < 256) return new CodeInstruction(OpCodes.Ldarg_S, Convert.ToByte(index)); + else return new CodeInstruction(OpCodes.Ldarg, index); + } + } + + /// Creates a CodeInstruction storing to an argument with the given index, using the shorter forms when possible + /// The index of the argument + /// + /// + public static CodeInstruction StoreArgument(int index) + { + if (index < 256) return new CodeInstruction(OpCodes.Starg_S, Convert.ToByte(index)); + else return new CodeInstruction(OpCodes.Starg, index); + } + // --- TOSTRING /// Returns a string representation of the code instruction diff --git a/Harmony/Tools/CodeMatch.cs b/Harmony/Tools/CodeMatch.cs index 364a6932..e15d8974 100644 --- a/Harmony/Tools/CodeMatch.cs +++ b/Harmony/Tools/CodeMatch.cs @@ -125,6 +125,106 @@ internal bool Matches(List codes, CodeInstruction instruction) return true; } + /// Creates a code match for local loads + /// Whether to match for address loads + /// An optional name + /// + public static CodeMatch LoadsLocal(bool useAddress = false, string name = null) + { + CodeMatch match = new CodeMatch(null, null, name); + + if (useAddress) + { + match.opcodes.AddRange(new[] + { + OpCodes.Ldloca_S, + OpCodes.Ldloca + }); + } + else + { + match.opcodes.AddRange(new[] + { + OpCodes.Ldloc_0, + OpCodes.Ldloc_1, + OpCodes.Ldloc_2, + OpCodes.Ldloc_3, + OpCodes.Ldloc_S, + OpCodes.Ldloc + }); + } + + return match; + } + + /// Creates a code match for local stores + /// An optional name + /// + public static CodeMatch StoresLocal(string name = null) + { + CodeMatch match = new CodeMatch(null, null, name); + + match.opcodes.AddRange(new[] + { + OpCodes.Stloc_0, + OpCodes.Stloc_1, + OpCodes.Stloc_2, + OpCodes.Stloc_3, + OpCodes.Stloc_S, + OpCodes.Stloc + }); + + return match; + } + + /// Creates a code match for argument loads + /// Whether to match for address loads + /// An optional name + /// + public static CodeMatch LoadsArgument(bool useAddress = false, string name = null) + { + CodeMatch match = new CodeMatch(null, null, name); + + if (useAddress) + { + match.opcodes.AddRange(new[] + { + OpCodes.Ldarga_S, + OpCodes.Ldarga + }); + } + else + { + match.opcodes.AddRange(new[] + { + OpCodes.Ldarg_0, + OpCodes.Ldarg_1, + OpCodes.Ldarg_2, + OpCodes.Ldarg_3, + OpCodes.Ldarg_S, + OpCodes.Ldarg + }); + } + + return match; + } + + /// Creates a code match for argument stores + /// An optional name + /// + public static CodeMatch StoresArgument(string name = null) + { + CodeMatch match = new CodeMatch(null, null, name); + + match.opcodes.AddRange(new[] + { + OpCodes.Starg_S, + OpCodes.Starg + }); + + return match; + } + /// Returns a string that represents the match /// A string representation /// diff --git a/Harmony/Tools/Extensions.cs b/Harmony/Tools/Extensions.cs index cc092c19..ecdb41ac 100644 --- a/Harmony/Tools/Extensions.cs +++ b/Harmony/Tools/Extensions.cs @@ -480,6 +480,40 @@ public static bool StoresField(this CodeInstruction code, FieldInfo field) return code.opcode == stfldCode && Equals(code.operand, field); } + /// Returns the index targeted by this ldloc, ldloca, or stloc + /// The + /// The index it targets + /// + /// + public static int LocalIndex(this CodeInstruction code) + { + if (code.opcode == OpCodes.Ldloc_0 || code.opcode == OpCodes.Stloc_0) return 0; + else if (code.opcode == OpCodes.Ldloc_1 || code.opcode == OpCodes.Stloc_1) return 1; + else if (code.opcode == OpCodes.Ldloc_2 || code.opcode == OpCodes.Stloc_2) return 2; + else if (code.opcode == OpCodes.Ldloc_3 || code.opcode == OpCodes.Stloc_3) return 3; + else if (code.opcode == OpCodes.Ldloc_S || code.opcode == OpCodes.Ldloc) return Convert.ToInt32(code.operand); + else if (code.opcode == OpCodes.Stloc_S || code.opcode == OpCodes.Stloc) return Convert.ToInt32(code.operand); + else if (code.opcode == OpCodes.Ldloca_S || code.opcode == OpCodes.Ldloca) return Convert.ToInt32(code.operand); + else throw new ArgumentException("Instruction is not a load or store", "code"); + } + + /// Returns the index targeted by this ldarg, ldarga, or starg + /// The + /// The index it targets + /// + /// + public static int ArgumentIndex(this CodeInstruction code) + { + if (code.opcode == OpCodes.Ldarg_0) return 0; + else if (code.opcode == OpCodes.Ldarg_1) return 1; + else if (code.opcode == OpCodes.Ldarg_2) return 2; + else if (code.opcode == OpCodes.Ldarg_3) return 3; + else if (code.opcode == OpCodes.Ldarg_S || code.opcode == OpCodes.Ldarg) return Convert.ToInt32(code.operand); + else if (code.opcode == OpCodes.Starg_S || code.opcode == OpCodes.Starg) return Convert.ToInt32(code.operand); + else if (code.opcode == OpCodes.Ldarga_S || code.opcode == OpCodes.Ldarga) return Convert.ToInt32(code.operand); + else throw new ArgumentException("Instruction is not a load or store", "code"); + } + /// Adds labels to the code instruction and return it /// The /// One or several to add