Example #1
0
            /// <summary>Changes the spouse.Contains check to use spouse.Equals instead.</summary>
            public static IEnumerable <CIL> spouseContainsEquals_Transpiler(IEnumerable <CIL> instructions, MethodBase original)
            {
                try
                {
                    var codes      = new List <CodeInstruction>(instructions);
                    int patchCount = 0;

                    for (int i = 0; i < codes.Count - 4; i++)
                    {
                        // This is the snippet of code we want to find and change:
                        //     OLD Game1.player.spouse.Contains(this.Name) or who.spouse.Contains(this.Name) (who is Farmer)
                        //     NEW Game1.player.spouse.Equals(this.Name) or who.spouse.Equals(this.Name)
                        // It can be done with a single opcode call change from string.Contains to string.Equals

                        if (//callvirt instance string StardewValley.Farmer::get_spouse()
                            codes[i].opcode == OpCodes.Callvirt &&
                            (MethodInfo)codes[i].operand == typeof(Farmer).GetProperty("spouse").GetGetMethod() &&
                            //ldarg.0
                            codes[i + 1].opcode == OpCodes.Ldarg_0 &&
                            //call instance string StardewValley.Character::get_Name()
                            codes[i + 2].opcode == OpCodes.Call &&
                            (MethodInfo)codes[i + 2].operand == typeof(Character).GetProperty("Name").GetGetMethod() &&
                            //callvirt instance bool[mscorlib] System.String::Contains(string)
                            codes[i + 3].opcode == OpCodes.Callvirt &&
                            (MethodInfo)codes[i + 3].operand == typeof(string).GetMethod("Contains", new Type[] { typeof(string) }))
                        {
                            // Insert the new replacement instruction
                            codes[i + 3] = new CIL(OpCodes.Callvirt, typeof(string).GetMethod("Equals", new Type[] { typeof(string) }));
                            patchCount  += 1;
                        }
                    }
                    // Log results of the patch attempt
                    if (patchCount == 0)
                    {
                        ModMonitor.LogOnce($"Couldn't find a code location to apply {nameof(spouseContainsEquals_Transpiler)} patch to {original.DeclaringType.Name}.{original.Name}\n" +
                                           $"This is probably fine. Maybe a game update or another harmony mod already patched it?", LogLevel.Info);
                    }
                    else //patched at least once
                    {
                        //do stuff
                        ModMonitor.LogOnce($"Applied bugfix patch to {original.DeclaringType.Name}.{original.Name}: {nameof(spouseContainsEquals_Transpiler)}", LogLevel.Trace);

                        if (patchCount > 1)
                        {
                            ModMonitor.LogOnce($"Found an unusual number of patch locations for {nameof(spouseContainsEquals_Transpiler)} in {original.DeclaringType.Name}.{original.Name}\n" +
                                               $"This might cause unexpected behaviour. Please share your SMAPI log with the creator of this mod.", LogLevel.Warn);
                        }
                    }

                    return(codes.AsEnumerable());
                }
                catch (Exception ex)
                {
                    ModMonitor.Log($"Failed in {nameof(spouseContainsEquals_Transpiler)}:\n{ex}", LogLevel.Error);
                    return(instructions); // use original code
                }
            }
Example #2
0
            /// <summary>Adds a hook that calls FixedHUDMessage_Hook inside the game's addItemToInventoryBool method.</summary>
            public static IEnumerable<CIL> addItemToInventoryBool_Transpiler(IEnumerable<CIL> instructions)
            {
                try
                {
                    var codes = new List<CodeInstruction>(instructions);

                    for (int i = 0; i < codes.Count - 9; i++)
                    {
                        // This is the line of code we want to find and change:
                        //     Game1.addHUDMessage(new HUDMessage(displayName, Math.Max(1, item.Stack), true, color, item));
                        //     Game1.addHUDMessage(new HUDMessage(displayName, 1, true, color, item));
                        // We can most easily do this by replacing several instructions with nop

                        if (//ldloc.3
                            codes[i].opcode == OpCodes.Ldloc_3 &&
                            //ldc.i4.1                             // Replace with nop
                            codes[i + 1].opcode == OpCodes.Ldc_I4_1 &&
                            //ldarg.1                                // Replace with nop
                            codes[i + 2].opcode == OpCodes.Ldarg_1 &&
                            //callvirt instance int32 StardewValley.Item::get_Stack() // Replace with nop
                            codes[i + 3].opcode == OpCodes.Callvirt &&
                            (MethodInfo)codes[i + 3].operand == typeof(Item).GetMethod("get_Stack") &&
                            //call int32 [mscorlib]System.Math::Max(int32, int32) // Replace with Ldc_I4_1
                            codes[i + 4].opcode == OpCodes.Call &&
                            (MethodInfo)codes[i + 4].operand == typeof(Math).GetMethod("Max", types: new Type[] { typeof(int), typeof(int) }) &&
                            //ldc.i4.1
                            codes[i + 5].opcode == OpCodes.Ldc_I4_1 &&
                            //ldloc.2
                            codes[i + 6].opcode == OpCodes.Ldloc_2 &&
                            //ldarg.1
                            codes[i + 7].opcode == OpCodes.Ldarg_1 &&
                            //newobj instance void StardewValley.HUDMessage::.ctor(string, int32, bool, valuetype [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Color, class StardewValley.Item)
                            codes[i + 8].opcode == OpCodes.Newobj &&
                            (ConstructorInfo)codes[i + 8].operand == typeof(HUDMessage).GetConstructor(
                                types: new Type[] { typeof(string), typeof(int), typeof(bool), typeof(Color), typeof(Item) }) &&
                            //call void StardewValley.Game1::addHUDMessage(class StardewValley.HUDMessage)
                            codes[i + 9].opcode == OpCodes.Call &&
                            (MethodInfo)codes[i + 9].operand == typeof(Game1).GetMethod("addHUDMessage"))
                        {
                            // Compose the new instructions to use as replacements
                            codes[i + 8] = new CIL(OpCodes.Call, Instance.Helper.Reflection.GetMethod(
                                typeof(FarmerPatch), nameof(FixedHUDMessage_Hook)).MethodInfo); // Call my function that returns a fixed HUDMessage
                            ModMonitor.LogOnce($"Added hook to Farmer.addItemToInventoryBool method: {nameof(addItemToInventoryBool_Transpiler)}", LogLevel.Trace);
                        }
                    }
                    return codes.AsEnumerable();
                }
                catch (Exception ex)
                {
                    ModMonitor.Log($"Failed in {nameof(addItemToInventoryBool_Transpiler)}:\n{ex}", LogLevel.Error);
                    return instructions; // use original code
                }
            }
Example #3
0
 public static void PrintInstruction(Harmony.CodeInstruction instr)
 {
     if (instr.labels.Count > 0)
     {
         var           labels  = instr.labels.ToArray();
         StringBuilder builder = new StringBuilder();
         builder.Append($"LABELS : : : > {labels[0].GetHashCode()}");
         int index = 1;
         while (index < instr.labels.Count)
         {
             builder.Append($", {labels[index].GetHashCode()}");
             index++;
         }
         Debug(builder.ToString());
     }
     Debug($"INSTR : {instr.opcode,-10}\t : "
           + ((instr.operand != null && instr.operand.GetType() == typeof(Label))
                                 ? $": : > {instr.operand.GetHashCode(),-100}" : $"{instr.operand,-100}"));
 }
Example #4
0
 public CodeInstruction(CodeInstruction instruction)
 {
     opcode  = instruction.opcode;
     operand = instruction.operand;
     labels  = instruction.labels.ToArray().ToList();
 }
Example #5
0
 public CodeMatch(CodeInstruction instruction, string name = null)
     : this(instruction.opcode, instruction.operand, name)
 {
 }
Example #6
0
 public CodeMatcher SetInstructionAndAdvance(CodeInstruction instruction)
 {
     SetInstruction(instruction);
     Pos++;
     return(this);
 }
Example #7
0
        // edit operation -------------------------------------------------------

        public CodeMatcher SetInstruction(CodeInstruction instruction)
        {
            codes[Pos] = instruction;
            return(this);
        }
Example #8
0
 public virtual List <CodeInstruction> Process(CodeInstruction instruction)
 {
     return(null);
 }