예제 #1
0
        public static void RedirectConstructorFromBase(CecilContext stardewContext, Type asmType, Type[] test, string type, string method, Type[] parameters)
        {
            var types       = test.Select(n => stardewContext.GetTypeReference(n)).ToArray();
            var typeDef     = stardewContext.GetTypeDefinition(asmType.Namespace + "." + asmType.Name);
            var typeDefBase = stardewContext.GetTypeDefinition(asmType.BaseType.Namespace + "." + asmType.BaseType.Name);

            var newConstructorReference = stardewContext.GetConstructorReference(typeDef);
            var oldConstructorReference = stardewContext.GetConstructorReference(typeDefBase);

            var concreteFromConstructor = MakeHostInstanceGeneric(oldConstructorReference, types);
            var concreteToConstructor   = MakeHostInstanceGeneric(newConstructorReference, types);

            ILProcessor ilProcessor = stardewContext.GetMethodIlProcessor(type, method);

            var instructions = ilProcessor.Body.Instructions.Where(n => n.OpCode == OpCodes.Newobj && n.Operand is MethodReference &&
                                                                   ((MethodReference)n.Operand).FullName == concreteFromConstructor.FullName).ToList();

            foreach (var instruction in instructions)
            {
                ilProcessor.Replace(instruction, ilProcessor.Create(OpCodes.Newobj, concreteToConstructor));
            }
        }
예제 #2
0
        private static void InjectReturnableMethod <TParam, TThis, TInput, TLocal, TUseOutput, TMethodOutputBind>(CecilContext stardewContext, ILProcessor ilProcessor, Instruction target, MethodReference method, bool isExit = false)
        {
            ilProcessor.Body.SimplifyMacros();

            // Add UseReturnVal variable
            if (ilProcessor.Body.Variables.All(n => n.Name != "UseReturnVal"))
            {
                var boolType = stardewContext.GetTypeReference(typeof(bool));
                ilProcessor.Body.Variables.Add(new VariableDefinition("UseReturnVal", boolType));
            }
            var useOutputVariable = ilProcessor.Body.Variables.First(n => n.Name == "UseReturnVal");

            if (ilProcessor.Body.Variables.All(n => n.Name != "ReturnContainer-" + method.ReturnType.Name))
            {
                ilProcessor.Body.Variables.Add(new VariableDefinition("ReturnContainer-" + method.ReturnType.Name, method.ReturnType));
            }
            if (isExit)
            {
                if (ilProcessor.Body.Variables.All(n => n.Name != "OldReturnContainer"))
                {
                    ilProcessor.Body.Variables.Add(new VariableDefinition("OldReturnContainer", method.ReturnType));
                }
            }
            var containerVariable    = ilProcessor.Body.Variables.First(n => n.Name == "ReturnContainer-" + method.ReturnType.Name);
            var oldContainerVariable = ilProcessor.Body.Variables.FirstOrDefault(n => n.Name == "OldReturnContainer");

            List <Instruction> instructions = new List <Instruction>();

            if (isExit)
            {
                instructions.Add(ilProcessor.Create(OpCodes.Stloc, oldContainerVariable));
            }

            if (method.HasParameters)
            {
                foreach (var parameter in method.Parameters)
                {
                    var paramLdInstruction = GetInstructionForParameter <TParam, TThis, TInput, TLocal, TUseOutput, TMethodOutputBind>(stardewContext, ilProcessor, parameter);
                    if (paramLdInstruction == null)
                    {
                        throw new Exception($"Error parsing parameter setup on {parameter.Name}");
                    }

                    instructions.Add(paramLdInstruction);
                }
            }

            instructions.Add(ilProcessor.Create(OpCodes.Call, method));
            instructions.Add(ilProcessor.Create(OpCodes.Stloc, containerVariable));
            instructions.Add(ilProcessor.Create(OpCodes.Ldloc, useOutputVariable));
            if (!isExit)
            {
                instructions.Add(ilProcessor.Create(OpCodes.Brfalse, target));
                instructions.Add(ilProcessor.Create(OpCodes.Ldloc, containerVariable));
                instructions.Add(ilProcessor.Create(OpCodes.Br, ilProcessor.Body.Instructions.Last()));
            }
            else
            {
                var brToRet = ilProcessor.Create(OpCodes.Br, target);
                var loadOld = ilProcessor.Create(OpCodes.Ldloc, oldContainerVariable);
                var loadNew = ilProcessor.Create(OpCodes.Ldloc, containerVariable);
                instructions.Add(ilProcessor.Create(OpCodes.Brfalse, loadOld));
                instructions.Add(loadNew);
                instructions.Add(brToRet);
                instructions.Add(loadOld);
            }

            foreach (var inst in instructions)
            {
                ilProcessor.InsertBefore(target, inst);
            }

            ilProcessor.Body.OptimizeMacros();
        }
예제 #3
0
        public static void InjectGlobalRoutePreMethod(CecilContext stardewContext, string injecteeType, string injecteeMethod, int index)
        {
            var fieldDefinition    = stardewContext.GetFieldDefinition("Farmhand.Events.GlobalRouteManager", "IsEnabled");
            var methodIsListenedTo = stardewContext.GetMethodDefinition("Farmhand.Events.GlobalRouteManager", "IsBeingPreListenedTo");
            var methodDefinition   = stardewContext.GetMethodDefinition("Farmhand.Events.GlobalRouteManager", "GlobalRoutePreInvoke");
            var ilProcessor        = stardewContext.GetMethodIlProcessor(injecteeType, injecteeMethod);

            if (ilProcessor == null || methodDefinition == null || fieldDefinition == null)
            {
                return;
            }

            var method   = ilProcessor.Body.Method;
            var hasThis  = method.HasThis;
            var argIndex = 0;

            var first      = ilProcessor.Body.Instructions.First();
            var last       = ilProcessor.Body.Instructions.Last();
            var objectType = stardewContext.GetTypeReference(typeof(object));
            var voidType   = stardewContext.GetTypeReference(typeof(void));

            var newInstructions = new List <Instruction>();

            newInstructions.Add(ilProcessor.Create(OpCodes.Ldsfld, fieldDefinition));
            newInstructions.Add(ilProcessor.Create(OpCodes.Brfalse, first));

            newInstructions.Add(ilProcessor.Create(OpCodes.Ldc_I4, index));
            newInstructions.Add(ilProcessor.Create(OpCodes.Call, methodIsListenedTo));
            newInstructions.Add(ilProcessor.Create(OpCodes.Brfalse, first));

            newInstructions.Add(ilProcessor.Create(OpCodes.Ldc_I4, index));
            newInstructions.Add(ilProcessor.Create(OpCodes.Ldstr, injecteeType));
            newInstructions.Add(ilProcessor.Create(OpCodes.Ldstr, injecteeMethod));

            var outputVar = new VariableDefinition("GlobalRouteOutput", objectType);

            ilProcessor.Body.Variables.Add(outputVar);
            newInstructions.Add(ilProcessor.Create(OpCodes.Ldloca, outputVar));

            newInstructions.Add(ilProcessor.Create(OpCodes.Ldc_I4, method.Parameters.Count + (hasThis ? 1 : 0)));
            newInstructions.Add(ilProcessor.Create(OpCodes.Newarr, objectType));

            if (hasThis)
            {
                newInstructions.Add(ilProcessor.Create(OpCodes.Dup));
                newInstructions.Add(ilProcessor.Create(OpCodes.Ldc_I4, argIndex++));
                newInstructions.Add(ilProcessor.Create(OpCodes.Ldarg, ilProcessor.Body.ThisParameter));
                newInstructions.Add(ilProcessor.Create(OpCodes.Stelem_Ref));
            }

            foreach (var param in method.Parameters)
            {
                newInstructions.Add(ilProcessor.Create(OpCodes.Dup));
                newInstructions.Add(ilProcessor.Create(OpCodes.Ldc_I4, argIndex++));
                newInstructions.Add(ilProcessor.Create(OpCodes.Ldarg, param));
                if (param.ParameterType.IsPrimitive)
                {
                    newInstructions.Add(ilProcessor.Create(OpCodes.Box, param.ParameterType));
                }
                newInstructions.Add(ilProcessor.Create(OpCodes.Stelem_Ref));
            }

            newInstructions.Add(ilProcessor.Create(OpCodes.Call, methodDefinition));
            newInstructions.Add(ilProcessor.Create(OpCodes.Brfalse, first));

            if (method.ReturnType != null && method.ReturnType.FullName != voidType.FullName)
            {
                newInstructions.Add(ilProcessor.Create(OpCodes.Ldloc, outputVar));
                if (method.ReturnType.IsPrimitive || method.ReturnType.IsGenericParameter)
                {
                    newInstructions.Add(ilProcessor.Create(OpCodes.Unbox_Any, method.ReturnType));
                }
                else
                {
                    newInstructions.Add(ilProcessor.Create(OpCodes.Castclass, method.ReturnType));
                }
                newInstructions.Add(ilProcessor.Create(OpCodes.Br, last));
            }

            ilProcessor.Body.SimplifyMacros();
            if (newInstructions.Any())
            {
                var previousInstruction = newInstructions.First();
                ilProcessor.InsertBefore(first, previousInstruction);
                for (var i = 1; i < newInstructions.Count; ++i)
                {
                    ilProcessor.InsertAfter(previousInstruction, newInstructions[i]);
                    previousInstruction = newInstructions[i];
                }
            }
            ilProcessor.Body.OptimizeMacros();
        }
예제 #4
0
        public static void InjectGlobalRoutePostMethod(CecilContext stardewContext, string injecteeType, string injecteeMethod, int index)
        {
            var ilProcessor = stardewContext.GetMethodIlProcessor(injecteeType, injecteeMethod);

            if (ilProcessor == null)
            {
                return;
            }

            var objectType   = stardewContext.GetTypeReference(typeof(object));
            var voidType     = stardewContext.GetTypeReference(typeof(void));
            var boolType     = stardewContext.GetTypeReference(typeof(bool));
            var method       = ilProcessor.Body.Method;
            var hasThis      = method.HasThis;
            var returnsValue = method.ReturnType != null && method.ReturnType.FullName != voidType.FullName;

            VariableDefinition outputVar = null;

            var isEnabledField     = stardewContext.GetFieldDefinition("Farmhand.Events.GlobalRouteManager", "IsEnabled");
            var methodIsListenedTo = stardewContext.GetMethodDefinition("Farmhand.Events.GlobalRouteManager", "IsBeingPostListenedTo");
            MethodDefinition methodDefinition;

            methodDefinition = stardewContext.GetMethodDefinition("Farmhand.Events.GlobalRouteManager", "GlobalRoutePostInvoke",
                                                                  n => n.Parameters.Count == (returnsValue ? 5 : 4));

            if (methodDefinition == null || isEnabledField == null)
            {
                return;
            }

            var retInstructions = ilProcessor.Body.Instructions.Where(n => n.OpCode == OpCodes.Ret).ToArray();

            foreach (var ret in retInstructions)
            {
                var newInstructions = new List <Instruction>();

                newInstructions.Add(ilProcessor.PushFieldToStack(isEnabledField));
                newInstructions.Add(ilProcessor.BranchIfFalse(ret));

                newInstructions.Add(ilProcessor.PushInt32ToStack(index));
                newInstructions.Add(ilProcessor.Call(methodIsListenedTo));
                newInstructions.Add(ilProcessor.BranchIfFalse(ret));

                if (returnsValue)
                {
                    outputVar = new VariableDefinition("GlobalRouteOutput", objectType);
                    ilProcessor.Body.Variables.Add(outputVar);
                    if (method.ReturnType.IsPrimitive || method.ReturnType.IsGenericParameter)
                    {
                        newInstructions.Add(ilProcessor.Create(OpCodes.Box, method.ReturnType));
                    }
                    newInstructions.Add(ilProcessor.Create(OpCodes.Stloc, outputVar));
                }

                newInstructions.Add(ilProcessor.PushInt32ToStack(index));
                newInstructions.Add(ilProcessor.PushStringToStack(injecteeType));
                newInstructions.Add(ilProcessor.PushStringToStack(injecteeMethod));

                if (returnsValue)
                {
                    newInstructions.Add(ilProcessor.Create(OpCodes.Ldloca, outputVar));
                }

                int argIndex = 0;
                newInstructions.AddRange(ilProcessor.CreateArray(objectType, method.Parameters.Count + (hasThis ? 1 : 0)));

                if (method.HasThis)
                {
                    newInstructions.AddRange(ilProcessor.InsertParameterIntoArray(ilProcessor.Body.ThisParameter, argIndex++));
                }

                foreach (var param in method.Parameters)
                {
                    newInstructions.AddRange(ilProcessor.InsertParameterIntoArray(param, argIndex++));
                }

                newInstructions.Add(ilProcessor.Call(methodDefinition));

                if (returnsValue)
                {
                    newInstructions.Add(ilProcessor.Create(OpCodes.Ldloc, outputVar));
                    if (method.ReturnType.IsPrimitive || method.ReturnType.IsGenericParameter)
                    {
                        newInstructions.Add(ilProcessor.Create(OpCodes.Unbox_Any, method.ReturnType));
                    }
                    else
                    {
                        newInstructions.Add(ilProcessor.Create(OpCodes.Castclass, method.ReturnType));
                    }
                }

                ilProcessor.Body.SimplifyMacros();
                if (newInstructions.Any())
                {
                    var previousInstruction = newInstructions.First();
                    ilProcessor.InsertBefore(ret, previousInstruction);
                    for (var i = 1; i < newInstructions.Count; ++i)
                    {
                        ilProcessor.InsertAfter(previousInstruction, newInstructions[i]);
                        previousInstruction = newInstructions[i];
                    }
                }
                ilProcessor.Body.OptimizeMacros();
            }
        }
예제 #5
0
        private static void InjectMethod <TParam, TThis, TInput, TLocal>(CecilContext stardewContext, ILProcessor ilProcessor, Instruction target, MethodReference method, bool isExit = false, bool cancelable = false)
        {
            ilProcessor.Body.SimplifyMacros();

            var callEnterInstruction = ilProcessor.Create(OpCodes.Call, method);

            if (method.HasThis)
            {
                var loadObjInstruction = ilProcessor.Create(OpCodes.Ldarg_0);
                ilProcessor.InsertBefore(target, loadObjInstruction);
            }

            if (method.HasParameters)
            {
                Instruction prevInstruction    = null;
                var         paramLdInstruction = target;
                var         first = true;
                foreach (var parameter in method.Parameters)
                {
                    paramLdInstruction = GetInstructionForParameter <TParam, TThis, TInput, TLocal, Patcher, Patcher>(stardewContext, ilProcessor, parameter);
                    if (paramLdInstruction == null)
                    {
                        throw new Exception($"Error parsing parameter setup on {parameter.Name}");
                    }

                    if (isExit)
                    {
                        if (first)
                        {
                            first = false;
                            ilProcessor.Replace(target, paramLdInstruction);
                        }
                        else
                        {
                            ilProcessor.InsertAfter(prevInstruction, paramLdInstruction);
                        }
                        prevInstruction = paramLdInstruction;
                    }
                    else
                    {
                        ilProcessor.InsertBefore(target, paramLdInstruction);
                    }
                }

                if (isExit)
                {
                    if (first)
                    {
                        ilProcessor.Replace(target, callEnterInstruction);
                        ilProcessor.InsertAfter(callEnterInstruction, ilProcessor.Create(OpCodes.Ret));
                    }
                    else
                    {
                        ilProcessor.InsertAfter(prevInstruction, callEnterInstruction);
                        ilProcessor.InsertAfter(callEnterInstruction, ilProcessor.Create(OpCodes.Ret));
                    }
                }
                else
                {
                    ilProcessor.InsertAfter(paramLdInstruction, callEnterInstruction);
                }
            }
            else
            {
                if (isExit)
                {
                    ilProcessor.Replace(target, callEnterInstruction);
                    ilProcessor.InsertAfter(callEnterInstruction, ilProcessor.Create(OpCodes.Ret));
                }
                else
                {
                    ilProcessor.InsertBefore(target, callEnterInstruction);
                }
            }

            if (cancelable)
            {
                if (ilProcessor.Body.Method.MethodReturnType.ReturnType.FullName != stardewContext.GetTypeReference(typeof(void)).FullName)
                {
                    throw new InvalidOperationException("Cancelable hooks are only supported for methods returning void");
                }
                var branch = ilProcessor.Create(OpCodes.Brtrue, ilProcessor.Body.Instructions.Last());
                ilProcessor.InsertAfter(callEnterInstruction, branch);
            }

            ilProcessor.Body.OptimizeMacros();
        }