From 72cbffe58e36ca84de3e2ace10bb59012bfcd6c2 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 02:45:37 +0100 Subject: [PATCH 01/21] Add cursed inspector transpiler (not fully working) --- .../UI/Inspectors/CustomInspectorInjector.cs | 204 ++++++++++++++---- 1 file changed, 160 insertions(+), 44 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index 109e4a56..4de80591 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -6,6 +6,8 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Reflection; +using System.Reflection.Emit; namespace MonkeyLoader.Resonite.UI.Inspectors { @@ -17,61 +19,169 @@ internal sealed class CustomInspectorInjector { public override bool CanBeDisabled => true; - [HarmonyPrefix] - private static bool BuildUIForComponentPrefix(WorkerInspector __instance, Worker worker, - bool allowRemove, bool allowDuplicate, bool allowContainer, Predicate memberFilter) - { - if (!Enabled) - return true; - - var ui = new UIBuilder(__instance.Slot); - RadiantUI_Constants.SetupEditorStyle(ui); - ui.Style.RequireLockInToPress = true; - var vertical = ui.VerticalLayout(6f); - - if (worker is not Slot) - { - ui.Style.MinHeight = 32f; - ui.HorizontalLayout(4f); - ui.Style.MinHeight = 24f; - ui.Style.FlexibleWidth = 1000f; - - OnBuildInspectorHeader(ui, __instance, worker, allowContainer, allowDuplicate, allowRemove, memberFilter); + private static MethodInfo _buildHeaderMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(OnBuildInspectorHeader)); + private static MethodInfo _buildHeaderTextMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(OnBuildInspectorHeaderText)); + private static MethodInfo _buildBodyMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(OnBuildInspectorBody)); + private static MethodInfo _storeVerticalLayoutMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(StoreVerticalLayout)); - ui.NestInto(vertical.Slot); - } + private static VerticalLayout _verticalLayout; - OnBuildInspectorHeaderText(ui, worker); + private static void StoreVerticalLayout(VerticalLayout layout) + { + _verticalLayout = layout; + } - if (worker is ICustomInspector customInspector) + [HarmonyTranspiler] + static IEnumerable Transpiler(IEnumerable instructions) + { + bool storedVerticalLayout = false; + Label? headerLabel = null; + Label? headerTextLabel = null; + bool headerDone = false; + bool headerTextDone = false; + bool bodyDone = false; + bool afterHeaderBranch = false; + bool afterHeaderTextBranch = false; + int numBranches = 0; + int branchDepth = 0; + List labels = new(); + foreach (var instruction in instructions) { - try + bool didBranch = false; + if (instruction.Branches(out var label)) { - ui.Style.MinHeight = 24f; - customInspector.BuildInspectorUI(ui); + if (numBranches == 0) + headerLabel = label; + else if (numBranches == 4) + headerTextLabel = label; + numBranches++; + branchDepth++; + didBranch = true; + labels.Add(label); } - catch (Exception ex) + if (!didBranch) { - ui.Text((LocaleString)"EXCEPTION BUILDING UI. See log"); - UniLog.Error(ex.ToString(), stackTrace: false); + if (branchDepth > 0) + { + foreach (var storedLabel in labels.ToArray()) + { + if (instruction.labels.Contains(storedLabel!.Value)) + { + if (storedLabel == headerLabel) + afterHeaderBranch = true; + else if (storedLabel == headerTextLabel) + afterHeaderTextBranch = true; + labels.Remove(storedLabel); + branchDepth--; + break; + } + } + } + if (numBranches == 1 && !headerDone) + { + // do header + yield return new CodeInstruction(OpCodes.Ldloc_0); + yield return new CodeInstruction(OpCodes.Ldarg, 0); + yield return new CodeInstruction(OpCodes.Ldarg, 1); + yield return new CodeInstruction(OpCodes.Ldarg, 2); + yield return new CodeInstruction(OpCodes.Ldarg, 3); + yield return new CodeInstruction(OpCodes.Ldarg, 4); + yield return new CodeInstruction(OpCodes.Ldarg, 5); + yield return new CodeInstruction(OpCodes.Call, _buildHeaderMethod); + headerDone = true; + //continue; + } + if (numBranches == 6 && !headerTextDone) + { + // do header text + yield return new CodeInstruction(OpCodes.Ldloc_0); + yield return new CodeInstruction(OpCodes.Ldarg, 1); + yield return new CodeInstruction(OpCodes.Call, _buildHeaderTextMethod); + headerTextDone = true; + //continue; + } + if (numBranches == 7 && branchDepth == 0 && !bodyDone) + { + // do body + yield return new CodeInstruction(OpCodes.Ldloc_0); + yield return new CodeInstruction(OpCodes.Ldarg, 0); + yield return new CodeInstruction(OpCodes.Ldarg, 1); + yield return new CodeInstruction(OpCodes.Ldarg, 2); + yield return new CodeInstruction(OpCodes.Ldarg, 3); + yield return new CodeInstruction(OpCodes.Ldarg, 4); + yield return new CodeInstruction(OpCodes.Ldarg, 5); + yield return new CodeInstruction(OpCodes.Call, _buildBodyMethod); + bodyDone = true; + //continue; + } + } + if (headerDone && !afterHeaderBranch) continue; + if (headerTextDone && !afterHeaderTextBranch) continue; + yield return instruction; + if (!storedVerticalLayout && instruction.Calls(AccessTools.Method(typeof(UIBuilder), nameof(UIBuilder.VerticalLayout), [typeof(float), typeof(float), typeof(Alignment?), typeof(bool?), typeof(bool?)]))) + { + yield return new CodeInstruction(OpCodes.Dup); + yield return new CodeInstruction(OpCodes.Call, _storeVerticalLayoutMethod); + storedVerticalLayout = true; } } - else - { - WorkerInspector.BuildInspectorUI(worker, ui, memberFilter); - } - - OnBuildInspectorBody(ui, __instance, worker, allowContainer, allowDuplicate, allowRemove, memberFilter); - - ui.Style.MinHeight = 8f; - ui.Panel(); - ui.NestOut(); - - return false; } + //[HarmonyPrefix] + //private static bool BuildUIForComponentPrefix(WorkerInspector __instance, Worker worker, + // bool allowRemove, bool allowDuplicate, bool allowContainer, Predicate memberFilter) + //{ + // if (!Enabled) + // return true; + + // var ui = new UIBuilder(__instance.Slot); + // RadiantUI_Constants.SetupEditorStyle(ui); + // ui.Style.RequireLockInToPress = true; + // var vertical = ui.VerticalLayout(6f); + + // if (worker is not Slot) + // { + // ui.Style.MinHeight = 32f; + // ui.HorizontalLayout(4f); + // ui.Style.MinHeight = 24f; + // ui.Style.FlexibleWidth = 1000f; + + // OnBuildInspectorHeader(ui, __instance, worker, allowContainer, allowDuplicate, allowRemove, memberFilter); + + // ui.NestInto(vertical.Slot); + // } + + // OnBuildInspectorHeaderText(ui, worker); + + // if (worker is ICustomInspector customInspector) + // { + // try + // { + // ui.Style.MinHeight = 24f; + // customInspector.BuildInspectorUI(ui); + // } + // catch (Exception ex) + // { + // ui.Text((LocaleString)"EXCEPTION BUILDING UI. See log"); + // UniLog.Error(ex.ToString(), stackTrace: false); + // } + // } + // else + // { + // WorkerInspector.BuildInspectorUI(worker, ui, memberFilter); + // } + + // OnBuildInspectorBody(ui, __instance, worker, allowContainer, allowDuplicate, allowRemove, memberFilter); + + // ui.Style.MinHeight = 8f; + // ui.Panel(); + // ui.NestOut(); + + // return false; + //} + private static void OnBuildInspectorBody(UIBuilder ui, WorkerInspector inspector, Worker worker, - bool allowContainer, bool allowDuplicate, bool allowDestroy, Predicate memberFilter) + bool allowDestroy, bool allowDuplicate, bool allowContainer, Predicate memberFilter) { var root = ui.Root; @@ -83,8 +193,13 @@ private static void OnBuildInspectorBody(UIBuilder ui, WorkerInspector inspector } private static void OnBuildInspectorHeader(UIBuilder ui, WorkerInspector inspector, Worker worker, - bool allowContainer, bool allowDuplicate, bool allowDestroy, Predicate memberFilter) + bool allowDestroy, bool allowDuplicate, bool allowContainer, Predicate memberFilter) { + ui.Style.MinHeight = 32f; + ui.HorizontalLayout(4f); + ui.Style.MinHeight = 24f; + ui.Style.FlexibleWidth = 1000f; + var root = ui.Root; var eventData = new BuildInspectorHeaderEvent(ui, inspector, worker, allowContainer, allowDuplicate, allowDestroy, memberFilter); @@ -92,6 +207,7 @@ private static void OnBuildInspectorHeader(UIBuilder ui, WorkerInspector inspect Dispatch(eventData); ui.NestInto(root); + ui.NestInto(_verticalLayout.Slot); } private static void OnBuildInspectorHeaderText(UIBuilder ui, Worker worker) From af99c5709be5a5703ac0b01d53de384734f707f2 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 03:09:35 +0100 Subject: [PATCH 02/21] It works now --- .../UI/Inspectors/CustomInspectorInjector.cs | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index 4de80591..d5ddbd18 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -52,7 +52,7 @@ static IEnumerable Transpiler(IEnumerable inst { if (numBranches == 0) headerLabel = label; - else if (numBranches == 4) + else if (numBranches == 6) headerTextLabel = label; numBranches++; branchDepth++; @@ -65,7 +65,7 @@ static IEnumerable Transpiler(IEnumerable inst { foreach (var storedLabel in labels.ToArray()) { - if (instruction.labels.Contains(storedLabel!.Value)) + if (instruction.labels.Contains(storedLabel!.Value) && instruction.operand != (object)storedLabel!.Value) { if (storedLabel == headerLabel) afterHeaderBranch = true; @@ -89,30 +89,14 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Ldarg, 5); yield return new CodeInstruction(OpCodes.Call, _buildHeaderMethod); headerDone = true; - //continue; } - if (numBranches == 6 && !headerTextDone) + if (numBranches == 7 && !headerTextDone) { // do header text yield return new CodeInstruction(OpCodes.Ldloc_0); yield return new CodeInstruction(OpCodes.Ldarg, 1); yield return new CodeInstruction(OpCodes.Call, _buildHeaderTextMethod); headerTextDone = true; - //continue; - } - if (numBranches == 7 && branchDepth == 0 && !bodyDone) - { - // do body - yield return new CodeInstruction(OpCodes.Ldloc_0); - yield return new CodeInstruction(OpCodes.Ldarg, 0); - yield return new CodeInstruction(OpCodes.Ldarg, 1); - yield return new CodeInstruction(OpCodes.Ldarg, 2); - yield return new CodeInstruction(OpCodes.Ldarg, 3); - yield return new CodeInstruction(OpCodes.Ldarg, 4); - yield return new CodeInstruction(OpCodes.Ldarg, 5); - yield return new CodeInstruction(OpCodes.Call, _buildBodyMethod); - bodyDone = true; - //continue; } } if (headerDone && !afterHeaderBranch) continue; @@ -124,6 +108,19 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Call, _storeVerticalLayoutMethod); storedVerticalLayout = true; } + if (!bodyDone && instruction.Calls(AccessTools.Method(typeof(WorkerInspector), nameof(WorkerInspector.BuildInspectorUI)))) + { + // do body + yield return new CodeInstruction(OpCodes.Ldloc_0); + yield return new CodeInstruction(OpCodes.Ldarg, 0); + yield return new CodeInstruction(OpCodes.Ldarg, 1); + yield return new CodeInstruction(OpCodes.Ldarg, 2); + yield return new CodeInstruction(OpCodes.Ldarg, 3); + yield return new CodeInstruction(OpCodes.Ldarg, 4); + yield return new CodeInstruction(OpCodes.Ldarg, 5); + yield return new CodeInstruction(OpCodes.Call, _buildBodyMethod); + bodyDone = true; + } } } From c0990c19d8f026b5b0ea141e4fb56e5fcb4abe4e Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 03:10:50 +0100 Subject: [PATCH 03/21] Remove old prefix --- .../UI/Inspectors/CustomInspectorInjector.cs | 53 ------------------- 1 file changed, 53 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index d5ddbd18..1e469a3c 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -124,59 +124,6 @@ static IEnumerable Transpiler(IEnumerable inst } } - //[HarmonyPrefix] - //private static bool BuildUIForComponentPrefix(WorkerInspector __instance, Worker worker, - // bool allowRemove, bool allowDuplicate, bool allowContainer, Predicate memberFilter) - //{ - // if (!Enabled) - // return true; - - // var ui = new UIBuilder(__instance.Slot); - // RadiantUI_Constants.SetupEditorStyle(ui); - // ui.Style.RequireLockInToPress = true; - // var vertical = ui.VerticalLayout(6f); - - // if (worker is not Slot) - // { - // ui.Style.MinHeight = 32f; - // ui.HorizontalLayout(4f); - // ui.Style.MinHeight = 24f; - // ui.Style.FlexibleWidth = 1000f; - - // OnBuildInspectorHeader(ui, __instance, worker, allowContainer, allowDuplicate, allowRemove, memberFilter); - - // ui.NestInto(vertical.Slot); - // } - - // OnBuildInspectorHeaderText(ui, worker); - - // if (worker is ICustomInspector customInspector) - // { - // try - // { - // ui.Style.MinHeight = 24f; - // customInspector.BuildInspectorUI(ui); - // } - // catch (Exception ex) - // { - // ui.Text((LocaleString)"EXCEPTION BUILDING UI. See log"); - // UniLog.Error(ex.ToString(), stackTrace: false); - // } - // } - // else - // { - // WorkerInspector.BuildInspectorUI(worker, ui, memberFilter); - // } - - // OnBuildInspectorBody(ui, __instance, worker, allowContainer, allowDuplicate, allowRemove, memberFilter); - - // ui.Style.MinHeight = 8f; - // ui.Panel(); - // ui.NestOut(); - - // return false; - //} - private static void OnBuildInspectorBody(UIBuilder ui, WorkerInspector inspector, Worker worker, bool allowDestroy, bool allowDuplicate, bool allowContainer, Predicate memberFilter) { From a265ab3208dc288977ea581c100d4aeac354feb1 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 12:54:50 +0100 Subject: [PATCH 04/21] Improve it to not rely on counting number of branches --- .../UI/Inspectors/CustomInspectorInjector.cs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index 1e469a3c..f6e28b1b 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -45,15 +45,22 @@ static IEnumerable Transpiler(IEnumerable inst int numBranches = 0; int branchDepth = 0; List labels = new(); - foreach (var instruction in instructions) + var instArr = instructions.ToArray(); + for (int i = 0; i < instArr.Length; i++) { + var instruction = instArr[i]; + if (headerLabel is null && instruction.opcode == OpCodes.Brtrue && instArr[i-1].opcode == OpCodes.Isinst && instArr[i - 1].operand == (object)typeof(Slot)) + { + headerLabel = (Label)instruction.operand; + } + if (headerTextLabel is null && instruction.opcode == OpCodes.Brfalse_S && instArr[i-1].opcode == OpCodes.Ldloc_1 && instArr[i-2].opcode == OpCodes.Stloc_1 && + instArr[i-3].Calls(AccessTools.Method(typeof(CustomAttributeExtensions), nameof(CustomAttributeExtensions.GetCustomAttribute), [typeof(MemberInfo)], [typeof(InspectorHeaderAttribute)]))) + { + headerTextLabel = (Label)instruction.operand; + } bool didBranch = false; if (instruction.Branches(out var label)) { - if (numBranches == 0) - headerLabel = label; - else if (numBranches == 6) - headerTextLabel = label; numBranches++; branchDepth++; didBranch = true; @@ -77,7 +84,7 @@ static IEnumerable Transpiler(IEnumerable inst } } } - if (numBranches == 1 && !headerDone) + if (headerLabel != null && !headerDone) { // do header yield return new CodeInstruction(OpCodes.Ldloc_0); @@ -90,7 +97,7 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Call, _buildHeaderMethod); headerDone = true; } - if (numBranches == 7 && !headerTextDone) + if (headerTextLabel != null && !headerTextDone) { // do header text yield return new CodeInstruction(OpCodes.Ldloc_0); @@ -144,13 +151,10 @@ private static void OnBuildInspectorHeader(UIBuilder ui, WorkerInspector inspect ui.Style.MinHeight = 24f; ui.Style.FlexibleWidth = 1000f; - var root = ui.Root; - var eventData = new BuildInspectorHeaderEvent(ui, inspector, worker, allowContainer, allowDuplicate, allowDestroy, memberFilter); Dispatch(eventData); - ui.NestInto(root); ui.NestInto(_verticalLayout.Slot); } From 7070fed84030a71def376850993c45c75a356ddf Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 13:43:24 +0100 Subject: [PATCH 05/21] More improvements --- .../UI/Inspectors/CustomInspectorInjector.cs | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index f6e28b1b..5021b395 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -42,8 +42,6 @@ static IEnumerable Transpiler(IEnumerable inst bool bodyDone = false; bool afterHeaderBranch = false; bool afterHeaderTextBranch = false; - int numBranches = 0; - int branchDepth = 0; List labels = new(); var instArr = instructions.ToArray(); for (int i = 0; i < instArr.Length; i++) @@ -52,36 +50,33 @@ static IEnumerable Transpiler(IEnumerable inst if (headerLabel is null && instruction.opcode == OpCodes.Brtrue && instArr[i-1].opcode == OpCodes.Isinst && instArr[i - 1].operand == (object)typeof(Slot)) { headerLabel = (Label)instruction.operand; + labels.Add(headerLabel); } if (headerTextLabel is null && instruction.opcode == OpCodes.Brfalse_S && instArr[i-1].opcode == OpCodes.Ldloc_1 && instArr[i-2].opcode == OpCodes.Stloc_1 && instArr[i-3].Calls(AccessTools.Method(typeof(CustomAttributeExtensions), nameof(CustomAttributeExtensions.GetCustomAttribute), [typeof(MemberInfo)], [typeof(InspectorHeaderAttribute)]))) { headerTextLabel = (Label)instruction.operand; + labels.Add(headerTextLabel); + yield return new CodeInstruction(OpCodes.Pop); // skip this branch to match behavior of the old patch + continue; } bool didBranch = false; if (instruction.Branches(out var label)) { - numBranches++; - branchDepth++; didBranch = true; - labels.Add(label); } if (!didBranch) { - if (branchDepth > 0) + foreach (var storedLabel in labels.ToArray()) { - foreach (var storedLabel in labels.ToArray()) + if (instruction.labels.Contains(storedLabel!.Value) && instruction.operand != (object)storedLabel!.Value) { - if (instruction.labels.Contains(storedLabel!.Value) && instruction.operand != (object)storedLabel!.Value) - { - if (storedLabel == headerLabel) - afterHeaderBranch = true; - else if (storedLabel == headerTextLabel) - afterHeaderTextBranch = true; - labels.Remove(storedLabel); - branchDepth--; - break; - } + if (storedLabel == headerLabel) + afterHeaderBranch = true; + else if (storedLabel == headerTextLabel) + afterHeaderTextBranch = true; + labels.Remove(storedLabel); + break; } } if (headerLabel != null && !headerDone) From 24449db035cae22ff3bd5095d3534eb7b140d8eb Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 14:16:11 +0100 Subject: [PATCH 06/21] Add Enabled checks --- .../UI/Inspectors/CustomInspectorInjector.cs | 70 +++++++++++++++++-- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index 5021b395..d3bdac89 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -23,6 +23,7 @@ internal sealed class CustomInspectorInjector private static MethodInfo _buildHeaderTextMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(OnBuildInspectorHeaderText)); private static MethodInfo _buildBodyMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(OnBuildInspectorBody)); private static MethodInfo _storeVerticalLayoutMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(StoreVerticalLayout)); + private static MethodInfo _getEnabledMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(GetEnabled)); private static VerticalLayout _verticalLayout; @@ -31,8 +32,13 @@ private static void StoreVerticalLayout(VerticalLayout layout) _verticalLayout = layout; } + private static bool GetEnabled() + { + return Enabled; + } + [HarmonyTranspiler] - static IEnumerable Transpiler(IEnumerable instructions) + static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) { bool storedVerticalLayout = false; Label? headerLabel = null; @@ -43,6 +49,19 @@ static IEnumerable Transpiler(IEnumerable inst bool afterHeaderBranch = false; bool afterHeaderTextBranch = false; List labels = new(); + + Label skipHeaderLabel = generator.DefineLabel(); + Label afterHeaderOriginalLabel = generator.DefineLabel(); + bool injectedAfterHeaderOriginal = false; + + Label skipHeaderTextLabel = generator.DefineLabel(); + Label afterHeaderTextOriginalLabel = generator.DefineLabel(); + bool injectedAfterHeaderTextOriginal = false; + + Label skipBodyLabel = generator.DefineLabel(); + Label afterBodyOriginalLabel = generator.DefineLabel(); + bool injectedAfterBodyOriginal = false; + var instArr = instructions.ToArray(); for (int i = 0; i < instArr.Length; i++) { @@ -57,8 +76,6 @@ static IEnumerable Transpiler(IEnumerable inst { headerTextLabel = (Label)instruction.operand; labels.Add(headerTextLabel); - yield return new CodeInstruction(OpCodes.Pop); // skip this branch to match behavior of the old patch - continue; } bool didBranch = false; if (instruction.Branches(out var label)) @@ -81,6 +98,10 @@ static IEnumerable Transpiler(IEnumerable inst } if (headerLabel != null && !headerDone) { + // check Enabled + yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); + yield return new CodeInstruction(OpCodes.Brfalse, skipHeaderLabel); + // do header yield return new CodeInstruction(OpCodes.Ldloc_0); yield return new CodeInstruction(OpCodes.Ldarg, 0); @@ -90,19 +111,48 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Ldarg, 4); yield return new CodeInstruction(OpCodes.Ldarg, 5); yield return new CodeInstruction(OpCodes.Call, _buildHeaderMethod); + + // check Enabled + yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); + yield return new CodeInstruction(OpCodes.Brtrue, afterHeaderOriginalLabel); + + yield return new CodeInstruction(OpCodes.Nop) { labels = [skipHeaderLabel] }; headerDone = true; } if (headerTextLabel != null && !headerTextDone) { + // check Enabled + yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); + yield return new CodeInstruction(OpCodes.Brfalse, skipHeaderTextLabel); + // do header text yield return new CodeInstruction(OpCodes.Ldloc_0); yield return new CodeInstruction(OpCodes.Ldarg, 1); yield return new CodeInstruction(OpCodes.Call, _buildHeaderTextMethod); + + // check Enabled + yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); + yield return new CodeInstruction(OpCodes.Brtrue, afterHeaderTextOriginalLabel); + + yield return new CodeInstruction(OpCodes.Nop) { labels = [skipHeaderTextLabel] }; headerTextDone = true; } } - if (headerDone && !afterHeaderBranch) continue; - if (headerTextDone && !afterHeaderTextBranch) continue; + if (headerDone && afterHeaderBranch && !injectedAfterHeaderOriginal) + { + yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderOriginalLabel] }; + injectedAfterHeaderOriginal = true; + } + if (headerTextDone && afterHeaderTextBranch && !injectedAfterHeaderTextOriginal) + { + yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderTextOriginalLabel] }; + injectedAfterHeaderTextOriginal = true; + } + if (bodyDone && !injectedAfterBodyOriginal) + { + yield return new CodeInstruction(OpCodes.Nop) { labels = [afterBodyOriginalLabel] }; + injectedAfterBodyOriginal = true; + } yield return instruction; if (!storedVerticalLayout && instruction.Calls(AccessTools.Method(typeof(UIBuilder), nameof(UIBuilder.VerticalLayout), [typeof(float), typeof(float), typeof(Alignment?), typeof(bool?), typeof(bool?)]))) { @@ -112,6 +162,10 @@ static IEnumerable Transpiler(IEnumerable inst } if (!bodyDone && instruction.Calls(AccessTools.Method(typeof(WorkerInspector), nameof(WorkerInspector.BuildInspectorUI)))) { + // check Enabled + yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); + yield return new CodeInstruction(OpCodes.Brfalse, skipBodyLabel); + // do body yield return new CodeInstruction(OpCodes.Ldloc_0); yield return new CodeInstruction(OpCodes.Ldarg, 0); @@ -121,6 +175,12 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Ldarg, 4); yield return new CodeInstruction(OpCodes.Ldarg, 5); yield return new CodeInstruction(OpCodes.Call, _buildBodyMethod); + + // check Enabled + yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); + yield return new CodeInstruction(OpCodes.Brtrue, afterBodyOriginalLabel); + + yield return new CodeInstruction(OpCodes.Nop) { labels = [skipBodyLabel] }; bodyDone = true; } } From f1846534704d7568ec88f40dcf7800efbe759f70 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 14:33:49 +0100 Subject: [PATCH 07/21] Improve label names and simplify branching --- .../UI/Inspectors/CustomInspectorInjector.cs | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index d3bdac89..40f3bb40 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -50,15 +50,15 @@ static IEnumerable Transpiler(IEnumerable inst bool afterHeaderTextBranch = false; List labels = new(); - Label skipHeaderLabel = generator.DefineLabel(); + Label afterHeaderPatchLabel = generator.DefineLabel(); Label afterHeaderOriginalLabel = generator.DefineLabel(); bool injectedAfterHeaderOriginal = false; - Label skipHeaderTextLabel = generator.DefineLabel(); + Label afterHeaderTextPatchLabel = generator.DefineLabel(); Label afterHeaderTextOriginalLabel = generator.DefineLabel(); bool injectedAfterHeaderTextOriginal = false; - Label skipBodyLabel = generator.DefineLabel(); + Label afterBodyPatchLabel = generator.DefineLabel(); Label afterBodyOriginalLabel = generator.DefineLabel(); bool injectedAfterBodyOriginal = false; @@ -100,7 +100,7 @@ static IEnumerable Transpiler(IEnumerable inst { // check Enabled yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); - yield return new CodeInstruction(OpCodes.Brfalse, skipHeaderLabel); + yield return new CodeInstruction(OpCodes.Brfalse, afterHeaderPatchLabel); // do header yield return new CodeInstruction(OpCodes.Ldloc_0); @@ -112,29 +112,27 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Ldarg, 5); yield return new CodeInstruction(OpCodes.Call, _buildHeaderMethod); - // check Enabled - yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); - yield return new CodeInstruction(OpCodes.Brtrue, afterHeaderOriginalLabel); + // skip original + yield return new CodeInstruction(OpCodes.Br, afterHeaderOriginalLabel); - yield return new CodeInstruction(OpCodes.Nop) { labels = [skipHeaderLabel] }; + yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderPatchLabel] }; headerDone = true; } if (headerTextLabel != null && !headerTextDone) { // check Enabled yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); - yield return new CodeInstruction(OpCodes.Brfalse, skipHeaderTextLabel); + yield return new CodeInstruction(OpCodes.Brfalse, afterHeaderTextPatchLabel); // do header text yield return new CodeInstruction(OpCodes.Ldloc_0); yield return new CodeInstruction(OpCodes.Ldarg, 1); yield return new CodeInstruction(OpCodes.Call, _buildHeaderTextMethod); - // check Enabled - yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); - yield return new CodeInstruction(OpCodes.Brtrue, afterHeaderTextOriginalLabel); + // skip original + yield return new CodeInstruction(OpCodes.Br, afterHeaderTextOriginalLabel); - yield return new CodeInstruction(OpCodes.Nop) { labels = [skipHeaderTextLabel] }; + yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderTextPatchLabel] }; headerTextDone = true; } } @@ -164,7 +162,7 @@ static IEnumerable Transpiler(IEnumerable inst { // check Enabled yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); - yield return new CodeInstruction(OpCodes.Brfalse, skipBodyLabel); + yield return new CodeInstruction(OpCodes.Brfalse, afterBodyPatchLabel); // do body yield return new CodeInstruction(OpCodes.Ldloc_0); @@ -176,11 +174,10 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Ldarg, 5); yield return new CodeInstruction(OpCodes.Call, _buildBodyMethod); - // check Enabled - yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); - yield return new CodeInstruction(OpCodes.Brtrue, afterBodyOriginalLabel); + // skip original + yield return new CodeInstruction(OpCodes.Br, afterBodyOriginalLabel); - yield return new CodeInstruction(OpCodes.Nop) { labels = [skipBodyLabel] }; + yield return new CodeInstruction(OpCodes.Nop) { labels = [afterBodyPatchLabel] }; bodyDone = true; } } From 4dc2bc3e73265c07c09fc4bcb234f24aca6e3bb0 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 14:42:26 +0100 Subject: [PATCH 08/21] Simplify more --- .../UI/Inspectors/CustomInspectorInjector.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index 40f3bb40..6d2393e2 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -66,20 +66,18 @@ static IEnumerable Transpiler(IEnumerable inst for (int i = 0; i < instArr.Length; i++) { var instruction = instArr[i]; + bool didBranch = false; if (headerLabel is null && instruction.opcode == OpCodes.Brtrue && instArr[i-1].opcode == OpCodes.Isinst && instArr[i - 1].operand == (object)typeof(Slot)) { headerLabel = (Label)instruction.operand; labels.Add(headerLabel); + didBranch = true; } if (headerTextLabel is null && instruction.opcode == OpCodes.Brfalse_S && instArr[i-1].opcode == OpCodes.Ldloc_1 && instArr[i-2].opcode == OpCodes.Stloc_1 && instArr[i-3].Calls(AccessTools.Method(typeof(CustomAttributeExtensions), nameof(CustomAttributeExtensions.GetCustomAttribute), [typeof(MemberInfo)], [typeof(InspectorHeaderAttribute)]))) { headerTextLabel = (Label)instruction.operand; labels.Add(headerTextLabel); - } - bool didBranch = false; - if (instruction.Branches(out var label)) - { didBranch = true; } if (!didBranch) @@ -93,7 +91,6 @@ static IEnumerable Transpiler(IEnumerable inst else if (storedLabel == headerTextLabel) afterHeaderTextBranch = true; labels.Remove(storedLabel); - break; } } if (headerLabel != null && !headerDone) From 6803dbedab7c606326e28be29eff13d26b1bed83 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 15:10:30 +0100 Subject: [PATCH 09/21] Remove labels array and fixup custom inspectors --- .../UI/Inspectors/CustomInspectorInjector.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index 6d2393e2..1aae0d73 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -48,7 +48,6 @@ static IEnumerable Transpiler(IEnumerable inst bool bodyDone = false; bool afterHeaderBranch = false; bool afterHeaderTextBranch = false; - List labels = new(); Label afterHeaderPatchLabel = generator.DefineLabel(); Label afterHeaderOriginalLabel = generator.DefineLabel(); @@ -58,6 +57,7 @@ static IEnumerable Transpiler(IEnumerable inst Label afterHeaderTextOriginalLabel = generator.DefineLabel(); bool injectedAfterHeaderTextOriginal = false; + Label beforeBodyPatchLabel = generator.DefineLabel(); Label afterBodyPatchLabel = generator.DefineLabel(); Label afterBodyOriginalLabel = generator.DefineLabel(); bool injectedAfterBodyOriginal = false; @@ -70,28 +70,23 @@ static IEnumerable Transpiler(IEnumerable inst if (headerLabel is null && instruction.opcode == OpCodes.Brtrue && instArr[i-1].opcode == OpCodes.Isinst && instArr[i - 1].operand == (object)typeof(Slot)) { headerLabel = (Label)instruction.operand; - labels.Add(headerLabel); didBranch = true; } if (headerTextLabel is null && instruction.opcode == OpCodes.Brfalse_S && instArr[i-1].opcode == OpCodes.Ldloc_1 && instArr[i-2].opcode == OpCodes.Stloc_1 && instArr[i-3].Calls(AccessTools.Method(typeof(CustomAttributeExtensions), nameof(CustomAttributeExtensions.GetCustomAttribute), [typeof(MemberInfo)], [typeof(InspectorHeaderAttribute)]))) { headerTextLabel = (Label)instruction.operand; - labels.Add(headerTextLabel); didBranch = true; } if (!didBranch) { - foreach (var storedLabel in labels.ToArray()) + if (headerLabel.HasValue && instruction.labels.Contains(headerLabel.Value) && instruction.operand != (object)headerLabel.Value) { - if (instruction.labels.Contains(storedLabel!.Value) && instruction.operand != (object)storedLabel!.Value) - { - if (storedLabel == headerLabel) - afterHeaderBranch = true; - else if (storedLabel == headerTextLabel) - afterHeaderTextBranch = true; - labels.Remove(storedLabel); - } + afterHeaderBranch = true; + } + if (headerTextLabel.HasValue && instruction.labels.Contains(headerTextLabel.Value) && instruction.operand != (object)headerTextLabel.Value) + { + afterHeaderTextBranch = true; } if (headerLabel != null && !headerDone) { @@ -148,6 +143,11 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Nop) { labels = [afterBodyOriginalLabel] }; injectedAfterBodyOriginal = true; } + if (instruction.opcode == OpCodes.Leave_S && + (instArr[i - 1].Calls(AccessTools.Method(typeof(UniLog), nameof(UniLog.Error))) || instArr[i - 1].Calls(AccessTools.Method(typeof(ICustomInspector), nameof(ICustomInspector.BuildInspectorUI))))) + { + instruction.operand = beforeBodyPatchLabel; + } yield return instruction; if (!storedVerticalLayout && instruction.Calls(AccessTools.Method(typeof(UIBuilder), nameof(UIBuilder.VerticalLayout), [typeof(float), typeof(float), typeof(Alignment?), typeof(bool?), typeof(bool?)]))) { @@ -158,7 +158,7 @@ static IEnumerable Transpiler(IEnumerable inst if (!bodyDone && instruction.Calls(AccessTools.Method(typeof(WorkerInspector), nameof(WorkerInspector.BuildInspectorUI)))) { // check Enabled - yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); + yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod) { labels = [beforeBodyPatchLabel] }; yield return new CodeInstruction(OpCodes.Brfalse, afterBodyPatchLabel); // do body From 457e397db47c8e13e0aa58bfae5fed13794cda4c Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 15:21:29 +0100 Subject: [PATCH 10/21] Simplify more again --- .../UI/Inspectors/CustomInspectorInjector.cs | 44 +++++-------------- 1 file changed, 10 insertions(+), 34 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index 1aae0d73..aca4eb84 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -41,21 +41,15 @@ private static bool GetEnabled() static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) { bool storedVerticalLayout = false; - Label? headerLabel = null; - Label? headerTextLabel = null; + Label? afterHeaderBranchLabel = null; + Label? afterHeaderTextBranchLabel = null; bool headerDone = false; bool headerTextDone = false; bool bodyDone = false; - bool afterHeaderBranch = false; - bool afterHeaderTextBranch = false; Label afterHeaderPatchLabel = generator.DefineLabel(); - Label afterHeaderOriginalLabel = generator.DefineLabel(); - bool injectedAfterHeaderOriginal = false; Label afterHeaderTextPatchLabel = generator.DefineLabel(); - Label afterHeaderTextOriginalLabel = generator.DefineLabel(); - bool injectedAfterHeaderTextOriginal = false; Label beforeBodyPatchLabel = generator.DefineLabel(); Label afterBodyPatchLabel = generator.DefineLabel(); @@ -67,28 +61,20 @@ static IEnumerable Transpiler(IEnumerable inst { var instruction = instArr[i]; bool didBranch = false; - if (headerLabel is null && instruction.opcode == OpCodes.Brtrue && instArr[i-1].opcode == OpCodes.Isinst && instArr[i - 1].operand == (object)typeof(Slot)) + if (afterHeaderBranchLabel is null && instruction.opcode == OpCodes.Brtrue && instArr[i-1].opcode == OpCodes.Isinst && instArr[i - 1].operand == (object)typeof(Slot)) { - headerLabel = (Label)instruction.operand; + afterHeaderBranchLabel = (Label)instruction.operand; didBranch = true; } - if (headerTextLabel is null && instruction.opcode == OpCodes.Brfalse_S && instArr[i-1].opcode == OpCodes.Ldloc_1 && instArr[i-2].opcode == OpCodes.Stloc_1 && + if (afterHeaderTextBranchLabel is null && instruction.opcode == OpCodes.Brfalse_S && instArr[i-1].opcode == OpCodes.Ldloc_1 && instArr[i-2].opcode == OpCodes.Stloc_1 && instArr[i-3].Calls(AccessTools.Method(typeof(CustomAttributeExtensions), nameof(CustomAttributeExtensions.GetCustomAttribute), [typeof(MemberInfo)], [typeof(InspectorHeaderAttribute)]))) { - headerTextLabel = (Label)instruction.operand; + afterHeaderTextBranchLabel = (Label)instruction.operand; didBranch = true; } if (!didBranch) { - if (headerLabel.HasValue && instruction.labels.Contains(headerLabel.Value) && instruction.operand != (object)headerLabel.Value) - { - afterHeaderBranch = true; - } - if (headerTextLabel.HasValue && instruction.labels.Contains(headerTextLabel.Value) && instruction.operand != (object)headerTextLabel.Value) - { - afterHeaderTextBranch = true; - } - if (headerLabel != null && !headerDone) + if (afterHeaderBranchLabel != null && !headerDone) { // check Enabled yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); @@ -105,12 +91,12 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Call, _buildHeaderMethod); // skip original - yield return new CodeInstruction(OpCodes.Br, afterHeaderOriginalLabel); + yield return new CodeInstruction(OpCodes.Br, afterHeaderBranchLabel); yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderPatchLabel] }; headerDone = true; } - if (headerTextLabel != null && !headerTextDone) + if (afterHeaderTextBranchLabel != null && !headerTextDone) { // check Enabled yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); @@ -122,22 +108,12 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Call, _buildHeaderTextMethod); // skip original - yield return new CodeInstruction(OpCodes.Br, afterHeaderTextOriginalLabel); + yield return new CodeInstruction(OpCodes.Br, afterHeaderTextBranchLabel); yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderTextPatchLabel] }; headerTextDone = true; } } - if (headerDone && afterHeaderBranch && !injectedAfterHeaderOriginal) - { - yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderOriginalLabel] }; - injectedAfterHeaderOriginal = true; - } - if (headerTextDone && afterHeaderTextBranch && !injectedAfterHeaderTextOriginal) - { - yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderTextOriginalLabel] }; - injectedAfterHeaderTextOriginal = true; - } if (bodyDone && !injectedAfterBodyOriginal) { yield return new CodeInstruction(OpCodes.Nop) { labels = [afterBodyOriginalLabel] }; From 7a0e7a35b6b0b730ed2a34b94a9546c83aebfbb9 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 16:09:18 +0100 Subject: [PATCH 11/21] More comments --- .../UI/Inspectors/CustomInspectorInjector.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index aca4eb84..26fff58f 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -80,7 +80,7 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); yield return new CodeInstruction(OpCodes.Brfalse, afterHeaderPatchLabel); - // do header + // do header patch yield return new CodeInstruction(OpCodes.Ldloc_0); yield return new CodeInstruction(OpCodes.Ldarg, 0); yield return new CodeInstruction(OpCodes.Ldarg, 1); @@ -90,9 +90,10 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Ldarg, 5); yield return new CodeInstruction(OpCodes.Call, _buildHeaderMethod); - // skip original + // skip original if did patch yield return new CodeInstruction(OpCodes.Br, afterHeaderBranchLabel); + // mark after patch (start of original) yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderPatchLabel] }; headerDone = true; } @@ -102,14 +103,15 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); yield return new CodeInstruction(OpCodes.Brfalse, afterHeaderTextPatchLabel); - // do header text + // do header text patch yield return new CodeInstruction(OpCodes.Ldloc_0); yield return new CodeInstruction(OpCodes.Ldarg, 1); yield return new CodeInstruction(OpCodes.Call, _buildHeaderTextMethod); - // skip original + // skip original if did patch yield return new CodeInstruction(OpCodes.Br, afterHeaderTextBranchLabel); + // mark after patch (start of original) yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderTextPatchLabel] }; headerTextDone = true; } @@ -137,7 +139,7 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod) { labels = [beforeBodyPatchLabel] }; yield return new CodeInstruction(OpCodes.Brfalse, afterBodyPatchLabel); - // do body + // do body patch yield return new CodeInstruction(OpCodes.Ldloc_0); yield return new CodeInstruction(OpCodes.Ldarg, 0); yield return new CodeInstruction(OpCodes.Ldarg, 1); @@ -147,9 +149,10 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Ldarg, 5); yield return new CodeInstruction(OpCodes.Call, _buildBodyMethod); - // skip original + // skip original if did patch yield return new CodeInstruction(OpCodes.Br, afterBodyOriginalLabel); + // mark after patch (start of original) yield return new CodeInstruction(OpCodes.Nop) { labels = [afterBodyPatchLabel] }; bodyDone = true; } From 43682b8511a2a1eacae2c6b7b01d0d03f8536748 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:47:48 +0100 Subject: [PATCH 12/21] It now matches the old patch completely --- .../UI/Inspectors/CustomInspectorInjector.cs | 109 ++++++++++-------- 1 file changed, 60 insertions(+), 49 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index 26fff58f..9742ee4e 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -37,6 +37,20 @@ private static bool GetEnabled() return Enabled; } + private static Label? GetNextBranchLabel(CodeInstruction[] codes, int startIndex) + { + int i = startIndex; + while (i < codes.Length) + { + if (codes[i].Branches(out var label)) + { + return label; + } + i++; + } + return null; + } + [HarmonyTranspiler] static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) { @@ -56,65 +70,62 @@ static IEnumerable Transpiler(IEnumerable inst Label afterBodyOriginalLabel = generator.DefineLabel(); bool injectedAfterBodyOriginal = false; - var instArr = instructions.ToArray(); + CodeInstruction[] instArr = instructions.ToArray(); for (int i = 0; i < instArr.Length; i++) { var instruction = instArr[i]; - bool didBranch = false; + bool branchFound = false; if (afterHeaderBranchLabel is null && instruction.opcode == OpCodes.Brtrue && instArr[i-1].opcode == OpCodes.Isinst && instArr[i - 1].operand == (object)typeof(Slot)) { afterHeaderBranchLabel = (Label)instruction.operand; - didBranch = true; + branchFound = true; } - if (afterHeaderTextBranchLabel is null && instruction.opcode == OpCodes.Brfalse_S && instArr[i-1].opcode == OpCodes.Ldloc_1 && instArr[i-2].opcode == OpCodes.Stloc_1 && - instArr[i-3].Calls(AccessTools.Method(typeof(CustomAttributeExtensions), nameof(CustomAttributeExtensions.GetCustomAttribute), [typeof(MemberInfo)], [typeof(InspectorHeaderAttribute)]))) + if (!branchFound && afterHeaderBranchLabel != null && !headerDone) { - afterHeaderTextBranchLabel = (Label)instruction.operand; - didBranch = true; + // check Enabled + yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); + yield return new CodeInstruction(OpCodes.Brfalse, afterHeaderPatchLabel); + + // do header patch + yield return new CodeInstruction(OpCodes.Ldloc_0); + yield return new CodeInstruction(OpCodes.Ldarg, 0); + yield return new CodeInstruction(OpCodes.Ldarg, 1); + yield return new CodeInstruction(OpCodes.Ldarg, 2); + yield return new CodeInstruction(OpCodes.Ldarg, 3); + yield return new CodeInstruction(OpCodes.Ldarg, 4); + yield return new CodeInstruction(OpCodes.Ldarg, 5); + yield return new CodeInstruction(OpCodes.Call, _buildHeaderMethod); + + // skip original if did patch + yield return new CodeInstruction(OpCodes.Br, afterHeaderBranchLabel); + + // mark after patch (start of original) + yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderPatchLabel] }; + headerDone = true; } - if (!didBranch) + // this patch calls OnBuildInspectorHeaderText unconditionally (even if there is no InspectorHeaderAttribute) + if (afterHeaderTextBranchLabel is null && !headerTextDone && + instruction.opcode == OpCodes.Ldloc_1 && + instArr[i-1].opcode == OpCodes.Stloc_1 && + instArr[i-2].Calls(AccessTools.Method(typeof(CustomAttributeExtensions), nameof(CustomAttributeExtensions.GetCustomAttribute), [typeof(MemberInfo)], [typeof(InspectorHeaderAttribute)]))) { - if (afterHeaderBranchLabel != null && !headerDone) - { - // check Enabled - yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); - yield return new CodeInstruction(OpCodes.Brfalse, afterHeaderPatchLabel); - - // do header patch - yield return new CodeInstruction(OpCodes.Ldloc_0); - yield return new CodeInstruction(OpCodes.Ldarg, 0); - yield return new CodeInstruction(OpCodes.Ldarg, 1); - yield return new CodeInstruction(OpCodes.Ldarg, 2); - yield return new CodeInstruction(OpCodes.Ldarg, 3); - yield return new CodeInstruction(OpCodes.Ldarg, 4); - yield return new CodeInstruction(OpCodes.Ldarg, 5); - yield return new CodeInstruction(OpCodes.Call, _buildHeaderMethod); - - // skip original if did patch - yield return new CodeInstruction(OpCodes.Br, afterHeaderBranchLabel); - - // mark after patch (start of original) - yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderPatchLabel] }; - headerDone = true; - } - if (afterHeaderTextBranchLabel != null && !headerTextDone) - { - // check Enabled - yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); - yield return new CodeInstruction(OpCodes.Brfalse, afterHeaderTextPatchLabel); - - // do header text patch - yield return new CodeInstruction(OpCodes.Ldloc_0); - yield return new CodeInstruction(OpCodes.Ldarg, 1); - yield return new CodeInstruction(OpCodes.Call, _buildHeaderTextMethod); - - // skip original if did patch - yield return new CodeInstruction(OpCodes.Br, afterHeaderTextBranchLabel); - - // mark after patch (start of original) - yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderTextPatchLabel] }; - headerTextDone = true; - } + afterHeaderTextBranchLabel = GetNextBranchLabel(instArr, i+1); + + // check Enabled + yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); + yield return new CodeInstruction(OpCodes.Brfalse, afterHeaderTextPatchLabel); + + // do header text patch + yield return new CodeInstruction(OpCodes.Ldloc_0); + yield return new CodeInstruction(OpCodes.Ldarg, 1); + yield return new CodeInstruction(OpCodes.Call, _buildHeaderTextMethod); + + // skip original if did patch + yield return new CodeInstruction(OpCodes.Br, afterHeaderTextBranchLabel); + + // mark after patch (start of original) + yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderTextPatchLabel] }; + headerTextDone = true; } if (bodyDone && !injectedAfterBodyOriginal) { From f7a2351149e66865d7d398d72c63b321d1c060ce Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 18:35:37 +0100 Subject: [PATCH 13/21] Simplify more again again --- .../UI/Inspectors/CustomInspectorInjector.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index 9742ee4e..d0a97019 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -67,8 +67,6 @@ static IEnumerable Transpiler(IEnumerable inst Label beforeBodyPatchLabel = generator.DefineLabel(); Label afterBodyPatchLabel = generator.DefineLabel(); - Label afterBodyOriginalLabel = generator.DefineLabel(); - bool injectedAfterBodyOriginal = false; CodeInstruction[] instArr = instructions.ToArray(); for (int i = 0; i < instArr.Length; i++) @@ -127,11 +125,6 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderTextPatchLabel] }; headerTextDone = true; } - if (bodyDone && !injectedAfterBodyOriginal) - { - yield return new CodeInstruction(OpCodes.Nop) { labels = [afterBodyOriginalLabel] }; - injectedAfterBodyOriginal = true; - } if (instruction.opcode == OpCodes.Leave_S && (instArr[i - 1].Calls(AccessTools.Method(typeof(UniLog), nameof(UniLog.Error))) || instArr[i - 1].Calls(AccessTools.Method(typeof(ICustomInspector), nameof(ICustomInspector.BuildInspectorUI))))) { @@ -160,8 +153,7 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Ldarg, 5); yield return new CodeInstruction(OpCodes.Call, _buildBodyMethod); - // skip original if did patch - yield return new CodeInstruction(OpCodes.Br, afterBodyOriginalLabel); + // there is no "original" in this case // mark after patch (start of original) yield return new CodeInstruction(OpCodes.Nop) { labels = [afterBodyPatchLabel] }; From 651512b184cd8356530e8067256120e005169092 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 19:40:45 +0100 Subject: [PATCH 14/21] Use a local to store the vertical layout --- .../UI/Inspectors/CustomInspectorInjector.cs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index d0a97019..54975464 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -22,14 +22,13 @@ internal sealed class CustomInspectorInjector private static MethodInfo _buildHeaderMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(OnBuildInspectorHeader)); private static MethodInfo _buildHeaderTextMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(OnBuildInspectorHeaderText)); private static MethodInfo _buildBodyMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(OnBuildInspectorBody)); - private static MethodInfo _storeVerticalLayoutMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(StoreVerticalLayout)); + private static MethodInfo _getSlotMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(GetSlot)); private static MethodInfo _getEnabledMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(GetEnabled)); + private static MethodInfo _nestIntoMethod = AccessTools.Method(typeof(UIBuilder), nameof(UIBuilder.NestInto), [typeof(Slot)]); - private static VerticalLayout _verticalLayout; - - private static void StoreVerticalLayout(VerticalLayout layout) + private static Slot GetSlot(VerticalLayout layout) { - _verticalLayout = layout; + return layout.Slot; } private static bool GetEnabled() @@ -61,6 +60,8 @@ static IEnumerable Transpiler(IEnumerable inst bool headerTextDone = false; bool bodyDone = false; + var verticalLayoutLocal = generator.DeclareLocal(typeof(VerticalLayout)); + Label afterHeaderPatchLabel = generator.DefineLabel(); Label afterHeaderTextPatchLabel = generator.DefineLabel(); @@ -94,6 +95,11 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Ldarg, 5); yield return new CodeInstruction(OpCodes.Call, _buildHeaderMethod); + yield return new CodeInstruction(OpCodes.Ldloc_0); + yield return new CodeInstruction(OpCodes.Ldloc, verticalLayoutLocal.LocalIndex); + yield return new CodeInstruction(OpCodes.Callvirt, _getSlotMethod); + yield return new CodeInstruction(OpCodes.Callvirt, _nestIntoMethod); + // skip original if did patch yield return new CodeInstruction(OpCodes.Br, afterHeaderBranchLabel); @@ -134,7 +140,7 @@ static IEnumerable Transpiler(IEnumerable inst if (!storedVerticalLayout && instruction.Calls(AccessTools.Method(typeof(UIBuilder), nameof(UIBuilder.VerticalLayout), [typeof(float), typeof(float), typeof(Alignment?), typeof(bool?), typeof(bool?)]))) { yield return new CodeInstruction(OpCodes.Dup); - yield return new CodeInstruction(OpCodes.Call, _storeVerticalLayoutMethod); + yield return new CodeInstruction(OpCodes.Stloc, verticalLayoutLocal.LocalIndex); storedVerticalLayout = true; } if (!bodyDone && instruction.Calls(AccessTools.Method(typeof(WorkerInspector), nameof(WorkerInspector.BuildInspectorUI)))) @@ -185,8 +191,6 @@ private static void OnBuildInspectorHeader(UIBuilder ui, WorkerInspector inspect var eventData = new BuildInspectorHeaderEvent(ui, inspector, worker, allowContainer, allowDuplicate, allowDestroy, memberFilter); Dispatch(eventData); - - ui.NestInto(_verticalLayout.Slot); } private static void OnBuildInspectorHeaderText(UIBuilder ui, Worker worker) From 412e03ccaf279a6eebd6819ac352e8f19155f903 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 19:46:40 +0100 Subject: [PATCH 15/21] Actually we don't need to store the vertical layout --- .../UI/Inspectors/CustomInspectorInjector.cs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index 54975464..a3b997aa 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -53,15 +53,12 @@ private static bool GetEnabled() [HarmonyTranspiler] static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) { - bool storedVerticalLayout = false; Label? afterHeaderBranchLabel = null; Label? afterHeaderTextBranchLabel = null; bool headerDone = false; bool headerTextDone = false; bool bodyDone = false; - var verticalLayoutLocal = generator.DeclareLocal(typeof(VerticalLayout)); - Label afterHeaderPatchLabel = generator.DefineLabel(); Label afterHeaderTextPatchLabel = generator.DefineLabel(); @@ -95,11 +92,6 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Ldarg, 5); yield return new CodeInstruction(OpCodes.Call, _buildHeaderMethod); - yield return new CodeInstruction(OpCodes.Ldloc_0); - yield return new CodeInstruction(OpCodes.Ldloc, verticalLayoutLocal.LocalIndex); - yield return new CodeInstruction(OpCodes.Callvirt, _getSlotMethod); - yield return new CodeInstruction(OpCodes.Callvirt, _nestIntoMethod); - // skip original if did patch yield return new CodeInstruction(OpCodes.Br, afterHeaderBranchLabel); @@ -137,12 +129,6 @@ static IEnumerable Transpiler(IEnumerable inst instruction.operand = beforeBodyPatchLabel; } yield return instruction; - if (!storedVerticalLayout && instruction.Calls(AccessTools.Method(typeof(UIBuilder), nameof(UIBuilder.VerticalLayout), [typeof(float), typeof(float), typeof(Alignment?), typeof(bool?), typeof(bool?)]))) - { - yield return new CodeInstruction(OpCodes.Dup); - yield return new CodeInstruction(OpCodes.Stloc, verticalLayoutLocal.LocalIndex); - storedVerticalLayout = true; - } if (!bodyDone && instruction.Calls(AccessTools.Method(typeof(WorkerInspector), nameof(WorkerInspector.BuildInspectorUI)))) { // check Enabled @@ -183,6 +169,8 @@ private static void OnBuildInspectorBody(UIBuilder ui, WorkerInspector inspector private static void OnBuildInspectorHeader(UIBuilder ui, WorkerInspector inspector, Worker worker, bool allowDestroy, bool allowDuplicate, bool allowContainer, Predicate memberFilter) { + var root = ui.Root; + ui.Style.MinHeight = 32f; ui.HorizontalLayout(4f); ui.Style.MinHeight = 24f; @@ -191,6 +179,8 @@ private static void OnBuildInspectorHeader(UIBuilder ui, WorkerInspector inspect var eventData = new BuildInspectorHeaderEvent(ui, inspector, worker, allowContainer, allowDuplicate, allowDestroy, memberFilter); Dispatch(eventData); + + ui.NestInto(root); } private static void OnBuildInspectorHeaderText(UIBuilder ui, Worker worker) From f0985a5a288d80c9c65dd95c78d9d9a48ef03eb1 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Fri, 20 Jun 2025 19:49:37 +0100 Subject: [PATCH 16/21] Remove unnecessary method --- .../UI/Inspectors/CustomInspectorInjector.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index a3b997aa..05838bbb 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -22,14 +22,7 @@ internal sealed class CustomInspectorInjector private static MethodInfo _buildHeaderMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(OnBuildInspectorHeader)); private static MethodInfo _buildHeaderTextMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(OnBuildInspectorHeaderText)); private static MethodInfo _buildBodyMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(OnBuildInspectorBody)); - private static MethodInfo _getSlotMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(GetSlot)); private static MethodInfo _getEnabledMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(GetEnabled)); - private static MethodInfo _nestIntoMethod = AccessTools.Method(typeof(UIBuilder), nameof(UIBuilder.NestInto), [typeof(Slot)]); - - private static Slot GetSlot(VerticalLayout layout) - { - return layout.Slot; - } private static bool GetEnabled() { From 64d49e6ac9381b5a0492d2e8536620482cfaf22e Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Sat, 21 Jun 2025 02:16:02 +0100 Subject: [PATCH 17/21] Increment index inside indexer and remove braces on If --- .../UI/Inspectors/CustomInspectorInjector.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index 05838bbb..5fbbd1e1 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -34,11 +34,8 @@ private static bool GetEnabled() int i = startIndex; while (i < codes.Length) { - if (codes[i].Branches(out var label)) - { + if (codes[i++].Branches(out var label)) return label; - } - i++; } return null; } From 8f6f93eb762ff9fbf1859b5f50daa39c1d962f74 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Sat, 21 Jun 2025 02:22:45 +0100 Subject: [PATCH 18/21] Store the methods used in transpiler --- .../UI/Inspectors/CustomInspectorInjector.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index 5fbbd1e1..8e2de229 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -56,6 +56,11 @@ static IEnumerable Transpiler(IEnumerable inst Label beforeBodyPatchLabel = generator.DefineLabel(); Label afterBodyPatchLabel = generator.DefineLabel(); + var getCustomAttributeMethod = AccessTools.Method(typeof(CustomAttributeExtensions), nameof(CustomAttributeExtensions.GetCustomAttribute), [typeof(MemberInfo)], [typeof(InspectorHeaderAttribute)]); + var unilogErrorMethod = AccessTools.Method(typeof(UniLog), nameof(UniLog.Error)); + var customInspectorBuildUiMethod = AccessTools.Method(typeof(ICustomInspector), nameof(ICustomInspector.BuildInspectorUI)); + var workerInspectorBuildUiMethod = AccessTools.Method(typeof(WorkerInspector), nameof(WorkerInspector.BuildInspectorUI)); + CodeInstruction[] instArr = instructions.ToArray(); for (int i = 0; i < instArr.Length; i++) { @@ -93,7 +98,7 @@ static IEnumerable Transpiler(IEnumerable inst if (afterHeaderTextBranchLabel is null && !headerTextDone && instruction.opcode == OpCodes.Ldloc_1 && instArr[i-1].opcode == OpCodes.Stloc_1 && - instArr[i-2].Calls(AccessTools.Method(typeof(CustomAttributeExtensions), nameof(CustomAttributeExtensions.GetCustomAttribute), [typeof(MemberInfo)], [typeof(InspectorHeaderAttribute)]))) + instArr[i-2].Calls(getCustomAttributeMethod)) { afterHeaderTextBranchLabel = GetNextBranchLabel(instArr, i+1); @@ -114,12 +119,12 @@ static IEnumerable Transpiler(IEnumerable inst headerTextDone = true; } if (instruction.opcode == OpCodes.Leave_S && - (instArr[i - 1].Calls(AccessTools.Method(typeof(UniLog), nameof(UniLog.Error))) || instArr[i - 1].Calls(AccessTools.Method(typeof(ICustomInspector), nameof(ICustomInspector.BuildInspectorUI))))) + (instArr[i - 1].Calls(unilogErrorMethod) || instArr[i - 1].Calls(customInspectorBuildUiMethod))) { instruction.operand = beforeBodyPatchLabel; } yield return instruction; - if (!bodyDone && instruction.Calls(AccessTools.Method(typeof(WorkerInspector), nameof(WorkerInspector.BuildInspectorUI)))) + if (!bodyDone && instruction.Calls(workerInspectorBuildUiMethod)) { // check Enabled yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod) { labels = [beforeBodyPatchLabel] }; From 23c203cadb3b934b8cb2a3c61f46e13fe3d9e799 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Mon, 23 Jun 2025 01:01:38 +0100 Subject: [PATCH 19/21] Make the patch more robust by not using hardcoded local indices --- .../UI/Inspectors/CustomInspectorInjector.cs | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index 8e2de229..f309d641 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -40,6 +40,13 @@ private static bool GetEnabled() return null; } + private static int GetLocalIndex(CodeInstruction code) + { + if (code.operand is LocalBuilder localBuilder) + return localBuilder.LocalIndex; + return code.LocalIndex(); + } + [HarmonyTranspiler] static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) { @@ -61,24 +68,30 @@ static IEnumerable Transpiler(IEnumerable inst var customInspectorBuildUiMethod = AccessTools.Method(typeof(ICustomInspector), nameof(ICustomInspector.BuildInspectorUI)); var workerInspectorBuildUiMethod = AccessTools.Method(typeof(WorkerInspector), nameof(WorkerInspector.BuildInspectorUI)); + int uiBuilderLocalIndex = -1; + CodeInstruction[] instArr = instructions.ToArray(); for (int i = 0; i < instArr.Length; i++) { var instruction = instArr[i]; bool branchFound = false; + if (uiBuilderLocalIndex == -1 && instruction.opcode == OpCodes.Newobj && instruction.operand == AccessTools.Constructor(typeof(UIBuilder), [typeof(Slot), typeof(Slot)]) && instArr[i+1].IsStloc()) + { + uiBuilderLocalIndex = GetLocalIndex(instArr[i+1]); + } if (afterHeaderBranchLabel is null && instruction.opcode == OpCodes.Brtrue && instArr[i-1].opcode == OpCodes.Isinst && instArr[i - 1].operand == (object)typeof(Slot)) { afterHeaderBranchLabel = (Label)instruction.operand; branchFound = true; } - if (!branchFound && afterHeaderBranchLabel != null && !headerDone) + if (uiBuilderLocalIndex != -1 && !branchFound && afterHeaderBranchLabel != null && !headerDone) { // check Enabled yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); yield return new CodeInstruction(OpCodes.Brfalse, afterHeaderPatchLabel); // do header patch - yield return new CodeInstruction(OpCodes.Ldloc_0); + yield return new CodeInstruction(OpCodes.Ldloc, uiBuilderLocalIndex); yield return new CodeInstruction(OpCodes.Ldarg, 0); yield return new CodeInstruction(OpCodes.Ldarg, 1); yield return new CodeInstruction(OpCodes.Ldarg, 2); @@ -95,7 +108,7 @@ static IEnumerable Transpiler(IEnumerable inst headerDone = true; } // this patch calls OnBuildInspectorHeaderText unconditionally (even if there is no InspectorHeaderAttribute) - if (afterHeaderTextBranchLabel is null && !headerTextDone && + if (uiBuilderLocalIndex != -1 && afterHeaderTextBranchLabel is null && !headerTextDone && instruction.opcode == OpCodes.Ldloc_1 && instArr[i-1].opcode == OpCodes.Stloc_1 && instArr[i-2].Calls(getCustomAttributeMethod)) @@ -107,7 +120,7 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Brfalse, afterHeaderTextPatchLabel); // do header text patch - yield return new CodeInstruction(OpCodes.Ldloc_0); + yield return new CodeInstruction(OpCodes.Ldloc, uiBuilderLocalIndex); yield return new CodeInstruction(OpCodes.Ldarg, 1); yield return new CodeInstruction(OpCodes.Call, _buildHeaderTextMethod); @@ -124,14 +137,14 @@ static IEnumerable Transpiler(IEnumerable inst instruction.operand = beforeBodyPatchLabel; } yield return instruction; - if (!bodyDone && instruction.Calls(workerInspectorBuildUiMethod)) + if (uiBuilderLocalIndex != -1 && !bodyDone && instruction.Calls(workerInspectorBuildUiMethod)) { // check Enabled yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod) { labels = [beforeBodyPatchLabel] }; yield return new CodeInstruction(OpCodes.Brfalse, afterBodyPatchLabel); // do body patch - yield return new CodeInstruction(OpCodes.Ldloc_0); + yield return new CodeInstruction(OpCodes.Ldloc, uiBuilderLocalIndex); yield return new CodeInstruction(OpCodes.Ldarg, 0); yield return new CodeInstruction(OpCodes.Ldarg, 1); yield return new CodeInstruction(OpCodes.Ldarg, 2); From c0bec8cb87ddbed739d819fafc4ea5f47bafb309 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Mon, 23 Jun 2025 01:09:00 +0100 Subject: [PATCH 20/21] Store UIBuilder constructor and add another check for if the local index was found --- .../UI/Inspectors/CustomInspectorInjector.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index f309d641..c0a86a19 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -67,6 +67,7 @@ static IEnumerable Transpiler(IEnumerable inst var unilogErrorMethod = AccessTools.Method(typeof(UniLog), nameof(UniLog.Error)); var customInspectorBuildUiMethod = AccessTools.Method(typeof(ICustomInspector), nameof(ICustomInspector.BuildInspectorUI)); var workerInspectorBuildUiMethod = AccessTools.Method(typeof(WorkerInspector), nameof(WorkerInspector.BuildInspectorUI)); + var uiBuilderConstructor = AccessTools.Constructor(typeof(UIBuilder), [typeof(Slot), typeof(Slot)]); int uiBuilderLocalIndex = -1; @@ -75,7 +76,7 @@ static IEnumerable Transpiler(IEnumerable inst { var instruction = instArr[i]; bool branchFound = false; - if (uiBuilderLocalIndex == -1 && instruction.opcode == OpCodes.Newobj && instruction.operand == AccessTools.Constructor(typeof(UIBuilder), [typeof(Slot), typeof(Slot)]) && instArr[i+1].IsStloc()) + if (uiBuilderLocalIndex == -1 && instruction.opcode == OpCodes.Newobj && instruction.operand == uiBuilderConstructor && instArr[i+1].IsStloc()) { uiBuilderLocalIndex = GetLocalIndex(instArr[i+1]); } @@ -131,7 +132,7 @@ static IEnumerable Transpiler(IEnumerable inst yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderTextPatchLabel] }; headerTextDone = true; } - if (instruction.opcode == OpCodes.Leave_S && + if (uiBuilderLocalIndex != -1 && instruction.opcode == OpCodes.Leave_S && (instArr[i - 1].Calls(unilogErrorMethod) || instArr[i - 1].Calls(customInspectorBuildUiMethod))) { instruction.operand = beforeBodyPatchLabel; From 4c75c4db5cd712a394b5e172cb760a0a49eb8a7e Mon Sep 17 00:00:00 2001 From: Arne Kiesewetter Date: Sat, 5 Jul 2025 14:00:38 +0200 Subject: [PATCH 21/21] Some code style cleanup --- .../UI/Inspectors/CustomInspectorInjector.cs | 270 +++++++++--------- 1 file changed, 140 insertions(+), 130 deletions(-) diff --git a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs index c0a86a19..83f53dbc 100644 --- a/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs +++ b/MonkeyLoader.Resonite.Integration/UI/Inspectors/CustomInspectorInjector.cs @@ -19,148 +19,26 @@ internal sealed class CustomInspectorInjector { public override bool CanBeDisabled => true; - private static MethodInfo _buildHeaderMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(OnBuildInspectorHeader)); - private static MethodInfo _buildHeaderTextMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(OnBuildInspectorHeaderText)); - private static MethodInfo _buildBodyMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(OnBuildInspectorBody)); - private static MethodInfo _getEnabledMethod = AccessTools.Method(typeof(CustomInspectorInjector), nameof(GetEnabled)); + private static bool GetEnabled() => Enabled; - private static bool GetEnabled() + private static int GetLocalIndex(CodeInstruction code) { - return Enabled; + if (code.operand is LocalBuilder localBuilder) + return localBuilder.LocalIndex; + + return code.LocalIndex(); } private static Label? GetNextBranchLabel(CodeInstruction[] codes, int startIndex) { - int i = startIndex; + var i = startIndex; while (i < codes.Length) { if (codes[i++].Branches(out var label)) return label; } - return null; - } - - private static int GetLocalIndex(CodeInstruction code) - { - if (code.operand is LocalBuilder localBuilder) - return localBuilder.LocalIndex; - return code.LocalIndex(); - } - - [HarmonyTranspiler] - static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) - { - Label? afterHeaderBranchLabel = null; - Label? afterHeaderTextBranchLabel = null; - bool headerDone = false; - bool headerTextDone = false; - bool bodyDone = false; - - Label afterHeaderPatchLabel = generator.DefineLabel(); - - Label afterHeaderTextPatchLabel = generator.DefineLabel(); - Label beforeBodyPatchLabel = generator.DefineLabel(); - Label afterBodyPatchLabel = generator.DefineLabel(); - - var getCustomAttributeMethod = AccessTools.Method(typeof(CustomAttributeExtensions), nameof(CustomAttributeExtensions.GetCustomAttribute), [typeof(MemberInfo)], [typeof(InspectorHeaderAttribute)]); - var unilogErrorMethod = AccessTools.Method(typeof(UniLog), nameof(UniLog.Error)); - var customInspectorBuildUiMethod = AccessTools.Method(typeof(ICustomInspector), nameof(ICustomInspector.BuildInspectorUI)); - var workerInspectorBuildUiMethod = AccessTools.Method(typeof(WorkerInspector), nameof(WorkerInspector.BuildInspectorUI)); - var uiBuilderConstructor = AccessTools.Constructor(typeof(UIBuilder), [typeof(Slot), typeof(Slot)]); - - int uiBuilderLocalIndex = -1; - - CodeInstruction[] instArr = instructions.ToArray(); - for (int i = 0; i < instArr.Length; i++) - { - var instruction = instArr[i]; - bool branchFound = false; - if (uiBuilderLocalIndex == -1 && instruction.opcode == OpCodes.Newobj && instruction.operand == uiBuilderConstructor && instArr[i+1].IsStloc()) - { - uiBuilderLocalIndex = GetLocalIndex(instArr[i+1]); - } - if (afterHeaderBranchLabel is null && instruction.opcode == OpCodes.Brtrue && instArr[i-1].opcode == OpCodes.Isinst && instArr[i - 1].operand == (object)typeof(Slot)) - { - afterHeaderBranchLabel = (Label)instruction.operand; - branchFound = true; - } - if (uiBuilderLocalIndex != -1 && !branchFound && afterHeaderBranchLabel != null && !headerDone) - { - // check Enabled - yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); - yield return new CodeInstruction(OpCodes.Brfalse, afterHeaderPatchLabel); - - // do header patch - yield return new CodeInstruction(OpCodes.Ldloc, uiBuilderLocalIndex); - yield return new CodeInstruction(OpCodes.Ldarg, 0); - yield return new CodeInstruction(OpCodes.Ldarg, 1); - yield return new CodeInstruction(OpCodes.Ldarg, 2); - yield return new CodeInstruction(OpCodes.Ldarg, 3); - yield return new CodeInstruction(OpCodes.Ldarg, 4); - yield return new CodeInstruction(OpCodes.Ldarg, 5); - yield return new CodeInstruction(OpCodes.Call, _buildHeaderMethod); - - // skip original if did patch - yield return new CodeInstruction(OpCodes.Br, afterHeaderBranchLabel); - - // mark after patch (start of original) - yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderPatchLabel] }; - headerDone = true; - } - // this patch calls OnBuildInspectorHeaderText unconditionally (even if there is no InspectorHeaderAttribute) - if (uiBuilderLocalIndex != -1 && afterHeaderTextBranchLabel is null && !headerTextDone && - instruction.opcode == OpCodes.Ldloc_1 && - instArr[i-1].opcode == OpCodes.Stloc_1 && - instArr[i-2].Calls(getCustomAttributeMethod)) - { - afterHeaderTextBranchLabel = GetNextBranchLabel(instArr, i+1); - - // check Enabled - yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod); - yield return new CodeInstruction(OpCodes.Brfalse, afterHeaderTextPatchLabel); - - // do header text patch - yield return new CodeInstruction(OpCodes.Ldloc, uiBuilderLocalIndex); - yield return new CodeInstruction(OpCodes.Ldarg, 1); - yield return new CodeInstruction(OpCodes.Call, _buildHeaderTextMethod); - - // skip original if did patch - yield return new CodeInstruction(OpCodes.Br, afterHeaderTextBranchLabel); - - // mark after patch (start of original) - yield return new CodeInstruction(OpCodes.Nop) { labels = [afterHeaderTextPatchLabel] }; - headerTextDone = true; - } - if (uiBuilderLocalIndex != -1 && instruction.opcode == OpCodes.Leave_S && - (instArr[i - 1].Calls(unilogErrorMethod) || instArr[i - 1].Calls(customInspectorBuildUiMethod))) - { - instruction.operand = beforeBodyPatchLabel; - } - yield return instruction; - if (uiBuilderLocalIndex != -1 && !bodyDone && instruction.Calls(workerInspectorBuildUiMethod)) - { - // check Enabled - yield return new CodeInstruction(OpCodes.Call, _getEnabledMethod) { labels = [beforeBodyPatchLabel] }; - yield return new CodeInstruction(OpCodes.Brfalse, afterBodyPatchLabel); - - // do body patch - yield return new CodeInstruction(OpCodes.Ldloc, uiBuilderLocalIndex); - yield return new CodeInstruction(OpCodes.Ldarg, 0); - yield return new CodeInstruction(OpCodes.Ldarg, 1); - yield return new CodeInstruction(OpCodes.Ldarg, 2); - yield return new CodeInstruction(OpCodes.Ldarg, 3); - yield return new CodeInstruction(OpCodes.Ldarg, 4); - yield return new CodeInstruction(OpCodes.Ldarg, 5); - yield return new CodeInstruction(OpCodes.Call, _buildBodyMethod); - - // there is no "original" in this case - - // mark after patch (start of original) - yield return new CodeInstruction(OpCodes.Nop) { labels = [afterBodyPatchLabel] }; - bodyDone = true; - } - } + return null; } private static void OnBuildInspectorBody(UIBuilder ui, WorkerInspector inspector, Worker worker, @@ -257,5 +135,137 @@ private static void OnBuildInspectorHeaderText(UIBuilder ui, Worker worker) ui.PopStyle(); ui.NestInto(root); } + + [HarmonyTranspiler] + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + // Patching progress trackers + var isHeaderDone = false; + var isHeaderTextDone = false; + var isBodyDone = false; + + Label? afterHeaderBranchLabel = null; + var afterHeaderPatchLabel = generator.DefineLabel(); + + Label? afterHeaderTextBranchLabel = null; + var afterHeaderTextPatchLabel = generator.DefineLabel(); + + var beforeBodyPatchLabel = generator.DefineLabel(); + var afterBodyPatchLabel = generator.DefineLabel(); + + // Vanilla methods to look for + var getCustomAttributeMethod = AccessTools.Method(typeof(CustomAttributeExtensions), nameof(CustomAttributeExtensions.GetCustomAttribute), [typeof(MemberInfo)], [typeof(InspectorHeaderAttribute)]); + var unilogErrorMethod = AccessTools.Method(typeof(UniLog), nameof(UniLog.Error)); + var customInspectorBuildUiMethod = AccessTools.Method(typeof(ICustomInspector), nameof(ICustomInspector.BuildInspectorUI)); + var workerInspectorBuildUiMethod = AccessTools.Method(typeof(WorkerInspector), nameof(WorkerInspector.BuildInspectorUI)); + var uiBuilderConstructor = AccessTools.Constructor(typeof(UIBuilder), [typeof(Slot), typeof(Slot)]); + + // Injector methods to insert calls for + var thisType = typeof(CustomInspectorInjector); + var buildHeaderMethod = AccessTools.Method(thisType, nameof(OnBuildInspectorHeader)); + var buildHeaderTextMethod = AccessTools.Method(thisType, nameof(OnBuildInspectorHeaderText)); + var buildBodyMethod = AccessTools.Method(thisType, nameof(OnBuildInspectorBody)); + var getEnabledMethod = AccessTools.Method(thisType, nameof(GetEnabled)); + + var uiBuilderLocalIndex = -1; + var instArr = instructions.ToArray(); + + for (var i = 0; i < instArr.Length; i++) + { + var instruction = instArr[i]; + var branchFound = false; + + if (uiBuilderLocalIndex == -1 && instruction.opcode == OpCodes.Newobj && (ConstructorInfo)instruction.operand == uiBuilderConstructor && instArr[i + 1].IsStloc()) + { + uiBuilderLocalIndex = GetLocalIndex(instArr[i + 1]); + } + + if (afterHeaderBranchLabel is null && instruction.opcode == OpCodes.Brtrue && instArr[i - 1].opcode == OpCodes.Isinst && instArr[i - 1].operand == (object)typeof(Slot)) + { + afterHeaderBranchLabel = (Label)instruction.operand; + branchFound = true; + } + + if (uiBuilderLocalIndex != -1 && !branchFound && afterHeaderBranchLabel != null && !isHeaderDone) + { + // check Enabled + yield return new CodeInstruction(OpCodes.Call, getEnabledMethod); + yield return new CodeInstruction(OpCodes.Brfalse, afterHeaderPatchLabel); + + // do header patch + yield return new CodeInstruction(OpCodes.Ldloc, uiBuilderLocalIndex); + yield return new CodeInstruction(OpCodes.Ldarg, 0); + yield return new CodeInstruction(OpCodes.Ldarg, 1); + yield return new CodeInstruction(OpCodes.Ldarg, 2); + yield return new CodeInstruction(OpCodes.Ldarg, 3); + yield return new CodeInstruction(OpCodes.Ldarg, 4); + yield return new CodeInstruction(OpCodes.Ldarg, 5); + yield return new CodeInstruction(OpCodes.Call, buildHeaderMethod); + + // skip original if did patch + yield return new CodeInstruction(OpCodes.Br, afterHeaderBranchLabel); + + // mark after patch (start of original) + yield return new CodeInstruction(OpCodes.Nop).WithLabels(afterHeaderPatchLabel); + isHeaderDone = true; + } + + // this patch calls OnBuildInspectorHeaderText unconditionally (even if there is no InspectorHeaderAttribute) + if (uiBuilderLocalIndex != -1 && afterHeaderTextBranchLabel is null && !isHeaderTextDone && + instruction.opcode == OpCodes.Ldloc_1 && + instArr[i - 1].opcode == OpCodes.Stloc_1 && + instArr[i - 2].Calls(getCustomAttributeMethod)) + { + afterHeaderTextBranchLabel = GetNextBranchLabel(instArr, i + 1); + + // check Enabled + yield return new CodeInstruction(OpCodes.Call, getEnabledMethod); + yield return new CodeInstruction(OpCodes.Brfalse, afterHeaderTextPatchLabel); + + // do header text patch + yield return new CodeInstruction(OpCodes.Ldloc, uiBuilderLocalIndex); + yield return new CodeInstruction(OpCodes.Ldarg, 1); + yield return new CodeInstruction(OpCodes.Call, buildHeaderTextMethod); + + // skip original if did patch + yield return new CodeInstruction(OpCodes.Br, afterHeaderTextBranchLabel); + + // mark after patch (start of original) + yield return new CodeInstruction(OpCodes.Nop).WithLabels(afterHeaderTextPatchLabel); + isHeaderTextDone = true; + } + + if (uiBuilderLocalIndex != -1 && instruction.opcode == OpCodes.Leave_S && + (instArr[i - 1].Calls(unilogErrorMethod) || instArr[i - 1].Calls(customInspectorBuildUiMethod))) + { + instruction.operand = beforeBodyPatchLabel; + } + + yield return instruction; + + if (uiBuilderLocalIndex != -1 && !isBodyDone && instruction.Calls(workerInspectorBuildUiMethod)) + { + // check Enabled + yield return new CodeInstruction(OpCodes.Call, getEnabledMethod).WithLabels(beforeBodyPatchLabel); + yield return new CodeInstruction(OpCodes.Brfalse, afterBodyPatchLabel); + + // do body patch + yield return new CodeInstruction(OpCodes.Ldloc, uiBuilderLocalIndex); + yield return new CodeInstruction(OpCodes.Ldarg, 0); + yield return new CodeInstruction(OpCodes.Ldarg, 1); + yield return new CodeInstruction(OpCodes.Ldarg, 2); + yield return new CodeInstruction(OpCodes.Ldarg, 3); + yield return new CodeInstruction(OpCodes.Ldarg, 4); + yield return new CodeInstruction(OpCodes.Ldarg, 5); + yield return new CodeInstruction(OpCodes.Call, buildBodyMethod); + + // there is no "original" in this case + + // mark after patch (start of original) + yield return new CodeInstruction(OpCodes.Nop).WithLabels(afterBodyPatchLabel); + isBodyDone = true; + } + } + } } } \ No newline at end of file