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;
 }
Exemple #3
0
		// 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);
            }
        }
Exemple #7
0
        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);
            }
        }
Exemple #11
0
        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
        }
Exemple #12
0
        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]);
            }
        }
Exemple #17
0
		/// <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;
		}