using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("HqExtra")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("HqExtra")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("c9dfa95d-75b1-4796-9f58-e9aa57cedc41")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace j_red { public class ModConfig { public ConfigEntry headBobbing; public ConfigEntry toggleSprint; } [BepInPlugin("asta.HqExtra", "HqExtra", "1.0.0")] public class ModBase : BaseUnityPlugin { private const string GUID = "asta.HqExtra"; private const string ModName = "HqExtra"; private const string ModVersion = "1.0.0"; private readonly Harmony harmony = new Harmony("asta.HqExtra"); private static ModBase Instance; public static ModConfig config; internal ManualLogSource logger; internal static ManualLogSource Log => Instance?.logger; private void Awake() { if ((Object)(object)Instance == (Object)null) { Instance = this; config = new ModConfig(); config.toggleSprint = ((BaseUnityPlugin)this).Config.Bind("General", "Toggle Sprint", false, "If sprinting should toggle on key press instead of requiring the key to be held."); config.headBobbing = ((BaseUnityPlugin)this).Config.Bind("General", "Head Bobbing", true, "If head bobbing should be enabled."); } logger = Logger.CreateLogSource("asta.HqExtra"); logger.LogInfo((object)"HqExtra v1.0.0 initialized."); harmony.PatchAll(); } } } namespace j_red.Patches { [HarmonyPatch(typeof(PlayerControllerB))] internal class PlayerControllerBPatch { public static Camera playerCam = null; private static float initialFov; private static readonly Vector3 cameraRotation = new Vector3(90f, 0f, 0f); [HarmonyPatch("Awake")] [HarmonyPostfix] private static void CacheCameraContainer(ref PlayerControllerB __instance) { Transform transform = ((Component)__instance).transform; playerCam = ((Component)transform.Find("ScavengerModel/metarig/CameraContainer/MainCamera")).GetComponent(); initialFov = playerCam.fieldOfView; } [HarmonyPatch("LateUpdate")] [HarmonyPrefix] private static void LateUpdatePatch(ref PlayerControllerB __instance) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) if (!__instance.inTerminalMenu && !__instance.inSpecialInteractAnimation && !__instance.playingQuickSpecialAnimation && !ModBase.config.headBobbing.Value) { __instance.cameraContainerTransform.position = new Vector3(__instance.cameraContainerTransform.position.x, ((Component)__instance.playerModelArmsMetarig).transform.position.y, __instance.cameraContainerTransform.position.z); __instance.cameraContainerTransform.localRotation = Quaternion.Euler(cameraRotation); } } } [HarmonyPatch] internal static class SettingsMenuPatch { private const string ClonePrefix = "Accessibility_"; private const float VerticalSpacing = 25f; private static bool hasLoggedSuccessfulInjection; private static bool hasLoggedMissingTemplate; private static readonly Dictionary, bool> PendingValues = new Dictionary, bool>(); [HarmonyPatch(typeof(QuickMenuManager), "OpenQuickMenu")] [HarmonyPostfix] private static void InjectSettingsOnQuickMenuOpen(QuickMenuManager __instance) { ManualLogSource log = ModBase.Log; if (log != null) { log.LogInfo((object)"QuickMenuManager.OpenQuickMenu called."); } object settingsPanelRoot; if (!((Object)(object)__instance != (Object)null)) { settingsPanelRoot = null; } else { GameObject settingsPanel = __instance.settingsPanel; settingsPanelRoot = ((settingsPanel != null) ? settingsPanel.transform : null); } EnsureInjected((Transform)settingsPanelRoot, "QuickMenu"); } [HarmonyPatch(typeof(MenuManager), "EnableUIPanel")] [HarmonyPostfix] private static void InjectSettingsOnEnablePanel(GameObject enablePanel) { if (!((Object)(object)enablePanel == (Object)null)) { ManualLogSource log = ModBase.Log; if (log != null) { log.LogInfo((object)("MenuManager.EnableUIPanel called for: " + ((Object)enablePanel).name)); } if (((Object)enablePanel).name == "SettingsPanel") { EnsureInjected(enablePanel.transform, "MainMenu"); } } } [HarmonyPatch(typeof(IngamePlayerSettings), "SaveChangedSettings")] [HarmonyPostfix] private static void ApplyPendingSettingsOnConfirm() { ApplyPendingValues(); } [HarmonyPatch(typeof(IngamePlayerSettings), "DiscardChangedSettings")] [HarmonyPostfix] private static void DiscardPendingSettingsOnCancel() { DiscardPendingValues(); } internal static void EnsureInjected(Transform settingsPanelRoot, string source) { if (ModBase.config == null || (Object)(object)settingsPanelRoot == (Object)null) { return; } GameObject val = FindTemplateToggle(settingsPanelRoot); if ((Object)(object)val == (Object)null) { if (!hasLoggedMissingTemplate) { ManualLogSource log = ModBase.Log; if (log != null) { log.LogInfo((object)("ControlsOptions template toggle not found in " + source + " settings panel.")); } DumpControlsOptionsCandidates(settingsPanelRoot); hasLoggedMissingTemplate = true; } return; } hasLoggedMissingTemplate = false; Transform parent = val.transform.parent; if ((Object)(object)parent == (Object)null) { return; } LayoutAnchor orCreateLayoutAnchor = GetOrCreateLayoutAnchor(val); float baseAnchoredY = orCreateLayoutAnchor.BaseAnchoredY; int baseSiblingIndex = orCreateLayoutAnchor.BaseSiblingIndex; CreateOrRefreshToggle(parent, val, "ToggleSprint", "Toggle Sprint", ModBase.config.toggleSprint, baseAnchoredY, baseSiblingIndex); CreateOrRefreshToggle(parent, val, "HeadBobbing", "Head Bobbing", ModBase.config.headBobbing, baseAnchoredY - 25f, baseSiblingIndex + 1); RepositionOriginal(val, baseAnchoredY - 50f, baseSiblingIndex + 2); RemoveInjectedToggle(parent, "LockFOV"); RemoveInjectedToggle(parent, "DisableMotionSway"); RectTransform val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (val2 != null) { LayoutRebuilder.ForceRebuildLayoutImmediate(val2); } if (!hasLoggedSuccessfulInjection) { ManualLogSource log2 = ModBase.Log; if (log2 != null) { log2.LogInfo((object)("Accessibility toggles injected under ControlsOptions from " + source + ".")); } hasLoggedSuccessfulInjection = true; } } private static GameObject FindTemplateToggle(Transform settingsPanelRoot) { Transform[] componentsInChildren = ((Component)settingsPanelRoot).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !(((Object)val).name != "ControlsOptions")) { GameObject val2 = FindPreferredTemplate(val); if ((Object)(object)val2 != (Object)null) { return val2; } } } return null; } private static GameObject FindPreferredTemplate(Transform controlsOptions) { GameObject val = null; for (int i = 0; i < controlsOptions.childCount; i++) { Transform child = controlsOptions.GetChild(i); if ((Object)(object)child == (Object)null) { continue; } SettingsOption component = ((Component)child).GetComponent(); TextMeshProUGUI componentInChildren = ((Component)child).GetComponentInChildren(true); if (!((Object)(object)component == (Object)null) && !((Object)(object)componentInChildren == (Object)null)) { if ((Object)(object)val == (Object)null) { val = ((Component)child).gameObject; } string text = ((Object)child).name ?? string.Empty; string text2 = ((TMP_Text)componentInChildren).text ?? string.Empty; if (text.Contains("Arachn") || text2.Contains("Arachn")) { return ((Component)child).gameObject; } } } return val; } private static void DumpControlsOptionsCandidates(Transform settingsPanelRoot) { Transform[] componentsInChildren = ((Component)settingsPanelRoot).GetComponentsInChildren(true); int num = 0; foreach (Transform val in componentsInChildren) { if ((Object)(object)val == (Object)null || ((Object)val).name != "ControlsOptions") { continue; } num++; ManualLogSource log = ModBase.Log; if (log != null) { log.LogInfo((object)("Found ControlsOptions: " + GetTransformPath(val))); } for (int j = 0; j < val.childCount; j++) { Transform child = val.GetChild(j); TextMeshProUGUI val2 = (((Object)(object)child != (Object)null) ? ((Component)child).GetComponentInChildren(true) : null); SettingsOption val3 = (((Object)(object)child != (Object)null) ? ((Component)child).GetComponent() : null); string text = (((Object)(object)val2 != (Object)null) ? ((TMP_Text)val2).text : ""); ManualLogSource log2 = ModBase.Log; if (log2 != null) { log2.LogInfo((object)("ControlsOptions child: " + ((child != null) ? ((Object)child).name : null) + " | text=" + text + " | settingsOption=" + ((Object)(object)val3 != (Object)null))); } } } ManualLogSource log3 = ModBase.Log; if (log3 != null) { log3.LogInfo((object)("ControlsOptions count: " + num)); } } private static string GetTransformPath(Transform transform) { string text = ((Object)transform).name; Transform parent = transform.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } private static void CreateOrRefreshToggle(Transform parent, GameObject original, string id, string label, ConfigEntry entry, float targetY, int targetSiblingIndex) { string text = "Accessibility_" + id; Transform val = parent.Find(text); if ((Object)(object)val != (Object)null) { ModSettingsToggle component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { component.Refresh(); } UpdatePosition(((Component)val).gameObject, original, targetY); val.SetSiblingIndex(Mathf.Max(0, targetSiblingIndex)); return; } GameObject val2 = Object.Instantiate(original, parent); ((Object)val2).name = text; SetLabel(val2, label); ReplaceToggleBehaviour(val2, entry, label); UpdatePosition(val2, original, targetY); val2.transform.SetSiblingIndex(Mathf.Max(0, targetSiblingIndex)); ManualLogSource log = ModBase.Log; if (log != null) { log.LogInfo((object)("Added settings toggle: " + text)); } } private static void RemoveInjectedToggle(Transform parent, string id) { Transform val = parent.Find("Accessibility_" + id); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } } private static void SetLabel(GameObject clone, string label) { TextMeshProUGUI[] componentsInChildren = clone.GetComponentsInChildren(true); if (componentsInChildren != null && componentsInChildren.Length != 0) { ((TMP_Text)componentsInChildren[0]).text = label; } } private static void ReplaceToggleBehaviour(GameObject clone, ConfigEntry entry, string label) { SettingsOption component = clone.GetComponent(); TMP_Text text = (TMP_Text)(object)(((Object)(object)component != (Object)null) ? component.textElement : clone.GetComponentInChildren(true)); Image val = (((Object)(object)component != (Object)null) ? component.toggleImage : FindBestToggleImage(clone)); Sprite enabled = (((Object)(object)component != (Object)null) ? component.enabledImage : null); Sprite disabled = (((Object)(object)component != (Object)null) ? component.disabledImage : null); SettingsOption[] componentsInChildren = clone.GetComponentsInChildren(true); foreach (SettingsOption val2 in componentsInChildren) { Object.DestroyImmediate((Object)(object)val2); } if ((Object)(object)val == (Object)null) { ManualLogSource log = ModBase.Log; if (log != null) { log.LogWarning((object)("Toggle image not found for " + label)); } return; } ModSettingsToggle modSettingsToggle = clone.GetComponent(); if ((Object)(object)modSettingsToggle == (Object)null) { modSettingsToggle = clone.AddComponent(); } modSettingsToggle.Initialize(entry, label, text, val, enabled, disabled); modSettingsToggle.Refresh(); } private static Image FindBestToggleImage(GameObject clone) { Image[] componentsInChildren = clone.GetComponentsInChildren(true); for (int num = componentsInChildren.Length - 1; num >= 0; num--) { Image val = componentsInChildren[num]; if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)(object)clone) { return val; } } return clone.GetComponentInChildren(true); } internal static bool GetCurrentValue(ConfigEntry entry) { if (entry == null) { return false; } bool value; return PendingValues.TryGetValue(entry, out value) ? value : entry.Value; } internal static void TogglePendingValue(ConfigEntry entry) { if (entry != null) { bool value = !GetCurrentValue(entry); PendingValues[entry] = value; MarkSettingsDirty(); RefreshAllInjectedToggles(); } } private static void ApplyPendingValues() { if (PendingValues.Count == 0) { return; } foreach (KeyValuePair, bool> pendingValue in PendingValues) { pendingValue.Key.Value = pendingValue.Value; ManualLogSource log = ModBase.Log; if (log != null) { log.LogInfo((object)("Applied setting on confirm: " + ((ConfigEntryBase)pendingValue.Key).Definition.Key + " -> " + pendingValue.Value)); } } PendingValues.Clear(); RefreshAllInjectedToggles(); } private static void DiscardPendingValues() { if (PendingValues.Count != 0) { ManualLogSource log = ModBase.Log; if (log != null) { log.LogInfo((object)"Discarded pending settings changes."); } PendingValues.Clear(); RefreshAllInjectedToggles(); } } private static void MarkSettingsDirty() { IngamePlayerSettings instance = IngamePlayerSettings.Instance; if (!((Object)(object)instance == (Object)null)) { Traverse.Create((object)instance).Field("changesNotApplied").SetValue((object)true); AccessTools.Method(typeof(IngamePlayerSettings), "SetChangesNotAppliedTextVisible", (Type[])null, (Type[])null)?.Invoke(instance, new object[1] { true }); } } private static void RefreshAllInjectedToggles() { ModSettingsToggle[] array = Object.FindObjectsOfType(true); for (int i = 0; i < array.Length; i++) { array[i].Refresh(); } } private static void RepositionOriginal(GameObject original, float targetY, int targetSiblingIndex) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) RectTransform component = original.GetComponent(); if (!((Object)(object)component == (Object)null)) { component.anchoredPosition = new Vector2(component.anchoredPosition.x, targetY); original.transform.SetSiblingIndex(Mathf.Max(0, targetSiblingIndex)); } } private static void UpdatePosition(GameObject clone, GameObject original, float targetY) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) RectTransform component = clone.GetComponent(); RectTransform component2 = original.GetComponent(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component2 == (Object)null)) { component.anchorMin = component2.anchorMin; component.anchorMax = component2.anchorMax; component.pivot = component2.pivot; component.sizeDelta = component2.sizeDelta; component.anchoredPosition = new Vector2(component2.anchoredPosition.x, targetY); } } private static LayoutAnchor GetOrCreateLayoutAnchor(GameObject original) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) LayoutAnchor component = original.GetComponent(); if ((Object)(object)component != (Object)null) { return component; } RectTransform component2 = original.GetComponent(); component = original.AddComponent(); component.BaseAnchoredY = (((Object)(object)component2 != (Object)null) ? component2.anchoredPosition.y : 0f); component.BaseSiblingIndex = original.transform.GetSiblingIndex(); return component; } } internal sealed class ModSettingsToggle : MonoBehaviour, IPointerClickHandler, IEventSystemHandler, ISubmitHandler { private ConfigEntry configEntry; private string label; private TMP_Text labelText; private Image toggleImage; private Sprite enabledSprite; private Sprite disabledSprite; internal void Initialize(ConfigEntry entry, string labelValue, TMP_Text text, Image image, Sprite enabled, Sprite disabled) { configEntry = entry; label = labelValue; labelText = text; toggleImage = image; enabledSprite = enabled; disabledSprite = disabled; } public void OnPointerClick(PointerEventData eventData) { ToggleValue(); } public void OnSubmit(BaseEventData eventData) { ToggleValue(); } private void ToggleValue() { if (configEntry != null) { SettingsMenuPatch.TogglePendingValue(configEntry); ManualLogSource log = ModBase.Log; if (log != null) { log.LogInfo((object)(label + " pending -> " + SettingsMenuPatch.GetCurrentValue(configEntry))); } } } internal void Refresh() { if (configEntry != null) { if ((Object)(object)labelText != (Object)null) { labelText.text = label; } if ((Object)(object)toggleImage != (Object)null) { bool currentValue = SettingsMenuPatch.GetCurrentValue(configEntry); toggleImage.sprite = (currentValue ? enabledSprite : disabledSprite); } } } private void OnEnable() { Refresh(); } } internal sealed class LayoutAnchor : MonoBehaviour { internal float BaseAnchoredY; internal int BaseSiblingIndex; } [HarmonyPatch(typeof(PlayerControllerB))] internal static class ToggleSprintPatch { private static readonly CodeSearch SprintLookupSearch = new CodeSearch(new CodeSearchDescriptor((CodeInstruction i) => i.opcode == OpCodes.Call && i.operand != null && i.operand.ToString().Contains("IngamePlayerSettings")), new CodeSearchDescriptor((CodeInstruction i) => i.opcode == OpCodes.Stloc_0), new CodeSearchDescriptor[3] { new CodeSearchDescriptor((CodeInstruction i) => i.opcode == OpCodes.Ldstr && i.operand != null && i.operand.ToString().Contains("Sprint")), new CodeSearchDescriptor((CodeInstruction i) => i.opcode == OpCodes.Callvirt && i.operand != null && i.operand.ToString().Contains("FindAction")), new CodeSearchDescriptor((CodeInstruction i) => i.opcode == OpCodes.Callvirt && i.operand != null && i.operand.ToString().Contains("ReadValue")) }); private static readonly Dictionary SprintToggled = new Dictionary(); private static readonly Dictionary WasPressedLastFrame = new Dictionary(); private static float GetSprintInput(PlayerControllerB instance) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)instance == (Object)null) { return 0f; } if (!ModBase.config.toggleSprint.Value || !SprintToggled.TryGetValue(instance, out var value) || !value) { MovementActions movement = instance.playerActions.Movement; return ((MovementActions)(ref movement)).Sprint.ReadValue(); } return 1f; } private static bool SprintIsTogglable(PlayerControllerB player) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)IngamePlayerSettings.Instance == (Object)null) { return false; } Vector2 val = IngamePlayerSettings.Instance.playerInput.actions.FindAction("Move", false).ReadValue(); if (((Vector2)(ref val)).sqrMagnitude < 0.05f) { return false; } if (player.inTerminalMenu || player.isTypingChat || player.inSpecialInteractAnimation || player.playingQuickSpecialAnimation) { return false; } return true; } [HarmonyPatch("Start")] [HarmonyPostfix] private static void StartPostfix(PlayerControllerB __instance) { SprintToggled[__instance] = false; WasPressedLastFrame[__instance] = false; } [HarmonyPatch("OnDisable")] [HarmonyPostfix] private static void OnDisablePostfix(PlayerControllerB __instance) { SprintToggled.Remove(__instance); WasPressedLastFrame.Remove(__instance); } [HarmonyPatch("Update")] [HarmonyPrefix] private static void UpdatePrefix(PlayerControllerB __instance) { if ((Object)(object)__instance == (Object)null || !__instance.isPlayerControlled) { return; } if (!SprintToggled.ContainsKey(__instance)) { SprintToggled[__instance] = false; } if (!WasPressedLastFrame.ContainsKey(__instance)) { WasPressedLastFrame[__instance] = false; } if (!ModBase.config.toggleSprint.Value) { SprintToggled[__instance] = false; WasPressedLastFrame[__instance] = false; return; } if (!SprintIsTogglable(__instance)) { if (SprintToggled[__instance]) { SprintToggled[__instance] = false; WasPressedLastFrame[__instance] = false; } return; } float num = IngamePlayerSettings.Instance.playerInput.actions.FindAction("Sprint", false).ReadValue(); if (num > 0.3f) { if (!WasPressedLastFrame[__instance]) { SprintToggled[__instance] = !SprintToggled[__instance]; WasPressedLastFrame[__instance] = true; ManualLogSource log = ModBase.Log; if (log != null) { log.LogInfo((object)("Toggle Sprint -> " + SprintToggled[__instance])); } } } else { WasPressedLastFrame[__instance] = false; } } [HarmonyPatch("Update")] [HarmonyTranspiler] private static IEnumerable UpdateTranspiler(IEnumerable instructions) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown List list = new List(instructions); Tuple tuple = SprintLookupSearch.FindPatch(list); if (tuple == null) { ManualLogSource log = ModBase.Log; if (log != null) { log.LogWarning((object)"Toggle Sprint transpiler could not find sprint lookup in PlayerControllerB.Update."); } return list; } for (int i = tuple.Item1; i < tuple.Item2; i++) { list[i].opcode = OpCodes.Nop; list[i].operand = null; } list[tuple.Item2 - 2] = new CodeInstruction(OpCodes.Ldarg_0, (object)null); list[tuple.Item2 - 1] = new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(ToggleSprintPatch), "GetSprintInput", (Type[])null, (Type[])null)); ManualLogSource log2 = ModBase.Log; if (log2 != null) { log2.LogInfo((object)"Toggle Sprint transpiler patched PlayerControllerB.Update."); } return list; } } internal sealed class CodeSearchDescriptor { private readonly Func matchFunction; internal CodeSearchDescriptor(Func matchFunction) { this.matchFunction = matchFunction ?? throw new ArgumentNullException("matchFunction"); } internal bool Matches(CodeInstruction instruction) { return matchFunction(instruction); } } internal sealed class CodeSearch { private readonly CodeSearchDescriptor start; private readonly CodeSearchDescriptor end; private readonly IReadOnlyList validators; internal CodeSearch(CodeSearchDescriptor start, CodeSearchDescriptor end, IReadOnlyList validators) { this.start = start ?? throw new ArgumentNullException("start"); this.end = end ?? throw new ArgumentNullException("end"); this.validators = validators; } internal Tuple FindPatch(List instructions) { int? num = null; int? num2 = null; int num3 = 0; if (instructions == null) { return null; } for (int i = 0; i < instructions.Count; i++) { CodeInstruction val = instructions[i]; if (val != null) { if (start.Matches(val)) { num = i; num3 = 0; } if (num.HasValue && validators != null && num3 < validators.Count && validators[num3].Matches(val)) { num3++; } if (end.Matches(val) && num.HasValue && (validators == null || num3 >= validators.Count)) { num2 = i; break; } } } return (num.HasValue && num2.HasValue) ? new Tuple(num.Value, num2.Value) : null; } } }