Decompiled source of SteveFixes v1.2.8

BepInEx\patchers\SteveFixes\SteveFixes.Preloader.dll

Decompiled 7 hours ago
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using BepInEx;
using BepInEx.Logging;
using Mono.Cecil;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace SteveFixesPreloader;

public static class MacronBinaryCompatibilityPatcher
{
	private sealed class BytePatch
	{
		public readonly int Offset;

		public readonly byte[] Before;

		public readonly byte[] After;

		public readonly string Name;

		public BytePatch(int offset, byte[] before, byte[] after, string name)
		{
			Offset = offset;
			Before = before;
			After = after;
			Name = name;
		}

		public void Validate(byte[] bytes)
		{
			if (Offset < 0 || Offset + Before.Length > bytes.Length)
			{
				throw new InvalidOperationException("Macron byte patch outside file bounds: " + Name);
			}
			for (int i = 0; i < Before.Length; i++)
			{
				if (bytes[Offset + i] != Before[i])
				{
					throw new InvalidOperationException("Macron byte guard failed for: " + Name + ". No changes were written.");
				}
			}
		}

		public void Apply(byte[] bytes)
		{
			for (int i = 0; i < After.Length; i++)
			{
				bytes[Offset + i] = After[i];
			}
		}
	}

	private const string OriginalHash = "e46f6cfcedc69b46623334a3bc8664831c34e8bb796bbd1d10bf1360185c5b9e";

	private const string CompatV122Hash = "11a769576d504c96949b84063ed34e57b8e43175ef46476eaa6fbd5273dc986a";

	private const string CompatV123Hash = "5033f0fe3d4d29ad729e2613bcb1b5da6d5c2b114e553f4ddfd533a0ddae535b";

	private const string Death9Hash = "bced1e1786954f4be0711d4cdc5b2cba0108c169231d9c18aaaf8b2e073427bd";

	private const string LegacyCustomHash = "c9655c4d53cc1a276ae0326f640d5727374706469c27db3cb0e42fd412f78216";

	private static ManualLogSource Log;

	public static IEnumerable<string> TargetDLLs
	{
		get
		{
			yield break;
		}
	}

	public static void Initialize()
	{
		Log = Logger.CreateLogSource("Steve Fixes Preloader");
		try
		{
			PatchMacronIfKnown();
		}
		catch (Exception ex)
		{
			Log.LogError((object)("[SteveFixes][Preloader] Macron preloader failed safely; file left as-is: " + ex));
		}
	}

	public static void Patch(AssemblyDefinition assembly)
	{
	}

	public static void Finish()
	{
	}

	private static void PatchMacronIfKnown()
	{
		string pluginPath = Paths.PluginPath;
		if (string.IsNullOrEmpty(pluginPath) || !Directory.Exists(pluginPath))
		{
			Log.LogWarning((object)"[SteveFixes][Preloader] BepInEx plugin path not found; Macron patch skipped.");
			return;
		}
		string[] files = Directory.GetFiles(pluginPath, "MacronMod.dll", SearchOption.AllDirectories);
		if (files.Length == 0)
		{
			Log.LogWarning((object)"[SteveFixes][Preloader] MacronMod.dll not found; Macron binary compatibility patch inactive.");
			return;
		}
		if (files.Length != 1)
		{
			Log.LogError((object)("[SteveFixes][Preloader] Found " + files.Length + " MacronMod.dll files. Refusing to patch any of them."));
			return;
		}
		string text = files[0];
		string text2 = Sha256(text);
		Log.LogInfo((object)("[SteveFixes][Preloader] MacronMod.dll SHA256=" + text2));
		if (text2 == "bced1e1786954f4be0711d4cdc5b2cba0108c169231d9c18aaaf8b2e073427bd")
		{
			Log.LogInfo((object)"[SteveFixes][Preloader] MACRON_BINARY_READY v81=True deathDelay=9s alreadyPatched=True");
			return;
		}
		if (text2 == "c9655c4d53cc1a276ae0326f640d5727374706469c27db3cb0e42fd412f78216")
		{
			Log.LogWarning((object)"[SteveFixes][Preloader] Legacy custom Macron DLL detected. Leaving it untouched; use original MacronEnemy 1.2.0 for the supported path.");
			return;
		}
		if (text2 != "e46f6cfcedc69b46623334a3bc8664831c34e8bb796bbd1d10bf1360185c5b9e" && text2 != "11a769576d504c96949b84063ed34e57b8e43175ef46476eaa6fbd5273dc986a" && text2 != "5033f0fe3d4d29ad729e2613bcb1b5da6d5c2b114e553f4ddfd533a0ddae535b")
		{
			Log.LogWarning((object)"[SteveFixes][Preloader] Unknown MacronMod.dll build. Checksum guard refused the binary patch; file left untouched.");
			return;
		}
		byte[] bytes = File.ReadAllBytes(text);
		List<BytePatch> list = new List<BytePatch>();
		if (text2 == "e46f6cfcedc69b46623334a3bc8664831c34e8bb796bbd1d10bf1360185c5b9e")
		{
			list.Add(new BytePatch(3139, new byte[5] { 40, 45, 0, 0, 10 }, new byte[5] { 38, 38, 38, 0, 0 }, "MacronAI.EnableEnemyMesh obsolete base call"));
			list.Add(new BytePatch(8243, new byte[5] { 111, 45, 0, 0, 10 }, new byte[5] { 111, 16, 0, 0, 6 }, "DespawnBody -> MacronAI.EnableEnemyMesh"));
			list.Add(new BytePatch(8826, new byte[5] { 111, 45, 0, 0, 10 }, new byte[5] { 111, 16, 0, 0, 6 }, "SelfActivate -> MacronAI.EnableEnemyMesh"));
		}
		else if (text2 == "11a769576d504c96949b84063ed34e57b8e43175ef46476eaa6fbd5273dc986a")
		{
			list.Add(new BytePatch(8243, new byte[5] { 111, 45, 0, 0, 10 }, new byte[5] { 111, 16, 0, 0, 6 }, "DespawnBody -> MacronAI.EnableEnemyMesh"));
		}
		list.Add(new BytePatch(5256, new byte[7] { 2, 3, 40, 94, 0, 0, 10 }, new byte[7] { 2, 23, 125, 48, 0, 0, 10 }, "KillEnemy: defer base death via isEnemyDead"));
		list.Add(new BytePatch(7993, new byte[5] { 34, 0, 0, 224, 64 }, new byte[5] { 34, 0, 0, 16, 65 }, "DespawnBody wait: 7s -> 9s"));
		list.Add(new BytePatch(8022, new byte[31]
		{
			0, 4, 2, 34, 0, 0, 64, 64, 125, 34,
			0, 0, 4, 2, 34, 0, 0, 0, 0, 125,
			35, 0, 0, 4, 56, 172, 0, 0, 0, 2,
			2
		}, new byte[31]
		{
			0, 4, 7, 22, 125, 48, 0, 0, 10, 7,
			22, 40, 94, 0, 0, 10, 56, 197, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
			2
		}, "DespawnBody: exact delayed base-death finalization"));
		foreach (BytePatch item in list)
		{
			item.Validate(bytes);
		}
		string text3 = text + ".stevefixes-original.bak";
		if (text2 == "e46f6cfcedc69b46623334a3bc8664831c34e8bb796bbd1d10bf1360185c5b9e" && !File.Exists(text3))
		{
			File.Copy(text, text3, overwrite: false);
		}
		if (text2 == "5033f0fe3d4d29ad729e2613bcb1b5da6d5c2b114e553f4ddfd533a0ddae535b")
		{
			string text4 = text + ".stevefixes-v123-predeath.bak";
			if (!File.Exists(text4))
			{
				File.Copy(text, text4, overwrite: false);
			}
		}
		foreach (BytePatch item2 in list)
		{
			item2.Apply(bytes);
			Log.LogDebug((object)("[SteveFixes][Preloader] Patched: " + item2.Name));
		}
		string text5 = text + ".stevefixes.tmp";
		try
		{
			File.WriteAllBytes(text5, bytes);
			string text6 = Sha256(text5);
			if (text6 != "bced1e1786954f4be0711d4cdc5b2cba0108c169231d9c18aaaf8b2e073427bd")
			{
				throw new InvalidOperationException("Patched Macron hash mismatch: " + text6);
			}
			File.Copy(text5, text, overwrite: true);
		}
		finally
		{
			if (File.Exists(text5))
			{
				File.Delete(text5);
			}
		}
		Log.LogInfo((object)"[SteveFixes][Preloader] MACRON_BINARY_READY v81=True deathDelay=9s alreadyPatched=False");
	}

	private static string Sha256(string path)
	{
		using SHA256 sHA = SHA256.Create();
		using FileStream inputStream = File.OpenRead(path);
		byte[] array = sHA.ComputeHash(inputStream);
		return BitConverter.ToString(array).Replace("-", "").ToLowerInvariant();
	}
}

BepInEx\plugins\SteveFixes\SteveFixes.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace SteveFixesMod;

[BepInPlugin("boogie.stevefixes", "Steve Fixes", "1.2.8")]
public sealed class SteveFixes : BaseUnityPlugin
{
	private sealed class MacronState
	{
		public bool ShovelAlreadySpawned;

		public bool HitPointsInitialized;

		public int DanceIndex = -1;
	}

	private sealed class LevelBaseline
	{
		public int LevelInstanceId;

		public string StableKey;

		public string PlanetName;

		public readonly List<EnemySnapshot> Entries = new List<EnemySnapshot>();
	}

	private sealed class EnemySnapshot
	{
		public EnemyType EnemyType;

		public string EnemyName;

		public int Rarity;
	}

	public const string PluginGuid = "boogie.stevefixes";

	public const string PluginName = "Steve Fixes";

	public const string PluginVersion = "1.2.8";

	private const float PollInterval = 0.2f;

	private const float FirstBaselineDelay = 0.75f;

	private const float RepairPass1Delay = 0.15f;

	private const float RepairPass2Delay = 1f;

	private const float RepairPass3Delay = 3f;

	private const float CurrentLevelSanityInterval = 1f;

	private ConfigEntry<bool> enemyPoolLifecycleFixEnabled;

	private ConfigEntry<bool> verboseLogging;

	private ConfigEntry<bool> lllBundleStatusLifecycleFixEnabled;

	private ConfigEntry<bool> lllVerboseLogging;

	private ConfigEntry<bool> macronCompatibilityFixEnabled;

	private ConfigEntry<int> macronHitsToKill;

	private float nextPoll;

	private float nextCurrentSanity;

	private int lastSorInstanceId = int.MinValue;

	private int lastCurrentLevelInstanceId = int.MinValue;

	private int lobbyGeneration;

	private bool baselineCaptured;

	private float baselineCaptureAt = -1f;

	private int repairStage;

	private float nextRepairAt = -1f;

	private static ManualLogSource SharedLog;

	private static Type lllNetworkBundleManagerType;

	private static MethodInfo lllRefreshLoadStatusMethod;

	private static MemberInfo lllInstanceMember;

	private static bool lllPatchInstalled;

	private static bool lllVerbose;

	private static float lastLllRedirectAt = -100f;

	private Harmony harmony;

	private static Type macronAiType;

	private static MethodInfo macronSpawnShovelMethod;

	private static MethodInfo macronHitEnemyMethod;

	private static MethodInfo macronPlayDanceMusicMethod;

	private static MethodInfo macronPlayMusicMethod;

	private static MethodInfo macronNetworkObjectIdGetter;

	private static FieldInfo macronEnemyHpField;

	private static int macronConfiguredHitsToKill = 8;

	private static bool macronPatchInstalled;

	private static bool macronDancePatchInstalled;

	private static bool macronDancePlaylistReady;

	private static int macronDanceClipCount;

	private static float nextMacronPatchAttempt;

	private static readonly AudioClip[] MacronDanceClips = (AudioClip[])(object)new AudioClip[10];

	private static readonly ConditionalWeakTable<object, MacronState> MacronStates = new ConditionalWeakTable<object, MacronState>();

	private Harmony macronHarmony;

	private readonly Dictionary<int, LevelBaseline> baselineByInstanceId = new Dictionary<int, LevelBaseline>();

	private readonly Dictionary<string, LevelBaseline> baselineByStableKey = new Dictionary<string, LevelBaseline>(StringComparer.OrdinalIgnoreCase);

	private static readonly HashSet<string> LethalLibBlock = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
	{
		"Locker", "ImmortalSnail", "Macron", "Light Eater", "Doctor's Brain", "MysteryButton", "SCP 106", "SCP 939", "scp1507", "scp1507Alpha",
		"Shy guy", "CountryRoadCreature", "Cabinet", "Scary Lamp", "Thing"
	};

	private void Awake()
	{
		enemyPoolLifecycleFixEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("EnemyPoolLifecycleFix", "Enabled", true, "Restore only missing LethalLib enemy entries after recreating a lobby in the same game process.");
		verboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("EnemyPoolLifecycleFix", "VerboseLogging", false, "Log individual repaired moon names in addition to summary lines.");
		lllBundleStatusLifecycleFixEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("LLLBundleStatusLifecycleFix", "Enabled", true, "Reroute stale LethalLevelLoader NetworkBundleManager refresh callbacks to the current spawned manager after lobby recreation.");
		lllVerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("LLLBundleStatusLifecycleFix", "VerboseLogging", false, "Log every LethalLevelLoader bundle-status refresh decision. Leave disabled for normal play.");
		macronCompatibilityFixEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("MacronCompatibilityFix", "Enabled", true, "Enable the pack-specific Macron Enemy v81 compatibility/runtime fixes.");
		macronHitsToKill = ((BaseUnityPlugin)this).Config.Bind<int>("MacronCompatibilityFix", "HitsToKill", 8, "Base Macron HP in shovel-force units. 8 means eight force=1 hits; stronger damage upgrades naturally reduce the hit count.");
		macronConfiguredHitsToKill = Math.Max(1, Math.Min(100, macronHitsToKill.Value));
		SharedLog = ((BaseUnityPlugin)this).Logger;
		lllVerbose = lllVerboseLogging.Value;
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[SteveFixes] Loaded v1.2.8. EnemyPoolLifecycleFix=" + enemyPoolLifecycleFixEnabled.Value + " LLLBundleStatusLifecycleFix=" + lllBundleStatusLifecycleFixEnabled.Value + " MacronCompatibilityFix=" + macronCompatibilityFixEnabled.Value));
		((BaseUnityPlugin)this).Logger.LogInfo((object)"[SteveFixes] EnemyPoolLifecycleFix is the validated v1.0.2 implementation (15/15 restoration, idempotent).");
		if (lllBundleStatusLifecycleFixEnabled.Value)
		{
			InstallLllBundleStatusPatch();
		}
		nextMacronPatchAttempt = 0f;
	}

	private void Update()
	{
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		if (macronCompatibilityFixEnabled.Value && !macronPatchInstalled && realtimeSinceStartup >= nextMacronPatchAttempt)
		{
			nextMacronPatchAttempt = realtimeSinceStartup + 1f;
			TryInstallMacronRuntimePatches();
		}
		if (!enemyPoolLifecycleFixEnabled.Value || realtimeSinceStartup < nextPoll)
		{
			return;
		}
		nextPoll = realtimeSinceStartup + 0.2f;
		StartOfRound instance = StartOfRound.Instance;
		if ((Object)(object)instance == (Object)null)
		{
			return;
		}
		int num = SafeInstanceId((Object)(object)instance);
		if (num != lastSorInstanceId)
		{
			OnStartOfRoundChanged(instance, num, realtimeSinceStartup);
		}
		if (!baselineCaptured)
		{
			if (baselineCaptureAt > 0f && realtimeSinceStartup >= baselineCaptureAt)
			{
				CaptureBaseline(instance);
			}
			return;
		}
		if (lobbyGeneration >= 2 && repairStage > 0 && nextRepairAt > 0f && realtimeSinceStartup >= nextRepairAt)
		{
			RepairAllLevels(instance, "new-lobby-pass-" + repairStage);
			if (repairStage == 1)
			{
				repairStage = 2;
				nextRepairAt = realtimeSinceStartup + 0.85f;
			}
			else if (repairStage == 2)
			{
				repairStage = 3;
				nextRepairAt = realtimeSinceStartup + 2f;
			}
			else
			{
				repairStage = 0;
				nextRepairAt = -1f;
			}
		}
		if (lobbyGeneration < 2 || !(realtimeSinceStartup >= nextCurrentSanity))
		{
			return;
		}
		nextCurrentSanity = realtimeSinceStartup + 1f;
		SelectableLevel currentLevel = instance.currentLevel;
		int num2 = SafeInstanceId((Object)(object)currentLevel);
		if (num2 != lastCurrentLevelInstanceId)
		{
			lastCurrentLevelInstanceId = num2;
			if ((Object)(object)currentLevel != (Object)null)
			{
				RepairLevel(currentLevel, "current-level-change", logWhenRepaired: true);
			}
		}
		else if ((Object)(object)currentLevel != (Object)null)
		{
			RepairLevel(currentLevel, "current-level-sanity", logWhenRepaired: false);
		}
	}

	private void OnStartOfRoundChanged(StartOfRound sor, int sorId, float now)
	{
		int num = lastSorInstanceId;
		lastSorInstanceId = sorId;
		lastCurrentLevelInstanceId = int.MinValue;
		lobbyGeneration++;
		((BaseUnityPlugin)this).Logger.LogWarning((object)("[SteveFixes] StartOfRound changed " + num + " -> " + sorId + "; lobbyGeneration=" + lobbyGeneration));
		if (!baselineCaptured)
		{
			baselineCaptureAt = now + 0.75f;
			repairStage = 0;
			nextRepairAt = -1f;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[SteveFixes] First lobby detected. Baseline capture scheduled in " + 0.75f.ToString("0.00") + "s."));
		}
		else
		{
			repairStage = 1;
			nextRepairAt = now + 0.15f;
			nextCurrentSanity = now + 0.5f;
			((BaseUnityPlugin)this).Logger.LogWarning((object)"[SteveFixes] Later lobby detected. Enemy pool repair armed.");
		}
	}

	private void CaptureBaseline(StartOfRound sor)
	{
		baselineCaptureAt = -1f;
		if ((Object)(object)sor == (Object)null || sor.levels == null || sor.levels.Length == 0)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"[SteveFixes] Baseline capture delayed: StartOfRound.levels unavailable.");
			baselineCaptureAt = Time.realtimeSinceStartup + 1f;
			return;
		}
		baselineByInstanceId.Clear();
		baselineByStableKey.Clear();
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		for (int i = 0; i < sor.levels.Length; i++)
		{
			SelectableLevel val = sor.levels[i];
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			num++;
			LevelBaseline levelBaseline = BuildLevelBaseline(val);
			if (levelBaseline != null && levelBaseline.Entries.Count != 0)
			{
				num2++;
				num3 += levelBaseline.Entries.Count;
				int num4 = SafeInstanceId((Object)(object)val);
				if (num4 != 0)
				{
					baselineByInstanceId[num4] = levelBaseline;
				}
				string text = StableLevelKey(val);
				if (!string.IsNullOrEmpty(text) && !baselineByStableKey.ContainsKey(text))
				{
					baselineByStableKey.Add(text, levelBaseline);
				}
			}
		}
		baselineCaptured = num3 > 0;
		if (!baselineCaptured)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"[SteveFixes] Baseline capture found ZERO tracked LethalLib entries. Fix will stay disarmed and retry in 1s.");
			baselineCaptureAt = Time.realtimeSinceStartup + 1f;
			return;
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[SteveFixes] BASELINE_CAPTURED levelsSeen=" + num + " levelsWithLL=" + num2 + " entries=" + num3 + " trackedNames=" + LethalLibBlock.Count));
	}

	private LevelBaseline BuildLevelBaseline(SelectableLevel level)
	{
		if ((Object)(object)level == (Object)null || level.Enemies == null)
		{
			return null;
		}
		LevelBaseline levelBaseline = new LevelBaseline();
		levelBaseline.LevelInstanceId = SafeInstanceId((Object)(object)level);
		levelBaseline.StableKey = StableLevelKey(level);
		levelBaseline.PlanetName = Safe(level.PlanetName);
		for (int i = 0; i < level.Enemies.Count; i++)
		{
			SpawnableEnemyWithRarity val = level.Enemies[i];
			if (val != null && !((Object)(object)val.enemyType == (Object)null))
			{
				string text = Safe(val.enemyType.enemyName);
				if (LethalLibBlock.Contains(text))
				{
					EnemySnapshot enemySnapshot = new EnemySnapshot();
					enemySnapshot.EnemyType = val.enemyType;
					enemySnapshot.EnemyName = text;
					enemySnapshot.Rarity = val.rarity;
					levelBaseline.Entries.Add(enemySnapshot);
				}
			}
		}
		return levelBaseline;
	}

	private void RepairAllLevels(StartOfRound sor, string reason)
	{
		if ((Object)(object)sor == (Object)null || sor.levels == null)
		{
			return;
		}
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		for (int i = 0; i < sor.levels.Length; i++)
		{
			SelectableLevel val = sor.levels[i];
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			LevelBaseline levelBaseline = FindBaseline(val);
			if (levelBaseline == null || levelBaseline.Entries.Count == 0)
			{
				continue;
			}
			num++;
			int num4 = RepairLevelWithBaseline(val, levelBaseline);
			if (num4 > 0)
			{
				num2++;
				num3 += num4;
				if (verboseLogging.Value)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("[SteveFixes] REPAIRED planet='" + Safe(val.PlanetName) + "' added=" + num4 + " reason=" + reason));
				}
			}
		}
		if (num3 > 0)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("[SteveFixes] REPAIR_SUMMARY reason=" + reason + " checked=" + num + " repairedLevels=" + num2 + " entriesAdded=" + num3));
		}
		else
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[SteveFixes] REPAIR_SUMMARY reason=" + reason + " checked=" + num + " repairedLevels=0 entriesAdded=0"));
		}
	}

	private int RepairLevel(SelectableLevel level, string reason, bool logWhenRepaired)
	{
		LevelBaseline levelBaseline = FindBaseline(level);
		if (levelBaseline == null || levelBaseline.Entries.Count == 0)
		{
			return 0;
		}
		int num = RepairLevelWithBaseline(level, levelBaseline);
		if (num > 0 && (logWhenRepaired || verboseLogging.Value))
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("[SteveFixes] REPAIRED_CURRENT planet='" + Safe(level.PlanetName) + "' added=" + num + " reason=" + reason));
		}
		return num;
	}

	private int RepairLevelWithBaseline(SelectableLevel level, LevelBaseline baseline)
	{
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Expected O, but got Unknown
		if ((Object)(object)level == (Object)null || baseline == null || level.Enemies == null)
		{
			return 0;
		}
		int num = 0;
		for (int i = 0; i < baseline.Entries.Count; i++)
		{
			EnemySnapshot enemySnapshot = baseline.Entries[i];
			if (enemySnapshot != null && !((Object)(object)enemySnapshot.EnemyType == (Object)null) && !ContainsEnemy(level.Enemies, enemySnapshot))
			{
				SpawnableEnemyWithRarity item = new SpawnableEnemyWithRarity(enemySnapshot.EnemyType, enemySnapshot.Rarity);
				level.Enemies.Add(item);
				num++;
			}
		}
		return num;
	}

	private bool ContainsEnemy(List<SpawnableEnemyWithRarity> list, EnemySnapshot expected)
	{
		if (list == null || expected == null)
		{
			return false;
		}
		for (int i = 0; i < list.Count; i++)
		{
			SpawnableEnemyWithRarity val = list[i];
			if (val != null && !((Object)(object)val.enemyType == (Object)null))
			{
				if (object.ReferenceEquals(val.enemyType, expected.EnemyType))
				{
					return true;
				}
				string a = Safe(val.enemyType.enemyName);
				if (!string.IsNullOrEmpty(expected.EnemyName) && string.Equals(a, expected.EnemyName, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
		}
		return false;
	}

	private LevelBaseline FindBaseline(SelectableLevel level)
	{
		if ((Object)(object)level == (Object)null)
		{
			return null;
		}
		int num = SafeInstanceId((Object)(object)level);
		if (num != 0 && baselineByInstanceId.TryGetValue(num, out var value))
		{
			return value;
		}
		string text = StableLevelKey(level);
		if (!string.IsNullOrEmpty(text) && baselineByStableKey.TryGetValue(text, out value))
		{
			return value;
		}
		return null;
	}

	private void InstallLllBundleStatusPatch()
	{
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Expected O, but got Unknown
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Expected O, but got Unknown
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Expected O, but got Unknown
		try
		{
			lllNetworkBundleManagerType = AccessTools.TypeByName("LethalLevelLoader.NetworkBundleManager");
			if (lllNetworkBundleManagerType == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"[SteveFixes][LLL] NetworkBundleManager type not found. LLL bundle-status fix disabled for this process.");
				return;
			}
			lllRefreshLoadStatusMethod = AccessTools.Method(lllNetworkBundleManagerType, "RefreshLoadStatus", (Type[])null, (Type[])null);
			if (lllRefreshLoadStatusMethod == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"[SteveFixes][LLL] RefreshLoadStatus method not found. LLL bundle-status fix disabled for this process.");
				return;
			}
			PropertyInfo property = lllNetworkBundleManagerType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				lllInstanceMember = property;
			}
			else
			{
				FieldInfo field = lllNetworkBundleManagerType.GetField("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field == null)
				{
					field = lllNetworkBundleManagerType.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				lllInstanceMember = field;
			}
			harmony = new Harmony("boogie.stevefixes.lll_bundle_status");
			HarmonyMethod val = new HarmonyMethod(typeof(SteveFixes).GetMethod("LllRefreshLoadStatusPrefix", BindingFlags.Static | BindingFlags.NonPublic));
			HarmonyMethod val2 = new HarmonyMethod(typeof(SteveFixes).GetMethod("LllRefreshLoadStatusFinalizer", BindingFlags.Static | BindingFlags.NonPublic));
			harmony.Patch((MethodBase)lllRefreshLoadStatusMethod, val, (HarmonyMethod)null, (HarmonyMethod)null, val2, (HarmonyMethod)null);
			lllPatchInstalled = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[SteveFixes][LLL] LLL_BUNDLE_STATUS_PATCH_READY method=" + lllNetworkBundleManagerType.FullName + "." + lllRefreshLoadStatusMethod.Name + " singletonMember=" + ((lllInstanceMember == null) ? "<fallback-scan>" : lllInstanceMember.Name)));
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[SteveFixes][LLL] Failed to install bundle-status patch: " + ex));
		}
	}

	private static bool LllRefreshLoadStatusPrefix(object __instance)
	{
		if (!lllPatchInstalled || __instance == null)
		{
			return true;
		}
		try
		{
			object obj = ResolveCurrentLllNetworkBundleManager(__instance);
			if (obj == null || object.ReferenceEquals(obj, __instance))
			{
				if (lllVerbose && SharedLog != null)
				{
					SharedLog.LogInfo((object)("[SteveFixes][LLL] REFRESH_OK manager=" + DescribeLllManager(__instance)));
				}
				return true;
			}
			if (!IsUsableLllManager(obj))
			{
				return true;
			}
			if (SharedLog != null)
			{
				SharedLog.LogWarning((object)("[SteveFixes][LLL] STALE_REFRESH_REDIRECT stale=" + DescribeLllManager(__instance) + " current=" + DescribeLllManager(obj)));
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup - lastLllRedirectAt > 0.05f)
			{
				lastLllRedirectAt = realtimeSinceStartup;
				lllRefreshLoadStatusMethod.Invoke(obj, null);
			}
			else if (lllVerbose && SharedLog != null)
			{
				SharedLog.LogInfo((object)"[SteveFixes][LLL] STALE_REFRESH_REDIRECT coalesced duplicate callback.");
			}
			return false;
		}
		catch (Exception ex)
		{
			if (SharedLog != null)
			{
				SharedLog.LogError((object)("[SteveFixes][LLL] Prefix failed open; allowing original RefreshLoadStatus. " + Unwrap(ex)));
			}
			return true;
		}
	}

	private static Exception LllRefreshLoadStatusFinalizer(object __instance, Exception __exception)
	{
		if (__exception == null || !lllPatchInstalled || __instance == null)
		{
			return __exception;
		}
		Exception ex = Unwrap(__exception);
		if (!(ex is KeyNotFoundException))
		{
			return __exception;
		}
		try
		{
			object obj = ResolveCurrentLllNetworkBundleManager(__instance);
			if (obj == null || object.ReferenceEquals(obj, __instance) || !IsUsableLllManager(obj))
			{
				if (SharedLog != null)
				{
					SharedLog.LogError((object)("[SteveFixes][LLL] CURRENT_MANAGER_RPC_FAILURE preserved: " + ex.Message));
				}
				return __exception;
			}
			if (SharedLog != null)
			{
				SharedLog.LogWarning((object)("[SteveFixes][LLL] STALE_RPC_RECOVERY retrying RefreshLoadStatus on current manager after: " + ex.Message));
			}
			lllRefreshLoadStatusMethod.Invoke(obj, null);
			return null;
		}
		catch (Exception ex2)
		{
			if (SharedLog != null)
			{
				SharedLog.LogError((object)("[SteveFixes][LLL] STALE_RPC_RECOVERY failed: " + Unwrap(ex2)));
			}
			return __exception;
		}
	}

	private static object ResolveCurrentLllNetworkBundleManager(object invokingInstance)
	{
		object obj = ReadLllSingleton();
		if (IsUsableLllManager(obj))
		{
			return obj;
		}
		if (lllNetworkBundleManagerType != null)
		{
			Object[] array = Resources.FindObjectsOfTypeAll(lllNetworkBundleManagerType);
			object obj2 = null;
			foreach (object obj3 in array)
			{
				if (IsUnityAlive(obj3))
				{
					if (obj2 == null)
					{
						obj2 = obj3;
					}
					if (ReadBool(obj3, "IsSpawned") == true)
					{
						return obj3;
					}
				}
			}
			if (obj2 != null && object.ReferenceEquals(obj2, invokingInstance))
			{
				return invokingInstance;
			}
			return obj2;
		}
		return null;
	}

	private static object ReadLllSingleton()
	{
		if (lllInstanceMember == null)
		{
			return null;
		}
		try
		{
			PropertyInfo propertyInfo = lllInstanceMember as PropertyInfo;
			if (propertyInfo != null)
			{
				return propertyInfo.GetValue(null, null);
			}
			FieldInfo fieldInfo = lllInstanceMember as FieldInfo;
			if (fieldInfo != null)
			{
				return fieldInfo.GetValue(null);
			}
		}
		catch
		{
		}
		return null;
	}

	private static bool IsUsableLllManager(object candidate)
	{
		if (!IsUnityAlive(candidate))
		{
			return false;
		}
		bool? flag = ReadBool(candidate, "IsSpawned");
		if (flag.HasValue)
		{
			return flag.Value;
		}
		return true;
	}

	private static bool IsUnityAlive(object candidate)
	{
		if (candidate == null)
		{
			return false;
		}
		Object val = (Object)((candidate is Object) ? candidate : null);
		if (val != (Object)null)
		{
			return true;
		}
		if (candidate is Object)
		{
			return false;
		}
		return true;
	}

	private static bool? ReadBool(object instance, string propertyName)
	{
		if (instance == null)
		{
			return null;
		}
		try
		{
			PropertyInfo property = instance.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.PropertyType == typeof(bool))
			{
				return (bool)property.GetValue(instance, null);
			}
		}
		catch
		{
		}
		return null;
	}

	private static string DescribeLllManager(object manager)
	{
		if (manager == null)
		{
			return "<null>";
		}
		int num = 0;
		Object val = (Object)((manager is Object) ? manager : null);
		if (val != (Object)null)
		{
			try
			{
				num = val.GetInstanceID();
			}
			catch
			{
			}
		}
		bool? flag = ReadBool(manager, "IsSpawned");
		return manager.GetType().Name + "#" + num + " IsSpawned=" + (flag.HasValue ? flag.Value.ToString() : "?");
	}

	private static Exception Unwrap(Exception ex)
	{
		Exception ex2 = ex;
		while (ex2 is TargetInvocationException && ex2.InnerException != null)
		{
			ex2 = ex2.InnerException;
		}
		return ex2;
	}

	private void TryInstallMacronRuntimePatches()
	{
		//IL_019c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Expected O, but got Unknown
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d5: Expected O, but got Unknown
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0205: Expected O, but got Unknown
		//IL_0241: Unknown result type (might be due to invalid IL or missing references)
		//IL_024f: Expected O, but got Unknown
		if (macronPatchInstalled || !macronCompatibilityFixEnabled.Value)
		{
			return;
		}
		try
		{
			macronAiType = AccessTools.TypeByName("MacronAI");
			if (macronAiType == null)
			{
				return;
			}
			macronSpawnShovelMethod = AccessTools.Method(macronAiType, "SpawnShovel", Type.EmptyTypes, (Type[])null);
			if (macronSpawnShovelMethod == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"[SteveFixes][Macron] SpawnShovel() not found. Macron runtime fix left inactive.");
				return;
			}
			MethodInfo[] methods = macronAiType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (string.Equals(methodInfo.Name, "HitEnemy", StringComparison.Ordinal))
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length == 4 && parameters[0].ParameterType == typeof(int))
					{
						macronHitEnemyMethod = methodInfo;
					}
				}
			}
			macronPlayDanceMusicMethod = AccessTools.Method(macronAiType, "PlayDanceMusic", Type.EmptyTypes, (Type[])null);
			macronPlayMusicMethod = AccessTools.Method(macronAiType, "PlayMusic", new Type[1] { typeof(AudioClip) }, (Type[])null);
			macronNetworkObjectIdGetter = AccessTools.PropertyGetter(macronAiType, "NetworkObjectId");
			Type baseType = macronAiType;
			while (baseType != null && macronEnemyHpField == null)
			{
				macronEnemyHpField = baseType.GetField("enemyHP", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				baseType = baseType.BaseType;
			}
			if (macronHitEnemyMethod == null || macronEnemyHpField == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"[SteveFixes][Macron] HitEnemy/enemyHP not found. Refusing partial runtime patch so combat balance remains predictable.");
				return;
			}
			macronHarmony = new Harmony("boogie.stevefixes.macron_runtime");
			macronHarmony.Patch((MethodBase)macronSpawnShovelMethod, new HarmonyMethod(typeof(SteveFixes).GetMethod("MacronSpawnShovelPrefix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			macronHarmony.Patch((MethodBase)macronHitEnemyMethod, new HarmonyMethod(typeof(SteveFixes).GetMethod("MacronHitEnemyPrefix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			if (macronPlayDanceMusicMethod != null && macronPlayMusicMethod != null)
			{
				macronHarmony.Patch((MethodBase)macronPlayDanceMusicMethod, new HarmonyMethod(typeof(SteveFixes).GetMethod("MacronPlayDanceMusicPrefix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				macronDancePatchInstalled = true;
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"[SteveFixes][Macron] Dance API shape not found. dance1-10 override disabled; stock dance audio remains available.");
			}
			macronPatchInstalled = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[SteveFixes][Macron] MACRON_FIX_READY safeMode=True oneShovel=True hitsToKill=" + macronConfiguredHitsToKill + " dancePatch=" + macronDancePatchInstalled + " deathPatch=Binary9s"));
			if (!macronDancePatchInstalled)
			{
				return;
			}
			string text = null;
			try
			{
				if (macronAiType.Assembly != null && !string.IsNullOrEmpty(macronAiType.Assembly.Location))
				{
					text = Path.GetDirectoryName(macronAiType.Assembly.Location);
				}
			}
			catch
			{
			}
			if (!string.IsNullOrEmpty(text))
			{
				((MonoBehaviour)this).StartCoroutine(LoadMacronDancePlaylist(text));
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"[SteveFixes][Macron] Could not resolve MacronMod.dll folder. dance1-10 override disabled; stock dance audio remains available.");
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[SteveFixes][Macron] Failed to install runtime patches: " + Unwrap(ex)));
		}
	}

	private IEnumerator LoadMacronDancePlaylist(string macronDir)
	{
		macronDancePlaylistReady = false;
		macronDanceClipCount = 0;
		for (int i = 0; i < MacronDanceClips.Length; i++)
		{
			MacronDanceClips[i] = null;
		}
		for (int j = 1; j <= 10; j++)
		{
			string path = Path.Combine(macronDir, "macron_dance" + j + ".mp3");
			if (!File.Exists(path))
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[SteveFixes][Macron] Missing macron_dance" + j + ".mp3"));
				continue;
			}
			string uri = "file:///" + path.Replace("\\", "/");
			UnityWebRequest request;
			try
			{
				request = UnityWebRequestMultimedia.GetAudioClip(uri, (AudioType)13);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[SteveFixes][Macron] Could not create audio request for dance" + j + ": " + ex.Message));
				continue;
			}
			yield return request.SendWebRequest();
			if ((int)request.result != 1)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[SteveFixes][Macron] Failed loading dance" + j + ": " + request.error));
				request.Dispose();
				continue;
			}
			AudioClip clip = null;
			try
			{
				clip = DownloadHandlerAudioClip.GetContent(request);
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[SteveFixes][Macron] Failed decoding dance" + j + ": " + ex2.Message));
			}
			request.Dispose();
			if ((Object)(object)clip != (Object)null)
			{
				((Object)clip).name = "SteveFixes_MacronDance" + j;
				MacronDanceClips[j - 1] = clip;
				macronDanceClipCount++;
			}
		}
		macronDancePlaylistReady = macronDanceClipCount == 10;
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[SteveFixes][Macron] DANCE_PLAYLIST_READY clips=" + macronDanceClipCount + "/10 active=" + macronDancePlaylistReady));
		if (!macronDancePlaylistReady)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"[SteveFixes][Macron] dance1-10 playlist incomplete. Falling back to Macron's original dance1-3 behavior.");
		}
	}

	private static bool MacronSpawnShovelPrefix(object __instance)
	{
		if (!macronPatchInstalled || __instance == null)
		{
			return true;
		}
		try
		{
			MacronState orCreateValue = MacronStates.GetOrCreateValue(__instance);
			if (orCreateValue.ShovelAlreadySpawned)
			{
				return false;
			}
			orCreateValue.ShovelAlreadySpawned = true;
			return true;
		}
		catch (Exception ex)
		{
			if (SharedLog != null)
			{
				SharedLog.LogWarning((object)("[SteveFixes][Macron] One-shovel guard failed open: " + Unwrap(ex).Message));
			}
			return true;
		}
	}

	private static bool MacronPlayDanceMusicPrefix(object __instance)
	{
		if (!macronPatchInstalled || !macronDancePatchInstalled || !macronDancePlaylistReady || __instance == null)
		{
			return true;
		}
		try
		{
			ulong num = 0uL;
			bool flag = false;
			int num2;
			if (macronNetworkObjectIdGetter != null)
			{
				object obj = macronNetworkObjectIdGetter.Invoke(__instance, null);
				if (obj != null)
				{
					num = Convert.ToUInt64(obj);
					num2 = (int)(num % 10);
					flag = true;
				}
				else
				{
					num2 = 0;
				}
			}
			else
			{
				num2 = 0;
			}
			if (!flag)
			{
				MacronState orCreateValue = MacronStates.GetOrCreateValue(__instance);
				if (orCreateValue.DanceIndex < 0 || orCreateValue.DanceIndex >= 10)
				{
					orCreateValue.DanceIndex = Random.Range(0, 10);
				}
				num2 = orCreateValue.DanceIndex;
			}
			AudioClip val = MacronDanceClips[num2];
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			macronPlayMusicMethod.Invoke(__instance, new object[1] { val });
			if (SharedLog != null)
			{
				string text = (flag ? num.ToString() : "fallback");
				SharedLog.LogInfo((object)("[SteveFixes][Macron] DANCE_PICK " + (num2 + 1) + "/10 mode=DirectStable networkObjectId=" + text));
			}
			return false;
		}
		catch (Exception ex)
		{
			if (SharedLog != null)
			{
				SharedLog.LogWarning((object)("[SteveFixes][Macron] Dance override failed open: " + Unwrap(ex).Message));
			}
			return true;
		}
	}

	private static void MacronHitEnemyPrefix(object __instance)
	{
		if (!macronPatchInstalled || __instance == null || macronEnemyHpField == null)
		{
			return;
		}
		try
		{
			MacronState orCreateValue = MacronStates.GetOrCreateValue(__instance);
			if (!orCreateValue.HitPointsInitialized)
			{
				int num = Convert.ToInt32(macronEnemyHpField.GetValue(__instance));
				macronEnemyHpField.SetValue(__instance, macronConfiguredHitsToKill);
				orCreateValue.HitPointsInitialized = true;
				if (SharedLog != null)
				{
					SharedLog.LogInfo((object)("[SteveFixes][Macron] HP_INIT " + num + " -> " + macronConfiguredHitsToKill));
				}
			}
		}
		catch (Exception ex)
		{
			if (SharedLog != null)
			{
				SharedLog.LogWarning((object)("[SteveFixes][Macron] HP initialization failed open: " + Unwrap(ex).Message));
			}
		}
	}

	private static string StableLevelKey(SelectableLevel level)
	{
		if ((Object)(object)level == (Object)null)
		{
			return string.Empty;
		}
		string text = Safe(((Object)level).name);
		string text2 = Safe(level.PlanetName);
		return text + "|" + text2;
	}

	private static int SafeInstanceId(Object obj)
	{
		if (obj == (Object)null)
		{
			return 0;
		}
		try
		{
			return obj.GetInstanceID();
		}
		catch
		{
			return 0;
		}
	}

	private static string Safe(string text)
	{
		return text ?? string.Empty;
	}
}