public static IEnumerable <CodeInstruction> Transpiler(ILGenerator il, IEnumerable <CodeInstruction> instructions)
        {
            var originalInstructions = new List <CodeInstruction>(instructions);

            var netAiGetNodeBuildingMethod = typeof(NetAI).GetMethod("GetNodeBuilding");

            var pillarPatcherGetActiveNodeBuildingMethod = typeof(ActivePillarPatcher).GetMethod("GetActiveNodeBuilding");

            if (netAiGetNodeBuildingMethod == null || pillarPatcherGetActiveNodeBuildingMethod == null)
            {
                Debug.LogError("Necessary methods not found. Cancelling transpiler!");
                return(originalInstructions);
            }

            var codes = new List <CodeInstruction>(originalInstructions);

            // Replace all GetNodeBuilding calls with GetActiveNodeBuilding
            for (var index = 0; index < codes.Count; index++)
            {
                if (codes[index].opcode == OpCodes.Callvirt)
                {
                    if (codes[index].operand == netAiGetNodeBuildingMethod)
                    {
                        TranspilerUtils.LogDebug("Found GetNodeBuilding(ushort nodeID, ref NetNode data, out BuildingInfo building, out float heightOffset)");
                        codes[index] = new CodeInstruction(codes[index])
                        {
                            opcode  = OpCodes.Call,
                            operand = pillarPatcherGetActiveNodeBuildingMethod
                        };
                    }
                }
            }

            return(codes);
        }
Esempio n. 2
0
        static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions, MethodBase original)
        {
            var codes         = instructions.ToCodeList();
            int iGetSegment1  = codes.Search(c => c.Calls(mGetSegment), count: 1);
            int iStSegmentID1 = codes.Search(c => c.IsStloc(), startIndex: iGetSegment1);
            var ldSegmentID1  = TranspilerUtils.BuildLdLocFromStLoc(codes[iStSegmentID1]);

            int iGetSegment2  = codes.Search(c => c.Calls(mGetSegment), count: 1);
            int iStSegmentID2 = codes.Search(c => c.IsStloc(), startIndex: iGetSegment2);
            var ldSegmentID2  = TranspilerUtils.BuildLdLocFromStLoc(codes[iStSegmentID2]);

            codes.InsertInstructions(iStSegmentID2 + 1, new[] {
                ldSegmentID1,
                ldSegmentID2,
                new CodeInstruction(OpCodes.Call, ShiftData.mPrefix),
            });

            // DC part of the code is before m_requireSegmentRenderers
            int i_requireSegmentRenderers = codes.Search(c => c.LoadsField(f_requireSegmentRenderers));

            codes.InsertInstructions(i_requireSegmentRenderers, new[] {
                new CodeInstruction(OpCodes.Call, ShiftData.mPostfix),
            });

            return(codes);
        }
        // code from: https://github.com/Strdate/SmartIntersections/blob/master/SmartIntersections/Patch/LoadPathsPatch.cs
        public static IEnumerable <CodeInstruction> Transpiler(ILGenerator il, IEnumerable <CodeInstruction> instructions)
        {
            var fTempNodeBuffer = AccessTools.DeclaredField(typeof(NetManager), nameof(NetManager.m_tempNodeBuffer))
                                  ?? throw new Exception("cound not find NetManager.m_tempNodeBuffer");
            var mClear = AccessTools.DeclaredMethod(fTempNodeBuffer.FieldType, nameof(FastList <ushort> .Clear))
                         ?? throw new Exception("cound not find m_tempNodeBuffer.Clear");
            var mAfterIntersectionBuilt = AccessTools.DeclaredMethod(
                typeof(LoadPathsPatch), nameof(AfterIntersectionBuilt))
                                          ?? throw new Exception("cound not find AfterIntersectionBuilt()");

            List <CodeInstruction> codes = TranspilerUtils.ToCodeList(instructions);

            bool comp(int i) =>
            codes[i].opcode == OpCodes.Ldfld && codes[i].operand == fTempNodeBuffer &&
            codes[i + 1].opcode == OpCodes.Callvirt && codes[i + 1].operand == mClear;

            int index = codes.Search(comp, startIndex: 0, count: 2);

            index -= 1; // index to insert instructions.

            var newInstructions = new[] {
                new CodeInstruction(OpCodes.Ldarg_0),
                new CodeInstruction(OpCodes.Call, mAfterIntersectionBuilt),
            };

            codes.InsertInstructions(index, newInstructions);
            return(codes);
        }
 //static bool Prefix(ushort nodeID){}
 public static IEnumerable<CodeInstruction> Transpiler(MethodBase original, IEnumerable<CodeInstruction> instructions) {
     try {
         var codes = TranspilerUtils.ToCodeList(instructions);
         CheckMedianCommons.ApplyCheckMedian(codes, original, occurance: 1);
         return codes;
     } catch (Exception e) {
         e.Log();
         throw e;
     }
 }
Esempio n. 5
0
        public static IEnumerable <CodeInstruction> Transpiler(ILGenerator il, IEnumerable <CodeInstruction> instructions)
        {
            var originalInstructions = new List <CodeInstruction>(instructions);

            var netAiGetNodeBuildingMethod    = typeof(NetAI).GetMethod("GetNodeBuilding");
            var netAiCheckBuildPositionMethod = typeof(NetAI).GetMethod("CheckBuildPosition");

            var pillarPatcherGetActiveNodeBuildingMethod = typeof(ActivePillarPatcher).GetMethod("GetActiveNodeBuilding");
            var patchCheckBuildPositionMethod            = typeof(NetToolCreateNodePatch).GetMethod("CheckBuildPosition");

            if (netAiGetNodeBuildingMethod == null || pillarPatcherGetActiveNodeBuildingMethod == null)
            {
                Debug.LogError("Necessary methods not found. Cancelling transpiler!");
                return(originalInstructions);
            }

            var codes = new List <CodeInstruction>(originalInstructions);

            // Replace all GetNodeBuilding calls with GetActiveNodeBuilding
            for (var index = 0; index < codes.Count; index++)
            {
                if (codes[index].opcode == OpCodes.Callvirt)
                {
                    if (codes[index].operand == netAiGetNodeBuildingMethod)
                    {
                        TranspilerUtils.LogDebug("Found GetNodeBuilding(ushort nodeID, ref NetNode data, out BuildingInfo building, out float heightOffset)");
                        codes[index] = new CodeInstruction(codes[index])
                        {
                            opcode  = OpCodes.Call,
                            operand = pillarPatcherGetActiveNodeBuildingMethod
                        };
                    }
                }
            }

            // Replace all CheckBuildPosition calls with Patcher.CheckBuildPosition
            for (var index = 0; index < codes.Count; index++)
            {
                if (codes[index].opcode == OpCodes.Callvirt)
                {
                    if (codes[index].operand == netAiCheckBuildPositionMethod)
                    {
                        TranspilerUtils.LogDebug("Found CheckBuildPosition(bool test, bool visualize, bool overlay, bool autofix, ref NetTool.ControlPoint startPoint, ref NetTool.ControlPoint middlePoint, ref NetTool.ControlPoint endPoint, out BuildingInfo ownerBuilding, out Vector3 ownerPosition, out Vector3 ownerDirection, out int productionRate)");
                        codes[index] = new CodeInstruction(codes[index])
                        {
                            opcode  = OpCodes.Call,
                            operand = patchCheckBuildPositionMethod
                        };
                    }
                }
            }

            return(codes);
        }
Esempio n. 6
0
 public static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions, MethodBase original)
 {
     try {
         var codes = TranspilerUtils.ToCodeList(instructions);
         SegmentOverlay.Patch(codes, original);
         Log.Info($"{ReflectionHelpers.ThisMethod} patched {original} successfully!");
         return(codes);
     } catch (Exception e) {
         Log.Error(e.ToString());
         throw e;
     }
 }
 public static IEnumerable<CodeInstruction> Transpiler(MethodBase original, IEnumerable<CodeInstruction> instructions) {
     try {
         var codes = TranspilerUtils.ToCodeList(instructions);
         CheckPropFlagsCommons.PatchCheckFlags(codes, original);
         Log.Info($"{ReflectionHelpers.ThisMethod} patched {original} successfully!");
         return codes;
     }
     catch (Exception e) {
         Log.Error(e.ToString());
         throw e;
     }
 }
Esempio n. 8
0
 public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase original) {
     try {
         var codes = TranspilerUtils.ToCodeList(instructions);
         CheckSegmentFlagsCommons.PatchCheckFlags(codes, original);
         Log.Info($"{ThisMethod} patched {original} successfully!");
         return codes;
     }
     catch (Exception ex) {
         Log.Error(ex.ToString());
         throw ex;
     }
 }
Esempio n. 9
0
        public static IEnumerable<CodeInstruction> Transpiler(ILGenerator il, IEnumerable<CodeInstruction> instructions) {
            try {
                var codes = TranspilerUtils.ToCodeList(instructions);
                CalculateMaterialCommons.PatchCheckFlags(codes, occurance: 2, Target);

                Log("successfully patched NetNode.RenderInstance");
                return codes;
            }catch(Exception e) {
                KianCommons.Log.Error(e.ToString());
                throw e;
            }
        }
        //static bool Prefix(ushort nodeID){}
        public static IEnumerable<CodeInstruction> Transpiler(ILGenerator il, IEnumerable<CodeInstruction> instructions) {
            try {
                var codes = TranspilerUtils.ToCodeList(instructions);
                CheckTracksCommons.ApplyCheckTracks(codes, Target, occurance:1);

                Log("successfully patched NetNode.RenderInstance");
                return codes;
            }catch(Exception e) {
                Log(e + "\n" + Environment.StackTrace);
                throw e;
            }
        }
        public static IEnumerable <CodeInstruction> Transpiler(ILGenerator il, IEnumerable <CodeInstruction> instructions)
        {
            var originalInstructions = new List <CodeInstruction>(instructions);

            var netAiGetSegmentColorMethod = typeof(NetAI).GetMethod("GetColor", new[] { typeof(ushort), typeof(global::NetSegment).MakeByRefType(), typeof(InfoManager.InfoMode) });
            var netAiGetNodeColorMethod    = typeof(NetAI).GetMethod("GetColor", new[] { typeof(ushort), typeof(global::NetNode).MakeByRefType(), typeof(InfoManager.InfoMode) });

            var colorPatcherGetSegmentColorMethod = typeof(ColorPatcher).GetMethod("GetSegmentColor");
            var colorPatcherGetNodeColorMethod    = typeof(ColorPatcher).GetMethod("GetNodeColor");

            if (netAiGetSegmentColorMethod == null || netAiGetNodeColorMethod == null || colorPatcherGetSegmentColorMethod == null || colorPatcherGetNodeColorMethod == null)
            {
                Debug.LogError("Necessary methods not found. Cancelling transpiler!");
                return(originalInstructions);
            }

            var codes = new List <CodeInstruction>(originalInstructions);

            // Replace all GetColor calls with GetSegmentColor/GetNodeColor
            for (var index = 0; index < codes.Count; index++)
            {
                if (codes[index].opcode == OpCodes.Callvirt)
                {
                    if (codes[index].operand == netAiGetSegmentColorMethod)
                    {
                        TranspilerUtils.LogDebug("Found GetColor(ushort segmentID, ref NetSegment data, InfoManager.InfoMode infoMode)");
                        codes[index] = new CodeInstruction(codes[index])
                        {
                            opcode  = OpCodes.Call,
                            operand = colorPatcherGetSegmentColorMethod
                        };
                    }
                    else if (codes[index].operand == netAiGetNodeColorMethod)
                    {
                        TranspilerUtils.LogDebug("Found GetColor(ushort nodeID, ref NetNode data, InfoManager.InfoMode infoMode)");
                        codes[index] = new CodeInstruction(codes[index])
                        {
                            opcode  = OpCodes.Call,
                            operand = colorPatcherGetNodeColorMethod
                        };
                    }
                }
            }

            return(codes);
        }
Esempio n. 12
0
        public static IEnumerable <CodeInstruction> Transpiler(
            IEnumerable <CodeInstruction> instructions, MethodBase original)
        {
            try {
                var codes = TranspilerUtils.ToCodeList(instructions);
                // (node.m_connectGroup == NetInfo.ConnectGroup.None || (node.m_connectGroup & info2.m_connectGroup & NetInfo.ConnectGroup.AllGroups) != NetInfo.ConnectGroup.None)
                // (node4.m_connectGroup == NetInfo.ConnectGroup.None || (node4.m_connectGroup & info.m_connectGroup & NetInfo.ConnectGroup.AllGroups) != NetInfo.ConnectGroup.None)
                CheckNodeConnectGroupNone.Patch(codes, original); // 2
                CheckNodeConnectGroup.Patch(codes, original);     // 2

                Log.Info($"{ReflectionHelpers.ThisMethod} patched {original} successfully!");
                return(codes);
            } catch (Exception e) {
                Log.Error(e.ToString());
                throw e;
            }
        }
Esempio n. 13
0
 public static IEnumerable <CodeInstruction> Transpiler(
     IEnumerable <CodeInstruction> instructions, MethodBase original)
 {
     try {
         var codes = TranspilerUtils.ToCodeList(instructions);
         NodeOverlay.Patch(codes, original, occuranceDrawMesh: 1, counterGetSegment: 2); // DC
         NodeOverlay.Patch(codes, original, occuranceDrawMesh: 2, counterGetSegment: 1); // junction
         NodeOverlay.Patch(codes, original, occuranceDrawMesh: 3, counterGetSegment: 0); // end
         NodeOverlay.PatchBend(codes, original, occuranceDrawMesh: 4);                   // end
         NodeOverlay.Patch(codes, original, occuranceDrawMesh: 5, counterGetSegment: 0); // DC-bend
         Log.Info($"{ReflectionHelpers.ThisMethod} patched {original} successfully!");
         return(codes);
     } catch (Exception e) {
         Log.Error(e.ToString());
         throw e;
     }
 }
Esempio n. 14
0
        public static IEnumerable<CodeInstruction> Transpiler(MethodBase original, IEnumerable<CodeInstruction> instructions) {
            try {
                var codes = TranspilerUtils.ToCodeList(instructions);
                CheckNodeFlagsCommons.PatchCheckFlags(codes, original, occuranceCheckFlags: 1, counterGetSegment: 2); //DC
                CheckNodeFlagsCommons.PatchCheckFlags(codes, original, occuranceCheckFlags: 2, counterGetSegment: 2); //DC // CS has copy pasted code.
                CheckNodeFlagsCommons.PatchCheckFlags(codes, original, occuranceCheckFlags: 3, counterGetSegment: 1); //Junction
                CheckNodeFlagsCommons.PatchCheckFlags(codes, original, occuranceCheckFlags: 4, counterGetSegment: 0); //End

                // Bend node -> segment.Checkflags (does not use info flags.)
                CheckNodeFlagsCommons.PatchCheckFlags(codes, original, occuranceCheckFlags: 5, counterGetSegment: 0); //DC Bend 

                Log.Info($"{ReflectionHelpers.ThisMethod} patched {original} successfully!");
                return codes;
            } catch (Exception e) {
                Log.Error(e.ToString());
                throw e;
            }
        }
Esempio n. 15
0
        public static IEnumerable <CodeInstruction> Transpiler(ILGenerator il, IEnumerable <CodeInstruction> instructions)
        {
            var netSegmentInfoGetter = typeof(global::NetSegment).GetProperty("Info")?.GetGetMethod();

            if (netSegmentInfoGetter == null)
            {
                Debug.LogError("Necessary field not found. Cancelling transpiler!");
                return(instructions);
            }

            var originalCodes = new List <CodeInstruction>(instructions);
            var codes         = new List <CodeInstruction>(originalCodes);

            var index = 0;

            CodeInstruction infoLocalVarLdloc = null;

            for (; index < codes.Count; index++)
            {
                // IL_0003: call instance class NetInfo NetSegment::get_Info()
                if (codes[index].opcode == OpCodes.Call && codes[index].operand == netSegmentInfoGetter && TranspilerUtils.IsStLoc(codes[index + 1]))
                {
                    infoLocalVarLdloc = TranspilerUtils.BuildLdLocFromStLoc(codes[index + 1]);
                    index            += 2;
                    break;
                }
            }

            if (infoLocalVarLdloc == null)
            {
                Debug.LogError("NetSegmentCalculateGroupDataPatch: info local variable not found. Cancelling transpiler!");
                return(originalCodes);
            }

            var segmentIdLdInstruction = new CodeInstruction(OpCodes.Ldarg_1); // segmentID is first argument

            if (!NetSegmentRenderPatch.PatchLanesAndSegments(il, codes, infoLocalVarLdloc, segmentIdLdInstruction, ref index))
            {
                Debug.LogError("NetSegmentCalculateGroupDataPatch: Could not apply NetSegmentRenderPatch. Cancelling transpiler!");
                return(originalCodes);
            }

            return(codes);
        }
Esempio n. 16
0
        public static IEnumerable<CodeInstruction> Transpiler(MethodBase original, IEnumerable<CodeInstruction> instructions) {
            try {
                var codes = TranspilerUtils.ToCodeList(instructions);
                CheckNodeFlagsCommons.PatchCheckFlags(codes, original, occuranceCheckFlags: 1, counterGetSegment: 2, true); //DC
                CheckNodeFlagsCommons.PatchCheckFlags(codes, original, occuranceCheckFlags: 2, counterGetSegment: 2, true); //DC

                // Unlike RenderInstance and CalculateGroupData, counterGetSegment for PopulateGroupData Junction is 2:
                CheckNodeFlagsCommons.PatchCheckFlags(codes, original, occuranceCheckFlags: 3, counterGetSegment: 2); //Junction
                CheckNodeFlagsCommons.PatchCheckFlags(codes, original, occuranceCheckFlags: 4, counterGetSegment: 0); // End

                // End - BEND ->  segment.Checkflags (does not use info flags.)
                // Bend node -> segment.Checkflags (does not use info flags.)
                CheckNodeFlagsCommons.PatchCheckFlags(codes, original, occuranceCheckFlags: 5, counterGetSegment: 0, true); //DC Bend


                Log.Info($"{ReflectionHelpers.ThisMethod} patched {original} successfully!");
                return codes;
            } catch (Exception e) {
                Log.Error(e.ToString());
                throw e;
            }
        }
        private static CodeInstruction FindNum6LocalVar(List <CodeInstruction> codes, CodeInstruction netInfoLocalVarStLoc, ref int index, int endIndex)
        {
            if (!TranspilerUtils.IsStLoc(netInfoLocalVarStLoc))
            {
                Debug.LogError("netInfoLocalVarStLoc is not stloc! Cancelling transpiler!");
                return(null);
            }

            // NetSegment netSegment5 = Singleton<NetManager>.instance.m_segments.m_buffer[num6];
            // IL_07cc: call !0 class [ColossalManaged] ColossalFramework.Singleton`1<class NetManager>::get_instance()
            // IL_07d1: ldfld class Array16`1<valuetype NetSegment> NetManager::m_segments
            // IL_07d6: ldfld !0[] class Array16`1<valuetype NetSegment>::m_buffer
            // IL_07db: ldloc.s 51
            // IL_07dd: ldelema NetSegment
            // IL_07e2: ldobj NetSegment
            // IL_07e7: stloc.s 61
            // netInfo = netSegment5.Info;
            // IL_07e9: ldloca.s 61
            // IL_07eb: call instance class NetInfo NetSegment::get_Info()
            // IL_07f0: stloc.s 48
            for (; index < endIndex; index++)
            {
                // IL_07f0: stloc.s 48
                if (TranspilerUtils.IsSameInstruction(codes[index], netInfoLocalVarStLoc))
                {
                    TranspilerUtils.LogDebug("Found netInfo = ...");

                    // IL_07db: ldloc.s 51
                    if (TranspilerUtils.IsLdLoc(codes[index - 6]))
                    {
                        TranspilerUtils.LogDebug("Found num6");
                        return(TranspilerUtils.BuildLdLocFromLdLoc(codes[index - 6]));
                    }
                }
            }

            Debug.LogError("Unable to find num6. Cancelling transpiler!");
            return(null);
        }
        private static CodeInstruction FindSegment7LocalVar(List <CodeInstruction> codes, CodeInstruction info4LocalVarStLoc, ref int index, int endIndex)
        {
            if (!TranspilerUtils.IsStLoc(info4LocalVarStLoc))
            {
                Debug.LogError("info4LocalVarStLoc is not stloc! Cancelling transpiler!");
                return(null);
            }

            // NetSegment netSegment3 = Singleton<NetManager>.instance.m_segments.m_buffer[segment7];
            // IL_05a9: call !0 class [ColossalManaged]ColossalFramework.Singleton`1<class NetManager>::get_instance()
            // IL_05ae: ldfld class Array16`1<valuetype NetSegment> NetManager::m_segments
            // IL_05b3: ldfld !0[] class Array16`1<valuetype NetSegment>::m_buffer
            // IL_05b8: ldloc.s 38
            // IL_05ba: ldelema NetSegment
            // IL_05bf: ldobj NetSegment
            // IL_05c4: stloc.s 39
            // NetInfo info4 = netSegment3.Info;
            // IL_05c6: ldloca.s 39
            // IL_05c8: call instance class NetInfo NetSegment::get_Info()
            // IL_05cd: stloc.s 40
            for (; index < endIndex; index++)
            {
                // IL_05cd: stloc.s 40
                if (TranspilerUtils.IsSameInstruction(codes[index], info4LocalVarStLoc))
                {
                    TranspilerUtils.LogDebug("Found info4 = ...");

                    // // IL_05b8: ldloc.s 38
                    if (TranspilerUtils.IsLdLoc(codes[index - 6]))
                    {
                        TranspilerUtils.LogDebug("Found segment7");
                        return(TranspilerUtils.BuildLdLocFromLdLoc(codes[index - 6]));
                    }
                }
            }

            Debug.LogError("Unable to find segment7. Cancelling transpiler!");
            return(null);
        }
Esempio n. 19
0
        public static IEnumerable <CodeInstruction> Transpiler(ILGenerator il, MethodBase original, IEnumerable <CodeInstruction> instructions)
        {
            var originalInstructions = new List <CodeInstruction>(instructions);

            var allowedCallersField = original.DeclaringType?.GetField("AllowedCallers", BindingFlags.Static | BindingFlags.NonPublic);

            var isAllowedCallerMethod = typeof(NetManagerDetourPatch).GetMethod("IsAllowedCaller", BindingFlags.Static | BindingFlags.Public);

            if (allowedCallersField == null || isAllowedCallerMethod == null)
            {
                Debug.LogError("Necessary members not found. Cancelling transpiler!");
                return(originalInstructions);
            }

            var codes = new List <CodeInstruction>(originalInstructions);

            for (var index = 0; index < codes.Count; index++)
            {
                if (codes[index].opcode == OpCodes.Ldsfld && codes[index].operand == allowedCallersField)
                {
                    if (TranspilerUtils.IsLdLoc(codes[index + 1]))
                    {
                        if (codes[index + 2].opcode == OpCodes.Call && (codes[index + 2].operand as MethodBase).Name == "Contains")
                        {
                            codes[index + 2] = new CodeInstruction(codes[index + 2])
                            {
                                opcode  = OpCodes.Call,
                                operand = isAllowedCallerMethod
                            };
                            Debug.Log("Patched ParallelRoadTool to work with NS2!");
                            break;
                        }
                    }
                }
            }

            return(codes);
        }
 static MethodBase TargetMethod()
 {
     return(TranspilerUtils.GetCoroutineMoveNext(
                typeof(AssetImporterAssetTemplate),
                nameof(AssetImporterAssetTemplate.RefreshCoroutine)));
 }
Esempio n. 21
0
        public static IEnumerable <CodeInstruction> Transpiler(ILGenerator il, IEnumerable <CodeInstruction> instructions)
        {
            var netNodeRefreshEndDataMethod =
                typeof(global::NetNode).GetMethod("RefreshEndData", BindingFlags.NonPublic | BindingFlags.Instance);
            var netNodeGetSegmentMethod = typeof(global::NetNode).GetMethod("GetSegment");
            var netInfoNodesField       = typeof(NetInfo).GetField("m_nodes");

            var netNodeRenderPatchShouldRenderJunctionNodeMethod =
                typeof(NetNodeRenderPatch).GetMethod("ShouldRenderJunctionNode");

            var netNodeRenderPatchShouldRenderBendNodeMethod =
                typeof(NetNodeRenderPatch).GetMethod("ShouldRenderBendNode");

            if (netNodeRefreshEndDataMethod == null || netNodeGetSegmentMethod == null || netInfoNodesField == null || netNodeRenderPatchShouldRenderJunctionNodeMethod == null || netNodeRenderPatchShouldRenderBendNodeMethod == null)
            {
                Debug.LogError("NetNodeRenderInstancePatch: Necessary methods and field not found. Cancelling transpiler!");
                return(instructions);
            }

            var index = 0;

            var originalCodes = new List <CodeInstruction>(instructions);
            var codes         = new List <CodeInstruction>(originalCodes);

            var refreshEndDataCallFound = false;

            for (; index < codes.Count; index++)
            {
                // IL_0066: call instance void NetNode::RefreshEndData(uint16, class NetInfo, uint32, valuetype RenderManager/Instance&)
                if (codes[index].opcode == OpCodes.Call && codes[index].operand == netNodeRefreshEndDataMethod)
                {
                    refreshEndDataCallFound = true;
                    break;
                }
            }

            if (!refreshEndDataCallFound)
            {
                Debug.LogError("NetNodeRenderInstancePatch: RefreshEndData call not found. Cancelling transpiler!");
                return(originalCodes);
            }

            var junctionFlagCheckFound = false;

            for (; index < codes.Count; index++)
            {
                // if ((flags & Flags.Junction) != 0)
                // IL_0077: ldarg.s 'flags'
                // IL_0079: ldc.i4 128
                if (codes[index].opcode == OpCodes.Ldarg_S && (byte)codes[index].operand == FlagsArgIndex &&
                    codes[index + 1].opcode == OpCodes.Ldc_I4 && (int)codes[index + 1].operand == (int)global::NetNode.Flags.Junction)
                {
                    junctionFlagCheckFound = true;
                    break;
                }
            }

            if (!junctionFlagCheckFound)
            {
                Debug.LogError("NetNodeRenderInstancePatch: junctionFlagCheck not found. Cancelling transpiler!");
                return(originalCodes);
            }

            CodeInstruction segmentLocalVarLdloc  = null;
            CodeInstruction segment2LocalVarLdloc = null;
            CodeInstruction nodeLocalVarLdLoc     = null;

            for (; index < codes.Count; index++)
            {
                // IL_009c: call instance uint16 NetNode::GetSegment(int32)
                if (codes[index].opcode == OpCodes.Call && codes[index].operand == netNodeGetSegmentMethod && TranspilerUtils.IsStLoc(codes[index + 1]))
                {
                    // IL_00a1: stloc.0
                    segmentLocalVarLdloc = TranspilerUtils.BuildLdLocFromStLoc(codes[index + 1]);
                    index += 2;
                    break;
                }
            }

            for (; index < codes.Count; index++)
            {
                // IL_00ac: call instance uint16 NetNode::GetSegment(int32)
                if (codes[index].opcode == OpCodes.Call && codes[index].operand == netNodeGetSegmentMethod && TranspilerUtils.IsStLoc(codes[index + 1]))
                {
                    // IL_00b1: stloc.1
                    segment2LocalVarLdloc = TranspilerUtils.BuildLdLocFromStLoc(codes[index + 1]);
                    index += 2;
                    break;
                }
            }

            for (; index < codes.Count; index++)
            {
                // IL_013e: ldfld class NetInfo/Node[] NetInfo::m_nodes
                if (codes[index].opcode == OpCodes.Ldfld && codes[index].operand == netInfoNodesField && TranspilerUtils.IsStLoc(codes[index + 3]))
                {
                    // IL_0146: stloc.s 6
                    nodeLocalVarLdLoc = TranspilerUtils.BuildLdLocFromStLoc(codes[index + 3]);
                    index            += 4;
                    break;
                }
            }

            if (segmentLocalVarLdloc == null || segment2LocalVarLdloc == null || nodeLocalVarLdLoc == null)
            {
                Debug.LogError("NetNodeRenderInstancePatch: Necessary field for junction not found. Cancelling transpiler!");
                Debug.LogError($"{segmentLocalVarLdloc}, {segment2LocalVarLdloc}, {nodeLocalVarLdLoc}");
                return(originalCodes);
            }

            var junctionRenderCheckInserted = false;

            for (; index < codes.Count; index++)
            {
                // IL_017c: ldc.i4 987135
                // IL_0181: and
                // IL_0182: brfalse IL_0570
                if (codes[index].opcode == OpCodes.Ldc_I4 && (int)codes[index].operand == (int)NetInfo.ConnectGroup.AllGroups &&
                    codes[index + 1].opcode == OpCodes.And && codes[index + 2].opcode == OpCodes.Brfalse)
                {
                    var labelIfFalse      = codes[index + 2].operand;
                    var insertionPosition = index + 3;

                    // && NetNodeRenderPatch.NetNodeRenderPatch(node, segment, segment2)
                    // IL_01FD: ldloc.s V_6
                    // IL_01FF: ldloc.0
                    // IL_0200: ldloc.1
                    // IL_0201: call bool[NetworkSkins] NetworkSkins.Patches.NetNodeRenderPatch::ShouldRenderJunctionNode(class NetInfo/Node, uint16, uint16)
                    // IL_0206: brfalse.s IL_024A
                    var renderCheckInstructions = new[]
                    {
                        new CodeInstruction(nodeLocalVarLdLoc),
                        new CodeInstruction(segmentLocalVarLdloc),
                        new CodeInstruction(segment2LocalVarLdloc),
                        new CodeInstruction(OpCodes.Call, netNodeRenderPatchShouldRenderJunctionNodeMethod),
                        new CodeInstruction(OpCodes.Brfalse, labelIfFalse),
                    };

                    renderCheckInstructions[0].labels.AddRange(codes[insertionPosition].labels);
                    codes[insertionPosition].labels.Clear();

                    codes.InsertRange(insertionPosition, renderCheckInstructions);
                    TranspilerUtils.LogDebug("Junction render check inserted");

                    index = insertionPosition + renderCheckInstructions.Length;
                    junctionRenderCheckInserted = true;
                    break;
                }
            }

            if (!junctionRenderCheckInserted)
            {
                Debug.LogError("NetNodeRenderInstancePatch: Render check 1 not inserted. Cancelling transpiler!");
                return(originalCodes);
            }

            var bendFlagCheckFound = false;

            for (; index < codes.Count; index++)
            {
                // else if ((flags & Flags.Bend) != 0)
                // IL_0e28: ldarg.s 'flags'
                // IL_0e2a: ldc.i4.s 64
                if (codes[index].opcode == OpCodes.Ldarg_S && (byte)codes[index].operand == FlagsArgIndex &&
                    codes[index + 1].opcode == OpCodes.Ldc_I4_S && (sbyte)codes[index + 1].operand == (sbyte)global::NetNode.Flags.Bend)
                {
                    bendFlagCheckFound = true;
                    break;
                }
            }

            if (!bendFlagCheckFound)
            {
                Debug.LogError("NetNodeRenderInstancePatch: bendFlagCheck not found. Cancelling transpiler!");
                return(originalCodes);
            }

            CodeInstruction segment5LocalVarLdloc = null;
            CodeInstruction segment6LocalVarLdloc = null;
            CodeInstruction node4LocalVarLdLoc    = null;

            for (; index < codes.Count; index++)
            {
                // IL_11ae: call instance uint16 NetNode::GetSegment(int32)
                if (codes[index].opcode == OpCodes.Call && codes[index].operand == netNodeGetSegmentMethod && TranspilerUtils.IsStLoc(codes[index + 1]))
                {
                    // IL_00a1: stloc.s 32
                    segment5LocalVarLdloc = TranspilerUtils.BuildLdLocFromStLoc(codes[index + 1]);
                    index += 2;
                    break;
                }
            }

            for (; index < codes.Count; index++)
            {
                // IL_11bf: call instance uint16 NetNode::GetSegment(int32)
                if (codes[index].opcode == OpCodes.Call && codes[index].operand == netNodeGetSegmentMethod && TranspilerUtils.IsStLoc(codes[index + 1]))
                {
                    // IL_11c4: stloc.s 33
                    segment6LocalVarLdloc = TranspilerUtils.BuildLdLocFromStLoc(codes[index + 1]);
                    index += 2;
                    break;
                }
            }

            for (; index < codes.Count; index++)
            {
                // IL_120d: ldfld class NetInfo/Node[] NetInfo::m_nodes
                if (codes[index].opcode == OpCodes.Ldfld && codes[index].operand == netInfoNodesField && TranspilerUtils.IsStLoc(codes[index + 3]))
                {
                    // IL_1215: stloc.s 35
                    node4LocalVarLdLoc = TranspilerUtils.BuildLdLocFromStLoc(codes[index + 3]);
                    index += 4;
                    break;
                }
            }

            if (segment5LocalVarLdloc == null || segment6LocalVarLdloc == null || node4LocalVarLdLoc == null)
            {
                Debug.LogError("NetNodeRenderInstancePatch: Necessary field for bend not found. Cancelling transpiler!");
                Debug.LogError($"{segment5LocalVarLdloc} {segment6LocalVarLdloc} {node4LocalVarLdLoc}");
                return(originalCodes);
            }

            var bendRenderCheckInserted = false;

            for (; index < codes.Count; index++)
            {
                // IL_124b: ldc.i4 987135
                // IL_1250: and
                // IL_1251: brfalse IL_1637
                if (codes[index].opcode == OpCodes.Ldc_I4 && (int)codes[index].operand == (int)NetInfo.ConnectGroup.AllGroups &&
                    codes[index + 1].opcode == OpCodes.And && codes[index + 2].opcode == OpCodes.Brfalse)
                {
                    var labelIfFalse      = codes[index + 2].operand;
                    var insertionPosition = index + 3;

                    // ldloc.s 35
                    // ldloc.s 32
                    // ldloc.s 33
                    // call bool[NetworkSkins] NetworkSkins.Patches.NetNodeRenderPatch::ShouldRenderJunctionNode(class NetInfo/Node, uint16, uint16)
                    // brfalse.s IL_1637
                    var renderCheckInstructions = new[]
                    {
                        new CodeInstruction(node4LocalVarLdLoc),
                        new CodeInstruction(segment5LocalVarLdloc),
                        new CodeInstruction(segment6LocalVarLdloc),
                        new CodeInstruction(OpCodes.Call, netNodeRenderPatchShouldRenderBendNodeMethod),
                        new CodeInstruction(OpCodes.Brfalse, labelIfFalse),
                    };

                    renderCheckInstructions[0].labels.AddRange(codes[insertionPosition].labels);
                    codes[insertionPosition].labels.Clear();

                    codes.InsertRange(insertionPosition, renderCheckInstructions);
                    TranspilerUtils.LogDebug("Bend render check inserted");

                    index = insertionPosition + renderCheckInstructions.Length;
                    bendRenderCheckInserted = true;
                    break;
                }
            }

            if (!bendRenderCheckInserted)
            {
                Debug.LogError("NetNodeRenderInstancePatch: Bend render check not inserted. Cancelling transpiler!");
                return(originalCodes);
            }

            return(codes);
        }
Esempio n. 22
0
 static MethodBase TargetMethod() => TranspilerUtils.DeclaredMethod <CheckOverlap>(typeof(PassengerCarAI));
        public static IEnumerable <CodeInstruction> Transpiler(ILGenerator il, IEnumerable <CodeInstruction> instructions)
        {
            var netNodeFlagsField = typeof(global::NetNode).GetField("m_flags");

            var netNodeGetSegmentMethod = typeof(global::NetNode).GetMethod("GetSegment");

            var netInfoNodesField = typeof(NetInfo).GetField("m_nodes");

            var netInfoNodeDirectConnectField = typeof(NetInfo.Node).GetField("m_directConnect");

            var netNodeRenderPatchShouldRenderJunctionNodeMethod =
                typeof(NetNodeRenderPatch).GetMethod("ShouldRenderJunctionNode");

            var netNodeRenderPatchShouldRenderBendNodeLodMethod =
                typeof(NetNodeRenderPatch).GetMethod("ShouldRenderBendNodeLod");

            if (netNodeFlagsField == null || netNodeGetSegmentMethod == null || netInfoNodesField == null || netInfoNodeDirectConnectField == null || netNodeRenderPatchShouldRenderJunctionNodeMethod == null || netNodeRenderPatchShouldRenderBendNodeLodMethod == null)
            {
                Debug.LogError("Necessary methods and field not found. Cancelling transpiler!");
                return(instructions);
            }

            var index = 0;

            var originalCodes = new List <CodeInstruction>(instructions);
            var codes         = new List <CodeInstruction>(originalCodes);

            var junctionFlagCheckFound = false;

            for (; index < codes.Count; index++)
            {
                // if ((m_flags & Flags.Junction) != 0)
                // IL_004b: ldfld valuetype NetNode/Flags NetNode::m_flags
                // IL_0079: ldc.i4 128
                if (codes[index].opcode == OpCodes.Ldfld && codes[index].operand == netNodeFlagsField &&
                    codes[index + 1].opcode == OpCodes.Ldc_I4 && (int)codes[index + 1].operand == (int)global::NetNode.Flags.Junction)
                {
                    junctionFlagCheckFound = true;
                    break;
                }
            }

            if (!junctionFlagCheckFound)
            {
                Debug.LogError("NetNodeGroupDataPatch: junctionFlagCheck not found. Cancelling transpiler!");
                return(originalCodes);
            }

            CodeInstruction segmentLocalVarLdloc  = null;
            CodeInstruction segment2LocalVarLdloc = null;
            CodeInstruction nodeLocalVarLdLoc     = null;

            // ushort segment = GetSegment(i);
            for (; index < codes.Count; index++)
            {
                // IL_0073: call instance uint16 NetNode::GetSegment(int32)
                if (codes[index].opcode == OpCodes.Call && codes[index].operand == netNodeGetSegmentMethod && TranspilerUtils.IsStLoc(codes[index + 1]))
                {
                    // IL_0078: stloc.s 5
                    segmentLocalVarLdloc = TranspilerUtils.BuildLdLocFromStLoc(codes[index + 1]);
                    index += 2;
                    break;
                }
            }

            // ushort segment2 = GetSegment(j);
            for (; index < codes.Count; index++)
            {
                // IL_0107: call instance uint16 NetNode::GetSegment(int32)
                if (codes[index].opcode == OpCodes.Call && codes[index].operand == netNodeGetSegmentMethod && TranspilerUtils.IsStLoc(codes[index + 1]))
                {
                    // IL_010c: stloc.s 11
                    segment2LocalVarLdloc = TranspilerUtils.BuildLdLocFromStLoc(codes[index + 1]);
                    index += 2;
                    break;
                }
            }

            // NetInfo.Node node = info2.m_nodes[k];
            for (; index < codes.Count; index++)
            {
                // IL_0359: ldfld class NetInfo/Node[] NetInfo::m_nodes
                // IL_035e: ldloc.s 21
                // IL_0360: ldelem.ref
                // IL_0361: stloc.s 22
                if (codes[index].opcode == OpCodes.Ldfld && codes[index].operand == netInfoNodesField &&
                    TranspilerUtils.IsLdLoc(codes[index + 1]) &&
                    codes[index + 2].opcode == OpCodes.Ldelem_Ref &&
                    TranspilerUtils.IsStLoc(codes[index + 3]))
                {
                    // IL_0361: stloc.s 22
                    nodeLocalVarLdLoc = TranspilerUtils.BuildLdLocFromStLoc(codes[index + 3]);
                    index            += 4;
                    break;
                }
            }

            if (segmentLocalVarLdloc == null || segment2LocalVarLdloc == null || nodeLocalVarLdLoc == null)
            {
                Debug.LogError("NetNodeGroupDataPatch: Necessary field for junction not found. Cancelling transpiler!");
                Debug.LogError($"{segmentLocalVarLdloc}, {segment2LocalVarLdloc}, {nodeLocalVarLdLoc}");
                return(originalCodes);
            }

            var junctionRenderCheckInserted = false;

            for (; index < codes.Count; index++)
            {
                // IL_017c: ldloc.s 22
                // IL_0181: ldfld bool NetInfo/Node::m_directConnect
                // IL_0182: brfalse IL_04c3
                if (TranspilerUtils.IsSameInstruction(codes[index], nodeLocalVarLdLoc, true) &&
                    codes[index + 1].opcode == OpCodes.Ldfld && codes[index + 1].operand == netInfoNodeDirectConnectField &&
                    codes[index + 2].opcode == OpCodes.Brfalse)
                {
                    var labelIfFalse      = codes[index + 2].operand;
                    var insertionPosition = index + 3;

                    // && ShouldRenderJunctionNode(node, segment, segment2)
                    // IL_01FD: ldloc.s 22
                    // IL_01FF: ldloc.s 11
                    // IL_0200: ldloc.s 22
                    // IL_0201: call bool[NetworkSkins] NetworkSkins.Patches.NetNodeRenderPatch::ShouldRenderJunctionNode(class NetInfo/Node, uint16, uint16)
                    // IL_0206: brfalse.s IL_04c3
                    var renderCheckInstructions = new[]
                    {
                        new CodeInstruction(nodeLocalVarLdLoc),
                        new CodeInstruction(segmentLocalVarLdloc),
                        new CodeInstruction(segment2LocalVarLdloc),
                        new CodeInstruction(OpCodes.Call, netNodeRenderPatchShouldRenderJunctionNodeMethod),
                        new CodeInstruction(OpCodes.Brfalse, labelIfFalse),
                    };

                    renderCheckInstructions[0].labels.AddRange(codes[insertionPosition].labels);
                    codes[insertionPosition].labels.Clear();

                    codes.InsertRange(insertionPosition, renderCheckInstructions);
                    TranspilerUtils.LogDebug("Junction render check inserted");

                    index = insertionPosition + renderCheckInstructions.Length;
                    junctionRenderCheckInserted = true;
                    break;
                }
            }

            if (!junctionRenderCheckInserted)
            {
                Debug.LogError("NetNodeGroupDataPatch: Junction render check not inserted. Cancelling transpiler!");
                return(originalCodes);
            }

            var bendFlagCheckFound = false;

            for (; index < codes.Count; index++)
            {
                // else if ((m_flags & Flags.Bend) != 0)
                // IL_0e28: ldarg.s 'flags'
                // IL_0e2a: ldc.i4.s 64
                if (codes[index].opcode == OpCodes.Ldfld && codes[index].operand == netNodeFlagsField &&
                    codes[index + 1].opcode == OpCodes.Ldc_I4_S && (sbyte)codes[index + 1].operand == (sbyte)global::NetNode.Flags.Bend)
                {
                    bendFlagCheckFound = true;
                    break;
                }
            }

            if (!bendFlagCheckFound)
            {
                Debug.LogError("NetNodeGroupDataPatch: bendFlagCheck not found. Cancelling transpiler!");
                return(originalCodes);
            }

            CodeInstruction node5LocalVarLdLoc = null;

            for (; index < codes.Count; index++)
            {
                // IL_0359: ldfld class NetInfo/Node[] NetInfo::m_nodes
                // IL_05ed: ldloc.s 44
                // IL_05ef: ldelem.ref
                // IL_05f0: stloc.s 45
                if (codes[index].opcode == OpCodes.Ldfld && codes[index].operand == netInfoNodesField &&
                    TranspilerUtils.IsLdLoc(codes[index + 1]) &&
                    codes[index + 2].opcode == OpCodes.Ldelem_Ref &&
                    TranspilerUtils.IsStLoc(codes[index + 3]))
                {
                    // IL_0361: stloc.s 22
                    node5LocalVarLdLoc = TranspilerUtils.BuildLdLocFromStLoc(codes[index + 3]);
                    index += 4;
                    break;
                }
            }

            if (node5LocalVarLdLoc == null)
            {
                Debug.LogError("NetNodeGroupDataPatch: node5 local var for bend not found. Cancelling transpiler!");
                return(originalCodes);
            }

            var bendRenderCheckInserted = false;

            for (; index < codes.Count; index++)
            {
                // IL_0990: ldloc.s 40
                // IL_0992: ldfld bool NetInfo/Node::m_directConnect
                // IL_0997: brfalse IL_0b19
                if (codes[index].opcode == OpCodes.Ldc_I4 && (int)codes[index].operand == (int)NetInfo.ConnectGroup.AllGroups &&
                    codes[index + 1].opcode == OpCodes.And && codes[index + 2].opcode == OpCodes.Brfalse)
                {
                    var labelIfFalse      = codes[index + 2].operand;
                    var insertionPosition = index + 3;

                    // && NetNodeRenderPatch.ShouldRenderBendNodeLod(nodeID, node5)
                    var renderCheckInstructions = new[]
                    {
                        new CodeInstruction(OpCodes.Ldarg_1), // nodeID
                        new CodeInstruction(node5LocalVarLdLoc),
                        new CodeInstruction(OpCodes.Call, netNodeRenderPatchShouldRenderBendNodeLodMethod),
                        new CodeInstruction(OpCodes.Brfalse, labelIfFalse),
                    };

                    renderCheckInstructions[0].labels.AddRange(codes[insertionPosition].labels);
                    codes[insertionPosition].labels.Clear();

                    codes.InsertRange(insertionPosition, renderCheckInstructions);
                    TranspilerUtils.LogDebug("Bend render check inserted");

                    index = insertionPosition + renderCheckInstructions.Length;
                    bendRenderCheckInserted = true;
                    break;
                }
            }

            if (!bendRenderCheckInserted)
            {
                Debug.LogError("NetNodeGroupDataPatch: Bend render check not inserted. Cancelling transpiler!");
                return(originalCodes);
            }

            return(codes);
        }
        public static bool PatchLanesAndSegments(ILGenerator il, List <CodeInstruction> codes, CodeInstruction infoLdInstruction, CodeInstruction segmentIdLdInstruction, ref int index)
        {
            var netInfoLanesField        = typeof(NetInfo).GetField("m_lanes");
            var netInfoSegmentsField     = typeof(NetInfo).GetField("m_segments");
            var segmentSkinsField        = typeof(NetworkSkinManager).GetField("SegmentSkins", BindingFlags.Static | BindingFlags.Public);
            var networkSkinLanesField    = typeof(NetworkSkin).GetField("m_lanes");
            var networkSkinSegmentsField = typeof(NetworkSkin).GetField("m_segments");

            if (netInfoLanesField == null || netInfoSegmentsField == null || segmentSkinsField == null || networkSkinLanesField == null || networkSkinSegmentsField == null)
            {
                Debug.LogError("Necessary field not found. Cancelling transpiler!");
                return(false);
            }

            var beginLabel = il.DefineLabel();

            codes[index].labels.Add(beginLabel);

            var customLanesLocalVar = il.DeclareLocal(typeof(NetInfo.Lane[]));

            customLanesLocalVar.SetLocalSymInfo("customLanes");

            var customSegmentsLocalVar = il.DeclareLocal(typeof(NetInfo.Segment[]));

            customSegmentsLocalVar.SetLocalSymInfo("customSegments");

            var customLanesInstructions = new[]
            {
                // NetInfo.Lane[] customLanes = info.m_lanes;
                new CodeInstruction(infoLdInstruction), // info
                new CodeInstruction(OpCodes.Ldfld, netInfoLanesField),
                new CodeInstruction(OpCodes.Stloc, customLanesLocalVar),

                // NetInfo.Segment[] customSegments = info.m_segments;
                new CodeInstruction(infoLdInstruction), // info
                new CodeInstruction(OpCodes.Ldfld, netInfoSegmentsField),
                new CodeInstruction(OpCodes.Stloc, customSegmentsLocalVar),

                // if (SegmentSkinManager.SegmentSkins[segmentID] != null) {
                new CodeInstruction(OpCodes.Ldsfld, segmentSkinsField),
                new CodeInstruction(segmentIdLdInstruction), // segmentID
                new CodeInstruction(OpCodes.Ldelem_Ref),
                new CodeInstruction(OpCodes.Brfalse_S, beginLabel),

                // customLanes = SegmentSkinManager.SegmentSkins[segmentID].m_lanes;
                new CodeInstruction(OpCodes.Ldsfld, segmentSkinsField),
                new CodeInstruction(segmentIdLdInstruction), // segmentID
                new CodeInstruction(OpCodes.Ldelem_Ref),
                new CodeInstruction(OpCodes.Ldfld, networkSkinLanesField),
                new CodeInstruction(OpCodes.Stloc, customLanesLocalVar),

                // customSegments = SegmentSkinManager.SegmentSkins[segmentID].m:segments;
                new CodeInstruction(OpCodes.Ldsfld, segmentSkinsField),
                new CodeInstruction(segmentIdLdInstruction), // segmentID
                new CodeInstruction(OpCodes.Ldelem_Ref),
                new CodeInstruction(OpCodes.Ldfld, networkSkinSegmentsField),
                new CodeInstruction(OpCodes.Stloc, customSegmentsLocalVar),
                // }
            };

            codes.InsertRange(index, customLanesInstructions);

            index += customLanesInstructions.Length;

            // Replace all occurences of:
            //
            // info.m_lanes
            // -- with --
            // customLanes
            //
            // info.m_segments
            // -- with --
            // customSegments
            for (; index < codes.Count; index++)
            {
                if (TranspilerUtils.IsSameInstruction(codes[index], infoLdInstruction) && codes[index + 1].opcode == OpCodes.Ldfld)
                {
                    if (codes[index + 1].operand == netInfoLanesField)
                    {
                        // It is important that we copy the labels from the existing instruction!
                        // Otherwise "Label not marked" exception
                        codes[index] = new CodeInstruction(codes[index])
                        {
                            opcode  = OpCodes.Ldloc,
                            operand = customLanesLocalVar
                        };
                        codes.RemoveAt(index + 1);
                    }
                    else if (codes[index + 1].operand == netInfoSegmentsField)
                    {
                        // It is important that we copy the labels from the existing instruction!
                        // Otherwise "Label not marked" exception
                        codes[index] = new CodeInstruction(codes[index])
                        {
                            opcode  = OpCodes.Ldloc,
                            operand = customSegmentsLocalVar
                        };
                        codes.RemoveAt(index + 1);
                    }
                }
            }

            return(true);
        }
        public static IEnumerable <CodeInstruction> Transpiler(ILGenerator il, IEnumerable <CodeInstruction> instructions)
        {
            var originalInstructions = new List <CodeInstruction>(instructions);

            var netInfoCreatePavementField        = typeof(NetInfo).GetField("m_createPavement");
            var netInfoFlattenTerrainField        = typeof(NetInfo).GetField("m_flattenTerrain");
            var segmentSkinsField                 = typeof(NetworkSkinManager).GetField("SegmentSkins", BindingFlags.Static | BindingFlags.Public);
            var terrainSurfacePatcherApplyMethod  = typeof(TerrainSurfacePatcher).GetMethod("Apply");
            var terrainSurfacePatcherRevertMethod = typeof(TerrainSurfacePatcher).GetMethod("Revert");

            if (netInfoCreatePavementField == null || netInfoFlattenTerrainField == null || segmentSkinsField == null || terrainSurfacePatcherApplyMethod == null || terrainSurfacePatcherRevertMethod == null)
            {
                Debug.LogError("NetNodeTerrainUpdatedPatch: Necessary field and methods not found. Cancelling transpiler!");
                return(originalInstructions);
            }


            var codes = new List <CodeInstruction>(originalInstructions);

            var patcherStateLocalVar = il.DeclareLocal(typeof(TerrainSurfacePatcherState));

            patcherStateLocalVar.SetLocalSymInfo("patcherState");

            int index = 0;

            CodeInstruction num14LocalVarLdLoc    = null;
            CodeInstruction num13LocalVarLdLoc    = null;
            CodeInstruction info4LocalVarLdLoc    = null;
            CodeInstruction netInfo2LocalVarLdLoc = null;
            CodeInstruction segment7LocalVarLdLoc = null;
            CodeInstruction num6LocalVarLdLoc     = null;

            for (; index < codes.Count; index++)
            {
                // NetInfo netInfo2 = (num14 > num13 >> 1) ? netInfo : info4;
                //IL_0a41: ldloc.s 74 (num14)
                //IL_0a43: ldloc.s 71 (num13)
                //IL_0a45: ldc.i4.1
                //IL_0a46: shr
                //IL_0a47: bgt IL_0a53
                //IL_0a4c: ldloc.s 40 (info4)
                //IL_0a4e: br IL_0a55
                //IL_0a53: ldloc.s 48 (netInfo)
                //IL_0a55: stloc.s 75 (netInfo2)
                if (codes[index].opcode == OpCodes.Shr &&
                    TranspilerUtils.IsLdLoc(codes[index - 3]) &&
                    TranspilerUtils.IsLdLoc(codes[index - 2]) &&
                    codes[index - 1].opcode == OpCodes.Ldc_I4_1
                    // shr
                    && codes[index + 1].opcode == OpCodes.Bgt &&
                    TranspilerUtils.IsLdLoc(codes[index + 2]) &&
                    codes[index + 3].opcode == OpCodes.Br &&
                    TranspilerUtils.IsLdLoc(codes[index + 4]) &&
                    TranspilerUtils.IsStLoc(codes[index + 5])
                    )
                {
                    TranspilerUtils.LogDebug("Found NetInfo netInfo2 = (num14 > num13 >> 1) ? netInfo : info4;");
                    num14LocalVarLdLoc = TranspilerUtils.BuildLdLocFromLdLoc(codes[index - 3]);
                    num13LocalVarLdLoc = TranspilerUtils.BuildLdLocFromLdLoc(codes[index - 2]);
                    info4LocalVarLdLoc = TranspilerUtils.BuildLdLocFromLdLoc(codes[index + 2]);       // 40
                    var netInfoLocalVarLdLoc = TranspilerUtils.BuildLdLocFromLdLoc(codes[index + 4]); // 48
                    netInfo2LocalVarLdLoc = TranspilerUtils.BuildLdLocFromStLoc(codes[index + 5]);    // 75

                    var findIndex = 0;
                    segment7LocalVarLdLoc = FindSegment7LocalVar(codes, TranspilerUtils.BuildStLocFromLdLoc(info4LocalVarLdLoc), ref findIndex, index - 3); // 38
                    num6LocalVarLdLoc     = FindNum6LocalVar(codes, TranspilerUtils.BuildStLocFromLdLoc(netInfoLocalVarLdLoc), ref findIndex, index - 3);   // 51
                    break;
                }
            }



            if (num14LocalVarLdLoc == null || num13LocalVarLdLoc == null || info4LocalVarLdLoc == null || netInfo2LocalVarLdLoc == null || segment7LocalVarLdLoc == null || num6LocalVarLdLoc == null)
            {
                Debug.LogError("NetNodeTerrainUpdatedPatch: Some local variables not found! Cancelling transpiler!");
                Debug.LogError($"num14: {num14LocalVarLdLoc}, num13: {num13LocalVarLdLoc}, info4: {info4LocalVarLdLoc}, netInfo2: {netInfo2LocalVarLdLoc}, segment7: {segment7LocalVarLdLoc}, num6: {num6LocalVarLdLoc}");

                return(originalInstructions);
            }

            // segment7 is the segmentID of info4
            // num6 is the segmentID of netInfo

            // TerrainSurfacePatcherState patcherState = TerrainSurfacePatcher.Apply(netInfo, NetworkSkinManager.SegmentSkins[(int)((num14 > num13 >> 1) ? num6 : segment7)]);
            // bool flag8 = netInfo2.m_createPavement && (!netInfo2.m_lowerTerrain || (m_flags & Flags.OnGround) != Flags.None);
            var apply1Inserted = false;

            for (; index < codes.Count; index++)
            {
                // bool flag8 = netInfo2.m_createPavement && (!netInfo2.m_lowerTerrain || (m_flags & Flags.OnGround) != Flags.None);
                if (TranspilerUtils.IsSameInstruction(codes[index], netInfo2LocalVarLdLoc) &&
                    codes[index + 1].opcode == OpCodes.Ldfld && codes[index + 1].operand == netInfoCreatePavementField)
                {
                    TranspilerUtils.LogDebug("Found info4.m_createPavement");

                    var ldLocSegment7Label = il.DefineLabel();
                    var ldElemtRefLabel    = il.DefineLabel();

                    // TerrainSurfacePatcherState patcherState = TerrainSurfacePatcher.Apply(netInfo, NetworkSkinManager.SegmentSkins[(int)((num14 > num13 >> 1) ? num6 : segment7)]);
                    var apply1Instructions = new[]
                    {
                        new CodeInstruction(netInfo2LocalVarLdLoc),
                        new CodeInstruction(OpCodes.Ldsfld, segmentSkinsField),
                        new CodeInstruction(num14LocalVarLdLoc),
                        new CodeInstruction(num13LocalVarLdLoc),
                        new CodeInstruction(OpCodes.Ldc_I4_1),
                        new CodeInstruction(OpCodes.Shr),
                        new CodeInstruction(OpCodes.Bgt, ldLocSegment7Label),
                        new CodeInstruction(segment7LocalVarLdLoc),
                        new CodeInstruction(OpCodes.Br, ldElemtRefLabel),
                        new CodeInstruction(num6LocalVarLdLoc),  // ldLocSegment7Label
                        new CodeInstruction(OpCodes.Ldelem_Ref), // ldElemtRefLabel
                        new CodeInstruction(OpCodes.Call, terrainSurfacePatcherApplyMethod),
                        new CodeInstruction(OpCodes.Stloc, patcherStateLocalVar)
                    };
                    apply1Instructions[0].labels.AddRange(codes[index].labels);
                    codes[index].labels.Clear();

                    apply1Instructions[9].labels.Add(ldLocSegment7Label);
                    apply1Instructions[10].labels.Add(ldElemtRefLabel);

                    codes.InsertRange(index, apply1Instructions);
                    TranspilerUtils.LogDebug("Apply 1 inserted");

                    apply1Inserted = true;
                    index         += apply1Instructions.Length;
                    break;
                }
            }

            if (!apply1Inserted)
            {
                Debug.LogError("NetNodeTerrainUpdatedPatch: Apply Insertion 1 failed! Cancelling transpiler!");
                return(originalInstructions);
            }


            // NetworkSkins.TerrainSurfacePatcher.Revert(netInfo2, patcherState);
            // bool flag12 = netInfo2.m_flattenTerrain || (netInfo2.m_netAI.FlattenGroundNodes() && (m_flags & Flags.OnGround) != Flags.None);
            var revert1Inserted = false;

            for (; index < codes.Count; index++)
            {
                // bool flag12 = netInfo2.m_flattenTerrain || (netInfo2.m_netAI.FlattenGroundNodes() && (m_flags & Flags.OnGround) != Flags.None);
                if (TranspilerUtils.IsSameInstruction(codes[index], netInfo2LocalVarLdLoc) &&
                    codes[index + 1].opcode == OpCodes.Ldfld && codes[index + 1].operand == netInfoFlattenTerrainField)
                {
                    TranspilerUtils.LogDebug("Found info4.m_flattenTerrain");

                    var revert1Instructions = new[]
                    {
                        new CodeInstruction(netInfo2LocalVarLdLoc),
                        new CodeInstruction(OpCodes.Ldloc, patcherStateLocalVar),
                        new CodeInstruction(OpCodes.Call, terrainSurfacePatcherRevertMethod),
                    };

                    revert1Instructions[0].labels.AddRange(codes[index].labels);
                    codes[index].labels.Clear();

                    codes.InsertRange(index, revert1Instructions);
                    TranspilerUtils.LogDebug("Revert 1 inserted");

                    revert1Inserted = true;
                    index          += revert1Instructions.Length;
                    break;
                }
            }

            if (!revert1Inserted)
            {
                Debug.LogError("NetNodeTerrainUpdatedPatch: Revert Insertion 1 failed! Cancelling transpiler!");
                return(originalInstructions);
            }

            // patcherState = NetworkSkins.TerrainSurfacePatcher.Apply(info4, NetworkSkins.Skins.NetworkSkinManager.SegmentSkins[segment7]);
            // bool flag13 = info4.m_createPavement && (!info4.m_lowerTerrain || (m_flags & Flags.OnGround) != Flags.None);
            var apply2Inserted = false;

            for (; index < codes.Count; index++)
            {
                // bool flag13 = info4.m_createPavement && (!info4.m_lowerTerrain || (m_flags & Flags.OnGround) != Flags.None);
                if (TranspilerUtils.IsSameInstruction(codes[index], info4LocalVarLdLoc) &&
                    codes[index + 1].opcode == OpCodes.Ldfld && codes[index + 1].operand == netInfoCreatePavementField)
                {
                    TranspilerUtils.LogDebug("Found info4.m_createPavement");

                    // patcherState = NetworkSkins.TerrainSurfacePatcher.Apply(info4, NetworkSkins.Skins.NetworkSkinManager.SegmentSkins[segment7]);
                    var apply2Instructions = new[]
                    {
                        new CodeInstruction(info4LocalVarLdLoc),
                        new CodeInstruction(OpCodes.Ldsfld, segmentSkinsField),
                        new CodeInstruction(segment7LocalVarLdLoc),
                        new CodeInstruction(OpCodes.Ldelem_Ref),
                        new CodeInstruction(OpCodes.Call, terrainSurfacePatcherApplyMethod),
                        new CodeInstruction(OpCodes.Stloc, patcherStateLocalVar)
                    };

                    apply2Instructions[0].labels.AddRange(codes[index].labels);
                    codes[index].labels.Clear();

                    codes.InsertRange(index, apply2Instructions);
                    TranspilerUtils.LogDebug("Apply 2 inserted");

                    apply2Inserted = true;
                    index         += apply2Instructions.Length;
                    break;
                }
            }

            if (!apply2Inserted)
            {
                Debug.LogError("NetNodeTerrainUpdatedPatch: Apply Insertion 2 failed! Cancelling transpiler!");
                return(originalInstructions);
            }

            // NetworkSkins.TerrainSurfacePatcher.Revert(info4, patcherState);
            // bool flag17 = info4.m_flattenTerrain || (info4.m_netAI.FlattenGroundNodes() && (m_flags & Flags.OnGround) != Flags.None);
            var revert2Inserted = false;

            for (; index < codes.Count; index++)
            {
                // bool flag17 = info4.m_flattenTerrain || (info4.m_netAI.FlattenGroundNodes() && (m_flags & Flags.OnGround) != Flags.None);
                if (TranspilerUtils.IsSameInstruction(codes[index], info4LocalVarLdLoc) &&
                    codes[index + 1].opcode == OpCodes.Ldfld && codes[index + 1].operand == netInfoFlattenTerrainField)
                {
                    TranspilerUtils.LogDebug("Found info4.m_flattenTerrain");

                    // NetworkSkins.TerrainSurfacePatcher.Revert(info4, patcherState);
                    var revert2Instructions = new[]
                    {
                        new CodeInstruction(info4LocalVarLdLoc),
                        new CodeInstruction(OpCodes.Ldloc, patcherStateLocalVar),
                        new CodeInstruction(OpCodes.Call, terrainSurfacePatcherRevertMethod),
                    };

                    revert2Instructions[0].labels.AddRange(codes[index].labels);
                    codes[index].labels.Clear();

                    codes.InsertRange(index, revert2Instructions);
                    TranspilerUtils.LogDebug("Revert 2 inserted");

                    revert2Inserted = true;
                    index          += revert2Instructions.Length;
                    break;
                }
            }

            if (!revert2Inserted)
            {
                Debug.LogError("NetNodeTerrainUpdatedPatch: Revert Insertion 2 failed! Cancelling transpiler!");
                return(originalInstructions);
            }

            return(codes);
        }