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"); } }
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); } } }
/// <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); } }
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;
/// <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); } }
// 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; }
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); } }
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); } }
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()); }
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)); }
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))); }
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); }
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;
public IM_CodeInstruction(CodeInstruction q) { query = q; alt = FuzzyOpcodeTable.alt(q.opcode); }
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); }
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); } } }
private static Action CompileInstruction(CodeInstruction instruction) { return((Action)((DynamicMethod)instruction.operand).CreateDelegate(typeof(Action))); }
// 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); }
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()); }
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)); } } }
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; }
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"))); } } }
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()")); }