static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions) { bool patchComplete = false; var ilcodes = new List <CodeInstruction>(instructions); for (int i = 0; i < ilcodes.Count; i++) { if (PatchHelpers.InstructionsAreEqual(ilcodes[i], TargetInstruction)) { if (ilcodes[i - 2].opcode == OpCodes.Br) { object jumpTarget = ilcodes[i - 2].operand; List <CodeInstruction> optionSwitch = PatchHelpers.ILBlocks.IfOptionYesJumpTo(Options.Exploration.OptionStrings.DisableMagnets, jumpTarget); //clone and then null out this instruction, preserving the jump label here CodeInstruction shiftedInstruction = ilcodes[i - 1].Clone(); ilcodes[i - 1].opcode = OpCodes.Nop; ilcodes[i - 1].operand = null; //insert our option checking code next after the Nop/label ilcodes.InsertRange(i, optionSwitch); //reinsert the cloned instruction we copied earlier (now without a label) ilcodes.Insert(i + optionSwitch.Count, shiftedInstruction); patchComplete = true; break; } } } if (patchComplete) { PatchHelpers.LogPatchResult("MagneticPulse", "Patched successfully." /* Enables option to prevent pulsed field magnets from ripping items from your inventory. */); } else { PatchHelpers.LogPatchResult("MagneticPulse", "Failed. This patch may not be compatible with the current game version. " + "The option to prevent pulsed field magnets from ripping items from your inventory won't work."); } return(ilcodes.AsEnumerable()); }
static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions) { int idx = 0; int gap = 0; int patchSegment = 1; bool found = false; CodeInstruction instruction_LoadLocal_AbilityNodeArray = null; CodeInstruction instruction_LoadLocal_AbilityNodeIndex = null; CodeInstruction instruction_LoadLocal_TextBlock = null; bool patchComplete = false; foreach (var instruction in instructions) { if (found) { if (patchSegment == 1) { if (idx == 0 && instruction.opcode == OpCodes.Brfalse_S) { idx++; //do nothing, we're just confirming the expected instruction is here } else if (idx == 1 && PatchHelpers.IsLoadLocalInstruction(instruction)) { instruction_LoadLocal_AbilityNodeArray = instruction.Clone(); idx++; } else if (idx == 2 && PatchHelpers.IsLoadLocalInstruction(instruction)) { instruction_LoadLocal_AbilityNodeIndex = instruction.Clone(); found = false; idx = 0; patchSegment++; } } else if (!patchComplete) { if (idx == 0 && instruction.opcode == OpCodes.Stloc_S) { idx++; instruction_LoadLocal_TextBlock = new CodeInstruction(OpCodes.Ldloc_S, instruction.operand); } else if (idx == 1) { //inject our patch code //get the current AbilityNode item from the array and load it onto the stack yield return(instruction_LoadLocal_AbilityNodeArray); yield return(instruction_LoadLocal_AbilityNodeIndex); yield return(new CodeInstruction(OpCodes.Callvirt, List_AbilityNode_get_Item)); //load the TextBlock onto the stack before the game uses it to write to the screen yield return(instruction_LoadLocal_TextBlock); //call our custom function to update the text block yield return(new CodeInstruction(OpCodes.Call, AbilityManagerExtender_UpdateAbilityText)); PatchHelpers.LogPatchResult("AbilityManager", "Patched successfully." /* Improves activated ability descriptions and cooldown information on the Manage Abilities screen. */); patchComplete = true; } } } else if (patchSegment == 1) { if (PatchHelpers.InstructionsAreEqual(instruction, FirstSegmentTargetInstructions[idx])) { if (++idx == FirstSegmentTargetInstructions.Count()) { found = true; idx = 0; } gap = 0; } else { if (++gap > AllowedInstructionDistance) { idx = 0; } } } else if (patchSegment == 2) { if (PatchHelpers.InstructionsAreEqual(instruction, SecondSegmentTargetInstruction)) { found = true; } } yield return(instruction); } if (patchComplete == false) { PatchHelpers.LogPatchResult("AbilityManager", "Failed. This patch may not be compatible with the current game version. Improved activated ability descriptions and cooldown information won't be added to the Manage Abilities screen."); } }
static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions) { int patchSegment = 1; int patchSegment1_stloc_ct = 0; object ingredientList_LocalVarIndex = null; object ingredientBools_LocalVarIndex = null; object recipeList_LocalVarIndex = null; int idx = 0; int gap = 0; bool found = false; bool patchComplete = false; foreach (var instruction in instructions) { if (found) { if (patchSegment == 1) { if (instruction.opcode == OpCodes.Stloc_S) { patchSegment1_stloc_ct++; } if (patchSegment1_stloc_ct == 2) { //save the local variable index of the ingredient list ingredientList_LocalVarIndex = instruction.operand; patchSegment++; found = false; } } else if (patchSegment == 2) { if (instruction.opcode == OpCodes.Stloc_S) { ingredientBools_LocalVarIndex = instruction.operand; patchSegment++; found = false; } } else if (patchSegment == 3) { //ignore all the lines that push stuff onto the stack for Popup.ShowOptionList if (!PatchHelpers.InstructionsAreEqual(instruction, ThirdSegmentFinalInstruction)) { continue; } //replace the call to Popup.ShowOptionList with our custom ingredient selection menu yield return(new CodeInstruction(OpCodes.Ldloc_S, ingredientList_LocalVarIndex)); yield return(new CodeInstruction(OpCodes.Ldloc_S, ingredientBools_LocalVarIndex)); yield return(new CodeInstruction(OpCodes.Call, QudUX_IngredientSelectionScreen_Static_Show)); patchSegment++; found = false; continue; } else if (patchSegment == 4) { //unused } else if (patchSegment == 5) { if (recipeList_LocalVarIndex == null && instruction.opcode == OpCodes.Ldloc_S) { //grab the recipe list variable, we'll need it below recipeList_LocalVarIndex = instruction.operand; } else if (PatchHelpers.InstructionsAreEqual(instruction, FifthSegmentFinalInstruction)) { //replace the call to Popup.ShowOptionList with our custom recipe selection menu yield return(new CodeInstruction(OpCodes.Ldloc_S, recipeList_LocalVarIndex)); yield return(new CodeInstruction(OpCodes.Call, QudUX_RecipeSelectionScreen_Static_Show)); patchSegment++; found = false; } continue; } else if (!patchComplete) { if (PatchHelpers.InstructionsAreEqual(instruction, SixthSegmentFinalInstruction)) { patchComplete = true; PatchHelpers.LogPatchResult("Campfire", "Patched successfully." /* Adds completely new UI screens for ingredient- and recipe-based cooking. */); //allow this instruction to fall through, we want it and everything after it. } else { //null out various instructions (several of them are used as labels, so can't just ignore them) instruction.opcode = OpCodes.Nop; instruction.operand = null; } } } else if (patchSegment == 1) { if (PatchHelpers.InstructionsAreEqual(instruction, FirstSegmentTargetInstruction)) { found = true; idx = 0; } } else if (patchSegment == 2) { if (PatchHelpers.InstructionsAreEqual(instruction, SecondSegmentTargetInstructions[idx])) { if (++idx == SecondSegmentTargetInstructions.Count()) { found = true; idx = 0; } gap = 0; } else { if (++gap > AllowedInstructionDistance) { idx = 0; } } } else if (patchSegment == 3) { if (PatchHelpers.InstructionsAreEqual(instruction, ThirdSegmentTargetInstructions[idx])) { if (++idx == ThirdSegmentTargetInstructions.Count()) { found = true; instruction.opcode = OpCodes.Nop; //null out this instruction (can't remove it because it's used as a label) instruction.operand = null; idx = 0; } gap = 0; } else { if (++gap > AllowedInstructionDistance) { idx = 0; } } } else if (patchSegment == 4) { if (idx == 0) { if (PatchHelpers.InstructionsAreEqual(instruction, FourthSegmentTarget1_Instruction)) { idx++; } } else if (idx == 1) { if (instruction.opcode == FourthSegmentTarget2_OpCodeOnly) { idx++; } else { idx = 0; } } else if (idx == 2) { if (!PatchHelpers.InstructionsAreEqual(instruction, FourthSegmentTarget3_Instruction)) { if (++gap > FourthSegmentAllowedInstructionDistance) { idx = 0; gap = 0; } } else { instruction.opcode = OpCodes.Ldc_I4_1; //modify to set flag to true instead of false, so that recipes without ingredients on hand aren't hidden from the array patchSegment++; idx = 0; gap = 0; } } } else if (patchSegment == 5) { if (PatchHelpers.InstructionsAreEqual(instruction, FifthSegmentTargetInstruction)) { found = true; instruction.opcode = OpCodes.Nop; //null out this instruction (can't remove it because it's used as a label) instruction.operand = null; } } else if (patchSegment == 6) { if (PatchHelpers.InstructionsAreEqual(instruction, SixthSegmentTargetInstruction)) { found = true; instruction.opcode = OpCodes.Nop; instruction.operand = null; } } yield return(instruction); } if (patchComplete == false) { PatchHelpers.LogPatchResult("Campfire", "Failed. This patch may not be compatible with the current game version. " + "The game's default cooking UI pop-ups will be used instead of QudUX's revamped screens."); } }