using System; using System.Collections.Generic; using System.Linq; using Android.Graphics; using ModLoader.Common; using Mono.Cecil; using Mono.Cecil.Cil; namespace DllRewrite { internal class MethodPatcher { private readonly Dictionary fieldDict = new Dictionary(); private readonly Dictionary methodDict = new Dictionary(); private AssemblyDefinition MonoGame_Framework; private readonly AssemblyDefinition mscorlib; private readonly AssemblyDefinition Mono_Android; private readonly DefaultAssemblyResolver resolver; private readonly AssemblyDefinition StardewValley; private readonly Dictionary typeDict = new Dictionary(); public MethodPatcher() { this.resolver = new DefaultAssemblyResolver(); this.resolver.AddSearchDirectory(Constants.AssemblyPath); this.mscorlib = this.resolver.Resolve(new AssemblyNameReference("mscorlib", new Version("0.0.0.0"))); this.Mono_Android = this.resolver.Resolve(new AssemblyNameReference("Mono.Android", new Version("0.0.0.0"))); this.MonoGame_Framework = this.resolver.Resolve(new AssemblyNameReference("MonoGame.Framework", new Version("0.0.0.0"))); this.StardewValley = this.resolver.Resolve(new AssemblyNameReference("StardewValley", new Version("0.0.0.0"))); } public void InsertModHook(string name, TypeReference[] paraTypes, string[] paraNames, TypeReference returnType) { var typeModHooksObject = this.StardewValley.MainModule.GetType("StardewValley.ModHooks"); var hook = new MethodDefinition(name, MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.HideBySig, returnType); for (int i = 0; i < paraTypes.Length; i++) { var define = new ParameterDefinition(paraNames[i], ParameterAttributes.None, paraTypes[i]); hook.Parameters.Add(define); } switch (returnType.FullName) { case "System.Void": break; case "System.Boolean": case "System.Int32": hook.Body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4_1)); break; default: hook.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull)); break; } hook.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); if (typeModHooksObject.Methods.FirstOrDefault(item => item.Name == hook.Name) == null) typeModHooksObject.Methods.Add(hook); } public TypeReference GetTypeReference(string fullname) { return this.GetTypeReference(fullname, this.mscorlib); } public TypeReference GetTypeReference(string fullname, AssemblyDefinition assemblyDefinition) { if (this.typeDict.ContainsKey(fullname)) return this.typeDict[fullname]; TypeReference type = assemblyDefinition.MainModule.GetType(fullname).Resolve(); type = this.StardewValley.MainModule.ImportReference(type); this.typeDict.Add(fullname, type); return type; } public FieldReference GetFieldReference(string name, string typename, AssemblyDefinition assemblyDefinition) { string fullname = typename + "::" + name; if (this.fieldDict.ContainsKey(fullname)) return this.fieldDict[fullname]; FieldReference field = assemblyDefinition.MainModule.Types.FirstOrDefault(item => item.FullName == typename) .Fields.FirstOrDefault(item => item.Name == name); if (assemblyDefinition.FullName != this.StardewValley.FullName) field = this.StardewValley.MainModule.ImportReference(field); this.fieldDict.Add(fullname, field); return field; } public MethodReference GetMethodReference(string name, string typename, AssemblyDefinition assemblyDefinition, Func methodFilter = null, AssemblyDefinition refDefinition = null) { string fullname = typename + "." + name; if (this.fieldDict.ContainsKey(fullname)) return this.methodDict[fullname]; MethodReference method = assemblyDefinition.MainModule.Types .FirstOrDefault(item => item.FullName == typename).Methods.FirstOrDefault(item => { if (item.Name == name) { if (methodFilter == null) return true; else return methodFilter(item); } return false; }); if (assemblyDefinition.FullName != this.StardewValley.FullName) { if(refDefinition == null) method = this.StardewValley.MainModule.ImportReference(method); else method = refDefinition.MainModule.ImportReference(method); } this.methodDict.Add(fullname, method); return method; } public void ApplyGamePatch() { var typeMainActivity = this.StardewValley.MainModule.GetType("StardewValley.MainActivity"); typeMainActivity.CustomAttributes.Clear(); // Game.hook var typeGame1 = this.StardewValley.MainModule.GetType("StardewValley.Game1"); var constructor = typeGame1.Methods.FirstOrDefault(m => m.IsConstructor); var processor = constructor.Body.GetILProcessor(); foreach (var instruction in constructor.Body.Instructions) { if (instruction.OpCode == OpCodes.Ldstr && (string)instruction.Operand == "Content") { processor.InsertBefore(instruction, processor.Create(OpCodes.Call, this.GetMethodReference("get_ExternalStorageDirectory", "Android.OS.Environment", this.Mono_Android))); processor.InsertBefore(instruction, processor.Create(OpCodes.Callvirt, this.GetMethodReference("get_Path", "Java.IO.File", this.Mono_Android))); instruction.Operand = "SMDroid/Game/assets/Content"; processor.InsertAfter(instruction, processor.Create(OpCodes.Call, this.GetMethodReference("Combine", "System.IO.Path", this.mscorlib, (m)=>m.Parameters.Count == 2))); break; } } var hooksField = typeGame1.Fields.FirstOrDefault(f => f.Name == "hooks"); hooksField.IsFamilyOrAssembly = false; hooksField.IsPublic = true; // isRaining and isDebrisWeather var propertyDefinition = new PropertyDefinition("isRaining", PropertyAttributes.None, this.GetTypeReference("System.Boolean")); propertyDefinition.GetMethod = new MethodDefinition("get_isRaining", MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.SpecialName | MethodAttributes.Static | MethodAttributes.HideBySig, this.GetTypeReference("System.Boolean")); propertyDefinition.GetMethod.SemanticsAttributes = MethodSemanticsAttributes.Getter; processor = propertyDefinition.GetMethod.Body.GetILProcessor(); var typeRainManager = this.StardewValley.MainModule.GetType("StardewValley.RainManager"); var getMethod = typeRainManager.Methods.FirstOrDefault(m => m.Name == "get_Instance"); processor.Emit(OpCodes.Callvirt, getMethod); var isRainingField = this.GetFieldReference("isRaining", "StardewValley.RainManager", this.StardewValley); processor.Emit(OpCodes.Ldfld, isRainingField); processor.Emit(OpCodes.Ret); typeGame1.Methods.Add(propertyDefinition.GetMethod); typeGame1.Properties.Add(propertyDefinition); propertyDefinition = new PropertyDefinition("isDebrisWeather", PropertyAttributes.None, this.GetTypeReference("System.Boolean")); propertyDefinition.GetMethod = new MethodDefinition("get_isDebrisWeather", MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.SpecialName | MethodAttributes.Static | MethodAttributes.HideBySig, this.GetTypeReference("System.Boolean")); propertyDefinition.GetMethod.SemanticsAttributes = MethodSemanticsAttributes.Getter; processor = propertyDefinition.GetMethod.Body.GetILProcessor(); var typeWeatherDebrisManager = this.StardewValley.MainModule.GetType("StardewValley.WeatherDebrisManager"); getMethod = typeWeatherDebrisManager.Methods.FirstOrDefault(m => m.Name == "get_Instance"); processor.Emit(OpCodes.Callvirt, getMethod); var isDebrisWeatherField = this.GetFieldReference("isDebrisWeather", "StardewValley.WeatherDebrisManager", this.StardewValley); processor.Emit(OpCodes.Ldfld, isDebrisWeatherField); processor.Emit(OpCodes.Ret); typeGame1.Methods.Add(propertyDefinition.GetMethod); typeGame1.Properties.Add(propertyDefinition); //HUDMessage..ctor var typeHUDMessage = this.StardewValley.MainModule.GetType("StardewValley.HUDMessage"); var hudConstructor = new MethodDefinition(".ctor", MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.HideBySig, this.GetTypeReference("System.Void")); hudConstructor.Parameters.Add(new ParameterDefinition(this.GetTypeReference("System.String"))); hudConstructor.Parameters.Add(new ParameterDefinition(this.GetTypeReference("System.Int32"))); processor = hudConstructor.Body.GetILProcessor(); processor.Emit(OpCodes.Ldarg_0); processor.Emit(OpCodes.Ldarg_1); processor.Emit(OpCodes.Ldarg_2); processor.Emit(OpCodes.Ldc_I4_M1); var targetConstructor = typeHUDMessage.Methods.FirstOrDefault(item => { if (item.Parameters.Count == 3 && item.Parameters[0].ParameterType.FullName == "System.String" && item.Parameters[1].ParameterType.FullName == "System.Int32" && item.Parameters[1].ParameterType.FullName == "System.Int32") return true; return false; }); processor.Emit(OpCodes.Call, targetConstructor); processor.Emit(OpCodes.Ret); typeHUDMessage.Methods.Add(hudConstructor); // Back Button Fix var method = typeGame1.Methods.FirstOrDefault(m => m.Name == "_updateAndroidMenus"); processor = method.Body.GetILProcessor(); var inputField = this.GetFieldReference("input", "StardewValley.Game1", this.StardewValley); processor.Replace(method.Body.Instructions[0], processor.Create(OpCodes.Ldsfld, inputField)); var typeInputState = this.StardewValley.MainModule.GetType("StardewValley.InputState"); var GetGamePadState = typeInputState.Methods.FirstOrDefault(m => m.Name == "GetGamePadState"); processor.Replace(method.Body.Instructions[1], processor.Create(OpCodes.Callvirt, GetGamePadState)); } public void ApplyCommonMidHookEntry(TypeDefinition targetType, Func methodChecker, Func jointPointChecker, int jointPointOffset, string hookname) { byte i, j; var targetMethod = targetType.Methods.FirstOrDefault(method => methodChecker(method)); string qualifyName = targetType.FullName + "." + targetMethod.Name + "_mid"; var prefixHook = this.StardewValley.MainModule.GetType("StardewValley.ModHooks").Methods .FirstOrDefault(m => m.Name == hookname + "_Prefix"); var iLProcessor = targetMethod.Body.GetILProcessor(); var field = this.GetFieldReference("hooks", "StardewValley.Game1", this.StardewValley); var instructions = new List(); byte returnIndex = 0; byte parameterIndexBegin = 0; byte parameterIndexEnd = 0; byte parameterOffset = targetMethod.IsStatic ? (byte) 0 : (byte) 1; byte stateIndex = (byte) targetMethod.Body.Variables.Count; targetMethod.Body.Variables.Add(new VariableDefinition(this.GetTypeReference("System.Boolean"))); if (targetMethod.ReturnType.FullName != "System.Void") { returnIndex = (byte) targetMethod.Body.Variables.Count; targetMethod.Body.Variables.Add(new VariableDefinition(this.GetTypeReference("System.Object"))); } parameterIndexBegin = (byte) targetMethod.Body.Variables.Count; for (i = 0; i < targetMethod.Parameters.Count; i = (byte) (i + 1)) { targetMethod.Body.Variables.Add(new VariableDefinition(this.GetTypeReference("System.Object"))); parameterIndexEnd = (byte) targetMethod.Body.Variables.Count; } if (prefixHook != null) { var jointPoint = targetMethod.Body.Instructions.FirstOrDefault(ins => jointPointChecker(ins)); for (int x = jointPointOffset; x < 0; x++) jointPoint = jointPoint.Previous; for (int x = 0; x < jointPointOffset; x++) jointPoint = jointPoint.Next; i = parameterOffset; for (j = parameterIndexBegin; i < targetMethod.Parameters.Count + parameterOffset; j = (byte) (j + 1)) { instructions.Add(this._createLdargsInstruction(iLProcessor, i)); if (targetMethod.Parameters[i - parameterOffset].ParameterType.IsValueType) instructions.Add(iLProcessor.Create(OpCodes.Box, targetMethod.Parameters[i - parameterOffset].ParameterType)); instructions.Add(this._createStlocInstruction(iLProcessor, j)); i = (byte) (i + 1); } instructions.Add(iLProcessor.Create(OpCodes.Ldsfld, field)); instructions.Add(iLProcessor.Create(OpCodes.Ldstr, qualifyName)); if (!targetMethod.IsStatic) { instructions.Add(iLProcessor.Create(OpCodes.Ldarg_0)); if (targetType.IsValueType) instructions.Add(iLProcessor.Create(OpCodes.Box, targetType)); } i = parameterOffset; for (j = parameterIndexBegin; i < targetMethod.Parameters.Count + parameterOffset; j = (byte) (j + 1)) { instructions.Add(iLProcessor.Create(OpCodes.Ldloca_S, j)); i = (byte) (i + 1); } while (i < prefixHook.Parameters.Count - 2) { instructions.Add(iLProcessor.Create(OpCodes.Ldloca_S, (byte) 0)); i = (byte) (i + 1); } instructions.Add(iLProcessor.Create(OpCodes.Ldloca_S, returnIndex)); instructions.Add(iLProcessor.Create(OpCodes.Callvirt, prefixHook)); instructions.Add(this._createStlocInstruction(iLProcessor, stateIndex)); i = parameterOffset; for (j = parameterIndexBegin; i < targetMethod.Parameters.Count + parameterOffset; j = (byte) (j + 1)) { instructions.Add(this._createLdlocInstruction(iLProcessor, j)); if (targetMethod.Parameters[i - parameterOffset].ParameterType.IsValueType) instructions.Add(iLProcessor.Create(OpCodes.Unbox_Any, targetMethod.Parameters[i - parameterOffset].ParameterType)); else instructions.Add(iLProcessor.Create(OpCodes.Castclass, targetMethod.Parameters[i - parameterOffset].ParameterType)); instructions.Add(iLProcessor.Create(OpCodes.Starg_S, i)); i = (byte) (i + 1); } instructions.Add(this._createLdlocInstruction(iLProcessor, stateIndex)); instructions.Add(iLProcessor.Create(OpCodes.Brtrue, jointPoint)); if (targetMethod.ReturnType.FullName != "System.Void") { instructions.Add(this._createLdlocInstruction(iLProcessor, returnIndex)); if (targetMethod.ReturnType.IsValueType) instructions.Add(iLProcessor.Create(OpCodes.Unbox_Any, targetMethod.ReturnType)); else instructions.Add(iLProcessor.Create(OpCodes.Castclass, targetMethod.ReturnType)); } instructions.Add(iLProcessor.Create(OpCodes.Ret)); this.InsertInstructions(iLProcessor, jointPoint, instructions); } } public void ApplyCommonHookEntry(TypeDefinition targetType, string methodname, string hookname, bool patchPrefix = true, bool patchPostfix = true, Func methodFilter = null, string qualifyNameSuffix = "", FieldReference hooksField = null, MethodReference prefixHook = null, MethodReference postfixHook = null) { string qualifyName = $"{targetType.FullName}.{methodname}{qualifyNameSuffix}"; var targetMethod = targetType.Methods.FirstOrDefault(method => method.HasBody && method.Name == methodname && (methodFilter == null || methodFilter(method))); var typeModHooksObject = this.StardewValley.MainModule.GetType("StardewValley.ModHooks"); if(prefixHook == null) prefixHook = typeModHooksObject.Methods.FirstOrDefault(m => m.Name == hookname + "_Prefix"); if (postfixHook == null) postfixHook = typeModHooksObject.Methods.FirstOrDefault(m => m.Name == hookname + "_Postfix"); var processor = targetMethod.Body.GetILProcessor(); if(hooksField == null) hooksField = this.GetFieldReference("hooks", "StardewValley.Game1", this.StardewValley); Instruction jointPoint; List instructions; byte i, j, k; // state byte returnIndex = 0; byte parameterIndexBegin = 0, parameterIndexEnd = 0; byte parameterOffset = targetMethod.IsStatic ? (byte) 0 : (byte) 1; byte stateIndex = (byte) targetMethod.Body.Variables.Count; targetMethod.Body.Variables.Add(new VariableDefinition(this.GetTypeReference("System.Boolean"))); if (targetMethod.ReturnType.FullName != "System.Void") { // return returnIndex = (byte) targetMethod.Body.Variables.Count; targetMethod.Body.Variables.Add(new VariableDefinition(this.GetTypeReference("System.Object"))); } parameterIndexBegin = (byte) targetMethod.Body.Variables.Count; for (i = 0; i < targetMethod.Parameters.Count; i++) { targetMethod.Body.Variables.Add(new VariableDefinition(this.GetTypeReference("System.Object"))); parameterIndexEnd = (byte) targetMethod.Body.Variables.Count; } if (patchPrefix && prefixHook != null) { instructions = new List(); jointPoint = targetMethod.Body.Instructions[0]; k = (byte) (targetMethod.Parameters.Count + parameterOffset > prefixHook.Parameters.Count - 2 ? prefixHook.Parameters.Count - 2 - parameterOffset : targetMethod.Parameters.Count + parameterOffset); for (i = parameterOffset, j = parameterIndexBegin; i < k; i++, j++) { instructions.Add(this._createLdargsInstruction(processor, i)); if (targetMethod.Parameters[i - parameterOffset].ParameterType.IsValueType) instructions.Add(processor.Create(OpCodes.Box, targetMethod.Parameters[i - parameterOffset].ParameterType)); instructions.Add(this._createStlocInstruction(processor, j)); } instructions.Add(processor.Create(OpCodes.Ldsfld, hooksField)); instructions.Add(processor.Create(OpCodes.Ldstr, qualifyName)); if (!targetMethod.IsStatic) { instructions.Add(processor.Create(OpCodes.Ldarg_0)); if (targetType.IsValueType) instructions.Add(processor.Create(OpCodes.Box, targetType)); } k = (byte) (targetMethod.Parameters.Count + parameterOffset > prefixHook.Parameters.Count - 2 ? prefixHook.Parameters.Count - 2 - parameterOffset : targetMethod.Parameters.Count + parameterOffset); for (i = parameterOffset, j = parameterIndexBegin; i < k; i++, j++) instructions.Add(processor.Create(OpCodes.Ldloca_S, j)); for (; i < prefixHook.Parameters.Count - 2; i++) instructions.Add(processor.Create(OpCodes.Ldloca_S, (byte) 0)); instructions.Add(processor.Create(OpCodes.Ldloca_S, returnIndex)); instructions.Add(processor.Create(OpCodes.Callvirt, prefixHook)); instructions.Add(this._createStlocInstruction(processor, stateIndex)); for (i = parameterOffset, j = parameterIndexBegin; i < targetMethod.Parameters.Count + parameterOffset; i++, j++) { instructions.Add(this._createLdlocInstruction(processor, j)); if (targetMethod.Parameters[i - parameterOffset].ParameterType.IsValueType) instructions.Add(processor.Create(OpCodes.Unbox_Any, targetMethod.Parameters[i - parameterOffset].ParameterType)); else instructions.Add(processor.Create(OpCodes.Castclass, targetMethod.Parameters[i - parameterOffset].ParameterType)); instructions.Add(processor.Create(OpCodes.Starg_S, i)); } instructions.Add(this._createLdlocInstruction(processor, stateIndex)); instructions.Add(processor.Create(OpCodes.Brtrue, jointPoint)); if (targetMethod.ReturnType.FullName != "System.Void") { instructions.Add(this._createLdlocInstruction(processor, returnIndex)); if (targetMethod.ReturnType.IsValueType) instructions.Add(processor.Create(OpCodes.Unbox_Any, targetMethod.ReturnType)); else instructions.Add(processor.Create(OpCodes.Castclass, targetMethod.ReturnType)); } instructions.Add(processor.Create(OpCodes.Ret)); this.InsertInstructions(processor, jointPoint, instructions); } if (patchPostfix && postfixHook != null) { instructions = new List(); jointPoint = targetMethod.Body.Instructions[targetMethod.Body.Instructions.Count - 1]; if (targetMethod.ReturnType.FullName != "System.Void") { instructions.Add(processor.Create(OpCodes.Box, targetMethod.ReturnType)); instructions.Add(this._createStlocInstruction(processor, returnIndex)); } k = (byte) (targetMethod.Parameters.Count + parameterOffset > postfixHook.Parameters.Count - 3 ? prefixHook.Parameters.Count - 3 - parameterOffset : targetMethod.Parameters.Count + parameterOffset); for (i = parameterOffset, j = parameterIndexBegin; i < k; i++, j++) { instructions.Add(this._createLdargsInstruction(processor, i)); if (targetMethod.Parameters[i - parameterOffset].ParameterType.IsValueType) instructions.Add(processor.Create(OpCodes.Box, targetMethod.Parameters[i - parameterOffset].ParameterType)); instructions.Add(this._createStlocInstruction(processor, j)); } instructions.Add(processor.Create(OpCodes.Ldsfld, hooksField)); instructions.Add(processor.Create(OpCodes.Ldstr, qualifyName)); if (!targetMethod.IsStatic) { instructions.Add(processor.Create(OpCodes.Ldarg_0)); if (targetType.IsValueType) instructions.Add(processor.Create(OpCodes.Box, targetType)); } k = (byte) (targetMethod.Parameters.Count + parameterOffset > postfixHook.Parameters.Count - 3 ? prefixHook.Parameters.Count - 3 - parameterOffset : targetMethod.Parameters.Count + parameterOffset); for (i = parameterOffset, j = parameterIndexBegin; i < k; i++, j++) instructions.Add(processor.Create(OpCodes.Ldloca_S, j)); for (; i < postfixHook.Parameters.Count - 3; i++) instructions.Add(processor.Create(OpCodes.Ldloca_S, (byte) 0)); if (targetMethod.ReturnType.FullName != "System.Void") { instructions.Add(processor.Create(OpCodes.Ldloca_S, stateIndex)); instructions.Add(processor.Create(OpCodes.Ldloca_S, returnIndex)); instructions.Add(processor.Create(OpCodes.Callvirt, postfixHook)); instructions.Add(this._createLdlocInstruction(processor, returnIndex)); if (targetMethod.ReturnType.IsValueType) instructions.Add(processor.Create(OpCodes.Unbox_Any, targetMethod.ReturnType)); else instructions.Add(processor.Create(OpCodes.Castclass, targetMethod.ReturnType)); } else { instructions.Add(processor.Create(OpCodes.Ldloca_S, stateIndex)); instructions.Add(processor.Create(OpCodes.Ldloca_S, returnIndex)); instructions.Add(processor.Create(OpCodes.Callvirt, postfixHook)); } this.InsertInstructions(processor, jointPoint, instructions); for (int x = 0; x < targetMethod.Body.Instructions.Count - 1; x++) { var origin = targetMethod.Body.Instructions[x]; _patchPostfixReturn(processor, instructions, origin); } } } public void ApplyHookEntry(TypeDefinition targetType, string methodname, string hookname, bool isPrefix) { var typeModHooksObject = this.StardewValley.MainModule.GetType("StardewValley.ModHooks"); var hook = typeModHooksObject.Methods.FirstOrDefault(m => m.Name == hookname); foreach (var method in targetType.Methods) if (!method.IsConstructor && method.HasBody && method.Name == methodname) { var processor = method.Body.GetILProcessor(); Instruction jointPoint; var instructions = new List(); var hooksField = this.GetFieldReference("hooks", "StardewValley.Game1", this.StardewValley); if (!isPrefix) { jointPoint = method.Body.Instructions[method.Body.Instructions.Count - 1]; if (method.ReturnType.FullName != "System.Void") { method.Body.Variables.Add(new VariableDefinition(method.ReturnType)); instructions.Add(this._createStlocInstruction(processor, (byte) (method.Body.Variables.Count - 1))); } instructions.Add(processor.Create(OpCodes.Ldsfld, hooksField)); if (!method.IsStatic) instructions.Add(processor.Create(OpCodes.Ldarg_0)); for (byte i = method.IsStatic ? (byte) 0 : (byte) 1; i < method.Parameters.Count + (method.IsStatic ? (byte) 0 : (byte) 1); i++) instructions.Add(this._createLdargsInstruction(processor, i)); if (method.ReturnType.FullName != "System.Void") { instructions.Add(processor.Create(OpCodes.Ldloca_S, (byte) (method.Body.Variables.Count - 1))); instructions.Add(processor.Create(OpCodes.Callvirt, hook)); instructions.Add(this._createLdlocInstruction(processor, (byte) (method.Body.Variables.Count - 1))); } else instructions.Add(processor.Create(OpCodes.Callvirt, hook)); for (int i = 0; i < method.Body.Instructions.Count - 1; i++) { var origin = method.Body.Instructions[i]; _patchPostfixReturn(processor, instructions, origin); } } else { jointPoint = method.Body.Instructions[0]; if (method.ReturnType.FullName != "System.Void") method.Body.Variables.Add(new VariableDefinition(method.ReturnType)); instructions.Add(processor.Create(OpCodes.Ldsfld, hooksField)); if (!method.IsStatic) instructions.Add(processor.Create(OpCodes.Ldarg_0)); for (byte i = method.IsStatic ? (byte) 0 : (byte) 1; i < method.Parameters.Count + (method.IsStatic ? (byte) 0 : (byte) 1); i++) instructions.Add(this._createLdargsInstruction(processor, i)); if (method.ReturnType.FullName != "System.Void") { instructions.Add(processor.Create(OpCodes.Ldloca_S, (byte) (method.Body.Variables.Count - 1))); instructions.Add(processor.Create(OpCodes.Callvirt, hook)); instructions.Add(processor.Create(OpCodes.Brtrue, jointPoint)); instructions.Add(this._createLdlocInstruction(processor, (byte) (method.Body.Variables.Count - 1))); instructions.Add(processor.Create(OpCodes.Ret)); } else { instructions.Add(processor.Create(OpCodes.Callvirt, hook)); instructions.Add(processor.Create(OpCodes.Brtrue, jointPoint)); instructions.Add(processor.Create(OpCodes.Ret)); } } this.InsertInstructions(processor, jointPoint, instructions); } } private static void _patchPostfixReturn(ILProcessor processor, List instructions, Instruction origin) { if (origin.OpCode == OpCodes.Ret) processor.Replace(origin, processor.Create(OpCodes.Br, instructions[0])); else { if (origin.OpCode == OpCodes.Br && ((Instruction) origin.Operand).OpCode == OpCodes.Ret || origin.OpCode == OpCodes.Br_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Br; origin.Operand = instructions[0]; } else if (origin.OpCode == OpCodes.Brfalse && ((Instruction) origin.Operand).OpCode == OpCodes.Ret || origin.OpCode == OpCodes.Brfalse_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Brfalse; origin.Operand = instructions[0]; } else if (origin.OpCode == OpCodes.Brtrue && ((Instruction) origin.Operand).OpCode == OpCodes.Ret || origin.OpCode == OpCodes.Brtrue_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Brtrue; origin.Operand = instructions[0]; } else if (origin.OpCode == OpCodes.Beq && ((Instruction) origin.Operand).OpCode == OpCodes.Ret || origin.OpCode == OpCodes.Beq_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Beq; origin.Operand = instructions[0]; } else if (origin.OpCode == OpCodes.Bge && ((Instruction) origin.Operand).OpCode == OpCodes.Ret || origin.OpCode == OpCodes.Bge_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Bge; origin.Operand = instructions[0]; } else if (origin.OpCode == OpCodes.Bge_Un && ((Instruction) origin.Operand).OpCode == OpCodes.Ret || origin.OpCode == OpCodes.Bge_Un_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Bge_Un; origin.Operand = instructions[0]; } else if (origin.OpCode == OpCodes.Bgt && ((Instruction) origin.Operand).OpCode == OpCodes.Ret || origin.OpCode == OpCodes.Bgt_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Bgt; origin.Operand = instructions[0]; } else if (origin.OpCode == OpCodes.Bgt_Un && ((Instruction) origin.Operand).OpCode == OpCodes.Ret || origin.OpCode == OpCodes.Bgt_Un_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Bgt_Un; origin.Operand = instructions[0]; } else if (origin.OpCode == OpCodes.Ble && ((Instruction) origin.Operand).OpCode == OpCodes.Ret || origin.OpCode == OpCodes.Ble_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Ble; origin.Operand = instructions[0]; } else if (origin.OpCode == OpCodes.Ble_Un && ((Instruction) origin.Operand).OpCode == OpCodes.Ret || origin.OpCode == OpCodes.Ble_Un_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Ble_Un; origin.Operand = instructions[0]; } else if (origin.OpCode == OpCodes.Blt && ((Instruction) origin.Operand).OpCode == OpCodes.Ret || origin.OpCode == OpCodes.Blt_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Blt; origin.Operand = instructions[0]; } else if (origin.OpCode == OpCodes.Blt_Un && ((Instruction) origin.Operand).OpCode == OpCodes.Ret || origin.OpCode == OpCodes.Blt_Un_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Blt_Un; origin.Operand = instructions[0]; } else if (origin.OpCode == OpCodes.Bne_Un && ((Instruction) origin.Operand).OpCode == OpCodes.Ret || origin.OpCode == OpCodes.Bne_Un_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Bne_Un; origin.Operand = instructions[0]; } else if (origin.OpCode == OpCodes.Leave && ((Instruction) origin.Operand).OpCode == OpCodes.Ret || origin.OpCode == OpCodes.Leave_S && ((Instruction) origin.Operand).OpCode == OpCodes.Ret) { origin.OpCode = OpCodes.Leave; origin.Operand = instructions[0]; } } } private Instruction _createStlocInstruction(ILProcessor processor, byte index) { switch (index) { case 0: return processor.Create(OpCodes.Stloc_0); case 1: return processor.Create(OpCodes.Stloc_1); case 2: return processor.Create(OpCodes.Stloc_2); case 3: return processor.Create(OpCodes.Stloc_3); default: return processor.Create(OpCodes.Stloc_S, index); } } private Instruction _createLdlocInstruction(ILProcessor processor, byte index) { switch (index) { case 0: return processor.Create(OpCodes.Ldloc_0); case 1: return processor.Create(OpCodes.Ldloc_1); case 2: return processor.Create(OpCodes.Ldloc_2); case 3: return processor.Create(OpCodes.Ldloc_3); default: return processor.Create(OpCodes.Ldloc_S, index); } } private Instruction _createLdargsInstruction(ILProcessor processor, byte index) { switch (index) { case 0: return processor.Create(OpCodes.Ldarg_0); case 1: return processor.Create(OpCodes.Ldarg_1); case 2: return processor.Create(OpCodes.Ldarg_2); case 3: return processor.Create(OpCodes.Ldarg_3); default: return processor.Create(OpCodes.Ldarg_S, index); } } private void InsertInstructions(ILProcessor processor, Instruction jointPoint, List instructions) { foreach (var instruction in instructions) processor.InsertBefore(jointPoint, instruction); } public AssemblyDefinition InsertModHooks() { this.ApplyGamePatch(); this.InsertModHook("OnCommonHook_Prefix", new[] { this.GetTypeReference("System.String"), this.GetTypeReference("System.Object"), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")) }, new[] { "hookName", "__instance", "param1", "param2", "param3", "param4", "__result" }, this.GetTypeReference("System.Boolean")); this.InsertModHook("OnCommonStaticHook_Prefix", new[] { this.GetTypeReference("System.String"), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")) }, new[] { "hookName", "param1", "param2", "param3", "param4", "param5", "__result" }, this.GetTypeReference("System.Boolean")); this.InsertModHook("OnCommonHook_Postfix", new[] { this.GetTypeReference("System.String"), this.GetTypeReference("System.Object"), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Boolean")), new ByReferenceType(this.GetTypeReference("System.Object")) }, new[] { "hookName", "__instance", "param1", "param2", "param3", "param4", "__state", "__result" }, this.GetTypeReference("System.Void")); this.InsertModHook("OnCommonStaticHook_Postfix", new[] { this.GetTypeReference("System.String"), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Boolean")), new ByReferenceType(this.GetTypeReference("System.Object")) }, new[] { "hookName", "param1", "param2", "param3", "param4", "param5", "__state", "__result" }, this.GetTypeReference("System.Void")); this.InsertModHook("OnCommonHook10_Prefix", new[] { this.GetTypeReference("System.String"), this.GetTypeReference("System.Object"), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")) }, new[] { "hookName", "__instance", "param1", "param2", "param3", "param4", "param5", "param6", "param7", "param8", "param9", "__result" }, this.GetTypeReference("System.Boolean")); this.InsertModHook("OnCommonStaticHook10_Prefix", new[] { this.GetTypeReference("System.String"), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")) }, new[] { "hookName", "param1", "param2", "param3", "param4", "param5", "param6", "param7", "param8", "param9", "param10", "__result" }, this.GetTypeReference("System.Boolean")); this.InsertModHook("OnCommonHook10_Postfix", new[] { this.GetTypeReference("System.String"), this.GetTypeReference("System.Object"), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Boolean")), new ByReferenceType(this.GetTypeReference("System.Object")) }, new[] { "hookName", "__instance", "param1", "param2", "param3", "param4", "param5", "param6", "param7", "param8", "param9", "__state", "__result" }, this.GetTypeReference("System.Void")); this.InsertModHook("OnCommonStaticHook10_Postfix", new[] { this.GetTypeReference("System.String"), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Object")), new ByReferenceType(this.GetTypeReference("System.Boolean")), new ByReferenceType(this.GetTypeReference("System.Object")) }, new[] { "hookName", "param1", "param2", "param3", "param4", "param5", "param6", "param7", "param8", "param9", "param10", "__state", "__result" }, this.GetTypeReference("System.Void")); // On Game1 hooks var typeGame1 = this.StardewValley.MainModule.GetType("StardewValley.Game1"); this.ApplyCommonHookEntry(typeGame1, "getSourceRectForStandardTileSheet", "OnCommonStaticHook"); this.ApplyCommonHookEntry(typeGame1, "tryToCheckAt", "OnCommonStaticHook", false); this.ApplyCommonHookEntry(typeGame1, "getLocationRequest", "OnCommonStaticHook", true, false); this.ApplyCommonHookEntry(typeGame1, "loadForNewGame", "OnCommonStaticHook"); this.ApplyCommonHookEntry(typeGame1, "warpFarmer", "OnCommonStaticHook10", true, false, method => method.Parameters.Count == 6); this.ApplyCommonHookEntry(typeGame1, "saveWholeBackup", "OnCommonStaticHook"); this.ApplyCommonHookEntry(typeGame1, "MakeFullBackup", "OnCommonStaticHook"); TypeDefinition targetType = null; foreach (var definition21 in this.StardewValley.MainModule.GetTypes()) if (definition21.FullName == "StardewValley.Game1/<>c") targetType = definition21; this.ApplyCommonMidHookEntry(targetType, method => method.FullName.Contains("showEndOfNightStuff"), ins => ins.OpCode == OpCodes.Ldstr && (string) ins.Operand == "newRecord", -2, "OnCommonHook"); // On Object hooks var typeObject = this.StardewValley.MainModule.GetType("StardewValley.Object"); this.ApplyCommonHookEntry(typeObject, "canBePlacedHere", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeObject, "checkForAction", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeObject, "isIndexOkForBasicShippedCategory", "OnCommonStaticHook"); this.ApplyCommonHookEntry(typeObject, "drawWhenHeld", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeObject, "drawInMenuWithColour", "OnCommonHook10"); this.ApplyCommonHookEntry(typeObject, "draw", "OnCommonHook", true, false, method => method.Parameters.Count == 4); this.ApplyCommonHookEntry(typeObject, "draw", "OnCommonHook10", true, false, method => method.Parameters.Count == 5); this.ApplyCommonHookEntry(typeObject, "getDescription", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeObject, "maximumStackSize", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeObject, "addToStack", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeObject, "performObjectDropInAction", "OnCommonHook", true, false); // On ReadyCheckDialog hooks var typeReadyCheckDialog = this.StardewValley.MainModule.GetType("StardewValley.Menus.ReadyCheckDialog"); this.ApplyCommonHookEntry(typeReadyCheckDialog, "update", "OnCommonHook"); // On IClickableMenu hooks var typeIClickableMenu = this.StardewValley.MainModule.GetType("StardewValley.Menus.IClickableMenu"); this.ApplyCommonHookEntry(typeIClickableMenu, "drawToolTip", "OnCommonStaticHook10", false); // On Dialogue hooks var typeDialogue = this.StardewValley.MainModule.GetType("StardewValley.Dialogue"); this.ApplyCommonHookEntry(typeDialogue, ".ctor", "OnCommonHook", true, false, method => method.Parameters.Count == 2); // On CraftingRecipe hooks var typeCraftingRecipe = this.StardewValley.MainModule.GetType("StardewValley.CraftingRecipe"); this.ApplyCommonHookEntry(typeCraftingRecipe, "consumeIngredients", "OnCommonHook", true, false); // On Building hooks var typeBuilding = this.StardewValley.MainModule.GetType("StardewValley.Buildings.Building"); this.ApplyCommonHookEntry(typeBuilding, "load", "OnCommonHook"); // On GameLocation hooks var typeGameLocation = this.StardewValley.MainModule.GetType("StardewValley.GameLocation"); this.ApplyCommonHookEntry(typeGameLocation, "performTouchAction", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeGameLocation, "isActionableTile", "OnCommonHook", false); this.ApplyCommonHookEntry(typeGameLocation, "tryToAddCritters", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeGameLocation, "getSourceRectForObject", "OnCommonStaticHook"); this.ApplyCommonHookEntry(typeGameLocation, "answerDialogue", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeGameLocation, "Equals", "OnCommonHook", true, false, method => method.Parameters[0].ParameterType == this.GetTypeReference("StardewValley.GameLocation", this.StardewValley)); this.ApplyCommonHookEntry(typeGameLocation, "performAction", "OnCommonHook", true, false); // On Objects.TV hooks var typeObjectsTV = this.StardewValley.MainModule.GetType("StardewValley.Objects.TV"); this.ApplyCommonHookEntry(typeObjectsTV, "checkForAction", "OnCommonHook"); // On Furniture hooks var typeFurniture = this.StardewValley.MainModule.GetType("StardewValley.Objects.Furniture"); this.ApplyCommonHookEntry(typeFurniture, "draw", "OnCommonHook", true, false); // On ColoredObject hooks var typeColoredObject = this.StardewValley.MainModule.GetType("StardewValley.Objects.ColoredObject"); this.ApplyCommonHookEntry(typeColoredObject, "drawInMenu", "OnCommonHook10", false); // On HoeDirt hooks var typeHoeDirt = this.StardewValley.MainModule.GetType("StardewValley.TerrainFeatures.HoeDirt"); this.ApplyCommonHookEntry(typeHoeDirt, "dayUpdate", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeHoeDirt, "canPlantThisSeedHere", "OnCommonHook"); // On Tree hooks var typeTree = this.StardewValley.MainModule.GetType("StardewValley.TerrainFeatures.Tree"); this.ApplyCommonHookEntry(typeTree, "performToolAction", "OnCommonHook", true, false); // On FruitTree hooks var typeFruitTree = this.StardewValley.MainModule.GetType("StardewValley.TerrainFeatures.FruitTree"); this.ApplyCommonHookEntry(typeFruitTree, "performToolAction", "OnCommonHook", true, false); // On ResourceClump hooks var typeResourceClump = this.StardewValley.MainModule.GetType("StardewValley.TerrainFeatures.ResourceClump"); this.ApplyCommonHookEntry(typeResourceClump, "performToolAction", "OnCommonHook", true, false); // On Crop hooks var typeCrop = this.StardewValley.MainModule.GetType("StardewValley.Crop"); this.ApplyCommonHookEntry(typeCrop, "newDay", "OnCommonHook10", true, false); // On Utility hooks var typeUtility = this.StardewValley.MainModule.GetType("StardewValley.Utility"); this.ApplyCommonHookEntry(typeUtility, "pickFarmEvent", "OnCommonStaticHook", false); // On Farmer hooks var typeFarmer = this.StardewValley.MainModule.GetType("StardewValley.Farmer"); this.ApplyCommonHookEntry(typeFarmer, "doneEating", "OnCommonHook", false); this.ApplyCommonHookEntry(typeFarmer, "hasItemInInventory", "OnCommonHook", false); this.ApplyCommonHookEntry(typeFarmer, "getTallyOfObject", "OnCommonHook", false); // On MeleeWeapon hooks var typeMeleeWeapon = this.StardewValley.MainModule.GetType("StardewValley.Tools.MeleeWeapon"); this.ApplyCommonHookEntry(typeMeleeWeapon, "drawDuringUse", "OnCommonStaticHook10", true, false, method => method.IsStatic); // On Multiplayer hooks var typeMultiplayer = this.StardewValley.MainModule.GetType("StardewValley.Multiplayer"); this.ApplyCommonHookEntry(typeMultiplayer, "processIncomingMessage", "OnCommonHook", true, false); // On GameServer hooks var typeGameServer = this.StardewValley.MainModule.GetType("StardewValley.Network.GameServer"); this.ApplyCommonHookEntry(typeGameServer, "sendServerIntroduction", "OnCommonHook", false); // On NPC hooks var typeNPC = this.StardewValley.MainModule.GetType("StardewValley.NPC"); this.ApplyCommonHookEntry(typeNPC, "receiveGift", "OnCommonHook10", false); // On Farm hooks var typeFarm = this.StardewValley.MainModule.GetType("StardewValley.Farm"); this.ApplyCommonHookEntry(typeFarm, "addCrows", "OnCommonHook", true, false); // On GameMenu hooks var typeGameMenu = this.StardewValley.MainModule.GetType("StardewValley.Menus.GameMenu"); this.ApplyCommonHookEntry(typeGameMenu, "getTabNumberFromName", "OnCommonHook", false); // On FarmHouse hooks var typeFarmHouse = this.StardewValley.MainModule.GetType("StardewValley.Locations.FarmHouse"); this.ApplyCommonHookEntry(typeFarmHouse, "loadSpouseRoom", "OnCommonHook", true, false); // On Tool hooks var typeTool = this.StardewValley.MainModule.GetType("StardewValley.Tool"); this.ApplyCommonHookEntry(typeTool, "tilesAffected", "OnCommonHook", false); this.ApplyCommonHookEntry(typeTool, "get_Name", "OnCommonHook", true, false); this.ApplyCommonHookEntry(typeTool, "get_DisplayName", "OnCommonHook", true, false); // On Pickaxe hooks var typePickaxe = this.StardewValley.MainModule.GetType("StardewValley.Tools.Pickaxe"); this.ApplyCommonHookEntry(typePickaxe, "DoFunction", "OnCommonHook10", true, false); return this.StardewValley; } public AssemblyDefinition InsertMonoHooks() { // On Content hooks var typeTitleContainer = this.MonoGame_Framework.MainModule.GetType("Microsoft.Xna.Framework.TitleContainer"); var openStreamMethod = typeTitleContainer.Methods.FirstOrDefault(m => m.Name == "OpenStream"); var processor = openStreamMethod.Body.GetILProcessor(); var jointPoint = openStreamMethod.Body.Instructions[9]; processor.InsertBefore(jointPoint, processor.Create(OpCodes.Ldarg_0)); processor.InsertBefore(jointPoint, processor.Create(OpCodes.Ldc_I4_S, (sbyte)92)); processor.InsertBefore(jointPoint, processor.Create(OpCodes.Ldc_I4_S, (sbyte)47)); var replaceMethod = this.GetMethodReference("Replace", "System.String", this.mscorlib, m => m.Parameters.Count == 2 && m.Parameters[0].ParameterType.FullName == "System.Char" && m.Parameters[1].ParameterType.FullName == "System.Char", this.MonoGame_Framework); processor.InsertBefore(jointPoint, processor.Create(OpCodes.Callvirt, replaceMethod)); processor.InsertBefore(jointPoint, processor.Create(OpCodes.Ldc_I4_3)); var constructor = this.GetMethodReference(".ctor", "System.IO.FileStream", this.mscorlib, m => m.Parameters.Count == 2 && m.Parameters[0].ParameterType.FullName == "System.String" && m.Parameters[1].ParameterType.FullName == "System.IO.FileMode", this.MonoGame_Framework); processor.InsertBefore(jointPoint, processor.Create(OpCodes.Newobj, constructor)); processor.InsertBefore(jointPoint, processor.Create(OpCodes.Ret)); // On SpriteBatch hooks var typeSpriteBatch = this.MonoGame_Framework.MainModule.GetType("Microsoft.Xna.Framework.Graphics.SpriteBatch"); FieldReference hooksField = this.MonoGame_Framework.MainModule.ImportReference(this.GetFieldReference("hooks", "StardewValley.Game1", this.StardewValley)); var typeModHooksObject = this.StardewValley.MainModule.GetType("StardewValley.ModHooks"); var prefixHook = typeModHooksObject.Methods.FirstOrDefault(m => m.Name == "OnCommonHook_Prefix"); var prefixHook10 = typeModHooksObject.Methods.FirstOrDefault(m => m.Name == "OnCommonHook10_Prefix"); var prefixHookRef = this.MonoGame_Framework.MainModule.ImportReference(prefixHook); var prefixHook10Ref = this.MonoGame_Framework.MainModule.ImportReference(prefixHook10); this.ApplyCommonHookEntry(typeSpriteBatch, "Draw", "OnCommonHook", true, false, method => method.Parameters.Count == 3 && method.Parameters[1].ParameterType.FullName == "Microsoft.Xna.Framework.Rectangle", "", hooksField, prefixHookRef); this.ApplyCommonHookEntry(typeSpriteBatch, "Draw", "OnCommonHook", true, false, method => method.Parameters.Count == 3 && method.Parameters[1].ParameterType.FullName == "Microsoft.Xna.Framework.Vector2", "Vector", hooksField, prefixHookRef); this.ApplyCommonHookEntry(typeSpriteBatch, "Draw", "OnCommonHook", true, false, method => method.Parameters.Count == 4 && method.Parameters[1].ParameterType.FullName == "Microsoft.Xna.Framework.Rectangle", "2", hooksField, prefixHookRef); this.ApplyCommonHookEntry(typeSpriteBatch, "Draw", "OnCommonHook", true, false, method => method.Parameters.Count == 4 && method.Parameters[1].ParameterType.FullName == "Microsoft.Xna.Framework.Vector2", "Vector2", hooksField, prefixHookRef); this.ApplyCommonHookEntry(typeSpriteBatch, "Draw", "OnCommonHook10", true, false, method => method.Parameters.Count == 8, "8", hooksField, prefixHook10Ref); this.ApplyCommonHookEntry(typeSpriteBatch, "Draw", "OnCommonHook10", true, false, method => method.Parameters.Count == 9 && method.Parameters[6].ParameterType.FullName == "Microsoft.Xna.Framework.Vector2", "9", hooksField, prefixHook10Ref); return this.MonoGame_Framework; } } }