static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions, MethodBase original) { var codes = instructions.ToCodeList(); var ldSegmentID = GetLDArg(original, "nodeSegment"); // TODO aquire dynamically var ldSegmentIDA = new CodeInstruction(OpCodes.Ldloc_S, 20); var ldSegmentIDB = new CodeInstruction(OpCodes.Ldloc_S, 21); int index; // non-invert index = codes.Search(_c => _c.LoadsField(f_pavementWidth), count: 2); //A left codes.InsertInstructions(index + 1, //after new[] { ldSegmentIDA.Clone(), ldSegmentID.Clone(), // does not matter new CodeInstruction(OpCodes.Ldc_I4_2), // occurance new CodeInstruction(OpCodes.Call, mModifyPavement), }); index = codes.Search(_c => _c.LoadsField(f_pavementWidth), count: 3); //main right codes.InsertInstructions(index + 1, //after new[] { ldSegmentID.Clone(), ldSegmentIDB.Clone(), new CodeInstruction(OpCodes.Ldc_I4_3), // occurance new CodeInstruction(OpCodes.Call, mModifyPavement), }); // invert index = codes.Search(_c => _c.LoadsField(f_pavementWidth), count: 1); //A left codes.InsertInstructions(index + 1, //after new[] { ldSegmentID.Clone(), ldSegmentIDA.Clone(), new CodeInstruction(OpCodes.Ldc_I4_1), // occurance new CodeInstruction(OpCodes.Call, mModifyPavement), }); index = codes.Search(_c => _c.LoadsField(f_pavementWidth), count: 4); //main right codes.InsertInstructions(index + 1, //after new[] { ldSegmentIDB.Clone(), ldSegmentID.Clone(), // does not matter new CodeInstruction(OpCodes.Ldc_I4_4), // occurance new CodeInstruction(OpCodes.Call, mModifyPavement), }); return(codes); }
public static IEnumerable <CodeInstruction> Transpiler(MethodBase original, IEnumerable <CodeInstruction> instructions) { CodeInstruction ldNodeID = GetLDArg(original, "startNodeID"); CodeInstruction ldSegmentID = GetLDArg(original, "ignoreSegmentID"); CodeInstruction callGetClipSegmentEnd = new CodeInstruction(OpCodes.Call, mGetClipSegmentEnd); int n = 0; foreach (var instruction in instructions) { yield return(instruction); if (instruction.LoadsField(f_clipSegmentEnds)) { n++; yield return(ldNodeID.Clone()); yield return(ldSegmentID.Clone()); yield return(callGetClipSegmentEnd.Clone()); } } Log.Succeeded($"patched {n} instances of {f_clipSegmentEnds} in {original}"); }
/// <summary> /// Deep-copies the instruction, including labels and exception blocks. /// </summary> /// <param name="instruction">The instruction to fully clone.</param> /// <returns>Fully cloned instruction.</returns> public static CodeInstruction FullClone(this CodeInstruction instruction) { CodeInstruction clone = instruction.Clone(); clone.labels = instruction.labels.ConvertAll(l => l); // TODO: Clone labels? clone.blocks = instruction.blocks.ConvertAll(b => b.Clone()); return(clone); }
public static IEnumerable <CodeInstruction> Transpiler(MethodBase original, IEnumerable <CodeInstruction> instructions) { CodeInstruction ldSegmentID = GetLDArg(original, "segmentID"); CodeInstruction callGetClipSegmentStart = new CodeInstruction(OpCodes.Call, mGetClipSegmentStart); CodeInstruction callGetClipSegmentEnd = new CodeInstruction(OpCodes.Call, mGetClipSegmentEnd); int n = 0; foreach (var instruction in instructions) { yield return(instruction); if (instruction.LoadsField(f_clipSegmentEnds)) { yield return(ldSegmentID.Clone()); // first one is for start node and second one is for end node. switch (n) { case 0: yield return(callGetClipSegmentStart.Clone()); break; case 1: yield return(callGetClipSegmentEnd.Clone()); break; default: new Exception("expected only 2 occurrences").Log(); break; } n++; } } Log.Succeeded($"patched {n} instances of {f_clipSegmentEnds} in {original}"); }
public static List <CodeInstruction> ApplyPatch( List <CodeInstruction> codes, CodeInstruction ldSegmentID, CodeInstruction ldSegmentIDA, CodeInstruction ldSegmentIDB) { int index; /**************************************************** * non-invert */ index = codes.Search(_c => _c.LoadsField(f_pavementWidth), count: 2); //A left codes.InsertInstructions(index + 1, //after new[] { ldSegmentIDA.Clone(), ldSegmentID.Clone(), // does not matter new CodeInstruction(OpCodes.Ldc_I4_2), // occurance new CodeInstruction(OpCodes.Call, mModifyPavement), }); index = codes.Search(_c => _c.LoadsField(f_pavementWidth), count: 3); //main right codes.InsertInstructions(index + 1, //after new[] { ldSegmentID.Clone(), ldSegmentIDB.Clone(), new CodeInstruction(OpCodes.Ldc_I4_3), // occurance new CodeInstruction(OpCodes.Call, mModifyPavement), }); /**************************************************** * invert */ index = codes.Search(_c => _c.LoadsField(f_pavementWidth), count: 1); //A left codes.InsertInstructions(index + 1, //after new[] { ldSegmentID.Clone(), ldSegmentIDA.Clone(), new CodeInstruction(OpCodes.Ldc_I4_1), // occurance new CodeInstruction(OpCodes.Call, mModifyPavement), }); index = codes.Search(_c => _c.LoadsField(f_pavementWidth), count: 4); //main right codes.InsertInstructions(index + 1, //after new[] { ldSegmentIDB.Clone(), ldSegmentID.Clone(), // does not matter new CodeInstruction(OpCodes.Ldc_I4_4), // occurance new CodeInstruction(OpCodes.Call, mModifyPavement), }); /******************************** * m_dataVector0 */ index = codes.Search(_c => _c.LoadsField(f_pavementWidth), count: 5); //m_dataVector0.z codes.InsertInstructions(index + 1, //after new[] { ldSegmentID.Clone(), ldSegmentID.Clone(), // does not matter new CodeInstruction(OpCodes.Ldc_I4_5), // occurance new CodeInstruction(OpCodes.Call, mModifyPavement), }); index = codes.Search(_c => _c.LoadsField(f_pavementWidth), count: 6); //m_dataVector0.w codes.InsertInstructions(index + 1, //after new[] { ldSegmentID.Clone(), ldSegmentID.Clone(), // does not matter new CodeInstruction(OpCodes.Ldc_I4_6), // occurance new CodeInstruction(OpCodes.Call, mModifyPavement), }); return(codes); }
public static IEnumerable <CodeInstruction> SetControlsTranspiler(IEnumerable <CodeInstruction> instructions) { bool foundCancelCrouch = false; bool foundAutoRunCondition = false; bool foundMoveDirCondition = false; bool foundCancelAutorunCondition = false; bool foundMoveDirAssignment = false; CodeInstruction branchEndAutoRun = null; List <CodeInstruction> codes = new List <CodeInstruction>(instructions); //DumpIL(codes); for (int i = 0; i < codes.Count(); i++) { CodeInstruction code = codes[i]; if (!foundCancelCrouch && codes[i + 0].opcode == OpCodes.Ldarg_0 && codes[i + 1].opcode == OpCodes.Ldc_I4_0 && codes[i + 2].Calls(AccessTools.Method(typeof(Character), nameof(Character.SetCrouch)))) { //Debug.Log("foundit0 " + i); // Nop out the call to "this.SetCrouch(false)" codes[i + 0].opcode = OpCodes.Nop; codes[i + 0].operand = null; codes[i + 1].opcode = OpCodes.Nop; codes[i + 1].operand = null; codes[i + 2].opcode = OpCodes.Nop; codes[i + 2].operand = null; foundCancelCrouch = true; } // We're looking for a ldfld of m_autoRun followed by a brfalse: "if(this.m_autoRun)" if (!foundAutoRunCondition && code.opcode == OpCodes.Ldfld && code.LoadsField(AccessTools.DeclaredField(typeof(Player), nameof(Player.m_autoRun))) && codes[i + 1].opcode == OpCodes.Brfalse) { //Debug.Log("foundit1 " + i); foundAutoRunCondition = true; } // Nop out the "jump || crouch || movedir != Vector3.zero" conditions if (foundAutoRunCondition && codes[i - 5].opcode == OpCodes.Ldarg_S && codes[i - 4].opcode == OpCodes.Or && codes[i - 3].opcode == OpCodes.Ldarg_S && codes[i - 2].opcode == OpCodes.Or && codes[i - 1].Branches(out Label? asdfasdfasdsa) && codes[i + 0].opcode == OpCodes.Ldarg_1 && codes[i + 1].opcode == OpCodes.Call) { //Debug.Log("foundit3 " + i); codes[i - 5].opcode = OpCodes.Nop; codes[i - 5].operand = null; codes[i - 4].opcode = OpCodes.Nop; codes[i - 4].operand = null; codes[i - 3].opcode = OpCodes.Nop; codes[i - 3].operand = null; codes[i - 2].opcode = OpCodes.Nop; codes[i - 2].operand = null; // Leave codes[i - 1] alone since it's the branching instruction for the very first condition codes[i + 0].opcode = OpCodes.Nop; codes[i + 0].operand = null; codes[i + 1].opcode = OpCodes.Nop; codes[i + 1].operand = null; codes[i + 2].opcode = OpCodes.Nop; codes[i + 2].operand = null; codes[i + 3].opcode = OpCodes.Nop; codes[i + 3].operand = null; // Add in our own autorun canceling conditions: if either forwards or backwards are pressed. branchEndAutoRun = codes[i - 1]; codes.InsertRange(i, new List <CodeInstruction>() { new CodeInstruction(OpCodes.Ldstr, "Forward"), CodeInstruction.Call(typeof(ZInput), nameof(ZInput.GetButton)), branchEndAutoRun.Clone(), new CodeInstruction(OpCodes.Ldstr, "Backward"), CodeInstruction.Call(typeof(ZInput), nameof(ZInput.GetButton)), branchEndAutoRun.Clone() }); foundCancelAutorunCondition = true; } // Convert "else if (autoRun || blockHold)" into "else" if (foundCancelAutorunCondition && codes[i + 0].opcode == OpCodes.Ldarg_S && //&& codes[i + 0].operand as string == "10" codes[i + 1].opcode == OpCodes.Ldarg_S && //&& codes[i + 1].operand as string == "6" codes[i + 2].opcode == OpCodes.Or && codes[i + 3].Branches(out Label? asdfasdfsad)) { //Debug.Log("foundit4 " + i); codes[i + 0].opcode = OpCodes.Nop; codes[i + 0].operand = null; codes[i + 1].opcode = OpCodes.Nop; codes[i + 1].operand = null; codes[i + 2].opcode = OpCodes.Nop; codes[i + 2].operand = null; codes[i + 3].opcode = OpCodes.Nop; codes[i + 3].operand = null; foundMoveDirCondition = true; } // Lastly, add "movedir.x * Vector3.Cross(Vector3.up, this.m_lookDir)" to the player's movedir so that they can strafe while autorunning if (foundMoveDirCondition && codes[i - 3].opcode == OpCodes.Ldarg_0 && codes[i - 2].opcode == OpCodes.Ldarg_0 && codes[i - 1].LoadsField(AccessTools.Field(typeof(Character), nameof(Character.m_lookDir))) && codes[i - 0].StoresField(AccessTools.Field(typeof(Character), nameof(Character.m_moveDir)))) { //Debug.Log("foundit5 " + i); codes.InsertRange(i, new List <CodeInstruction>() { new CodeInstruction(OpCodes.Ldarg_1), CodeInstruction.LoadField(typeof(Vector3), nameof(Vector3.x)), new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(Vector3), nameof(Vector3.up))), new CodeInstruction(OpCodes.Ldarg_0), CodeInstruction.LoadField(typeof(Player), nameof(Player.m_lookDir)), CodeInstruction.Call(typeof(Vector3), nameof(Vector3.Cross)), new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Vector3), "op_Multiply", new Type[] { typeof(float), typeof(Vector3) })), new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Vector3), "op_Addition", new Type[] { typeof(Vector3), typeof(Vector3) })) }); foundMoveDirAssignment = true; break; // we done } } //DumpIL(codes); //Debug.Log(string.Format("{0} {1} {2} {3} {4}", foundAutoRunCondition, branchEndAutoRun != null, foundCancelAutorunCondition, foundMoveDirCondition, foundMoveDirAssignment)); if (!foundAutoRunCondition || branchEndAutoRun == null || !foundCancelAutorunCondition || !foundMoveDirCondition || !foundMoveDirAssignment) { throw new Exception("BetterAutoRun injection point NOT found!! Game has most likely updated and broken this mod!"); } if (!foundCancelCrouch) { Main.log.LogWarning("One of the BetterAutoRun injection points were not found, game has most likely updated and broken this mod."); Main.log.LogWarning("Autorun while crouching will not work but everything else should be fine."); } return(codes.AsEnumerable()); }
private static IEnumerable <CodeInstruction> UpdateFood_Transpiler(IEnumerable <CodeInstruction> instructions) { bool done = false; CodeInstruction previous = null; foreach (CodeInstruction instruction in instructions) { if (done) { yield return(instruction); continue; } if (instruction.opcode == OpCodes.Ldfld && ((FieldInfo)instruction.operand).Name == nameof(Player.Food.m_health) && previous != null) { // (1.0f - Mathf.Pow(1.0f - food.m_health / food.m_item.m_shared.m_food, CurveExponent.Value)) * food.m_item.m_shared.m_food yield return(new CodeInstruction(OpCodes.Ldc_R4, 1.0f)); yield return(new CodeInstruction(OpCodes.Ldc_R4, 1.0f)); yield return(previous); yield return(instruction); yield return(previous.Clone()); yield return(new CodeInstruction(OpCodes.Ldfld, typeof(Player.Food).GetField(nameof(Player.Food.m_item)))); yield return(new CodeInstruction(OpCodes.Ldfld, typeof(ItemDrop.ItemData).GetField(nameof(ItemDrop.ItemData.m_shared)))); yield return(new CodeInstruction(OpCodes.Ldfld, typeof(ItemDrop.ItemData.SharedData).GetField(nameof(ItemDrop.ItemData.SharedData.m_food)))); yield return(new CodeInstruction(OpCodes.Div)); yield return(new CodeInstruction(OpCodes.Sub)); yield return(new CodeInstruction(OpCodes.Ldc_R4, HealthCurveExponent.Value)); yield return(new CodeInstruction(OpCodes.Call, typeof(Mathf).GetMethod(nameof(Mathf.Pow)))); yield return(new CodeInstruction(OpCodes.Sub)); yield return(previous.Clone()); yield return(new CodeInstruction(OpCodes.Ldfld, typeof(Player.Food).GetField(nameof(Player.Food.m_item)))); yield return(new CodeInstruction(OpCodes.Ldfld, typeof(ItemDrop.ItemData).GetField(nameof(ItemDrop.ItemData.m_shared)))); yield return(new CodeInstruction(OpCodes.Ldfld, typeof(ItemDrop.ItemData.SharedData).GetField(nameof(ItemDrop.ItemData.SharedData.m_food)))); yield return(new CodeInstruction(OpCodes.Mul)); done = true; } else { if (previous != null) { yield return(previous); previous = null; } if (instruction.opcode == OpCodes.Ldloc_S) { previous = instruction; } else { yield return(instruction); } } } }