Пример #1
0
        static void InjectSaveHook()
        {
            var saveWorld = typeDef_WorldFile.GetMethod("saveWorld", MethodFlags.Public | MethodFlags.Static, typeSys.Boolean, typeSys.Boolean);

            var swb     = saveWorld.Body;
            var swbproc = swb.GetILProcessor();

            MethodDefinition invokeSaveWorld;
            var saveWorldDel = context.CreateDelegate("Terraria.PrismInjections", "WorldFile_OnSaveWorldDel", typeSys.Void, out invokeSaveWorld, typeSys.Boolean);

            var onSaveWorld = new FieldDefinition("P_OnSaveWorld", FieldAttributes.Public | FieldAttributes.Static, saveWorldDel);

            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 instr = swb.FindInstrSeqStart(toFind).Next.Next;

            for (int i = 0; i < toInject.Length; i++)
            {
                swbproc.InsertBefore(instr, toInject[i]);
            }
        }
Пример #2
0
        static void InsertSaveLoadHooks()
        {
            TypeDefinition typeDef_PlayerFileData = memRes.GetType("Terraria.IO.PlayerFileData");

            #region SavePlayer
            {
                var savePlayer = typeDef_Player.GetMethod("SavePlayer");

                MethodDefinition invokeSavePlayer;
                var onSavePlayerDel = context.CreateDelegate("Terraria.PrismInjections", "Player_OnSavePlayerDel", typeSys.Void, out invokeSavePlayer, typeDef_PlayerFileData);

                var onSavePlayer = new FieldDefinition("P_OnSavePlayer", FieldAttributes.Public | FieldAttributes.Static, onSavePlayerDel);
                typeDef_Player.Fields.Add(onSavePlayer);

                var spb    = savePlayer.Body;
                var spproc = spb.GetILProcessor();

                spproc.Remove(spb.Instructions.Last());

                spproc.Emit(OpCodes.Ldsfld, onSavePlayer);
                spproc.Emit(OpCodes.Ldarg_0);
                spproc.Emit(OpCodes.Callvirt, invokeSavePlayer);
                spproc.Emit(OpCodes.Ret);
            }
            #endregion

            #region LoadPlayer
            {
                // Insert load hook near end of LoadPlayer
                var loadPlayer = typeDef_Player.GetMethod("LoadPlayer", MethodFlags.Public | MethodFlags.Static, typeSys.String, typeSys.Boolean);

                MethodDefinition invokeLoadPlayer;
                var onLoadPlayerDel = context.CreateDelegate("Terraria.PrismInjections", "Player_OnLoadPlayerDel", typeSys.Void, out invokeLoadPlayer, typeDef_Player, typeSys.String);

                var onLoadPlayer = new FieldDefinition("P_OnLoadPlayer", FieldAttributes.Public | FieldAttributes.Static, onLoadPlayerDel);
                typeDef_Player.Fields.Add(onLoadPlayer);

                var lpb    = loadPlayer.Body;
                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
        }
Пример #3
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)

                MethodDefinition invokeSoundHit;
                var onReflProjSoundHit = context.CreateDelegate("Terraria.PrismInjections", "NPC_ReflectProjectile_PlaySoundHitDel", typeSys.Void, out invokeSoundHit, typeDef_NPC, typeSys.Int32);

                var reflectProjectile_PlaySoundHit = new FieldDefinition("P_ReflectProjectile_PlaySoundHit", FieldAttributes.Public | FieldAttributes.Static, onReflProjSoundHit);
                typeDef_NPC.Fields.Add(reflectProjectile_PlaySoundHit);

                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");

                MethodDefinition invokeSoundHit;
                var onStrikeNpcSoundHit = context.CreateDelegate("Terraria.PrismInjections", "NPC_StrikeNPC_PlaySoundHitDel", typeSys.Void, out invokeSoundHit,
                                                                 typeDef_NPC, typeSys.Int32, typeSys.Single, typeSys.Int32, typeSys.Boolean, typeSys.Boolean, typeSys.Boolean);

                var strikeNpc_PlaySoundHit = new FieldDefinition("P_StrikeNPC_PlaySoundHit", FieldAttributes.Public | FieldAttributes.Static, onStrikeNpcSoundHit);
                typeDef_NPC.Fields.Add(strikeNpc_PlaySoundHit);

                var snb    = strikeNpc.Body;
                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);
                instrs = snproc.RemoveInstructions(instrs, toRem.Length);

                snproc.InsertBefore(instrs, Instruction.Create(OpCodes.Ldsfld, strikeNpc_PlaySoundHit));
                snproc.EmitWrapperCall(invokeSoundHit, instrs);
            }
            #endregion
        }
Пример #4
0
        /// <summary>
        /// Wraps a method using a fancy delegate. Replaces all references of the method with the wrapped one and creates an "On[MethodName]" hook which passes the method's parent type followed by the type parameters of the original method.
        /// </summary>
        /// <param name="context">The current Cecil context.</param>
        /// <param name="delegateNS">The namespace of the delegate type to create.</param>
        /// <param name="delegateTypeName">The name of the delegate type to create.</param>
        /// <param name="origMethod">The method to wrap.</param>
        public static void Wrap(this MethodDefinition origMethod, CecilContext context, string delegateNS, string delegateTypeName, string fieldName)
        {
            MethodDefinition invokeDelegate;

            SingletonTRArr[0] = origMethod.DeclaringType;

            //If anyone knows a better way to insert one element at the beginning of an array and scoot
            //all the other elements down one index then go ahead and do it lol. I dunno how2array.
            var delegateArgs = (origMethod.IsStatic ? EmptyTRArr : SingletonTRArr).Concat(origMethod.Parameters.Select(p => p.ParameterType)).ToArray();

            var newDelegateType = context.CreateDelegate(delegateNS, delegateTypeName, origMethod.ReturnType, out invokeDelegate, delegateArgs);

            var newMethod = origMethod.ReplaceAndHook(invokeDelegate, origMethod, fieldName);

            origMethod.ReplaceAllMethodRefs(newMethod, context);
        }
Пример #5
0
        static void AddOnUpdateKeyboardHook()
        {
            OpCode[] search =
            {
                OpCodes.Call,      //IL_27a6: call valuetype [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Input.KeyboardState Microsoft.Xna.Framework.Input.Keyboard::GetState()
                OpCodes.Stsfld,    //IL_27ab: stsfld valuetype [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Input.KeyboardState Terraria.Main::keyState
                OpCodes.Ldsfld,    //IL_27b0: ldsfld bool Terraria.Main::editSign
                OpCodes.Brfalse_S, //IL_27b5: brfalse.s IL_27bd

                OpCodes.Ldc_I4_0,  //IL_27b7: ldc.i4.0
                OpCodes.Stsfld,    //IL_27b8: stsfld bool Terraria.Main::chatMode

                OpCodes.Ldsfld,    //IL_27bd: ldsfld bool Terraria.Main::chatMode
                OpCodes.Brtrue_S,  //IL_27c2: brtrue.s IL_27cf

                OpCodes.Ldc_I4_0,  //IL_27c4: ldc.i4.0
                OpCodes.Stsfld,    //IL_27c5: stsfld int32 Terraria.Main::startChatLine
                OpCodes.Br,        //IL_27ca: br IL_2a83

                OpCodes.Ldsfld,    //IL_27cf: ldsfld int32 Terraria.Main::screenHeight
                OpCodes.Ldc_I4_3,  //IL_27d4: ldc.i4.3
                OpCodes.Div,       //IL_27d5: div
                OpCodes.Conv_R4,   //IL_27d6: conv.r4
                OpCodes.Ldsfld,    //IL_27d7: ldsfld class [Microsoft.Xna.Framework.Graphics]Microsoft.Xna.Framework.Graphics.SpriteFont Terraria.Main::fontMouseText
                OpCodes.Ldstr,     //IL_27dc: ldstr "1"
                OpCodes.Callvirt,  //IL_27e1: callvirt instance valuetype [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector2 [Microsoft.Xna.Framework.Graphics]Microsoft.Xna.Framework.Graphics.SpriteFont::MeasureString(string)

                TerrariaPatcher.Platform == Platform.Windows ? OpCodes.Endfinally : OpCodes.Stloc_S,
                TerrariaPatcher.Platform == Platform.Windows ? OpCodes.Endfinally : OpCodes.Ldloca_S,

                OpCodes.Ldfld,     //IL_27e6: ldfld float32 [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector2::Y
                OpCodes.Div,       //IL_27eb: div
                OpCodes.Conv_I4,   //IL_27ec: conv.i4
                OpCodes.Ldc_I4_1,  //IL_27ed: ldc.i4.1
                OpCodes.Sub,       //IL_27ee: sub
                OpCodes.Stsfld,    //IL_27ef: stsfld int32 Terraria.Main::showCount
                OpCodes.Ldsflda,   //IL_27f4: ldsflda valuetype [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Input.KeyboardState Terraria.Main::keyState
                OpCodes.Ldc_I4_S,  //IL_27f9: ldc.i4.s 38
                OpCodes.Call,      //IL_27fb: call instance bool [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Input.KeyboardState::IsKeyDown(valuetype [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Input.Keys)
                OpCodes.Brfalse_S, //IL_2800: brfalse.s IL_2864
            };
            search = search.Where(o => o.Code != Code.Endfinally).ToArray();

            var mainUpdate     = typeDef_Main.GetMethod("Update");
            var mainUpdateBody = mainUpdate.Body;
            var first          = mainUpdateBody.FindInstrSeqStart(search);

            if (!(first != null && (first = first.Next) != null && (first = first.Next) != null))
            {
                Console.Error.WriteLine("Couldn't find instructions for MainPatcher::AddOnUpdateKeyboardHook()");
            }

            //public virtual void P_OnUpdateInputHook() { }
            MethodDefinition invokeOnUpdateKeyboardHook;
            var onUpdateKeyboardDelType  = context.CreateDelegate("Terraria.PrismInjections", "Main_Update_OnUpdateKeyboardDel", typeSys.Void, out invokeOnUpdateKeyboardHook, typeDef_Main, mainUpdate.Parameters[0].ParameterType /* HAH I WIN, XNA */);
            var onUpdateKeyboardDelField = new FieldDefinition("P_Main_Update_OnUpdateKeyboard", FieldAttributes.Public | FieldAttributes.Static, onUpdateKeyboardDelType);

            typeDef_Main.Fields.Add(onUpdateKeyboardDelField);

            var mainUpdateProc = mainUpdateBody.GetILProcessor();

            mainUpdateProc.InsertBefore(first, Instruction.Create(OpCodes.Ldsfld, onUpdateKeyboardDelField));
            mainUpdateProc.EmitWrapperCall(invokeOnUpdateKeyboardHook, first);
            //mainUpdateProc.InsertBefore(first, Instruction.Create(OpCodes.Call, invokeOnUpdateKeyboardHook));
        }