Пример #1
0
        public static IEnumerable <CodeInstruction> FindItem(IEnumerable <CodeInstruction> instructions, ILGenerator iLGenerator)
        {
            LocalBuilder           itemFound        = iLGenerator.DeclareLocal(typeof(bool));
            LocalBuilder           lastUsedRadius   = iLGenerator.DeclareLocal(typeof(int));
            LocalBuilder           lengthHorizontal = iLGenerator.DeclareLocal(typeof(float));
            LocalBuilder           intvec3          = iLGenerator.DeclareLocal(typeof(IntVec3));
            LocalBuilder           radius           = iLGenerator.DeclareLocal(typeof(int[]));
            List <CodeInstruction> instructionsList = instructions.ToList();
            int   currentInstructionIndex           = 0;
            int   matchFound = 0;
            Label IL_00b2    = iLGenerator.DefineLabel();
            Label IL_0122    = iLGenerator.DefineLabel();

            while (currentInstructionIndex < instructionsList.Count)
            {
                if (currentInstructionIndex + 3 < instructionsList.Count &&
                    instructionsList[currentInstructionIndex + 3].opcode == OpCodes.Call &&
                    (MethodInfo)instructionsList[currentInstructionIndex + 3].operand == AccessTools.Method(RimThreadedHarmony.awesomeInventoryJobsJobGiver_FindItemByRadius, "Reset")
                    )
                {
                    matchFound++;
                    // float lengthHorizontal = pawn.Map.Size.LengthHorizontal;
                    instructionsList[currentInstructionIndex].opcode  = OpCodes.Ldloc_0;
                    instructionsList[currentInstructionIndex].operand = null;
                    yield return(instructionsList[currentInstructionIndex]);

                    //yield return new CodeInstruction(OpCodes.Ldloc_0);
                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(RimThreadedHarmony.awesomeInventoryJobsJobGiver_FindItemByRadiusSub, "pawn")));

                    yield return(new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(Thing), "get_Map")));

                    yield return(new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(Map), "get_Size")));

                    yield return(new CodeInstruction(OpCodes.Stloc_S, intvec3.LocalIndex));

                    yield return(new CodeInstruction(OpCodes.Ldloca_S, intvec3.LocalIndex));

                    yield return(new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(IntVec3), "get_LengthHorizontal")));

                    yield return(new CodeInstruction(OpCodes.Stloc, lengthHorizontal.LocalIndex));

                    //  int[] array = new int[3]
                    //  {
                    //      Mathf.FloorToInt(lengthHorizontal * _tinyRadiusFactor),
                    //      Mathf.FloorToInt(lengthHorizontal * _smallRadiusFactor),
                    //      Mathf.FloorToInt(lengthHorizontal * _mediumRadiusFactor)
                    //  };
                    yield return(new CodeInstruction(OpCodes.Ldc_I4_3));

                    yield return(new CodeInstruction(OpCodes.Newarr, typeof(int)));

                    yield return(new CodeInstruction(OpCodes.Dup));

                    yield return(new CodeInstruction(OpCodes.Ldc_I4_0));

                    yield return(new CodeInstruction(OpCodes.Ldloc, lengthHorizontal.LocalIndex));

                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(RimThreadedHarmony.awesomeInventoryJobsJobGiver_FindItemByRadius, "_tinyRadiusFactor")));

                    yield return(new CodeInstruction(OpCodes.Mul));

                    yield return(new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Mathf), "FloorToInt", new Type[] { typeof(float) })));

                    yield return(new CodeInstruction(OpCodes.Stelem_I4));

                    yield return(new CodeInstruction(OpCodes.Dup));

                    yield return(new CodeInstruction(OpCodes.Ldc_I4_1));

                    yield return(new CodeInstruction(OpCodes.Ldloc, lengthHorizontal.LocalIndex));

                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(RimThreadedHarmony.awesomeInventoryJobsJobGiver_FindItemByRadius, "_smallRadiusFactor")));

                    yield return(new CodeInstruction(OpCodes.Mul));

                    yield return(new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Mathf), "FloorToInt", new Type[] { typeof(float) })));

                    yield return(new CodeInstruction(OpCodes.Stelem_I4));

                    yield return(new CodeInstruction(OpCodes.Dup));

                    yield return(new CodeInstruction(OpCodes.Ldc_I4_2));

                    yield return(new CodeInstruction(OpCodes.Ldloc, lengthHorizontal.LocalIndex));

                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(RimThreadedHarmony.awesomeInventoryJobsJobGiver_FindItemByRadius, "_mediumRadiusFactor")));

                    yield return(new CodeInstruction(OpCodes.Mul));

                    yield return(new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Mathf), "FloorToInt", new Type[] { typeof(float) })));

                    yield return(new CodeInstruction(OpCodes.Stelem_I4));

                    yield return(new CodeInstruction(OpCodes.Stloc, radius.LocalIndex));

                    // int lastUsedRadius = _lastUsedRadiusIndex;
                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(RimThreadedHarmony.awesomeInventoryJobsJobGiver_FindItemByRadius, "_lastUsedRadiusIndex")));

                    yield return(new CodeInstruction(OpCodes.Stloc, lastUsedRadius.LocalIndex));

                    // if (_itemFound)
                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(RimThreadedHarmony.awesomeInventoryJobsJobGiver_FindItemByRadius, "_itemFound")));

                    yield return(new CodeInstruction(OpCodes.Stloc, itemFound.LocalIndex));

                    yield return(new CodeInstruction(OpCodes.Ldloc, itemFound.LocalIndex));

                    yield return(new CodeInstruction(OpCodes.Brfalse_S, IL_00b2));

                    // lastUsedRadius--;
                    yield return(new CodeInstruction(OpCodes.Ldloc, lastUsedRadius.LocalIndex));

                    yield return(new CodeInstruction(OpCodes.Ldc_I4_1));

                    yield return(new CodeInstruction(OpCodes.Sub));

                    yield return(new CodeInstruction(OpCodes.Stloc, itemFound.LocalIndex));

                    currentInstructionIndex += 24;
                }
                else if (currentInstructionIndex + 1 < instructionsList.Count &&
                         instructionsList[currentInstructionIndex].opcode == OpCodes.Ldnull &&
                         instructionsList[currentInstructionIndex + 1].opcode == OpCodes.Stloc_1
                         )
                {
                    matchFound++;
                    // if (lastUsedRadius < 0)
                    CodeInstruction codeInstruction = new CodeInstruction(OpCodes.Ldloc, lastUsedRadius.LocalIndex);
                    codeInstruction.labels.Add(IL_00b2);
                    yield return(codeInstruction);

                    yield return(new CodeInstruction(OpCodes.Ldc_I4_0));

                    Label IL_00b8 = iLGenerator.DefineLabel();
                    yield return(new CodeInstruction(OpCodes.Bge_S, IL_00b8));

                    // lastUsedRadius = 0;
                    yield return(new CodeInstruction(OpCodes.Ldc_I4_0));

                    yield return(new CodeInstruction(OpCodes.Stloc, lastUsedRadius.LocalIndex));

                    // bool itemFound = false;
                    codeInstruction = new CodeInstruction(OpCodes.Ldc_I4_0);
                    codeInstruction.labels.Add(IL_00b8);
                    yield return(codeInstruction);

                    yield return(new CodeInstruction(OpCodes.Stloc, itemFound.LocalIndex));

                    yield return(instructionsList[currentInstructionIndex]);

                    currentInstructionIndex++;
                }
                //_radius[_lastUsedRadiusIndex]
                else if (currentInstructionIndex + 4 < instructionsList.Count &&
                         instructionsList[currentInstructionIndex + 4].opcode == OpCodes.Callvirt &&
                         (MethodInfo)instructionsList[currentInstructionIndex + 4].operand == AccessTools.Method(typeof(List <int>), "get_Item")
                         )
                {
                    matchFound++;
                    // array[num]
                    yield return(new CodeInstruction(OpCodes.Ldloc, radius.LocalIndex));

                    yield return(new CodeInstruction(OpCodes.Ldloc, lastUsedRadius.LocalIndex));

                    yield return(new CodeInstruction(OpCodes.Ldelem_I4));

                    currentInstructionIndex += 5;
                }
                // _itemFound = true;
                else if (currentInstructionIndex + 2 < instructionsList.Count &&
                         instructionsList[currentInstructionIndex + 1].opcode == OpCodes.Ldc_I4_1 &&
                         instructionsList[currentInstructionIndex + 2].opcode == OpCodes.Stfld &&
                         (FieldInfo)instructionsList[currentInstructionIndex + 2].operand == AccessTools.Field(RimThreadedHarmony.awesomeInventoryJobsJobGiver_FindItemByRadius, "_itemFound")
                         )
                {
                    matchFound++;
                    // itemFound = true;
                    instructionsList[currentInstructionIndex].opcode  = OpCodes.Ldc_I4_1;
                    instructionsList[currentInstructionIndex].operand = null;
                    yield return(instructionsList[currentInstructionIndex]);

                    yield return(new CodeInstruction(OpCodes.Stloc, itemFound.LocalIndex));

                    currentInstructionIndex += 3;
                }
                // if (++_lastUsedRadiusIndex == _radius.Count)
                // _lastUsedRadiusIndex = DefaultRadiusIndex;
                else if (currentInstructionIndex + 11 < instructionsList.Count &&
                         instructionsList[currentInstructionIndex + 11].opcode == OpCodes.Callvirt &&
                         (MethodInfo)instructionsList[currentInstructionIndex + 11].operand == AccessTools.Method(typeof(List <int>), "get_Count")
                         )
                {
                    matchFound++;
                    // num++;
                    yield return(new CodeInstruction(OpCodes.Ldloc, lastUsedRadius.LocalIndex));

                    yield return(new CodeInstruction(OpCodes.Ldc_I4_1));

                    yield return(new CodeInstruction(OpCodes.Add));

                    yield return(new CodeInstruction(OpCodes.Stloc, lastUsedRadius.LocalIndex));

                    // if (num >= _radius.Count)
                    yield return(new CodeInstruction(OpCodes.Ldloc, lastUsedRadius.LocalIndex));

                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(RimThreadedHarmony.awesomeInventoryJobsJobGiver_FindItemByRadius, "_radius")));

                    yield return(new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(List <int>), "get_Count")));

                    yield return(new CodeInstruction(OpCodes.Blt_S, IL_0122));

                    // num = DefaultRadiusIndex;
                    yield return(new CodeInstruction(OpCodes.Call, AccessTools.Method(RimThreadedHarmony.awesomeInventoryJobsJobGiver_FindItemByRadius, "get_DefaultRadiusIndex")));

                    yield return(new CodeInstruction(OpCodes.Stloc, lastUsedRadius.LocalIndex));

                    currentInstructionIndex += 16;
                }
                else if (currentInstructionIndex + 3 < instructionsList.Count &&
                         instructionsList[currentInstructionIndex + 1].opcode == OpCodes.Brtrue_S &&
                         instructionsList[currentInstructionIndex + 3].opcode == OpCodes.Dup
                         )
                {
                    matchFound++;
                    instructionsList[currentInstructionIndex].labels.Add(IL_0122);
                    yield return(instructionsList[currentInstructionIndex]);

                    currentInstructionIndex++;
                }
                else if (currentInstructionIndex == instructionsList.Count - 2)
                {
                    matchFound++;
                    CodeInstruction codeInstruction = new CodeInstruction(OpCodes.Ldarg_0);
                    codeInstruction.labels = instructionsList[currentInstructionIndex].labels;
                    instructionsList[currentInstructionIndex].labels = new List <Label>();
                    yield return(codeInstruction);

                    //yield return new CodeInstruction(OpCodes.Ldarg_0);
                    yield return(new CodeInstruction(OpCodes.Ldloc, lastUsedRadius.LocalIndex));

                    yield return(new CodeInstruction(OpCodes.Stfld, AccessTools.Field(RimThreadedHarmony.awesomeInventoryJobsJobGiver_FindItemByRadius, "_lastUsedRadiusIndex")));

                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                    yield return(new CodeInstruction(OpCodes.Ldloc, itemFound.LocalIndex));

                    yield return(new CodeInstruction(OpCodes.Stfld, AccessTools.Field(RimThreadedHarmony.awesomeInventoryJobsJobGiver_FindItemByRadius, "_itemFound")));

                    yield return(instructionsList[currentInstructionIndex]);

                    currentInstructionIndex++;
                }
                else
                {
                    yield return(instructionsList[currentInstructionIndex]);

                    currentInstructionIndex++;
                }
            }
            if (matchFound < 7)
            {
                Log.Error("IL code instructions not found");
            }
        }
Пример #2
0
        private static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator generator)
        {
            var newInstructions = ListPool <CodeInstruction> .Shared.Rent(instructions);

            // The index offset.
            const int offset = -2;

            // Find the index of "playerStats.SetHpAmount" method and add the offset.
            var index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Callvirt &&
                                                      (MethodInfo)instruction.operand == Method(typeof(CharacterClassManager), nameof(CharacterClassManager.SetClassID))) + offset;

            // Define the return label and add it to the starting instruction.
            var returnLabel = generator.DefineLabel();

            newInstructions[index].labels.Add(returnLabel);

            // Used to identify the real attacker,
            // if `info.IsPlayer` is true, transfers control to the label `attacterPlayerGetAfterInstance`
            var attackerInstanceVsHitInfoSource = generator.DefineLabel();
            var attacterPlayerGetAfterInstance  = generator.DefineLabel();

            // Declare the attacker local variable.
            var attacker = generator.DeclareLocal(typeof(Player));

            // Declare the target local variable.
            var target = generator.DeclareLocal(typeof(Player));

            CodeInstruction PlayerGetWithLabel()
            {
                var ci = new CodeInstruction(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(GameObject) }));

                ci.labels.Add(attacterPlayerGetAfterInstance);
                return(ci);
            }

            CodeInstruction Ldarg_1WithLabel()
            {
                var ci = new CodeInstruction(OpCodes.Ldarg_1);

                ci.labels.Add(attackerInstanceVsHitInfoSource);
                return(ci);
            }

            // Player attacker = Player.Get(info.IsPlayer ? info.RHub.gameObject : __instance.gameObject);
            // Player target = Player.Get(go);
            //
            // if (target == null || attacker == null || target.IsGodModeEnabled)
            //  return;
            //
            // var ev = new DiedEventArgs(attacker, target, info, true);
            //
            // Handlers.Player.OnDied(ev);
            //
            // info = ev.HitInformations;
            newInstructions.InsertRange(index, new[]
            {
                new CodeInstruction(OpCodes.Ldarga_S, 1),
                new CodeInstruction(OpCodes.Call, PropertyGetter(typeof(PlayerStats.HitInfo), nameof(PlayerStats.HitInfo.IsPlayer))),
                new CodeInstruction(OpCodes.Brtrue_S, attackerInstanceVsHitInfoSource),
                new CodeInstruction(OpCodes.Ldarg_0),
                new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(Component), nameof(Component.gameObject))),
                new CodeInstruction(OpCodes.Br_S, attacterPlayerGetAfterInstance),
                Ldarg_1WithLabel(),
                new CodeInstruction(OpCodes.Ldfld, Field(typeof(PlayerStats.HitInfo), nameof(PlayerStats.HitInfo.RHub))),
                new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(Component), nameof(Component.gameObject))),
                PlayerGetWithLabel(),
                new CodeInstruction(OpCodes.Stloc_S, attacker.LocalIndex),
                new CodeInstruction(OpCodes.Ldarg_2),
                new CodeInstruction(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(GameObject) })),
                new CodeInstruction(OpCodes.Dup),
                new CodeInstruction(OpCodes.Stloc_S, target.LocalIndex),
                new CodeInstruction(OpCodes.Brfalse_S, returnLabel),
                new CodeInstruction(OpCodes.Ldloc_S, attacker.LocalIndex),
                new CodeInstruction(OpCodes.Brfalse_S, returnLabel),
                new CodeInstruction(OpCodes.Ldloc_S, target.LocalIndex),
                new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(Player), nameof(Player.IsGodModeEnabled))),
                new CodeInstruction(OpCodes.Brtrue_S, returnLabel),
                new CodeInstruction(OpCodes.Ldloc_S, attacker.LocalIndex),
                new CodeInstruction(OpCodes.Ldloc_S, target.LocalIndex),
                new CodeInstruction(OpCodes.Ldarg_1),
                new CodeInstruction(OpCodes.Newobj, GetDeclaredConstructors(typeof(DiedEventArgs))[0]),
                new CodeInstruction(OpCodes.Dup),
                new CodeInstruction(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnDied))),
                new CodeInstruction(OpCodes.Call, PropertyGetter(typeof(DiedEventArgs), nameof(DiedEventArgs.HitInformations))),
                new CodeInstruction(OpCodes.Starg_S, 1),
            });

            for (int z = 0; z < newInstructions.Count; z++)
            {
                yield return(newInstructions[z]);
            }

            ListPool <CodeInstruction> .Shared.Return(newInstructions);
        }
        /* Dev Notes (Don't need to read this, a short explanation is just before the method below):
         * The IL of the region I'm interested in (As of RimWorld 0.17.6351.26908, generated via Harmony debug mode):
         *
         * L_0b20: br Label #71 //end of previous logic block ("CannotWearBecauseOfMissingBodyParts")
         * // want to insert the new logic here...
         * L_0b25: Label #70    //Make sure this label is on our new logic block and not right here.
         * L_0b25: ldstr "ForceWear"
         * L_0b2a: ldc.i4.1
         * L_0b2b: newarr System.Object
         * L_0b30: dup
         * L_0b31: ldc.i4.0
         * L_0b32: ldloc.s 42 (RimWorld.FloatMenuMakerMap+<AddHumanlikeOrders>c__AnonStorey43C)
         * L_0b34: ldfld RimWorld.Apparel apparel   // need to remember the instruction before this to load the apparel as an arg to caller.
         * L_0b39: callvirt System.String get_LabelShort()
         * L_0b3e: stelem.ref
         * L_0b3f: call System.String Translate(System.String, System.Object[])
         * L_0b44: ldloc.s 42 (RimWorld.FloatMenuMakerMap+<AddHumanlikeOrders>c__AnonStorey43C)
         * L_0b46: ldftn Void <>m__63B()
         * L_0b4c: newobj Void .ctor(Object, IntPtr)
         * L_0b51: ldc.i4.5
         * L_0b52: ldnull
         * L_0b53: ldnull
         * L_0b54: ldc.r4 0
         * L_0b59: ldnull
         * L_0b5a: ldnull
         * L_0b5b: newobj Void .ctor(String, Action, MenuOptionPriority, Action, Thing, Single, Func`2, WorldObject) // creates the force wear menu item.
         * L_0b60: ldloc.s 34 (RimWorld.FloatMenuMakerMap+<AddHumanlikeOrders>c__AnonStorey436)
         * L_0b62: ldfld Verse.Pawn pawn
         * L_0b67: ldloc.s 42 (RimWorld.FloatMenuMakerMap+<AddHumanlikeOrders>c__AnonStorey43C)
         * L_0b69: ldfld RimWorld.Apparel apparel
         * L_0b6e: call LocalTargetInfo op_Implicit(Verse.Thing)
         * L_0b73: ldstr "ReservedBy"
         * L_0b78: call Verse.FloatMenuOption DecoratePrioritizedTask(Verse.FloatMenuOption, Verse.Pawn, LocalTargetInfo, System.String)
         * L_0b7d: stloc.s 21 (Verse.FloatMenuOption)
         * L_0b7f: Label #69
         * L_0b7f: Label #71
         * L_0b7f: ldarg.2
         * L_0b80: ldloc.s 21 (Verse.FloatMenuOption)   // need to remember this instruction for re-use in call.
         * L_0b82: callvirt Void Add(Verse.FloatMenuOption) //adds the menu item to the list (opts).
         * L_0b87: Label #66    //This is the label we want to jump to in our new logic.
         * L_0b87: Label #67
         * L_0b87: ldloc.s 34 (RimWorld.FloatMenuMakerMap+<AddHumanlikeOrders>c__AnonStorey436)
         * L_0b89: ldfld Verse.Pawn pawn
         * L_0b8e: callvirt Verse.Map get_Map()
         * L_0b93: callvirt Boolean get_IsPlayerHome()
         *
         * A couple of routes, opted to allow the called new method to modify the list directly but could have altered the
         * IL structure to store the returned object (or null) to the local variable, branch if not null to the list.add code.
         * The path I went with should be easier to maintain.
         *
         * New method call signature: (Pawn, Apparel, List<FloatMenuOption)
         * In the target method that correspods to arg1, a field of a nested class (discovered dynamically via code inspection), and arg2.
         *
         * Detailed Patch Notes (plan, kept in sync with the code below):
         * *Search Phase:
         * -Locate ldstr "ForceWear"
         * --When found, store the List<Label>. (mem1)
         * -Start keeping a previous instruction cache.
         * -Locate ldfld RimWorld.Apparel apparel
         * --When found, store the previous instruction unaltered for re-use later. (mem2)
         * -Locate callvirt Void Add(Verse.FloatMenuOption)
         * -The next instruction found is expected to have a label.
         * --If it has a label, store the label as an object for re-use later. (mem3)
         * --If it DOES NOT have a label, dump the unaltered instructions as the patch failed.
         * *Patch phase:
         * -Locate ldstr "ForceWear"
         * -Insert (before)
         * --load arg1 onto the stack (Pawn), ensure that the label (mem1) is on this instruction.
         * --load the remembered class in mem1
         * --load the field RimWorld.Apparel apparel, expected that this replaces the previous item on the stack (so only 2 things so far).
         * --load arg2 onto the stack (List<FloatMenuOption)
         * --Call the new method (expected to absorb 3 items from the stack and add 1 bool back).
         * --Branch false to label remembered in mem2.
         * --Modify the instruction we located, strip the label from it.
         *
         */

        /* The goal of this infix is to add a check for if the pawn is too loaded down with stuff (worn/inventory) before allowing them to wear
         * something.
         * Because all 3 decompilers I checked the Core code with gave me different sets of logic had to do the patch entirely from IL with no decent high
         * level reference.  That made things a bit tougher, I've left most of my dev notes intact above but reading is mostly optional.
         * When maintaining this patch will need to temporarily return all the unmodified instructions with Harmony debug mode enabled and compare
         * the code block above (in dev notes) with what Harmony generated to locate the bug.  This patch *should* work fine if the section of
         * FloatMenuMakerMap.AddHumanlikeOrders doesn't change in logic significantly.
         * The label relocation is a little soft.
         */
        static IEnumerable <CodeInstruction> Modify_ForceWear(IEnumerable <CodeInstruction> instructions)
        {
            int             searchPhase  = 0;
            bool            patched      = false;
            string          targetString = "ForceWear";
            CodeInstruction previous     = null;

            List <Label>    startLabel    = null; // mem0
            CodeInstruction apparelField1 = null; // mem1
            CodeInstruction apparelField2 = null; // mem1
            List <Label>    branchLabel   = null; // mem2

            foreach (CodeInstruction instruction in instructions)
            {
                // NOTE: The reverse order of the phases is important.

                // -The next instruction found is expected to have a label.
                if (searchPhase == 3)
                {
                    if (instruction.labels != null)
                    {
                        branchLabel = instruction.labels;
                    }
                    break;
                }

                // -Locate callvirt Void Add(Verse.FloatMenuOption)
                if (searchPhase == 2 && instruction.opcode == OpCodes.Callvirt && (instruction.operand as MethodInfo) != null && (instruction.operand as MethodInfo).Name == "Add" &&
                    (previous.operand as LocalVariableInfo) != null && (previous.operand as LocalVariableInfo).LocalType == typeof(FloatMenuOption))
                {
                    searchPhase = 3;
                }

                // -Locate ldfld RimWorld.Apparel apparel
                if (searchPhase == 1 && instruction.opcode == OpCodes.Ldfld && (instruction.operand as FieldInfo) != null && (instruction.operand as FieldInfo).Name == "apparel")
                {
                    apparelField1 = previous;
                    apparelField2 = instruction;
                    searchPhase   = 2;
                }

                // -Locate ldstr "ForceWear"
                if (searchPhase == 0 && instruction.opcode == OpCodes.Ldstr && (instruction.operand as String) != null && (instruction.operand as String).Equals(targetString))
                {
                    startLabel  = instruction.labels;
                    searchPhase = 1;
                }

                // -Start keeping a previous instruction cache.
                if (searchPhase > 0 && searchPhase < 3)
                {
                    previous = instruction;
                }
            }
            if (!branchLabel.NullOrEmpty())
            {
                // search succeeded, find our insertion point again and insert the patch.
                foreach (CodeInstruction instruction in instructions)
                {
                    if (!patched && instruction.opcode == OpCodes.Ldstr && (instruction.operand as String) != null && (instruction.operand as String).Equals(targetString))
                    {
                        // arg0 Pawn, also set the label here.
                        CodeInstruction ldPawn = new CodeInstruction(OpCodes.Ldarg_1);
                        ldPawn.labels.AddRange(startLabel);
                        yield return(ldPawn);

                        // arg1 Apparel.
                        yield return(apparelField1);

                        yield return(apparelField2);

                        // arg2 List<FloatMenuOption>.
                        yield return(new CodeInstruction(OpCodes.Ldarg_2));

                        // call
                        yield return(new CodeInstruction(OpCodes.Call, typeof(FloatMenuMakerMap_Modify_AddHumanlikeOrders).GetMethod("ForceWearInventoryCheck", AccessTools.all)));

                        // branch if return from call is false, indicates original code should not run.
                        yield return(new CodeInstruction(OpCodes.Brfalse, branchLabel.First()));

                        // modify the original labeled instruction to remove it's label.
                        instruction.labels.Clear();
                    }
                    yield return(instruction);
                }
            }
            else
            {
                // patch failure, just dump the data out
                Log.Error(string.Concat(logPrefix, "Error applying patch to ForceWear, no change."));
                foreach (CodeInstruction instruction in instructions)
                {
                    yield return(instruction);
                }
            }
        }
Пример #4
0
        /// <summary>
        /// 1.1 Adjusts the layer offset for cutebold goggles so that they are drawn under other headgear.
        /// </summary>
        /// <param name="instructions">The instructions we are messing with.</param>
        /// <param name="ilGenerator">The IDGenerator that allows us to create local variables and labels.</param>
        /// <returns>All the code!</returns>

/* 1.1  private static IEnumerable<CodeInstruction> CuteboldRenderPawnInternalTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator ilGenerator)
 *      {
 *          FieldInfo hatInFront = AccessTools.Field(typeof(ApparelProperties), "hatRenderedFrontOfFace");
 *          MethodInfo drawMeshNowOrLater = AccessTools.Method(typeof(GenDraw), "DrawMeshNowOrLater");
 *
 *          bool nextDraw = false;
 *          float offset = 0.001f;
 *          Label notGoggles = ilGenerator.DefineLabel();
 *          LocalBuilder modified = ilGenerator.DeclareLocal(typeof(bool));
 *
 *          List<CodeInstruction> instructionList = instructions.ToList();
 *
 *          //
 *          // See drSpy decompile of PawnRenderer.RenderPawnInternal() for variable references
 *          //
 *          // Adjusts the y offset to put goggles below other headgear.
 *          //
 *          // modified = false;
 *          //
 *          // if (apparelGraphics[j].sourceApparel.def == Cutebold_DefOf.Cutebold_Goggles)
 *          // {
 *          //     loc2.y -= offset;
 *          //     modified = true;
 *          // }
 *          //
 *          List<CodeInstruction> checkForGoggles = new List<CodeInstruction>() {
 *              new CodeInstruction(OpCodes.Ldc_I4_0), // Load zero
 *              new CodeInstruction(OpCodes.Stloc_S, modified), // Set modified to zero (false)
 *
 *              new CodeInstruction(OpCodes.Ldloc_S, 14), // Loads apparelGraphics
 *              new CodeInstruction(OpCodes.Ldloc_S, 15), // Loads j (apparel number)
 *              new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(List<ApparelGraphicRecord>), "get_Item")), // Get the apparel graphic
 *              new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(ApparelGraphicRecord), "sourceApparel")), // Get the apparel
 *              new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(Thing), "def")), // Get the def of the apparel
 *
 *              new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(Cutebold_DefOf), "Cutebold_Goggles")), // Load the def for cutebold goggles
 *
 *              new CodeInstruction(OpCodes.Ceq), // Checks if the apparel are cutebold goggles
 *              new CodeInstruction(OpCodes.Brfalse, notGoggles), // If not, jump to regular execution
 *
 *                  new CodeInstruction(OpCodes.Ldloca_S, 11), // Loads loc2 (apparel drawing offset)
 *                  new CodeInstruction(OpCodes.Ldflda, AccessTools.Field(typeof(Vector3), "y")), // Gets the address to loc2.y
 *                  new CodeInstruction(OpCodes.Dup), // Copy it
 *                  new CodeInstruction(OpCodes.Ldind_R4), // Load the loc2.y value
 *                  new CodeInstruction(OpCodes.Ldc_R4, offset), // Loads the offset
 *                  new CodeInstruction(OpCodes.Sub), // Subtract offset from loc2.y
 *                  new CodeInstruction(OpCodes.Stind_R4), // Store new value at address of loc2.y
 *
 *                  new CodeInstruction(OpCodes.Ldc_I4_1), // Load one
 *                  new CodeInstruction(OpCodes.Stloc_S, modified), // Set modified to one (true)
 *
 *          };
 *
 *          //
 *          // See drSpy decompile of PawnRenderer.RenderPawnInternal() for variable references
 *          //
 *          // Reverts the y offset for other headgear.
 *          //
 *          // if(modified)
 *          // {
 *          //     loc2.y += offset;
 *          // }
 *          //
 *          List<CodeInstruction> revertChange = new List<CodeInstruction>()
 *          {
 *              new CodeInstruction(OpCodes.Ldloc_S, modified), // Load modified
 *              new CodeInstruction(OpCodes.Brfalse, null), // Check if modified is false and if it is, jump to the end (null to be replaced)
 *
 *                  new CodeInstruction(OpCodes.Ldloca_S, 11), // Loads loc2 (apparel drawing offset)
 *                  new CodeInstruction(OpCodes.Ldflda, AccessTools.Field(typeof(Vector3), "y")), // Gets the address to loc2.y
 *                  new CodeInstruction(OpCodes.Dup), // Copy it
 *                  new CodeInstruction(OpCodes.Ldind_R4), // Load the loc2.y value
 *                  new CodeInstruction(OpCodes.Ldc_R4, offset), // Loads the offset
 *                  new CodeInstruction(OpCodes.Add), // Subtract offset from loc2.y
 *                  new CodeInstruction(OpCodes.Stind_R4) // Store new value at address of loc2.y
 *          };
 *
 *          int x = -1;
 *
 *          for (int i = 0; i < instructionList.Count; i++)
 *          {
 *              CodeInstruction instruction = instructionList[i];
 *
 *              if (i > 4 && instructionList[i - 4].OperandIs(hatInFront))
 *              {
 *                  x = 30;
 *
 *                  foreach (CodeInstruction codeInstruction in checkForGoggles)
 *                  {
 *                      Log.Message("    +" + codeInstruction.ToString() + (codeInstruction.labels.Count > 0 ? codeInstruction.labels[0].ToString() : ""));
 *                      yield return codeInstruction;
 *                  }
 *
 *                  instruction.labels.Add(notGoggles);
 *                  nextDraw = true;
 *              }
 *
 *              if (nextDraw && instructionList[i - 1].OperandIs(drawMeshNowOrLater))
 *              {
 *                  revertChange[1].operand = instruction.operand; // Sets the jump value
 *
 *                  foreach (CodeInstruction codeInstruction in revertChange)
 *                  {
 *                     Log.Message("    +" + codeInstruction.ToString() + (codeInstruction.labels.Count > 0 ? codeInstruction.labels[0].ToString() : ""));
 *                      yield return codeInstruction;
 *                  }
 *
 *                  nextDraw = false;
 *              }
 *
 *              if (x > 0)
 *              {
 *                  Log.Message("    "+instruction.ToString() + (instruction.labels.Count > 0 ? instruction.labels[0].ToString() : ""));
 *                  x--;
 *              }
 *
 *              yield return instruction;
 *          }
 *      }*/

        /// <summary>
        /// Adjusts the layer offset for cutebold goggles so that they are drawn under other headgear.
        /// </summary>
        /// <param name="instructions">The instructions we are messing with.</param>
        /// <param name="ilGenerator">The IDGenerator that allows us to create local variables and labels.</param>
        /// <returns>All the code!</returns>
        private static IEnumerable <CodeInstruction> CuteboldGogglesFixTranspiler(IEnumerable <CodeInstruction> instructions, ILGenerator ilGenerator)
        {
            MethodInfo drawMeshNowOrLater = AccessTools.Method(typeof(GenDraw), "DrawMeshNowOrLater", new[] {
                typeof(Mesh),
                typeof(Vector3),
                typeof(Quaternion),
                typeof(Material),
                typeof(bool)
            });
            FieldInfo sourceApparel = AccessTools.Field(typeof(ApparelGraphicRecord), "sourceApparel");
            FieldInfo apparelDef    = AccessTools.Field(typeof(Thing), "def");
            FieldInfo gogglesDef    = AccessTools.Field(typeof(Cutebold_DefOf), "Cutebold_Goggles");
            FieldInfo vectorY       = AccessTools.Field(typeof(Vector3), "y");
            object    onHeadLoc     = null;

            bool         found      = false;
            bool         nextDraw   = false;
            float        offset     = 0.0005f;
            Label        notGoggles = ilGenerator.DefineLabel();
            LocalBuilder modified   = ilGenerator.DeclareLocal(typeof(bool));
            Label        done       = ilGenerator.DefineLabel();

            List <CodeInstruction> instructionList = instructions.ToList();

            /*
             * See drSpy decompile of PawnRenderer.<DrawHeadHair>g__DrawApparel|39_0() for variable references
             *
             * Adjusts the y offset to put goggles below other headgear.
             *
             * modified = false;
             *
             * if (apparelGraphics[i].sourceApparel.def == Cutebold_DefOf.Cutebold_Goggles)
             * {
             *     loc.y -= offset;
             *     modified = true;
             * }
             */
            List <CodeInstruction> checkForGoggles = new List <CodeInstruction>()
            {
                new CodeInstruction(OpCodes.Ldc_I4_0),             // Load zero
                new CodeInstruction(OpCodes.Stloc_S, modified),    // Set modified to zero (false)

                new CodeInstruction(OpCodes.Ldarg_1),              // Load ApparelGraphicsRecord
                new CodeInstruction(OpCodes.Ldfld, sourceApparel), // Get the apparel
                new CodeInstruction(OpCodes.Ldfld, apparelDef),    // Get the def of the apparel

                new CodeInstruction(OpCodes.Ldsfld, gogglesDef),   // Load the def for cutebold goggles

                new CodeInstruction(OpCodes.Ceq),                  // Checks if the apparel are cutebold goggles
                new CodeInstruction(OpCodes.Brfalse, notGoggles),  // If not, jump to regular execution*/

                new CodeInstruction(OpCodes.Ldarg_2),              // Loads reference to parent method
                new CodeInstruction(OpCodes.Ldflda, null),         // Loads the address to onHeadLoc field (TO BE REPLACED ON RUNTIME)
                new CodeInstruction(OpCodes.Ldarg_2),              // Loads reference to parent method
                new CodeInstruction(OpCodes.Ldflda, null),         // Loads the address to onHeadLoc field (TO BE REPLACED ON RUNTIME)
                new CodeInstruction(OpCodes.Ldfld, vectorY),       // Gets the onHeadLoc.y
                new CodeInstruction(OpCodes.Ldc_R4, offset),       // Loads the offset
                new CodeInstruction(OpCodes.Sub),                  // Subtract offset from onHeadLoc.y
                new CodeInstruction(OpCodes.Stfld, vectorY),       // Stores the new value into onHeadLoc.y

                new CodeInstruction(OpCodes.Ldc_I4_1),             // Load one
                new CodeInstruction(OpCodes.Stloc_S, modified),    // Set modified to one (true)

                new CodeInstruction(OpCodes.Nop)
                {
                    labels = new List <Label> {
                        notGoggles
                    }
                }                                                                        // Jump Target
            };

            /*
             * See drSpy decompile of PawnRenderer.RenderPawnInternal() for variable references
             *
             * Reverts the y offset for other headgear.
             *
             * if(modified)
             * {
             *     loc.y += offset;
             * }
             */
            List <CodeInstruction> revertChange = new List <CodeInstruction>()
            {
                new CodeInstruction(OpCodes.Ldloc_S, modified), // Load modified
                new CodeInstruction(OpCodes.Brfalse, done),     // Check if modified is false and if it is, jump done)

                new CodeInstruction(OpCodes.Ldarg_2),           // Loads reference to parent method
                new CodeInstruction(OpCodes.Ldflda, null),      // Loads the address to onHeadLoc field (TO BE REPLACED ON RUNTIME)
                new CodeInstruction(OpCodes.Ldarg_2),           // Loads reference to parent method
                new CodeInstruction(OpCodes.Ldflda, null),      // Loads the address to onHeadLoc field (TO BE REPLACED ON RUNTIME)
                new CodeInstruction(OpCodes.Ldfld, vectorY),    // Gets the onHeadLoc.y
                new CodeInstruction(OpCodes.Ldc_R4, offset),    // Loads the offset
                new CodeInstruction(OpCodes.Add),               // Subtract offset from onHeadLoc.y
                new CodeInstruction(OpCodes.Stfld, vectorY),    // Stores the new value into onHeadLoc.y

                new CodeInstruction(OpCodes.Nop)
                {
                    labels = new List <Label> {
                        done
                    }
                }                                                                  // Jump Target
            };

            //Log.Message("  Start Transpile");

            for (int i = 0; i < instructionList.Count; i++)
            {
                CodeInstruction instruction = instructionList[i];

                if (!found && instructionList[i + 10].OperandIs(drawMeshNowOrLater))
                {
                    found = true;

                    onHeadLoc = instructionList[i + 2].operand;

                    foreach (CodeInstruction codeInstruction in checkForGoggles)
                    {
                        if (codeInstruction.opcode == OpCodes.Ldflda && codeInstruction.operand == null)
                        {
                            codeInstruction.operand = onHeadLoc;
                        }
                        //Log.Message("    +" + codeInstruction.ToString() + (codeInstruction.labels.Count > 0 ? codeInstruction.labels[0].ToString() : ""));
                        yield return(codeInstruction);
                    }
                    nextDraw = true;
                }

                if (nextDraw && instructionList[i - 1].OperandIs(drawMeshNowOrLater))
                {
                    foreach (CodeInstruction codeInstruction in revertChange)
                    {
                        if (codeInstruction.opcode == OpCodes.Ldflda && codeInstruction.operand == null)
                        {
                            codeInstruction.operand = onHeadLoc;
                        }
                        //Log.Message("    +" + codeInstruction.ToString() + (codeInstruction.labels.Count > 0 ? codeInstruction.labels[0].ToString() : ""));
                        yield return(codeInstruction);
                    }

                    nextDraw = false;
                }

                //if(R4Count<=3)
                //Log.Message("    "+instruction.ToString() + (instruction.labels.Count > 0 ? instruction.labels[0].ToString() : ""));

                yield return(instruction);
            }
        }
Пример #5
0
            public static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
            {
                #if DEBUG
                Log.Message("Transpiler start: PathGrid.CalculatedCostAt (3 matches)");
                #endif

                var instructionList = instructions.ToList();

                var defInfo = AccessTools.Field(typeof(Thing), nameof(Thing.def));

                var pathCostInfo = AccessTools.Field(typeof(BuildableDef), nameof(BuildableDef.pathCost));

                var mapInfo = AccessTools.Field(typeof(PathGrid), "map");

                var finalTerrainPathCostInfo = AccessTools.Method(typeof(CalculatedCostAt), nameof(FinalTerrainPathCost));
                var finalThingPathCostInfo   = AccessTools.Method(typeof(CalculatedCostAt), nameof(FinalThingPathCost));

                for (int i = 0; i < instructionList.Count; i++)
                {
                    var instruction = instructionList[i];

                    // Add our helper method call to each reference to pathCost from...
                    if (instruction.opcode == OpCodes.Ldfld && instruction.OperandIs(pathCostInfo))
                    {
                        #if DEBUG
                        Log.Message("PathGrid.CalculatedCostAt match 1 of 3");
                        #endif

                        var prevInstruction = instructionList[i - 1];

                        // ...a ThingDef
                        if (prevInstruction.opcode == OpCodes.Ldfld && prevInstruction.OperandIs(defInfo))
                        {
                            #if DEBUG
                            Log.Message("PathGrid.CalculatedCostAt match 2 of 3");
                            #endif

                            yield return(instruction);                                               // thing.def.pathCost

                            yield return(instructionList[i - 2].Clone());                            // thing

                            instruction = new CodeInstruction(OpCodes.Call, finalThingPathCostInfo); // FinalPathCost(thing.def.pathCost, thing)
                        }

                        // ...a TerrainDef
                        if (prevInstruction.opcode == OpCodes.Ldloc_2)
                        {
                            #if DEBUG
                            Log.Message("PathGrid.CalculatedCostAt match 3 of 3");
                            #endif

                            yield return(instruction);                                                 // terrainDef.pathCost

                            yield return(prevInstruction.Clone());                                     // terrain

                            yield return(new CodeInstruction(OpCodes.Ldarg_3));                        // prevCell

                            yield return(new CodeInstruction(OpCodes.Ldarg_0));                        // this

                            yield return(new CodeInstruction(OpCodes.Ldfld, mapInfo));                 // this.map

                            instruction = new CodeInstruction(OpCodes.Call, finalTerrainPathCostInfo); // FinalTerrainPathCost(terrainDef.pathCost, terrain, prevCell, this.map)
                        }
                    }

                    yield return(instruction);
                }
            }
 public static bool CheckCallVirtName(CodeInstruction instruction, string name) => instruction.opcode == OpCodes.Callvirt &&
 //need to do reflection fuckery here because we can't access MonoMethod which is the operand type, not MehtodInfo like normal reflection
 instruction.operand.GetType().GetProperty("Name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).GetGetMethod().Invoke(instruction.operand, null).ToString().ToString() == name;
Пример #7
0
 /// <summary>
 /// Checks to see if the instruction is a call instruction to the specified method.
 /// </summary>
 /// <param name="instruction">The IL instruction.</param>
 /// <param name="method">The method which should be called.</param>
 /// <returns>true if it is a call or callvirt instruction to the specified method, or
 /// false otherwise.</returns>
 private bool IsCallTo(CodeInstruction instruction, MethodInfo method)
 {
     return(instruction != null && (instruction.opcode == OpCodes.Call || instruction.
                                    opcode == OpCodes.Callvirt) && method != null && method == (instruction.
                                                                                                operand as MethodInfo));
 }
        public static IEnumerable <CodeInstruction> DrawHeadHairTranspiler(IEnumerable <CodeInstruction> instructions, ILGenerator generator)
        {
            var  instructionList   = instructions.ToList();
            bool hideHairDeclared  = false;
            bool hideHairAssigned  = false;
            bool drawFacepaintCall = false;

            var hideFacepaint = generator.DeclareLocal(typeof(bool));

            var getItemInfo = AccessTools.Method(typeof(List <ApparelGraphicRecord>), "get_Item");

            var getHatsOnlyOnMapInfo = AccessTools.Property(typeof(Prefs), nameof(Prefs.HatsOnlyOnMap)).GetGetMethod();
            var ideologyActiveInfo   = AccessTools.Property(typeof(ModsConfig), nameof(ModsConfig.IdeologyActive)).GetGetMethod();

            var shouldHideHeadgearInfo = AccessTools.Method(typeof(HarmonyPatches_Facepaint), nameof(ShouldHideHeadgear));
            var renderFacepaint        = AccessTools.Method(typeof(HarmonyPatches_Facepaint), nameof(RenderFacepaint));

            for (int i = 0; i <= instructionList.Count - 1; i++)
            {
                var instruction = instructionList[i];

                // Looking for calls to Prefs.HatsOnlyOnMap
                if (instruction.opcode == OpCodes.Call && (MethodInfo)instruction.operand == getHatsOnlyOnMapInfo)
                {
                    yield return(instruction);

                    instruction = new CodeInstruction(OpCodes.Call, shouldHideHeadgearInfo);
                }

                // Looking for any assignments to 'bool flag'
                if (instruction.opcode == OpCodes.Stloc_3)
                {
                    if (!hideHairDeclared)
                    {
                        yield return(instruction);                                                         // bool beardHidden

                        yield return(new CodeInstruction(OpCodes.Ldc_I4_0));                               // false

                        instruction      = new CodeInstruction(OpCodes.Stloc_S, hideFacepaint.LocalIndex); // hideFacepaint = false
                        hideHairDeclared = true;
                    }
                    else if (!hideHairAssigned)
                    {
                        yield return(instruction); // flag = true

                        yield return(new CodeInstruction(OpCodes.Ldc_I4_1));

                        instruction      = new CodeInstruction(OpCodes.Stloc_S, hideFacepaint.LocalIndex); // hideFacepaint
                        hideHairAssigned = true;
                    }
                }
                else if (!drawFacepaintCall && hideHairAssigned && instruction.Calls(ideologyActiveInfo))// && instruction.operand is LocalBuilder lb && lb.LocalIndex == 4)
                {
                    yield return(new CodeInstruction(OpCodes.Ldarg_0)
                    {
                        labels = instruction.ExtractLabels()
                    });                                                                           // this

                    yield return(new CodeInstruction(OpCodes.Ldloc_S, hideFacepaint.LocalIndex)); // hideFacepaint

                    yield return(new CodeInstruction(OpCodes.Ldarg_S, 6));                        // bodyDrawType

                    yield return(new CodeInstruction(OpCodes.Ldarg_S, 7));                        // renderFlags

                    yield return(new CodeInstruction(OpCodes.Ldarg_S, 5));                        // headFacing

                    //yield return new CodeInstruction(OpCodes.Ldloc_S, 7); // loc2
                    yield return(new CodeInstruction(OpCodes.Ldloc_0));                                                                                        // display class

                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(PawnRenderer).GetNestedTypes(AccessTools.all)[0], "onHeadLoc"))); // onHeadLoc

                    yield return(new CodeInstruction(OpCodes.Ldloc_0));                                                                                        // display class

                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(PawnRenderer).GetNestedTypes(AccessTools.all)[0], "quat")));      // quaternion

                    yield return(new CodeInstruction(OpCodes.Call, renderFacepaint));                                                                          // RenderFacepaint(this, hideFacepaint, bodyDrawType, renderFlags, headFacing, loc2, quaternion)

                    drawFacepaintCall = true;
                }


                yield return(instruction);
            }
        }
Пример #9
0
        // StartCarryThing's delegate starts with several (very reasonable) tests.
        //   We want to insert our function call right after the tests, but
        //   before the first bit that checks the stack size of the Thing being carried:
        //   if (failIfStackCountLessThanJobCount && thing.stackCount<curJob.count)
        //        {
        //            actor.jobs.curDriver.EndJobWith(JobCondition.Incompletable);
        //            return;
        //        }
        //   If the test before this is passed, the code jumps to right where we want
        //     to insert.
        //   On failure, there is code with the unique string
        //      "StartCarryThing got availableStackSpace "
        //   So we look for that string, and then once we've found it, the next
        //     "throw" OpCode marks the end of the test.  That's where we insert our code:
        static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            var code = new List <CodeInstruction>(instructions);
            // have we passed "StartCarryThing got availableStackSpace "?
            bool foundCorrectException = false;

            // We keep track of where every BrTrue jump goes:
            //    One of them will be where we insert our code:
            System.Reflection.Emit.Label branchLabel;
            int i = 0; // we do 2 for loops on the same i

            for (; i < code.Count - 1; i++)
            {
                if (code[i].opcode == OpCodes.Brtrue)   // keep track of where Branch on True are going
                {
                    branchLabel = (Label)code[i].operand;
                }
                else if (!foundCorrectException && code[i].opcode == OpCodes.Ldstr &&
                         (string)code[i].operand == "StartCarryThing got availableStackSpace ")
                {
                    // We have passed the correct Branch on True command and
                    // are in the correct error code - we will insert after the exception is thrown!
                    foundCorrectException = true;
                }
                else if (foundCorrectException && code[i].opcode == OpCodes.Throw)
                {
                    yield return(code[i]); // the "throw"

                    i++;                   //advance past throw
                    // We steal the label from the next code and use it as the anchor the
                    //   Brtrue jump goes to:
                    if (!code[i].labels.Remove(branchLabel))
                    {
                        throw new SystemException("LWM.DeepStorage: Patching StartCarryThing failed: label failure");
                    }
                    // Ldarg_0 - this delegate() function on stack
                    var tmp = new CodeInstruction(OpCodes.Ldarg_0);
                    // It needs the jump label:
                    tmp.labels.Add(branchLabel);
                    yield return(tmp);

                    // pop the delegate() function off the stack to load its "toil" field:
                    yield return(new CodeInstruction(OpCodes.Ldfld,
                                                     Harmony.AccessTools.Field(predicateClass, "toil")));

                    yield return(new CodeInstruction(OpCodes.Ldloc_2));    // This SHOULD be Thing thing.

                    yield return(new CodeInstruction(OpCodes.Ldloc_S, 4)); // This SHOULD be num.

                    // Now call FillThisStackIfAble(toil, thing, num):
                    yield return(new CodeInstruction(OpCodes.Call, Harmony.AccessTools
                                                     .Method("LWM.DeepStorage.Patch_StartCarryThing_Delegate:FillThisStackIfAble")));

                    break; // exit this complicated for loop
                }
                // nothing special, return the instruction
                yield return(code[i]);
            }
            // Just return almost all the rest:
            // I know there's probably a snappy one-line C# way to do it, but this is fine:
            for (; i < code.Count - 1; i++)
            {
                yield return(code[i]);
            }
            var cleanUp = new CodeInstruction(OpCodes.Call, Harmony.AccessTools
                                              .Method("LWM.DeepStorage.Patch_StartCarryThing_Delegate:CleanUpStacks"))
            {
                // save the last instruction to swipe any labels/etc from the Return call
                labels = code[i].labels
            };

            yield return(cleanUp);

            var retCode = new CodeInstruction(OpCodes.Ret)
            {
                operand = code[i].operand,
                blocks  = code[i].blocks
            };

            yield return(retCode);

            yield break;
        }
Пример #10
0
                    public static IEnumerable <CodeInstruction> TranspilerPatchFor_ctor(IEnumerable <CodeInstruction> instructions, ILGenerator ilg)
                    {
                        var baseCtor = AccessTools.Constructor(typeof(MenuWithInventory),
                                                               new Type[] { typeof(InventoryMenu.highlightThisItem), typeof(Boolean), typeof(Boolean), typeof(Int32), typeof(Int32), typeof(Int32) });
                        Boolean patched = false;
                        var     lblSkip = ilg.DefineLabel();

                        foreach (var instruction in instructions)
                        {
                            if (!patched && instruction.opcode == OpCodes.Call &&
                                (MethodBase)instruction.operand == baseCtor)
                            {
                                yield return(new CodeInstruction(OpCodes.Ldarg_2));

                                yield return(new CodeInstruction(OpCodes.Isinst, typeof(Types.BaseTypes.ICustomItemGrabMenu.MenuWithInventoryCtorParams)));

                                yield return(new CodeInstruction(OpCodes.Brfalse, lblSkip));

                                // is MenuWithInventoryCtorParams
                                {
                                    yield return(new CodeInstruction(OpCodes.Pop));

                                    yield return(new CodeInstruction(OpCodes.Pop));

                                    yield return(new CodeInstruction(OpCodes.Pop));

                                    yield return(new CodeInstruction(OpCodes.Pop));

                                    yield return(new CodeInstruction(OpCodes.Pop));

                                    yield return(new CodeInstruction(OpCodes.Pop));

                                    yield return(new CodeInstruction(OpCodes.Pop));

                                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                                    yield return(new CodeInstruction(OpCodes.Ldarg_2));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(Types.BaseTypes.ICustomItemGrabMenu.MenuWithInventoryCtorParams), "HighlighterMethod")));

                                    yield return(new CodeInstruction(OpCodes.Ldarg_2));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(Types.BaseTypes.ICustomItemGrabMenu.MenuWithInventoryCtorParams), "OKButton")));

                                    yield return(new CodeInstruction(OpCodes.Ldarg_2));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(Types.BaseTypes.ICustomItemGrabMenu.MenuWithInventoryCtorParams), "TrashCan")));

                                    yield return(new CodeInstruction(OpCodes.Ldarg_2));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(Types.BaseTypes.ICustomItemGrabMenu.MenuWithInventoryCtorParams), "InventoryXOffset")));

                                    yield return(new CodeInstruction(OpCodes.Ldarg_2));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(Types.BaseTypes.ICustomItemGrabMenu.MenuWithInventoryCtorParams), "InventoryYOffset")));

                                    yield return(new CodeInstruction(OpCodes.Ldarg_2));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(Types.BaseTypes.ICustomItemGrabMenu.MenuWithInventoryCtorParams), "MenuOffsetHack")));

                                    yield return(instruction);

                                    yield return(new CodeInstruction(OpCodes.Ret));
                                }

                                // add branching for if condition was not true
                                var jmp = new CodeInstruction(instruction);
                                jmp.labels.Add(lblSkip);
                                yield return(jmp);

                                patched = true;
                            }
                            else
                            {
                                yield return(instruction);
                            }
                        }

                        if (patched)
                        {
                            GlobalVars.SMAPIMonitor.Log("Successfully patched 'StardewValley.Menus.ItemGrabMenu.ctor(IList<StardewValley.Item>, System.Object)'.", LogLevel.Info);
                        }
                        else
                        {
                            GlobalVars.SMAPIMonitor.Log("Could not patch 'StardewValley.Menus.ItemGrabMenu.ctor(IList<StardewValley.Item>, System.Object)' to redirect 'ICustomItemGrabMenu.ctor' to 'MenuWithInventory.ctor'!", LogLevel.Error);
                        }
                    }
Пример #11
0
                        public static IEnumerable <CodeInstruction> TranspilerPatchFor_ReinitializeComponents(IEnumerable <CodeInstruction> instructions, ILGenerator ilg)
                        {
                            var target_type        = Type.GetType("Pathoschild.Stardew.ChestsAnywhere.Menus.Overlays.BaseChestOverlay, ChestsAnywhere");
                            var xna_Rectangle_ctor = AccessTools.Constructor(typeof(Microsoft.Xna.Framework.Rectangle),
                                                                             new Type[] { typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32) });

                            Boolean patched = false;
                            var     lblSkip = ilg.DefineLabel();

                            foreach (var instruction in instructions)
                            {
                                if (!patched && instruction.opcode == OpCodes.Call && (MethodBase)instruction.operand == xna_Rectangle_ctor)
                                {
                                    yield return(instruction);

                                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(target_type, "Menu")));

                                    yield return(new CodeInstruction(OpCodes.Isinst, typeof(Types.CustomTypes.ChestExMenu.MainMenu)));

                                    yield return(new CodeInstruction(OpCodes.Brfalse, lblSkip));

                                    yield return(new CodeInstruction(OpCodes.Ldloca_S, 0));

                                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(target_type, "Menu")));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(MenuWithInventory), "inventory")));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(IClickableMenu), "xPositionOnScreen")));

                                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(target_type, "Menu")));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(MenuWithInventory), "inventory")));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(IClickableMenu), "yPositionOnScreen")));

                                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(target_type, "Menu")));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(IClickableMenu), "width")));

                                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(target_type, "Menu")));

                                    yield return(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(IClickableMenu), "height")));

                                    yield return(instruction);

                                    var jmp = new CodeInstruction(OpCodes.Nop);
                                    jmp.labels.Add(lblSkip);
                                    yield return(jmp);

                                    patched = true;
                                    continue;
                                }
                                yield return(instruction);
                            }


                            if (patched)
                            {
                                GlobalVars.SMAPIMonitor.Log("Successfully patched 'ChestsAnywhere.Menus.Overlays.BaseChestOverlay.ReinitializeComponents'.", LogLevel.Info);
                            }
                            else
                            {
                                GlobalVars.SMAPIMonitor.Log("Could not patch 'ChestsAnywhere.Menus.Overlays.BaseChestOverlay.ReinitializeComponents' to move ChestsAnywhere buttons!", LogLevel.Error);
                            }
                        }
Пример #12
0
        private static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            List <CodeInstruction> instructionList = instructions.ToList();
            bool foundTime          = false;
            bool foundFinalPosition = false;
            bool foundTransformUp   = false;
            bool foundZOffset       = false;
            bool foundPosition      = false;

            for (int i = 0; i < instructionList.Count; i++)
            {
                if (!foundTime &&
                    instructionList[i].opcode == OpCodes.Stloc_0)
                {
                    foundTime = true;
                    instructionList.Insert(i, new CodeInstruction(OpCodes.Ldarg_0));
                    instructionList.Insert(i + 1, new CodeInstruction(OpCodes.Ldfld, _jumpDurationField));
                    instructionList.Insert(i + 2, new CodeInstruction(OpCodes.Call, _noteJumpTimeAdjust));
                }

                if (!foundFinalPosition &&
                    instructionList[i].opcode == OpCodes.Stind_R4)
                {
                    foundFinalPosition = true;
                    instructionList.Insert(i + 2, new CodeInstruction(OpCodes.Ldarg_0));
                    instructionList.Insert(i + 3, new CodeInstruction(OpCodes.Ldarg_0));
                    instructionList.Insert(i + 4, new CodeInstruction(OpCodes.Ldfld, _localPositionField));
                    instructionList.Insert(i + 5, new CodeInstruction(OpCodes.Ldloc_1));
                    instructionList.Insert(i + 6, new CodeInstruction(OpCodes.Call, _definiteNoteJump));
                    instructionList.Insert(i + 7, new CodeInstruction(OpCodes.Stfld, _localPositionField));
                }

                if (!foundTransformUp &&
                    instructionList[i].opcode == OpCodes.Callvirt &&
                    ((MethodInfo)instructionList[i].operand).Name == "get_up")
                {
                    foundTransformUp       = true;
                    instructionList[i]     = new CodeInstruction(OpCodes.Call, _convertToLocalSpace);
                    instructionList[i + 1] = new CodeInstruction(OpCodes.Call, _popQuaternion);
                }

                // is there a better way of checking labels?
                if (!foundZOffset &&
                    instructionList[i].operand is Label &&
                    instructionList[i].operand.GetHashCode() == 21)
                {
                    foundZOffset = true;

                    // Add addition check to our quirky little variable to skip end position offset when we are using definitePosition
                    instructionList.Insert(i + 1, new CodeInstruction(OpCodes.Ldsfld, _definitePositionField));
                    instructionList.Insert(i + 2, new CodeInstruction(OpCodes.Brtrue_S, instructionList[i].operand));
                }

                if (!foundPosition &&
                    instructionList[i].opcode == OpCodes.Callvirt &&
                    ((MethodInfo)instructionList[i].operand).Name == "set_position")
                {
                    foundPosition = true;
                    instructionList[i].operand = _setLocalPosition;
                }
            }

            if (!foundTime)
            {
                NoodleLogger.Log("Failed to find stloc.0!", IPA.Logging.Logger.Level.Error);
            }

            if (!foundFinalPosition)
            {
                NoodleLogger.Log("Failed to find stind.r4!", IPA.Logging.Logger.Level.Error);
            }

            if (!foundTransformUp)
            {
                NoodleLogger.Log("Failed to find call to get_up!", IPA.Logging.Logger.Level.Error);
            }

            if (!foundZOffset)
            {
                NoodleLogger.Log("Failed to find brfalse.s to Label21!", IPA.Logging.Logger.Level.Error);
            }

            if (!foundPosition)
            {
                NoodleLogger.Log("Failed to find callvirt to set_position!", IPA.Logging.Logger.Level.Error);
            }

            return(instructionList.AsEnumerable());
        }
Пример #13
0
 public static IEnumerable <CodeInstruction> Replace(this IEnumerable <CodeInstruction> codes, int index, CodeInstruction newCode, bool moveLabelsAtIndex = false)
 {
     return(codes.ReplaceRange(index, 1, new CodeInstruction[] { newCode }, moveLabelsAtIndex));
 }
Пример #14
0
        public static IEnumerable <CodeInstruction> Add(IEnumerable <CodeInstruction> instructions, ILGenerator iLGenerator)
        {
            Type lockObjectType = typeof(object);
            List <CodeInstruction> loadLockObjectInstructions = new List <CodeInstruction>
            {
                new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(BattleLog_Patch), "addLogEntryLock")),
            };

            List <CodeInstruction> instructionsList = instructions.ToList();
            int             currentInstructionIndex = 0;
            CodeInstruction codeInstruction;

            LocalBuilder lockObject = iLGenerator.DeclareLocal(lockObjectType);
            LocalBuilder lockTaken  = iLGenerator.DeclareLocal(typeof(bool));

            for (int i = 0; i < loadLockObjectInstructions.Count - 1; i++)
            {
                yield return(loadLockObjectInstructions[i]);
            }
            codeInstruction        = loadLockObjectInstructions[loadLockObjectInstructions.Count - 1];
            codeInstruction.labels = instructionsList[currentInstructionIndex].labels;
            instructionsList[currentInstructionIndex].labels = new List <Label>();
            yield return(codeInstruction);

            yield return(new CodeInstruction(OpCodes.Stloc, lockObject.LocalIndex));

            yield return(new CodeInstruction(OpCodes.Ldc_I4_0));

            yield return(new CodeInstruction(OpCodes.Stloc, lockTaken.LocalIndex));

            codeInstruction = new CodeInstruction(OpCodes.Ldloc, lockObject.LocalIndex);
            codeInstruction.blocks.Add(new ExceptionBlock(ExceptionBlockType.BeginExceptionBlock));
            yield return(codeInstruction);

            yield return(new CodeInstruction(OpCodes.Ldloca_S, lockTaken.LocalIndex));

            yield return(new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Monitor), "Enter",
                                                                              new Type[] { typeof(object), typeof(bool).MakeByRefType() })));

            while (currentInstructionIndex < instructionsList.Count - 1)
            {
                yield return(instructionsList[currentInstructionIndex]);

                currentInstructionIndex++;
            }
            Label endHandlerDestination = iLGenerator.DefineLabel();

            yield return(new CodeInstruction(OpCodes.Leave_S, endHandlerDestination));

            codeInstruction = new CodeInstruction(OpCodes.Ldloc, lockTaken.LocalIndex);
            codeInstruction.blocks.Add(new ExceptionBlock(ExceptionBlockType.BeginFinallyBlock));
            yield return(codeInstruction);

            Label endFinallyDestination = iLGenerator.DefineLabel();

            yield return(new CodeInstruction(OpCodes.Brfalse_S, endFinallyDestination));

            yield return(new CodeInstruction(OpCodes.Ldloc, lockObject.LocalIndex));

            yield return(new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Monitor), "Exit")));

            codeInstruction = new CodeInstruction(OpCodes.Endfinally);
            codeInstruction.labels.Add(endFinallyDestination);
            codeInstruction.blocks.Add(new ExceptionBlock(ExceptionBlockType.EndExceptionBlock));
            yield return(codeInstruction);

            instructionsList[currentInstructionIndex].labels.Add(endHandlerDestination);
            yield return(instructionsList[currentInstructionIndex]);
        }
 private static bool SkipInstruction(CodeInstruction codeInstruction)
 {
     return(codeInstruction.opcode != OpCodes.Callvirt || codeInstruction.operand == null ||
            !codeInstruction.operand.ToString().Contains(nameof(VehicleManager.GetRandomVehicleInfo)));
 }
Пример #16
0
 public override bool match(CodeInstruction instruction)
 {
     return(query.Equals(instruction.opcode) || alt.Equals(instruction.opcode));
 }
        private static void ProcessOccurence(Type declaringType, int occurrences, List <CodeInstruction> newCodes,
                                             CodeInstruction codeInstruction)
        {
            var methodToCall =
                AccessTools.Method(typeof(XYZBuildingAIPatch), nameof(GetVehicleInfoWithoutType));
            int patchIndexOffset; //how many instructions the randomization code includes

            if (declaringType == typeof(TransportStationAI))
            {
                if (occurrences == 0)
                {
                    patchIndexOffset = 14;
                }
                else if (occurrences == 1)
                {
                    patchIndexOffset = 17;
                }
                else
                {
                    newCodes.Add(codeInstruction);
                    return;
                }
                methodToCall = AccessTools.Method(typeof(XYZBuildingAIPatch), nameof(GetVehicleInfoWithType));
            }
            else if (declaringType == typeof(CableCarStationAI) ||
                     declaringType == typeof(LandfillSiteAI) ||
                     declaringType == typeof(CemeteryAI) ||
                     declaringType == typeof(PoliceStationAI) ||
                     declaringType == typeof(HospitalAI) ||
                     declaringType == typeof(SnowDumpAI) ||
                     declaringType == typeof(MaintenanceDepotAI) ||
                     declaringType == typeof(PrivateAirportAI)
                     )
            {
                if (occurrences > 1)
                {
                    newCodes.Add(codeInstruction);
                    return;
                }

                patchIndexOffset = 14;
            }
            else if (declaringType == typeof(PostOfficeAI))
            {
                if (occurrences > 1)
                {
                    newCodes.Add(codeInstruction);
                    return;
                }

                patchIndexOffset = 11;
            }
            else if (declaringType == typeof(FireStationAI) || declaringType == typeof(HelicopterDepotAI))
            {
                if (occurrences > 2)
                {
                    newCodes.Add(codeInstruction);
                    return;
                }

                patchIndexOffset = 15;
                methodToCall     = AccessTools.Method(typeof(XYZBuildingAIPatch), nameof(GetVehicleInfoWithType));
            }
            else if (declaringType == typeof(DisasterResponseBuildingAI))
            {
                if (occurrences < 1 || occurrences > 3)
                {
                    newCodes.Add(codeInstruction);
                    return;
                }

                patchIndexOffset = 15;
                methodToCall     = AccessTools.Method(typeof(XYZBuildingAIPatch), nameof(GetVehicleInfoWithType));
            }
            else if (declaringType == typeof(FishingHarborAI))
            {
                if (occurrences > 2)
                {
                    newCodes.Add(codeInstruction);
                    return;
                }

                patchIndexOffset = 12;
                methodToCall     = AccessTools.Method(typeof(XYZBuildingAIPatch), nameof(GetVehicleInfoWithType));
            }
            else if (declaringType == typeof(WaterFacilityAI) || declaringType == typeof(TourBuildingAI))
            {
                if (occurrences > 0)
                {
                    newCodes.Add(codeInstruction);
                    return;
                }

                patchIndexOffset = 14;
                methodToCall     = AccessTools.Method(typeof(XYZBuildingAIPatch), nameof(GetVehicleInfoWithoutType));
            }
            else
            {
                throw new NotImplementedException("SVS2: unsupported patched type: " +
                                                  declaringType);
            }

            ChangeInstructions(newCodes, methodToCall, patchIndexOffset);
        }
Пример #18
0
 public abstract bool match(CodeInstruction instruction);
 public static bool CheckNewObjTypeName(CodeInstruction instruction, string name) => instruction.opcode == OpCodes.Newobj &&
 //need to do reflection fuckery here because we can't access MonoCMethod which is the operand type, not ConstructorInfo like normal reflection
 instruction.operand.GetType().GetProperty("DeclaringType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).GetGetMethod().Invoke(instruction.operand, null).ToString() == name;
Пример #20
0
 public IM_CodeInstruction(CodeInstruction q)
 {
     query = q;
     alt   = FuzzyOpcodeTable.alt(q.opcode);
 }
Пример #21
0
        static IEnumerable <CodeInstruction> CheckBuildConditions_Transpiler(ILGenerator gen, IEnumerable <CodeInstruction> instructions)
        {
            var codes = new List <CodeInstruction>(instructions);
            int i     = 0;

            //Apply Ignoring of inventory check
            for (i = 5; i < codes.Count - 2; i++)
            {
                if (codes[i].opcode == OpCodes.Ldfld && codes[i].operand?.ToString() == "System.Boolean willCover" &&
                    codes[i + 1].opcode == OpCodes.Brfalse &&
                    codes[i - 1].opcode == OpCodes.Ldloc_3 &&
                    codes[i - 2].opcode == OpCodes.Brfalse &&
                    codes[i - 3].opcode == OpCodes.Ldfld && codes[i - 3].operand?.ToString() == "System.Int32 coverObjId" &&
                    codes[i - 4].opcode == OpCodes.Ldloc_3 &&
                    codes[i - 5].opcode == OpCodes.Br &&
                    codes[i + 2].opcode == OpCodes.Ldloc_3)
                {
                    Label targetLabel = (Label)codes[i + 1].operand;
                    codes.InsertRange(i - 3, new CodeInstruction[] {
                        new CodeInstruction(OpCodes.Pop),
                        new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(FactoryManager), "get_IgnoreBasicBuildConditionChecks")),
                        new CodeInstruction(OpCodes.Brtrue_S, targetLabel),
                        new CodeInstruction(OpCodes.Ldloc_3)
                    });
                    break;
                }
            }

            //Apply Ignoring if inserter match the planet grid check
            for (i = 5; i < codes.Count - 2; i++)
            {
                if (codes[i].opcode == OpCodes.Brfalse &&
                    codes[i + 1].opcode == OpCodes.Ldloca_S &&
                    codes[i - 1].opcode == OpCodes.Ldloc_S &&
                    codes[i - 2].opcode == OpCodes.Stfld &&
                    codes[i - 3].opcode == OpCodes.Call &&
                    codes[i - 4].opcode == OpCodes.Ldfld &&
                    codes[i - 5].opcode == OpCodes.Ldloc_3 &&
                    codes[i + 2].opcode == OpCodes.Ldloc_3)
                {
                    Label targetLabel = (Label)codes[i].operand;
                    codes.InsertRange(i + 1, new CodeInstruction[] {
                        new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(FactoryManager), "get_IgnoreBasicBuildConditionChecks")),
                        new CodeInstruction(OpCodes.Brtrue_S, targetLabel),
                    });
                    break;
                }
            }

            //Apply ignoring of 3 ground checks
            for (i = 9; i < codes.Count - 4; i++)
            {
                if (codes[i - 8].opcode == OpCodes.Blt &&
                    codes[i - 7].opcode == OpCodes.Ldloc_3 &&
                    codes[i - 6].opcode == OpCodes.Ldfld &&
                    codes[i - 5].opcode == OpCodes.Ldfld &&
                    codes[i - 4].opcode == OpCodes.Brfalse &&
                    codes[i - 3].opcode == OpCodes.Ldloc_3 &&
                    codes[i - 2].opcode == OpCodes.Ldfld &&
                    codes[i - 1].opcode == OpCodes.Brfalse &&
                    codes[i].opcode == OpCodes.Br)
                {
                    codes.InsertRange(i - 7, new CodeInstruction[] {
                        new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(FactoryManager), "get_IgnoreBasicBuildConditionChecks")),
                        new CodeInstruction(OpCodes.Brtrue_S, codes[i].operand)
                    });

                    Label myCode = gen.DefineLabel();
                    codes[i - 7].labels.Add(myCode);

                    //Go back and fix the jump
                    for (int a = i; a > 0; a--)
                    {
                        if (codes[a].opcode == OpCodes.Brfalse &&
                            codes[a + 1].opcode == OpCodes.Ldarg_0 &&
                            codes[a - 1].opcode == OpCodes.Ldfld &&
                            codes[a + 2].opcode == OpCodes.Ldloca_S &&
                            codes[a - 2].opcode == OpCodes.Ldfld &&
                            codes[a + 3].opcode == OpCodes.Ldfld &&
                            codes[a - 3].opcode == OpCodes.Ldloc_3)
                        {
                            codes[a] = new CodeInstruction(OpCodes.Brfalse, myCode);
                            break;
                        }
                    }

                    break;
                }
            }

            //Apply patch for the ore check
            for (i = 9; i < codes.Count - 4; i++)
            {
                if (codes[i - 8].opcode == OpCodes.Br &&
                    codes[i - 7].opcode == OpCodes.Ldloc_3 &&
                    codes[i - 6].opcode == OpCodes.Ldc_I4_S &&
                    codes[i - 5].opcode == OpCodes.Stfld &&
                    codes[i - 4].opcode == OpCodes.Br &&
                    codes[i - 3].opcode == OpCodes.Ldloc_3 &&
                    codes[i - 2].opcode == OpCodes.Ldfld &&
                    codes[i - 1].opcode == OpCodes.Ldfld &&
                    codes[i].opcode == OpCodes.Brfalse)
                {
                    codes.InsertRange(i + 1, new CodeInstruction[] {
                        new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(FactoryManager), "get_IgnoreBasicBuildConditionChecks")),
                        new CodeInstruction(OpCodes.Brtrue_S, codes[i].operand)
                    });
                    break;
                }
            }

            //Apply patch for the oil check
            for (int a = i; a < codes.Count - 4; a++)
            {
                if (codes[a - 8].opcode == OpCodes.Ldloc_3 &&
                    codes[a - 7].opcode == OpCodes.Ldc_I4_S &&
                    codes[a - 6].opcode == OpCodes.Stfld &&
                    codes[a - 5].opcode == OpCodes.Br &&
                    codes[a - 4].opcode == OpCodes.Br &&
                    codes[a - 3].opcode == OpCodes.Ldloc_3 &&
                    codes[a - 2].opcode == OpCodes.Ldfld &&
                    codes[a - 1].opcode == OpCodes.Ldfld &&
                    codes[a].opcode == OpCodes.Brfalse)
                {
                    codes.InsertRange(a + 1, new CodeInstruction[] {
                        new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(FactoryManager), "get_IgnoreBasicBuildConditionChecks")),
                        new CodeInstruction(OpCodes.Brtrue_S, codes[a].operand)
                    });
                }
            }

            return(codes);
        }
Пример #22
0
            internal static IEnumerable <CodeInstruction> Transpiler(
                IEnumerable <CodeInstruction> method, ILGenerator generator)
            {
                var notEquals = typeof(UnityEngine.Object).GetMethod("op_Inequality",
                                                                     BindingFlags.Static | BindingFlags.Public) ??
                                throw new InvalidOperationException("notEquals");
                var getComponent = typeof(GameObject).GetMethod("GetComponent", BindingFlags.
                                                                Instance | BindingFlags.Public, null, new Type[] { }, null) ??
                                   throw new InvalidOperationException("getComponent");
                var field = typeof(TreeFilterableSideScreen).GetField("target", BindingFlags.
                                                                      Instance | BindingFlags.NonPublic) ??
                            throw new InvalidOperationException("target");
                var  gcExisting = getComponent.MakeGenericMethod(typeof(CreatureDeliveryPoint));
                var  gcNew      = getComponent.MakeGenericMethod(typeof(BottleEmptier));
                bool gc         = false;

                foreach (var instruction in method)
                {
                    var opcode = instruction.opcode;
                    if (opcode == OpCodes.Callvirt || opcode == OpCodes.Call)
                    {
                        var operand = instruction.operand as MethodInfo;
                        yield return(instruction);

                        if (operand == gcExisting)
                        {
                            // GetComponent<CreatureDeliveryPoint>()
                            gc = true;
                        }
                        else if (operand == notEquals && gc)
                        {
                            // != null;
                            // if (this
                            yield return(new CodeInstruction(OpCodes.Ldarg_0));

                            // .target
                            yield return(new CodeInstruction(OpCodes.Ldfld, field));

                            // .GetComponent<BottleEmptier>()
                            yield return(new CodeInstruction(OpCodes.Callvirt, gcNew));

                            // != null)
                            yield return(new CodeInstruction(OpCodes.Ldnull));

                            yield return(new CodeInstruction(OpCodes.Call, notEquals));

                            var lbl = generator.DefineLabel();
                            // {
                            yield return(new CodeInstruction(OpCodes.Brfalse, lbl));

                            // flag = true
                            yield return(new CodeInstruction(OpCodes.Pop));

                            yield return(new CodeInstruction(OpCodes.Ldc_I4_1));

                            // }
                            var target = new CodeInstruction(OpCodes.Nop);
                            target.labels.Add(lbl);
                            yield return(target);

                            gc = false;
                        }
                    }
                    else
                    {
                        yield return(instruction);
                    }
                }
            }
Пример #23
0
 private static Action CompileInstruction(CodeInstruction instruction)
 {
     return((Action)((DynamicMethod)instruction.operand).CreateDelegate(typeof(Action)));
 }
Пример #24
0
        // we have to pick that annoying hard-coded `index < 2` out of some for loops
        internal static IEnumerable <CodeInstruction> TranspileHardcodedIndex(IEnumerable <CodeInstruction> instructions, CodeInstruction var, OpCode val)
        {
            var instructionList = new List <CodeInstruction>(instructions);


            for (var i = 0; i < instructionList.Count; i++)
            {
                if (i >= 2 &&
                    instructionList[i - 2].opcode == var.opcode && instructionList[i - 2].operand == var.operand &&
                    instructionList[i - 1].opcode == val &&
                    instructionList[i].opcode == OpCodes.Clt)
                {
                    // we found it
                    instructionList[i - 1].opcode  = OpCodes.Ldc_I4_S;
                    instructionList[i - 1].operand = Mod.MAX_WIRE_TYPES;
                    break;
                }
            }

            return(instructionList);
        }
Пример #25
0
        public SplashScreen()
            : base(80, 23)
        {
            IsVisible = false;
            //effectsManager = new EffectsManager(this.TextSurface);
            // Setup the console text background
            string textTemplate = "sole SadCon";

            System.Text.StringBuilder text = new System.Text.StringBuilder(2200);

            for (int i = 0; i < Width * Height; i++)
            {
                text.Append(textTemplate);
            }
            Print(0, 0, text.ToString(), Color.Black, Color.Transparent);

            // Load the logo
            System.IO.Stream imageStream = Microsoft.Xna.Framework.TitleContainer.OpenStream("sad.png");
            var image = Texture2D.FromStream(Global.GraphicsDevice, imageStream);

            imageStream.Dispose();

            // Configure the logo
            _consoleImage         = image.ToSurface(Global.FontDefault, false);
            _consoleImagePosition = new Point(Width / 2 - _consoleImage.Width / 2, -1);
            _consoleImage.Tint    = Color.Black;

            // Configure the animations
            _animation = new InstructionSet();
            _animation.Instructions.AddLast(new Wait()
            {
                Duration = 0.3f
            });

            // Animation to move the angled gradient spotlight effect.
            var moveGradientInstruction = new CodeInstruction();

            moveGradientInstruction.CodeCallback = (inst) =>
            {
                _x += 1;

                if (_x > Width + 50)
                {
                    inst.IsFinished = true;
                }

                Color[] colors     = new Color[] { Color.Black, Color.Blue, Color.White, Color.Blue, Color.Black };
                float[] colorStops = new float[] { 0f, 0.2f, 0.5f, 0.8f, 1f };

                Algorithms.GradientFill(Font.Size, new Point(_x, 12), 10, 45, new Rectangle(0, 0, Width, Height), new ColorGradient(colors, colorStops), SetForeground);
            };
            _animation.Instructions.AddLast(moveGradientInstruction);

            // Animation to clear the SadConsole text.
            _animation.Instructions.AddLast(new CodeInstruction()
            {
                CodeCallback = (i) => { Fill(Color.Black, Color.Transparent, 0, null); i.IsFinished = true; }
            });

            // Animation for the logo text.
            var logoText = new ColorGradient(new Color[] { Color.Magenta, Color.Yellow }, new float[] { 0.0f, 1f }).ToColoredString("[| Powered by SadConsole |]");

            logoText.SetEffect(new SadConsole.Effects.Fade()
            {
                DestinationForeground = Color.Blue, FadeForeground = true, FadeDuration = 1f, Repeat = false, RemoveOnFinished = true, Permanent = true, CloneOnApply = true
            });
            _animation.Instructions.AddLast(new DrawString(this)
            {
                Position = new Point(26, this.Height - 1), Text = logoText, TotalTimeToPrint = 1f
            });

            // Animation for fading in the logo picture.
            _animation.Instructions.AddLast(new FadeTextSurfaceTint(_consoleImage, new ColorGradient(Color.Black, Color.Transparent), new TimeSpan(0, 0, 0, 0, 2000)));

            // Animation to blink SadConsole in the logo text
            _animation.Instructions.AddLast(new CodeInstruction()
            {
                CodeCallback = (i) =>
                {
                    SadConsole.Effects.Fade fadeEffect = new SadConsole.Effects.Fade();
                    fadeEffect.AutoReverse             = true;
                    fadeEffect.DestinationForeground   = new ColorGradient(Color.Blue, Color.Yellow);
                    fadeEffect.FadeForeground          = true;
                    fadeEffect.UseCellForeground       = false;
                    fadeEffect.Repeat           = true;
                    fadeEffect.FadeDuration     = 0.7f;
                    fadeEffect.RemoveOnFinished = true;

                    List <Cell> cells = new List <Cell>();
                    for (int index = 0; index < 10; index++)
                    {
                        var point = new Point(26, this.Height - 1).ToIndex(this.Width) + 14 + index;
                        cells.Add(Cells[point]);
                        cellindexes.Add(point);
                    }

                    SetEffect(cells, fadeEffect);
                    i.IsFinished = true;
                }
            });

            // Animation to delay, keeping the logo and all on there for 2 seconds, then destroy itself.
            _animation.Instructions.AddLast(new Wait()
            {
                Duration = 2.5f
            });
            _animation.Instructions.AddLast(new FadeTextSurfaceTint(_consoleImage, new ColorGradient(Color.Transparent, Color.Black), new TimeSpan(0, 0, 0, 0, 2000)));
            _animation.Instructions.AddLast(new CodeInstruction()
            {
                CodeCallback = (i) =>
                {
                    SplashCompleted?.Invoke();

                    i.IsFinished = true;
                }
            });
        }
        private static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            List <CodeInstruction> instructionList = instructions.ToList();
            bool foundRotation         = false;
            bool foundWidth            = false;
            bool foundLength           = false;
            int  instructrionListCount = instructionList.Count;

            for (int i = 0; i < instructrionListCount; i++)
            {
                if (!foundRotation &&
                    instructionList[i].opcode == OpCodes.Stfld &&
                    ((FieldInfo)instructionList[i].operand).Name == "_worldRotation")
                {
                    foundRotation = true;

                    instructionList[i - 1] = new CodeInstruction(OpCodes.Call, _getWorldRotation);
                    instructionList[i - 4] = new CodeInstruction(OpCodes.Ldarg_1);
                    instructionList.RemoveAt(i - 2);

                    instructionList.RemoveRange(i + 1, 2);
                    instructionList[i + 1] = new CodeInstruction(OpCodes.Ldarg_0);
                    instructionList[i + 2] = new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(ObstacleController), "_worldRotation"));
                    instructionList[i + 3] = new CodeInstruction(OpCodes.Call, _invertQuaternion);
                }

                if (!foundWidth &&
                    instructionList[i].opcode == OpCodes.Callvirt &&
                    ((MethodInfo)instructionList[i].operand).Name == "get_width")
                {
                    foundWidth = true;
                    instructionList.Insert(i + 2, new CodeInstruction(OpCodes.Ldarg_1));
                    instructionList.Insert(i + 3, new CodeInstruction(OpCodes.Call, _getCustomWidth));
                }

                if (!foundLength &&
                    instructionList[i].opcode == OpCodes.Stloc_2)
                {
                    foundLength = true;
                    instructionList.Insert(i, new CodeInstruction(OpCodes.Ldarg_1));
                    instructionList.Insert(i + 1, new CodeInstruction(OpCodes.Call, _getCustomLength));
                }
            }

            if (!foundRotation)
            {
                NoodleLogger.Log("Failed to find _worldRotation stfld!", IPA.Logging.Logger.Level.Error);
            }

            if (!foundWidth)
            {
                NoodleLogger.Log("Failed to find get_width call!", IPA.Logging.Logger.Level.Error);
            }

            if (!foundLength)
            {
                NoodleLogger.Log("Failed to find stloc.2!", IPA.Logging.Logger.Level.Error);
            }

            return(instructionList.AsEnumerable());
        }
Пример #27
0
        private static IEnumerable <CodeInstruction> Transpiler(ILGenerator gen, IEnumerable <CodeInstruction> instr)
        {
            #region fragment>>GUI.BeginGroup(position);
            OpCode[] opCodes1 =
            {
                OpCodes.Call,
                OpCodes.Stloc_S,
                OpCodes.Ldloc_S,
                OpCodes.Call,
            };
            String[] operands1 =
            {
                "UnityEngine.Rect ContractedBy(UnityEngine.Rect, Single)",
                "UnityEngine.Rect (8)",
                "UnityEngine.Rect (8)",
                "Void BeginGroup(UnityEngine.Rect)",
            };
            int step1 = 0;
            #endregion

            #region fragment>>GUI.EndGroup();
            OpCode[] opCodes2 =
            {
                OpCodes.Constrained,
                OpCodes.Callvirt,
                OpCodes.Endfinally,
                OpCodes.Call,
            };
            String[] operands2 =
            {
                "System.Collections.Generic.List`1+Enumerator[RimWorld.PrisonerInteractionModeDef]",
                "Void Dispose()",
                "",
                "Void EndGroup()",
            };
            int step2 = 0;
            #endregion

            #region fragment>>Rect position = rect6.ContractedBy(10f);
            OpCode[] opCodes3 =
            {
                OpCodes.Ldc_R4,
                OpCodes.Call,
                OpCodes.Stloc_S,
                OpCodes.Ldloc_S,
            };
            String[] operands3 =
            {
                "10",
                "UnityEngine.Rect ContractedBy(UnityEngine.Rect, Single)",
                "UnityEngine.Rect (8)",
                "UnityEngine.Rect (8)",
            };
            int step3 = 0;
            var rect  = HPatcher.FindOperandAfter(opCodes3, operands3, instr);
            #endregion
            foreach (var ci in instr)
            {
                // end scroll
                if (HPatcher.IsFragment(opCodes2, operands2, ci, ref step2, "AddScrollToPrisonerTab2"))
                {
                    var instruction = new CodeInstruction(OpCodes.Call, typeof(Patch_AddScrollToPrisonerTab).GetMethod(nameof(StopScrolling)));
                    instruction.labels.AddRange(ci.labels);
                    ci.labels.Clear();
                    yield return(instruction);
                }

/*                // resize
 *              if (HPatcher.IsFragment(opCodes3, operands3, ci, ref step3, "AddScrollToPrisonerTab3"))
 *              {
 *
 *              }*/

                yield return(ci);

                // begin scroll
                if (HPatcher.IsFragment(opCodes1, operands1, ci, ref step1, "AddScrollToPrisonerTab1"))
                {
                    yield return(new CodeInstruction(OpCodes.Ldloc_S, rect));

                    yield return(new CodeInstruction(OpCodes.Call, typeof(Patch_AddScrollToPrisonerTab).GetMethod(nameof(StartScrolling))));

                    yield return(new CodeInstruction(OpCodes.Stloc_S, rect));
                }
            }
        }
Пример #28
0
            public static void CalculatePose(PlayerAction_Build __instance, int startObjId, int castObjId)
            {
                IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
                {
                    List <CodeInstruction> instructionsList = instructions.ToList();

                    // Find the idx at which the "cargoTraffic" field of the PlanetFactory
                    // Is first accessed since this is the start of the instructions that compute posing

                    /* ex of the code in dotpeek:
                     * ```
                     * if (this.cursorValid && this.startObjId != this.castObjId && (this.startObjId > 0 && this.castObjId > 0))
                     * {
                     *   CargoTraffic cargoTraffic = this.factory.cargoTraffic; <- WE WANT TO START WITH THIS LINE (INCLUSIVE)
                     *   EntityData[] entityPool = this.factory.entityPool;
                     *   BeltComponent[] beltPool = cargoTraffic.beltPool;
                     *   this.posePairs.Clear();
                     *   this.startSlots.Clear();
                     * ```
                     */

                    int startIdx = -1;

                    for (int i = 0; i < instructionsList.Count; i++)
                    {
                        if (instructionsList[i].LoadsField(typeof(PlanetFactory).GetField("cargoTraffic")))
                        {
                            startIdx = i - 2; // need the two proceeding lines that are ldarg.0 and ldfld PlayerAction_Build::factory
                            break;
                        }
                    }
                    if (startIdx == -1)
                    {
                        throw new InvalidOperationException("Cannot patch sorter posing code b/c the start indicator isn't present");
                    }

                    // Find the idx at which the "posePairs" field of the PlayerAction_Build
                    // Is first accessed and followed by a call to get_Count

                    /*
                     * ex of the code in dotpeek:
                     * ```
                     *          else
                     *              flag6 = true;
                     *      }
                     *      else
                     *        flag6 = true;
                     *    }
                     *  }
                     *  if (this.posePairs.Count > 0) <- WE WANT TO END ON THIS LINE (EXCLUSIVE)
                     *  {
                     *    float num1 = 1000f;
                     *    float num2 = Vector3.Distance(this.currMouseRay.origin, this.cursorTarget) + 10f;
                     *    PlayerAction_Build.PosePair posePair2 = new PlayerAction_Build.PosePair();
                     * ```
                     */

                    int endIdx = -1;

                    for (int i = startIdx; i < instructionsList.Count - 1; i++) // go to the end - 1 b/c we need to check two instructions to find valid loc
                    {
                        if (instructionsList[i].LoadsField(typeof(PlayerAction_Build).GetField("posePairs")))
                        {
                            if (instructionsList[i + 1].Calls(typeof(List <PlayerAction_Build.PosePair>).GetMethod("get_Count")))
                            {
                                endIdx = i - 1; // need the proceeding line that is ldarg.0
                                break;
                            }
                        }
                    }
                    if (endIdx == -1)
                    {
                        throw new InvalidOperationException("Cannot patch sorter posing code b/c the end indicator isn't present");
                    }

                    // The first argument to an instance method (arg 0) is the instance itself
                    // Since this is a static method, the instance will still need to be passed
                    // For the IL instructions to work properly so manually pass the instance as
                    // The first argument to the method.
                    List <CodeInstruction> code = new List <CodeInstruction>()
                    {
                        new CodeInstruction(OpCodes.Ldarg_0),
                        new CodeInstruction(OpCodes.Ldarg_1),
                        CodeInstruction.StoreField(typeof(PlayerAction_Build), "startObjId"),
                        new CodeInstruction(OpCodes.Ldarg_0),
                        new CodeInstruction(OpCodes.Ldarg_2),
                        CodeInstruction.StoreField(typeof(PlayerAction_Build), "castObjId"),
                    };

                    for (int i = startIdx; i < endIdx; i++)
                    {
                        code.Add(instructionsList[i]);
                    }
                    return(code.AsEnumerable());
                }

                // make compiler happy
                _ = Transpiler(null);
                return;
            }
Пример #29
0
        private static IEnumerable <CodeInstruction> Transpiler(ILGenerator gen, MethodBase mBase,
                                                                IEnumerable <CodeInstruction> instr)
        {
            // Find >> this.bill
            OpCode[] opCodes0 =
            {
                OpCodes.Ldfld,
            };
            String[] operands0 =
            {
                "RimWorld.Bill_Production bill",
            };
            var billField = HPatcher.FindOperandAfter(opCodes0, operands0, instr);

            // Find label after >> if (listing_Standard.ButtonText(label, null))
            OpCode[] opCodes1 =
            {
                OpCodes.Ldloc_S,
                OpCodes.Ldloc_S,
                OpCodes.Ldnull,
                OpCodes.Callvirt,
                OpCodes.Brfalse
            };
            String[] operands1 =
            {
                "Verse.Listing_Standard (6)",
                "System.String (7)",
                "",
                "Boolean ButtonText(System.String, System.String)",
                "System.Reflection.Emit.Label",
            };
            var label = HPatcher.FindOperandAfter(opCodes1, operands1, instr);

            // Begin rect - start of scrollable view
            int step2 = 0;
            int step4 = 0;

            // Find fragment >> listing_Standard.Begin(rect3);
            OpCode[] opCodes2 =
            {
                OpCodes.Ldloc_S,
                OpCodes.Ldloc_2,
                OpCodes.Callvirt,
            };
            String[] operands2 =
            {
                "Verse.Listing_Standard (6)",
                "",
                "Void Begin(Rect)",
            };
            // End rect - end of scrollable view
            int step3 = 0;

            // Find fragment >> listing_Standard.End();
            OpCode[] opCodes3 =
            {
                OpCodes.Call,
                OpCodes.Stfld,
                OpCodes.Ldloc_S,
            };
            String[] operands3 =
            {
                "Int32 RoundToInt(Single)",
                "System.Int32 unpauseWhenYouHave",
                "Verse.Listing_Standard (6)",
            };

            foreach (var ci in instr)
            {
                if (ci.labels.Contains((Label)label))
                {
                    var injectedInstruction = new CodeInstruction(OpCodes.Ldloc_S, ci.operand);
                    injectedInstruction.labels.Add((Label)label);
                    yield return(injectedInstruction);

                    yield return(new CodeInstruction(OpCodes.Ldarg_0));

                    yield return(new CodeInstruction(OpCodes.Ldfld, billField));

                    yield return(new CodeInstruction(OpCodes.Call,
                                                     typeof(Patch_BillCheckbox).GetMethod("GroupExclusionButton")));

                    ci.labels.Remove((Label)label);
                }
                if (HPatcher.IsFragment(opCodes3, operands3, ci, ref step3, "Patch_BillCheckbox1"))
                {
                    var instruction = new CodeInstruction(OpCodes.Call, typeof(Patch_BillCheckbox).GetMethod("StopScrolling"));
                    instruction.labels.AddRange(ci.labels);
                    ci.labels.Clear();
                    yield return(instruction);
                }
                if (HPatcher.IsFragment(opCodes2, operands2, ci, ref step4, "Patch_BillCheckbox2"))
                {
                    yield return(new CodeInstruction(OpCodes.Call, typeof(Patch_BillCheckbox).GetMethod("SetRect")));
                }
                yield return(ci);

                if (HPatcher.IsFragment(opCodes2, operands2, ci, ref step2, "Patch_BillCheckbox3"))
                {
                    yield return(new CodeInstruction(OpCodes.Ldloc_2));

                    yield return(new CodeInstruction(OpCodes.Call, typeof(Patch_BillCheckbox).GetMethod("StartScrolling")));
                }
            }
        }
Пример #30
0
 private static bool ShouldPatch(CodeInstruction actual, CodeInstruction prev)
 {
     return(prev.opcode == OpCodes.Ldarg_1 && actual.opcode == OpCodes.Callvirt && actual.operand != null && actual.operand.ToString().Contains("RimWorld.Faction get_Faction()"));
 }