static void InjectLoadHook() { // only hooking to V2 shouldn't be bad: V1 worlds won't have mod data in them, because 1.3 (and prism) only write as V2 var loadWorld = typeDef_WorldFile.GetMethod("loadWorld"); MethodDef invokeLoadWorld; var loadWorldDel = context.CreateDelegate("Terraria.PrismInjections", "WorldFile_OnLoadWorldDel", typeSys.Void, out invokeLoadWorld, typeSys.Boolean); var onLoadWorld = new FieldDefUser("P_OnLoadWorld", new FieldSig(loadWorldDel.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_WorldFile.Fields.Add(onLoadWorld); OpCode[] toFind = { OpCodes.Br_S, OpCodes.Ldloc_S, OpCodes.Call, // wtf? OpCodes.Stloc_S }; Instruction[] toInject = { Instruction.Create(OpCodes.Ldsfld, onLoadWorld), Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Callvirt, invokeLoadWorld) }; var lwb = loadWorld.Body; using (var lwbproc = lwb.GetILProcessor()) { var instr = lwb.FindInstrSeqStart(toFind).Next(lwbproc).Next(lwbproc).Next(lwbproc).Next(lwbproc); for (int i = 0; i < toInject.Length; i++) lwbproc.InsertBefore(instr, toInject[i]); } }
private static FieldDef CreateField(ModuleDefMD module) { FieldAttributes attrb = FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.HasFieldRVA | FieldAttributes.CompilerControlled; FieldDef field = new FieldDefUser("", new FieldSig(module.CorLibTypes.String), attrb); RenameTask.Rename(field); return field; }
// This will open the current assembly, add a new class and method to it, // and then save the assembly to disk. public static void Run() { // Open the current module ModuleDefMD mod = ModuleDefMD.Load(typeof(Example2).Module); // Create a new public class that derives from System.Object TypeDef type1 = new TypeDefUser("My.Namespace", "MyType", mod.CorLibTypes.Object.TypeDefOrRef); type1.Attributes = TypeAttributes.Public | TypeAttributes.AutoLayout | TypeAttributes.Class | TypeAttributes.AnsiClass; // Make sure to add it to the module or any other type in the module. This is // not a nested type, so add it to mod.Types. mod.Types.Add(type1); // Create a public static System.Int32 field called MyField FieldDef field1 = new FieldDefUser("MyField", new FieldSig(mod.CorLibTypes.Int32), FieldAttributes.Public | FieldAttributes.Static); // Add it to the type we created earlier type1.Fields.Add(field1); // Add a static method that adds both inputs and the static field // and returns the result MethodImplAttributes methImplFlags = MethodImplAttributes.IL | MethodImplAttributes.Managed; MethodAttributes methFlags = MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.ReuseSlot; MethodDef meth1 = new MethodDefUser("MyMethod", MethodSig.CreateStatic(mod.CorLibTypes.Int32, mod.CorLibTypes.Int32, mod.CorLibTypes.Int32), methImplFlags, methFlags); type1.Methods.Add(meth1); // Create the CIL method body CilBody body = new CilBody(); meth1.Body = body; // Name the 1st and 2nd args a and b, respectively meth1.ParamDefs.Add(new ParamDefUser("a", 1)); meth1.ParamDefs.Add(new ParamDefUser("b", 2)); // Create a local. We don't really need it but let's add one anyway Local local1 = new Local(mod.CorLibTypes.Int32); body.Variables.Add(local1); // Add the instructions, and use the useless local body.Instructions.Add(OpCodes.Ldarg_0.ToInstruction()); body.Instructions.Add(OpCodes.Ldarg_1.ToInstruction()); body.Instructions.Add(OpCodes.Add.ToInstruction()); body.Instructions.Add(OpCodes.Ldsfld.ToInstruction(field1)); body.Instructions.Add(OpCodes.Add.ToInstruction()); body.Instructions.Add(OpCodes.Stloc.ToInstruction(local1)); body.Instructions.Add(OpCodes.Ldloc.ToInstruction(local1)); body.Instructions.Add(OpCodes.Ret.ToInstruction()); // Save the assembly to a file on disk mod.Write(@"C:\saved-assembly.dll"); }
public FieldDef create(byte[] data) { var arrayType = getArrayType(data.LongLength); var fieldSig = new FieldSig(new ValueTypeSig(arrayType)); var attrs = FieldAttributes.Assembly | FieldAttributes.Static; var field = new FieldDefUser(string.Format("field_{0}", unique++), fieldSig, attrs); module.UpdateRowId(field); field.HasFieldRVA = true; ourType.Fields.Add(field); var iv = new byte[data.Length]; Array.Copy(data, iv, data.Length); field.InitialValue = iv; return field; }
public static void Execute(ModuleDefMD module) { cctor = module.GlobalType.FindStaticConstructor(); Dictionary<FieldDef, Tuple<byte[], int>> fields = new Dictionary<FieldDef,Tuple<byte[], int>>(); List<byte> data = new List<byte>(); int count = 0; foreach (var method in module.GetTypes().SelectMany(type => type.Methods)) { if (method.HasBody) { List<Instruction> stringInstr = method.Body.Instructions.Where(instr => instr.OpCode == OpCodes.Ldstr).ToList(); for (int i = 0; i < stringInstr.Count; i++) { byte[] stringByte = Encoding.UTF8.GetBytes(stringInstr[i].Operand as string); data.AddRange(stringByte); FieldDef field = CreateField(module); fields.Add(field, Tuple.Create(stringByte, count)); method.DeclaringType.Fields.Add(field); stringInstr[i].OpCode = OpCodes.Ldsfld; stringInstr[i].Operand = field; count++; } } } staticFields = fields; data = Encrypt(data.ToArray()).ToList(); var dataType = new TypeDefUser("", "", module.CorLibTypes.GetTypeRef("System", "ValueType")); RenameTask.Rename(dataType); dataType.Layout = TypeAttributes.ExplicitLayout; dataType.Visibility = TypeAttributes.NestedPrivate; dataType.IsSealed = true; dataType.ClassLayout = new ClassLayoutUser(1, (uint)data.Count); module.GlobalType.NestedTypes.Add(dataType); var dataField = new FieldDefUser("", new FieldSig(dataType.ToTypeSig())) { IsStatic = true, HasFieldRVA = true, InitialValue = data.ToArray(), Access = FieldAttributes.CompilerControlled }; module.GlobalType.Fields.Add(dataField); GlobalDataField = dataField; RenameTask.Rename(dataField); NETUtils.listener.OnWriterEvent += OnWriterEvent; }
static void InjectBuffEffectsCall() { var updateNpc = typeDef_NPC.GetMethod("RealUpdateNPC"); MethodDef invokeEffects; var onBuffEffects = context.CreateDelegate("Terraria.PrismInjections", "NPC_BuffEffectsDel", typeSys.Void, out invokeEffects, typeDef_NPC.ToTypeSig()); var buffEffects = new FieldDefUser("P_OnBuffEffects", new FieldSig(onBuffEffects.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_NPC.Fields.Add(buffEffects); OpCode[] toRem = { OpCodes.Ldarg_0, OpCodes.Ldfld, OpCodes.Brfalse }; var unb = updateNpc.Body; using (var unproc = unb.GetILProcessor()) { Instruction instr; var soulDrain = typeDef_NPC.GetField("soulDrain"); int start = 0; while (true) { instr = unb.FindInstrSeqStart(toRem, start); if (context.SigComparer.Equals((FieldDef)instr.Next(unproc).Operand, soulDrain)) break; else start = unb.Instructions.IndexOf(instr) + 1; } unproc.InsertBefore(instr, Instruction.Create(OpCodes.Ldsfld, buffEffects)); unproc.EmitWrapperCall(invokeEffects, instr); } }
private FieldDef CreateField(RPContext ctx, TypeDef delegateType) { // Details will be filled in during metadata writing TypeDef randomType; do { randomType = ctx.Module.Types[ctx.Random.NextInt32(ctx.Module.Types.Count)]; } while (randomType.HasGenericParameters || randomType.IsGlobalModuleType || randomType.IsDelegate()); TypeSig fieldType = new CModOptSig(randomType, delegateType.ToTypeSig()); var field = new FieldDefUser("", new FieldSig(fieldType), FieldAttributes.Static | FieldAttributes.Assembly); field.CustomAttributes.Add(new CustomAttribute(GetKeyAttr(ctx).FindInstanceConstructors().First())); delegateType.Fields.Add(field); ctx.Marker.Mark(field); ctx.Name.SetCanRename(field, false); return field; }
void InjectData(ModuleDef stubModule, MethodDef method, byte[] data) { var dataType = new TypeDefUser("", "DataType", stubModule.CorLibTypes.GetTypeRef("System", "ValueType")); dataType.Layout = TypeAttributes.ExplicitLayout; dataType.Visibility = TypeAttributes.NestedPrivate; dataType.IsSealed = true; dataType.ClassLayout = new ClassLayoutUser(1, (uint)data.Length); stubModule.GlobalType.NestedTypes.Add(dataType); var dataField = new FieldDefUser("DataField", new FieldSig(dataType.ToTypeSig())) { IsStatic = true, HasFieldRVA = true, InitialValue = data, Access = FieldAttributes.CompilerControlled }; stubModule.GlobalType.Fields.Add(dataField); MutationHelper.ReplacePlaceholder(method, arg => { var repl = new List<Instruction>(); repl.AddRange(arg); repl.Add(Instruction.Create(OpCodes.Dup)); repl.Add(Instruction.Create(OpCodes.Ldtoken, dataField)); repl.Add(Instruction.Create(OpCodes.Call, stubModule.Import( typeof(RuntimeHelpers).GetMethod("InitializeArray")))); return repl.ToArray(); }); }
public static MethodDef ReplaceAndHook(this MethodDef toHook, MethodDef invokeHook, MethodDef realMethod, string fieldName) { //! no delegate type checking is done, runtime errors might happen if it doesn't match exactly //! here be dragons string origName = toHook.Name; var containing = toHook.DeclaringType; // create and add the hook delegate field var hookField = new FieldDefUser(fieldName, new FieldSig(invokeHook.DeclaringType.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); containing.Fields.Add(hookField); // change the hooked method name toHook.Name = "Real" + GetSafeMethodName(toHook.Name); // create a fake method with the original name that calls the hook delegate field var isstatic = (toHook.Attributes & MethodAttributes.Static) != 0; var statAdd = isstatic ? 0 : 1; var newMethod = new MethodDefUser(origName, isstatic ? MethodSig.CreateStatic (toHook.ReturnType, toHook.Parameters.Select(p => p.Type).ToArray()) : MethodSig.CreateInstance(toHook.ReturnType, toHook.Parameters.Skip(1).Select(p => p.Type).ToArray()), toHook.Attributes); for (int i = statAdd; i < toHook.Parameters.Count; i++) { newMethod.Parameters[i].CreateParamDef(); newMethod.Parameters[i].ParamDef.Name = toHook.Parameters[i].Name; newMethod.Parameters[i].ParamDef.Attributes = toHook.Parameters[i].ParamDef.Attributes; newMethod.Parameters[i].ParamDef.Constant = toHook.Parameters[i].ParamDef.Constant; newMethod.Parameters[i].ParamDef.MarshalType = toHook.Parameters[i].ParamDef.MarshalType; foreach (var ca in toHook.Parameters[i].ParamDef.CustomAttributes) newMethod.Parameters[i].ParamDef.CustomAttributes.Add(ca); } //if (<hookField> != null) return <hookField>((this,)? <args>); else return (this.)?<realMethod>(<args>); /* ldsfld class <hookDelegateType> <hookField> brfalse.s VANILLA // if (<hookField> == null) goto VANILLA; ldsfld class <hookDelegateType> <hookField> ldarg.0 // this (if instance) ldarg.1 .. ldarg.n callvirt instance <retval> <hookDelegateType>::Invoke(<args>) ret VANILLA: nop (for ease of adding the params afterwards) ldarg.0 // this (if instance) ldarg.1 .. ldarg.n call instance? <retval> <realMethod>(<args>) ret */ newMethod.Body = new CilBody(); var ins = newMethod.Body.Instructions; using (var p = newMethod.Body.GetILProcessor()) { Instruction VANILLA = OpCodes.Nop.ToInstruction(); p.Emit(OpCodes.Ldsfld, hookField); p.Emit(OpCodes.Brfalse_S, VANILLA); p.Emit(OpCodes.Ldsfld, hookField); //ilproc.EmitWrapperCall(toHook); for (ushort i = 0; i < toHook.Parameters.Count /*- statAdd*/; i++) p.Append(toHook.Parameters.GetLdargOf(i, false)); p.Emit(OpCodes.Callvirt, invokeHook); p.Emit(OpCodes.Ret); p.Append(VANILLA); //ilproc.EmitWrapperCall(realMethod.Resolve()); for (ushort i = 0; i < toHook.Parameters.Count /*- statAdd*/; i++) p.Append(toHook.Parameters.GetLdargOf(i, false)); p.Emit(OpCodes.Call, realMethod); p.Emit(OpCodes.Ret); } newMethod.Body.MaxStack = (ushort)(newMethod.Parameters.Count + 3 & 0xFFFF); toHook.DeclaringType.Methods.Add(newMethod); return newMethod; }
static void InjectMidUpdate() { var update = typeDef_Player.GetMethod("RealUpdate" /* method is wrapped */, MethodFlags.Public | MethodFlags.Instance, typeSys.Int32.ToTypeDefOrRef()); var grabItems = typeDef_Player.GetMethod("GrabItems", MethodFlags.Public | MethodFlags.Instance, typeSys.Int32.ToTypeDefOrRef()); MethodDef invokeMidUpdate; var onMidUpdateDel = context.CreateDelegate("Terraria.PrismInjections", "Player_MidUpdateDel", typeSys.Void, out invokeMidUpdate, typeDef_Player.ToTypeSig(), typeSys.Int32); var onMidUpdate = new FieldDefUser("P_OnMidUpdate", new FieldSig(onMidUpdateDel.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_Player.Fields.Add(onMidUpdate); var ub = update.Body; using (var uproc = ub.GetILProcessor()) { OpCode[] callGrabItems = { OpCodes.Ldarg_0, // this OpCodes.Ldarg_1, // id OpCodes.Call, // Player.GrabItems OpCodes.Ldsfld, // Main.mapFullScreen OpCodes.Brtrue }; Instruction instrs; int startInd = 0; while (true) { instrs = ub.FindInstrSeqStart(callGrabItems, startInd); if (instrs == null) break; startInd = ub.Instructions.IndexOf(instrs) + 3; var mtdToCall = instrs.Next(uproc).Next(uproc).Operand as MethodDef; if (mtdToCall == null) continue; // close enough if (context.SigComparer.Equals(mtdToCall.DeclaringType, grabItems.DeclaringType) && mtdToCall.FullName == grabItems.FullName) break; } instrs = instrs.Next(uproc).Next(uproc).Next(uproc); uproc.InsertBefore(instrs, Instruction.Create(OpCodes.Ldsfld, onMidUpdate)); uproc.EmitWrapperCall(invokeMidUpdate, instrs); } }
static void ReplaceSoundKilledCalls() { #region checkDead { var checkDead = typeDef_NPC.GetMethod("checkDead"); MethodDef invokeSoundKilled; var onCheckDeadSoundKilled = context.CreateDelegate("Terraria.PrismInjections", "NPC_checkDead_PlaySoundKilledDel", typeSys.Void, out invokeSoundKilled, typeDef_NPC.ToTypeSig()); var checkDead_PlaySoundKilled = new FieldDefUser("P_checkDead_PlaySoundKilled", new FieldSig(onCheckDeadSoundKilled.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_NPC.Fields.Add(checkDead_PlaySoundKilled); var cdb = checkDead.Body; using (var cdproc = cdb.GetILProcessor()) { OpCode[] toRem = { OpCodes.Ldarg_0, OpCodes.Ldfld, // NPC.soundHit OpCodes.Ldc_I4_0, OpCodes.Ble_S, // <after the call> OpCodes.Ldc_I4_4, // soundKilled ID OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldfld, // NPC.soundKilled OpCodes.Call // Main.PlaySound(int, int, int, int) }; var instrs = cdb.FindInstrSeqStart(toRem); var instrs_ = cdproc.RemoveInstructions(instrs, toRem.Length); Instruction newF; cdproc.InsertBefore(instrs_, newF = Instruction.Create(OpCodes.Ldsfld, checkDead_PlaySoundKilled)); cdproc.EmitWrapperCall(invokeSoundKilled, instrs_); for (int i = 0; i < cdb.Instructions.Count; i++) if (cdb.Instructions[i].Operand == instrs) cdb.Instructions[i].Operand = newF; } } #endregion #region RealAI { // this happens AFTER AI has been wrapped, thus RealAI has to be used instead of AI var realAI = typeDef_NPC.GetMethod("RealAI"); MethodDef invokeSoundKilled; var onRealAISoundKilled = context.CreateDelegate("Terraria.PrismInjections", "NPC_RealAI_PlaySoundKilledDel", typeSys.Void, out invokeSoundKilled, typeDef_NPC.ToTypeSig()); var realAI_PlaySoundKilled = new FieldDefUser("P_RealAI_PlaySoundKilled", new FieldSig(onRealAISoundKilled.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_NPC.Fields.Add(realAI_PlaySoundKilled); OpCode[] toRem = { OpCodes.Ldc_I4_4, // soundKilled ID OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldfld, // NPC.soundKilled OpCodes.Call // Main.PlaySound(int, int, int, int) }; using (var raproc = realAI.Body.GetILProcessor()) { var first = realAI.Body.FindInstrSeqStart(toRem); first = raproc.RemoveInstructions(first, toRem.Length); raproc.InsertBefore(first, Instruction.Create(OpCodes.Ldsfld, realAI_PlaySoundKilled)); raproc.EmitWrapperCall(invokeSoundKilled, first); } } #endregion }
static void ReplaceSoundHitCalls() { #region ReflectProjectile { // introduce a new scope level here so variables can be reused in the StrikeNPC part var reflectProjectile = typeDef_NPC.GetMethod("ReflectProjectile"); // first statement in the method is "Main.PlaySound(...);" // instruction after the "call Main.PlaySound" is a ldc.i4.0 (first one in the method) MethodDef invokeSoundHit; var onReflProjSoundHit = context.CreateDelegate("Terraria.PrismInjections", "NPC_ReflectProjectile_PlaySoundHitDel", typeSys.Void, out invokeSoundHit, typeDef_NPC.ToTypeSig(), typeSys.Int32); var reflectProjectile_PlaySoundHit = new FieldDefUser("P_ReflectProjectile_PlaySoundHit", new FieldSig(onReflProjSoundHit.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_NPC.Fields.Add(reflectProjectile_PlaySoundHit); using (var rpproc = reflectProjectile.Body.GetILProcessor()) { rpproc.RemoveInstructions(reflectProjectile.Body.Instructions.TakeWhile(i => i.OpCode.Code != Code.Ldc_I4_0).ToArray() /* must enumerate this already, invalidoperation will be thrown otherwise */); var first = reflectProjectile.Body.Instructions[0]; rpproc.InsertBefore(first, Instruction.Create(OpCodes.Ldsfld, reflectProjectile_PlaySoundHit)); rpproc.EmitWrapperCall(invokeSoundHit, first); } } #endregion #region StrikeNPC { var strikeNpc = typeDef_NPC.GetMethod("StrikeNPC"); MethodDef invokeSoundHit; var onStrikeNpcSoundHit = context.CreateDelegate("Terraria.PrismInjections", "NPC_StrikeNPC_PlaySoundHitDel", typeSys.Void, out invokeSoundHit, new[] { typeDef_NPC.ToTypeSig(), typeSys.Int32, typeSys.Single, typeSys.Int32, typeSys.Boolean, typeSys.Boolean, typeSys.Boolean }); var strikeNpc_PlaySoundHit = new FieldDefUser("P_StrikeNPC_PlaySoundHit", new FieldSig(onStrikeNpcSoundHit.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_NPC.Fields.Add(strikeNpc_PlaySoundHit); var snb = strikeNpc.Body; using (var snproc = snb.GetILProcessor()) { OpCode[] toRem = { OpCodes.Ldarg_0, OpCodes.Ldfld, // NPC.soundHit OpCodes.Ldc_I4_0, OpCodes.Ble_S, // <after the call> OpCodes.Ldc_I4_3, // soundHit ID OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldfld, // NPC.soundHit OpCodes.Call // Main.PlaySound(int, int, int, int) }; var instrs = snb.FindInstrSeqStart(toRem); var instrs_ = snproc.RemoveInstructions(instrs, toRem.Length); Instruction newF; snproc.InsertBefore(instrs_, newF = Instruction.Create(OpCodes.Ldsfld, strikeNpc_PlaySoundHit)); snproc.EmitWrapperCall(invokeSoundHit, instrs_); for (int i = 0; i < snb.Instructions.Count; i++) if (snb.Instructions[i].Operand == instrs) snb.Instructions[i].Operand = newF; } } #endregion }
static void AddPlaceThingHook() { /* IL_2D37: ldsfld int32 Terraria.Player::tileTargetX IL_2D3C: ldsfld int32 Terraria.Player::tileTargetY IL_2D41: ldarg.0 IL_2D42: ldfld class Terraria.Item[] Terraria.Player::inventory IL_2D47: ldarg.0 IL_2D48: ldfld int32 Terraria.Player::selectedItem IL_2D4D: ldelem.ref IL_2D4E: ldfld int32 Terraria.Item::createTile IL_2D53: ldc.i4.0 IL_2D54: ldloc.s 78 IL_2D56: ldarg.0 IL_2D57: ldfld int32 Terraria.Entity::whoAmI IL_2D5C: ldloc.s 72 IL_2D5E: call bool Terraria.WorldGen::PlaceTile(int32, int32, int32, bool, bool, int32, int32) IL_2D63: stloc.s 79 */ OpCode[] toFind = { OpCodes.Ldsfld, OpCodes.Ldsfld, OpCodes.Ldarg_0, OpCodes.Ldfld, OpCodes.Ldarg_0, OpCodes.Ldfld, OpCodes.Ldelem_Ref, OpCodes.Ldfld, OpCodes.Ldc_I4_0, OpCodes.Ldloc_S, OpCodes.Ldarg_0, OpCodes.Ldfld, OpCodes.Ldloc_S, OpCodes.Call, OpCodes.Stloc_S }; var placeThing = typeDef_Player.GetMethod("PlaceThing"); MethodDef invokePlaceThing; var onPlaceThingDel = context.CreateDelegate("Terraria.PrismInjections", "Player_OnPlaceThingDel", typeSys.Void, out invokePlaceThing, typeSys.Boolean); var onPlaceThing = new FieldDefUser("P_OnPlaceThing", new FieldSig(onPlaceThingDel.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_Player.Fields.Add(onPlaceThing); var ptb = placeThing.Body; using (var ptbproc = ptb.GetILProcessor()) { Instruction[] toInj = { Instruction.Create(OpCodes.Ldsfld , onPlaceThing ), Instruction.Create(OpCodes.Ldloc , ptb.Variables[79]), Instruction.Create(OpCodes.Callvirt, invokePlaceThing ) }; var first = ptb.FindInstrSeqStart(toFind); for (int i = 0; i < toFind.Length - 1; i++) first = first.Next(ptbproc); foreach (var i in toInj.Reverse()) ptbproc.InsertAfter(first, i); } }
static void ReplaceUseSoundCalls() { var typeDef_Item = memRes.GetType("Terraria.Item"); #region ItemCheck { var itemCheck = typeDef_Player.GetMethod("RealItemCheck"); // this method is wrapped MethodDef invokeUseSound; var onItemCheckUseSound = context.CreateDelegate("Terraria.PrismInjections", "Player_ItemCheck_PlayUseSoundDel", typeSys.Void, out invokeUseSound, typeDef_Item.ToTypeSig(), typeDef_Player.ToTypeSig()); var itemCheck_PlayUseSound0 = new FieldDefUser("P_ItemCheck_PlayUseSound0", new FieldSig(onItemCheckUseSound.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_Player.Fields.Add(itemCheck_PlayUseSound0); OpCode[] toRem = { OpCodes.Ldloc_2, OpCodes.Ldfld, // Item.sound OpCodes.Ldc_I4_0, OpCodes.Ble_S, // <after the call> OpCodes.Ldc_I4_2, // UseItem ID OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.Y OpCodes.Conv_I4, OpCodes.Ldloc_2, OpCodes.Ldfld, // Item.useSound OpCodes.Call // Main.PlaySound(int, int, int, int) }; using (var icproc = itemCheck.Body.GetILProcessor()) { var first = itemCheck.Body.FindInstrSeqStart(toRem); first = icproc.RemoveInstructions(first, toRem.Length); icproc.InsertBefore(first, Instruction.Create(OpCodes.Ldsfld, itemCheck_PlayUseSound0)); icproc.InsertBefore(first, Instruction.Create(OpCodes.Ldloc_2)); icproc.InsertBefore(first, Instruction.Create(OpCodes.Ldarg_0)); icproc.InsertBefore(first, Instruction.Create(OpCodes.Call, invokeUseSound)); // --- var itemCheck_PlayUseSound1 = new FieldDefUser("P_ItemCheck_PlayUseSound1", new FieldSig(onItemCheckUseSound.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_Player.Fields.Add(itemCheck_PlayUseSound1); toRem = new[] { OpCodes.Ldloc_2, OpCodes.Ldfld, // Item.sound OpCodes.Ldc_I4_0, OpCodes.Ble_S, // <after the call> OpCodes.Ldc_I4_2, // UseItem ID OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.Y OpCodes.Conv_I4, OpCodes.Ldloc_2, OpCodes.Ldfld, // Item.useSound OpCodes.Call // Main.PlaySound(int, int, int, int) }; first = itemCheck.Body.FindInstrSeqStart(toRem); first = icproc.RemoveInstructions(first, toRem.Length); icproc.InsertBefore(first, Instruction.Create(OpCodes.Ldsfld, itemCheck_PlayUseSound1)); icproc.InsertBefore(first, Instruction.Create(OpCodes.Ldloc_2)); icproc.InsertBefore(first, Instruction.Create(OpCodes.Ldarg_0)); icproc.InsertBefore(first, Instruction.Create(OpCodes.Call, invokeUseSound)); } } #endregion #region QuickBuff { var quickBuff = typeDef_Player.GetMethod("QuickBuff"); MethodDef invokeUseSound; var onQuickBuffUseSound = context.CreateDelegate("Terraria.PrismInjections", "Player_QuickBuff_PlayUseSoundDel", typeSys.Void, out invokeUseSound, typeDef_Item.ToTypeSig(), typeDef_Player.ToTypeSig()); var quickBuff_PlayUseSound = new FieldDefUser("P_QuickBuff_PlayUseSound", new FieldSig(onQuickBuffUseSound.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_Player.Fields.Add(quickBuff_PlayUseSound); var qbb = quickBuff.Body; using (var qbproc = qbb.GetILProcessor()) { // change local 0 to an item (instead of item.useSound int) qbb.Variables[0].Type = typeDef_Item.ToTypeSig(); // windows build uses short form -> addresses get messed up, but they're too specific to use anything else // remove .useSound var inst = qbb.Instructions.First(i => i.Offset == (TerrariaPatcher.Platform == Platform.Windows ? 0x0247 : 0x02aa)); qbproc.Remove(inst); // change ldc.i4.0 to ldnull inst = qbb.Instructions.First(i => i.Offset == (TerrariaPatcher.Platform == Platform.Windows ? 0x02d2 : 0x033e)); var p = inst.Previous(qbproc); qbproc.Remove(inst); qbproc.InsertAfter(p, Instruction.Create(OpCodes.Ldnull)); // change ble(.s) to beq(.s) inst = qbb.Instructions.First(i => i.Offset == (TerrariaPatcher.Platform == Platform.Windows ? 0x02d3 : 0x033f)); p = inst.Previous(qbproc); qbproc.Remove(inst); qbproc.InsertAfter(p, Instruction.Create(TerrariaPatcher.Platform == Platform.Windows ? OpCodes.Beq_S : OpCodes.Beq, (Instruction)inst.Operand)); OpCode[] toRem = { OpCodes.Ldc_I4_2, // UseItem ID OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.Y OpCodes.Conv_I4, OpCodes.Ldloc_0, OpCodes.Call // Main.PlaySound(int, int, int, int) }; var first = qbb.FindInstrSeqStart(toRem); first = qbproc.RemoveInstructions(first, toRem.Length); qbproc.InsertBefore(first, Instruction.Create(OpCodes.Ldsfld, quickBuff_PlayUseSound)); qbproc.InsertBefore(first, Instruction.Create(OpCodes.Ldloc_0)); // load item instance ons stack qbproc.InsertBefore(first, Instruction.Create(OpCodes.Ldarg_0)); qbproc.InsertBefore(first, Instruction.Create(OpCodes.Call, invokeUseSound)); } } #endregion #region QuickGrapple { var quickGrapple = typeDef_Player.GetMethod("QuickGrapple"); MethodDef invokeUseSound; var onQuickGrappleUseSound = context.CreateDelegate("Terraria.PrismInjections", "Player_QuickGrapple_PlayUseSoundDel", typeSys.Void, out invokeUseSound, typeDef_Item.ToTypeSig(), typeDef_Player.ToTypeSig()); var quickGrapple_PlayUseSound = new FieldDefUser("P_QuickGrapple_PlayUseSound", new FieldSig(onQuickGrappleUseSound.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_Player.Fields.Add(quickGrapple_PlayUseSound); OpCode[] toRem = { OpCodes.Ldc_I4_2, // UseItem ID OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.Y OpCodes.Conv_I4, OpCodes.Ldloc_3, OpCodes.Ldfld, // Item.useSound OpCodes.Call // Main.PlaySound(int, int, int, int) }; using (var qgproc = quickGrapple.Body.GetILProcessor()) { var first = quickGrapple.Body.FindInstrSeqStart(toRem); first = qgproc.RemoveInstructions(first, toRem.Length); qgproc.InsertBefore(first, Instruction.Create(OpCodes.Ldsfld, quickGrapple_PlayUseSound)); qgproc.InsertBefore(first, Instruction.Create(OpCodes.Ldloc_0)); // load item instance ons stack qgproc.InsertBefore(first, Instruction.Create(OpCodes.Ldarg_0)); qgproc.InsertBefore(first, Instruction.Create(OpCodes.Call, invokeUseSound)); } } #endregion #region QuickHeal { var quickHeal = typeDef_Player.GetMethod("QuickHeal"); MethodDef invokeUseSound; var onQuickHealUseSound = context.CreateDelegate("Terraria.PrismInjections", "Player_QuickHeal_PlayUseSoundDel", typeSys.Void, out invokeUseSound, typeDef_Item.ToTypeSig(), typeDef_Player.ToTypeSig()); var quickHeal_PlayUseSound = new FieldDefUser("P_QuickHeal_PlayUseSound", new FieldSig(onQuickHealUseSound.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_Player.Fields.Add(quickHeal_PlayUseSound); OpCode[] toRem = { OpCodes.Ldc_I4_2, // UseItem ID OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.Y OpCodes.Conv_I4, OpCodes.Ldloc_0, OpCodes.Ldfld, // Item.useSound OpCodes.Call // Main.PlaySound(int, int, int, int) }; using (var qhproc = quickHeal.Body.GetILProcessor()) { var first = quickHeal.Body.FindInstrSeqStart(toRem); var first_ = qhproc.RemoveInstructions(first, toRem.Length); Instruction newF; qhproc.InsertBefore(first_, newF = Instruction.Create(OpCodes.Ldsfld, quickHeal_PlayUseSound)); qhproc.InsertBefore(first_, Instruction.Create(OpCodes.Ldloc_0)); // load item instance ons stack qhproc.InsertBefore(first_, Instruction.Create(OpCodes.Ldarg_0)); qhproc.InsertBefore(first_, Instruction.Create(OpCodes.Callvirt, invokeUseSound)); for (int i = 0; i < quickHeal.Body.Instructions.Count; i++) if (quickHeal.Body.Instructions[i].Operand == first) quickHeal.Body.Instructions[i].Operand = newF; } } #endregion #region QuickMana { var quickMana = typeDef_Player.GetMethod("QuickMana"); MethodDef invokeUseSound; var onQuickManaUseSound = context.CreateDelegate("Terraria.PrismInjections", "Player_QuickMana_PlayUseSoundDel", typeSys.Void, out invokeUseSound, typeDef_Item.ToTypeSig(), typeDef_Player.ToTypeSig()); var quickMana_PlayUseSound = new FieldDefUser("P_QuickMana_PlayUseSound", new FieldSig(onQuickManaUseSound.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_Player.Fields.Add(quickMana_PlayUseSound); OpCode[] toRem = { OpCodes.Ldc_I4_2, // UseItem ID OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.Y OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldfld, // Player.inventory OpCodes.Ldloc_0, OpCodes.Ldelem_Ref, // <top of stack>[i] OpCodes.Ldfld, // Item.useSound OpCodes.Call // Main.PlaySound(int, int, int, int) }; using (var qmproc = quickMana.Body.GetILProcessor()) { var first = quickMana.Body.FindInstrSeqStart(toRem); var first_ = qmproc.RemoveInstructions(first, toRem.Length); Instruction newF; qmproc.InsertBefore(first_, newF = Instruction.Create(OpCodes.Ldsfld, quickMana_PlayUseSound)); qmproc.InsertBefore(first_, Instruction.Create(OpCodes.Ldarg_0)); qmproc.InsertBefore(first_, Instruction.Create(OpCodes.Ldfld, typeDef_Player.GetField("inventory"))); qmproc.InsertBefore(first_, Instruction.Create(OpCodes.Ldloc_0)); qmproc.InsertBefore(first_, Instruction.Create(OpCodes.Ldelem_Ref)); qmproc.InsertBefore(first_, Instruction.Create(OpCodes.Ldarg_0)); qmproc.InsertBefore(first_, Instruction.Create(OpCodes.Call, invokeUseSound)); for (int i = 0; i < quickMana.Body.Instructions.Count; i++) if (quickMana.Body.Instructions[i].Operand == first) quickMana.Body.Instructions[i].Operand = newF; } } #endregion #region QuickMount { var quickMount = typeDef_Player.GetMethod("QuickMount"); MethodDef invokeUseSound; var onQuickMountUseSound = context.CreateDelegate("Terraria.PrismInjections", "Player_QuickMount_PlayUseSoundDel", typeSys.Void, out invokeUseSound, typeDef_Item.ToTypeSig(), typeDef_Player.ToTypeSig()); var quickMount_PlayUseSound = new FieldDefUser("P_QuickMount_PlayUseSound", new FieldSig(onQuickMountUseSound.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_Player.Fields.Add(quickMount_PlayUseSound); OpCode[] toRem = TerrariaPatcher.Platform == Platform.Windows ? new[] { OpCodes.Ldloc_0, OpCodes.Ldfld, // Item.sound OpCodes.Ldc_I4_0, OpCodes.Ble_S, // <after the call> OpCodes.Ldc_I4_2, // UseItem ID OpCodes.Ldarg_0, OpCodes.Call, // Entity.Center OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Call, // Entity.Center OpCodes.Ldfld, // Vector2.Y OpCodes.Conv_I4, OpCodes.Ldloc_0, OpCodes.Ldfld, // Item.useSound OpCodes.Call // Main.PlaySound(int, int, int, int) } : new[] { OpCodes.Ldloc_0, OpCodes.Ldfld, // Item.sound OpCodes.Ldc_I4_0, OpCodes.Ble_S, // <after the call> OpCodes.Ldc_I4_2, // UseItem ID OpCodes.Ldarg_0, OpCodes.Call, // Entity.Center OpCodes.Stloc_2, OpCodes.Ldloca_S, OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Call, // Entity.Center OpCodes.Stloc_2, OpCodes.Ldloca_S, OpCodes.Ldfld, // Vector2.Y OpCodes.Conv_I4, OpCodes.Ldloc_0, OpCodes.Ldfld, // Item.useSound OpCodes.Call // Main.PlaySound(int, int, int, int) }; using (var qmproc = quickMount.Body.GetILProcessor()) { var first = quickMount.Body.FindInstrSeqStart(toRem); var index = quickMount.Body.Instructions.IndexOf(first); var next = quickMount.Body.Instructions[index + toRem.Length]; first = qmproc.RemoveInstructions(first, toRem.Length); qmproc.InsertBefore(next, Instruction.Create(OpCodes.Ldsfld, quickMount_PlayUseSound)); qmproc.InsertBefore(next, Instruction.Create(OpCodes.Ldloc_0)); qmproc.InsertBefore(next, Instruction.Create(OpCodes.Ldarg_0)); qmproc.InsertBefore(next, Instruction.Create(OpCodes.Call, invokeUseSound)); } } #endregion #region UpdatePet { var updatePet = typeDef_Player.GetMethod("UpdatePet"); MethodDef invokeUseSound; var onUpdatePetUseSound = context.CreateDelegate("Terraria.PrismInjections", "Player_UpdatePet_PlayUseSoundDel", typeSys.Void, out invokeUseSound, typeDef_Item.ToTypeSig(), typeDef_Player.ToTypeSig()); var updatePet_PlayUseSound = new FieldDefUser("P_UpdatePet_PlayUseSound", new FieldSig(onUpdatePetUseSound.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_Player.Fields.Add(updatePet_PlayUseSound); OpCode[] toRem = { OpCodes.Ldc_I4_2, // UseItem ID OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.Y OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldfld, // Player.miscEquips OpCodes.Ldc_I4_0, OpCodes.Ldelem_Ref, // <top of stack>[0] OpCodes.Ldfld, // Item.useSound OpCodes.Call // Main.PlaySound(int, int, int, int) }; using (var upproc = updatePet.Body.GetILProcessor()) { var first = updatePet.Body.FindInstrSeqStart(toRem); first = upproc.RemoveInstructions(first, toRem.Length); upproc.InsertBefore(first, Instruction.Create(OpCodes.Ldsfld, updatePet_PlayUseSound)); upproc.InsertBefore(first, Instruction.Create(OpCodes.Ldarg_0)); upproc.InsertBefore(first, Instruction.Create(OpCodes.Ldfld, typeDef_Player.GetField("miscEquips"))); upproc.InsertBefore(first, Instruction.Create(OpCodes.Ldc_I4_0)); upproc.InsertBefore(first, Instruction.Create(OpCodes.Ldelem_Ref)); upproc.InsertBefore(first, Instruction.Create(OpCodes.Ldarg_0)); upproc.InsertBefore(first, Instruction.Create(OpCodes.Call, invokeUseSound)); } } #endregion #region UpdatePetLight { var updatePetLight = typeDef_Player.GetMethod("UpdatePetLight"); MethodDef invokeUseSound; var onUpdatePetLightUseSound = context.CreateDelegate("Terraria.PrismInjections", "Player_UpdatePetLight_PlayUseSoundDel", typeSys.Void, out invokeUseSound, typeDef_Item.ToTypeSig(), typeDef_Player.ToTypeSig()); var updatePetLight_PlayUseSound = new FieldDefUser("P_UpdatePetLight_PlayUseSound", new FieldSig(onUpdatePetLightUseSound.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_Player.Fields.Add(updatePetLight_PlayUseSound); OpCode[] toRem = { OpCodes.Ldc_I4_2, // UseItem ID OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.X OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldflda, // Entity.position OpCodes.Ldfld, // Vector2.Y OpCodes.Conv_I4, OpCodes.Ldarg_0, OpCodes.Ldfld, // Player.miscEquips OpCodes.Ldc_I4_1, OpCodes.Ldelem_Ref, // <top of stack>[1] OpCodes.Ldfld, // Item.useSound OpCodes.Call // Main.PlaySound(int, int, int, int) }; using (var uplproc = updatePetLight.Body.GetILProcessor()) { var first = updatePetLight.Body.FindInstrSeqStart(toRem); first = uplproc.RemoveInstructions(first, toRem.Length); uplproc.InsertBefore(first, Instruction.Create(OpCodes.Ldsfld, updatePetLight_PlayUseSound)); uplproc.InsertBefore(first, Instruction.Create(OpCodes.Ldarg_0)); uplproc.InsertBefore(first, Instruction.Create(OpCodes.Ldfld, typeDef_Player.GetField("miscEquips"))); uplproc.InsertBefore(first, Instruction.Create(OpCodes.Ldc_I4_1)); uplproc.InsertBefore(first, Instruction.Create(OpCodes.Ldelem_Ref)); uplproc.InsertBefore(first, Instruction.Create(OpCodes.Ldarg_0)); uplproc.InsertBefore(first, Instruction.Create(OpCodes.Call, invokeUseSound)); } } #endregion }
static void InsertSaveLoadHooks() { TypeDef typeDef_PlayerFileData = memRes.GetType("Terraria.IO.PlayerFileData"); #region SavePlayer { var savePlayer = typeDef_Player.GetMethod("SavePlayer"); MethodDef invokeSavePlayer; var onSavePlayerDel = context.CreateDelegate("Terraria.PrismInjections", "Player_OnSavePlayerDel", typeSys.Void, out invokeSavePlayer, typeDef_PlayerFileData.ToTypeSig()); var onSavePlayer = new FieldDefUser("P_OnSavePlayer", new FieldSig(onSavePlayerDel.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_Player.Fields.Add(onSavePlayer); var spb = savePlayer.Body; using (var spproc = spb.GetILProcessor()) { var last = spb.Instructions.Last(); spproc.Remove(last); Instruction newF; spproc.Append(newF = Instruction.Create(OpCodes.Ldsfld, onSavePlayer)); spproc.Emit(OpCodes.Ldarg_0); spproc.Emit(OpCodes.Callvirt, invokeSavePlayer); spproc.Emit(OpCodes.Ret); for (int i = 0; i < spb.Instructions.Count; i++) if (spb.Instructions[i].Operand == last) spb.Instructions[i].Operand = newF; for (int i = 0; i < spb.ExceptionHandlers.Count; i++) if (spb.ExceptionHandlers[i].HandlerEnd == last) spb.ExceptionHandlers[i].HandlerEnd = newF; } } #endregion #region LoadPlayer { // Insert load hook near end of LoadPlayer var loadPlayer = typeDef_Player.GetMethod("LoadPlayer", MethodFlags.Public | MethodFlags.Static, typeSys.String.ToTypeDefOrRef(), typeSys.Boolean.ToTypeDefOrRef()); MethodDef invokeLoadPlayer; var onLoadPlayerDel = context.CreateDelegate("Terraria.PrismInjections", "Player_OnLoadPlayerDel", typeSys.Void, out invokeLoadPlayer, typeDef_Player.ToTypeSig(), typeSys.String); var onLoadPlayer = new FieldDefUser("P_OnLoadPlayer", new FieldSig(onLoadPlayerDel.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_Player.Fields.Add(onLoadPlayer); var lpb = loadPlayer.Body; using (var lpproc = lpb.GetILProcessor()) { var ldPlayerLoc = TerrariaPatcher.Platform == Platform.Windows ? OpCodes.Ldloc_1 : OpCodes.Ldloc_2; // player.skinVariant = (int)MathHelper.Clamp((float)player.skinVariant, 0f, 7f); OpCode[] toFind = { ldPlayerLoc, // player (for the stfld instruction at the end) ldPlayerLoc, OpCodes.Ldfld, // player.skinVariant OpCodes.Conv_R4, // (float)^ OpCodes.Ldc_R4, OpCodes.Ldc_R4, OpCodes.Call, // MathHelper.Clamp(^, 0f, 7f) OpCodes.Conv_I4, // (int)^ OpCodes.Stfld // ^^.skinVariant = ^ }; Instruction[] toInject = { Instruction.Create(OpCodes.Ldsfld, onLoadPlayer), Instruction.Create(OpCodes.Ldloc_1), Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Callvirt, invokeLoadPlayer) }; var first = lpb.FindInstrSeqStart(toFind); foreach (var i in toInject) lpproc.InsertBefore(first, i); // rewire the if before it to end at the injected instructions instead of the code we looked for foreach (var i in lpb.Instructions) if (i.Operand == first) i.Operand = toInject[0]; // not rewiring the if will lead to invalid IL, because the target instruction won't exist (because we're removing it here) lpproc.RemoveInstructions(first, toFind.Length); // remove the limitation while we're at it } } #endregion }
static void InjectSaveHook() { var saveWorld = typeDef_WorldFile.GetMethod("saveWorld", MethodFlags.Public | MethodFlags.Static, typeSys.Boolean.ToTypeDefOrRef(), typeSys.Boolean.ToTypeDefOrRef()); MethodDef invokeSaveWorld; var saveWorldDel = context.CreateDelegate("Terraria.PrismInjections", "WorldFile_OnSaveWorldDel", typeSys.Void, out invokeSaveWorld, typeSys.Boolean); var onSaveWorld = new FieldDefUser("P_OnSaveWorld", new FieldSig(saveWorldDel.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static); typeDef_WorldFile.Fields.Add(onSaveWorld); OpCode[] toFind = { OpCodes.Ldloc_S, OpCodes.Call, OpCodes.Leave_S }; Instruction[] toInject = { Instruction.Create(OpCodes.Ldsfld, onSaveWorld), Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Callvirt, invokeSaveWorld) }; var swb = saveWorld.Body; using (var swbproc = swb.GetILProcessor()) { var instr = swb.FindInstrSeqStart(toFind).Next(swbproc).Next(swbproc); for (int i = 0; i < toInject.Length; i++) swbproc.InsertBefore(instr, toInject[i]); } }
/// <summary> /// Clones the specified origin FieldDef. /// </summary> /// <param name="origin">The origin FieldDef.</param> /// <returns>The cloned FieldDef.</returns> static FieldDefUser Clone(FieldDef origin) { var ret = new FieldDefUser(origin.Name, null, origin.Attributes); return ret; }
/// <summary> /// Get a sensible list of all static field instructions. /// </summary> /// <returns>Instructions</returns> static IList<Instruction> GetStaticFieldInstructions(ModuleDef module, TypeDef mainType) { FieldDef field = new FieldDefUser( "StaticInteger", new FieldSig(module.CorLibTypes.Int32), FieldAttributes.Static | FieldAttributes.Public ); mainType.Fields.Add(field); var all = new List<Instruction>(); all.Add(OpCodes.Ldsfld.ToInstruction(field)); all.Add(OpCodes.Stsfld.ToInstruction(field)); all.Add(OpCodes.Ldsflda.ToInstruction(field)); // Pop causes the virtualizer to optimize in some cases, // and remove previous instruction // all.Add(OpCodes.Pop.ToInstruction()); all.Add(OpCodes.Ret.ToInstruction()); return all; }