Decompiled source of StratosTweaks v0.1.0

StratosTweaks.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using Silksong.ModMenu.Elements;
using Silksong.ModMenu.Plugin;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("StratosTweaks")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+ade4d818d8e80ebb82796a5a0d6c4c21f53068d9")]
[assembly: AssemblyProduct("StratosTweaks")]
[assembly: AssemblyTitle("StratosTweaks")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/AbsoluteStratos/StratosTweaks")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[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 BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace StratosTweaks
{
	internal sealed class AddressableCatalog
	{
		private const string RuntimeToken = "{UnityEngine.AddressableAssets.Addressables.RuntimePath}";

		private const string OurGroupMarker = "stratostweaks_assets_all";

		private const string DefaultLocalGroupMarker = "defaultlocalgroup_assets_all";

		private const string OurSharedMarker = "cf1fd25e771d52098d1bacfb793670d7_";

		private readonly ManualLogSource _log;

		private readonly HashSet<string> _loggedRewrites = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> _loadedCatalogs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> _shippedBundleNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

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

		private string _catalogDirectory = "";

		private Func<IResourceLocation, string>? _idTransform;

		public bool IsReady { get; private set; }

		public AddressableCatalog(ManualLogSource log)
		{
			_log = log;
		}

		public IEnumerator Load(string catalogFilePath)
		{
			catalogFilePath = Path.GetFullPath(catalogFilePath);
			if (_loadedCatalogs.Contains(catalogFilePath))
			{
				yield break;
			}
			if (!File.Exists(catalogFilePath))
			{
				_log.LogError((object)("Addressables catalog missing: " + catalogFilePath));
				string directoryName = Path.GetDirectoryName(catalogFilePath);
				if (!string.IsNullOrEmpty(directoryName) && Directory.Exists(directoryName))
				{
					_log.LogError((object)("Folder exists. Files: " + string.Join(", ", Directory.GetFiles(directoryName, "*", SearchOption.AllDirectories))));
				}
				yield break;
			}
			_catalogDirectory = Path.GetFullPath(Path.GetDirectoryName(catalogFilePath));
			IndexShippedBundles();
			_log.LogInfo((object)("Loading catalog " + _catalogDirectory));
			AsyncOperationHandle<IResourceLocator> init = Addressables.InitializeAsync();
			yield return init;
			if (init.IsValid() && (int)init.Status == 2)
			{
				_log.LogError((object)$"Addressables.InitializeAsync failed: {init.OperationException}");
				yield break;
			}
			InstallIdTransform();
			AsyncOperationHandle<IResourceLocator> catalog = Addressables.LoadContentCatalogAsync(catalogFilePath, false, "StratosTweaks");
			yield return catalog;
			if (!catalog.IsValid() || (int)catalog.Status != 1)
			{
				string text = ((!catalog.IsValid()) ? "handle released" : catalog.OperationException?.ToString());
				_log.LogError((object)("LoadContentCatalogAsync failed: " + text));
				yield break;
			}
			Addressables.Release<IResourceLocator>(catalog);
			_loadedCatalogs.Add(catalogFilePath);
			IsReady = true;
			_log.LogInfo((object)("Loaded catalog from " + _catalogDirectory));
		}

		private void IndexShippedBundles()
		{
			_shippedBundleNames.Clear();
			_shippedBundlePaths.Clear();
			if (!Directory.Exists(_catalogDirectory))
			{
				return;
			}
			string[] files = Directory.GetFiles(_catalogDirectory, "*.bundle", SearchOption.AllDirectories);
			foreach (string path in files)
			{
				string fullPath = Path.GetFullPath(path);
				if (fullPath.IndexOf($"{Path.DirectorySeparatorChar}BellTown{Path.DirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase) < 0)
				{
					string fileName = Path.GetFileName(path);
					_shippedBundleNames.Add(fileName);
					_shippedBundlePaths[fileName] = fullPath;
				}
			}
		}

		public void InstallIdTransform()
		{
			if (string.IsNullOrEmpty(_catalogDirectory))
			{
				return;
			}
			if (_idTransform == null)
			{
				Func<IResourceLocation, string> previous = Addressables.InternalIdTransformFunc;
				_idTransform = delegate(IResourceLocation location)
				{
					string internalId = location.InternalId;
					string text = ((previous != null) ? previous(location) : internalId);
					string text2 = (string.IsNullOrEmpty(text) ? internalId : text);
					return (IsOurBundle(internalId) || IsOurBundle(text2)) ? RewriteOurBundles(text2) : RewriteVanillaReference(text2);
				};
			}
			Addressables.InternalIdTransformFunc = _idTransform;
		}

		private bool IsOurBundle(string? id)
		{
			if (string.IsNullOrEmpty(id) || id.IndexOf("(reference)", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return false;
			}
			string bundleFileName = GetBundleFileName(id);
			if (bundleFileName != null && _shippedBundleNames.Contains(bundleFileName))
			{
				return true;
			}
			if (!id.Contains("stratostweaks_assets_all", StringComparison.OrdinalIgnoreCase) && !id.Contains("defaultlocalgroup_assets_all", StringComparison.OrdinalIgnoreCase))
			{
				return id.Contains("cf1fd25e771d52098d1bacfb793670d7_", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static string? GetBundleFileName(string internalId)
		{
			string text = internalId.Replace('/', '\\');
			int num = text.LastIndexOf('\\');
			string text2 = ((num >= 0) ? text.Substring(num + 1) : text);
			int num2 = text2.IndexOf('?');
			if (num2 >= 0)
			{
				text2 = text2.Substring(0, num2);
			}
			if (!text2.EndsWith(".bundle", StringComparison.OrdinalIgnoreCase))
			{
				return null;
			}
			return text2;
		}

		private string RewriteOurBundles(string internalId)
		{
			string text = MapToCatalogDir(internalId);
			if (text != internalId && _loggedRewrites.Add(text))
			{
				bool flag = File.Exists(text);
				_log.LogInfo((object)$"Rewrite bundle -> {text} (exists={flag})");
				if (!flag)
				{
					_log.LogError((object)("Expected bundle missing: " + text));
				}
			}
			return text;
		}

		private string MapToCatalogDir(string internalId)
		{
			string bundleFileName = GetBundleFileName(internalId);
			if (bundleFileName != null && _shippedBundlePaths.TryGetValue(bundleFileName, out string value))
			{
				return value;
			}
			if (internalId.IndexOf("{UnityEngine.AddressableAssets.Addressables.RuntimePath}", StringComparison.Ordinal) >= 0)
			{
				return Path.GetFullPath(internalId.Replace("{UnityEngine.AddressableAssets.Addressables.RuntimePath}", _catalogDirectory).Replace('/', Path.DirectorySeparatorChar));
			}
			string text = internalId.Replace('/', '\\');
			string text2 = Addressables.RuntimePath?.Replace('/', '\\');
			if (!string.IsNullOrEmpty(text2) && text.StartsWith(text2, StringComparison.OrdinalIgnoreCase))
			{
				string path = text.Substring(text2.Length).TrimStart('\\');
				return Path.GetFullPath(Path.Combine(_catalogDirectory, path));
			}
			string[] array = new string[2] { "StratosTweaks\\", "StandaloneWindows64\\" };
			foreach (string value2 in array)
			{
				int num = text.IndexOf(value2, StringComparison.OrdinalIgnoreCase);
				if (num >= 0)
				{
					return Path.GetFullPath(Path.Combine(_catalogDirectory, text.Substring(num)));
				}
			}
			return internalId;
		}

		private string RewriteVanillaReference(string internalId)
		{
			if (internalId.IndexOf("(reference)", StringComparison.OrdinalIgnoreCase) < 0)
			{
				return internalId;
			}
			string text = StripReferenceNaming(internalId);
			if (text != internalId && _loggedRewrites.Add(text))
			{
				bool flag = File.Exists(text);
				_log.LogInfo((object)$"Rewrite vanilla (reference) -> {text} (exists={flag})");
				if (!flag)
				{
					_log.LogError((object)("Game bundle missing after stripping (reference): " + text));
				}
			}
			return text;
		}

		private static string StripReferenceNaming(string internalId)
		{
			string text = internalId.Replace("(reference)", "", StringComparison.OrdinalIgnoreCase);
			if (!text.EndsWith(".bundle", StringComparison.OrdinalIgnoreCase))
			{
				return text;
			}
			int num = Math.Max(text.LastIndexOf('\\'), text.LastIndexOf('/')) + 1;
			string text2 = text.Substring(num, text.Length - num - ".bundle".Length);
			int num2 = text2.LastIndexOf('_');
			if (num2 < 0 || !IsHex32(text2, num2 + 1))
			{
				return text;
			}
			return text.Substring(0, num) + text2.Substring(0, num2) + ".bundle";
		}

		private static bool IsHex32(string value, int start)
		{
			if (value.Length - start != 32)
			{
				return false;
			}
			for (int i = start; i < value.Length; i++)
			{
				char c = value[i];
				if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F'))
				{
					return false;
				}
			}
			return true;
		}
	}
	internal sealed class BenchwarpIntegration : IDisposable
	{
		public const string PluginId = "io.github.homothetyhk.benchwarp";

		private readonly PositionTracker _tracker;

		private readonly ManualLogSource _log;

		private readonly List<(EventInfo Event, Delegate Handler)> _subscriptions = new List<(EventInfo, Delegate)>();

		private BenchwarpIntegration(PositionTracker tracker, PluginInfo pluginInfo, ManualLogSource log)
		{
			_tracker = tracker;
			_log = log;
			Type type = ((object)pluginInfo.Instance).GetType().Assembly.GetType("Benchwarp.Events.ModEvents") ?? throw new InvalidOperationException("Benchwarp.Events.ModEvents type not found.");
			Subscribe(type, "OnBenchwarp", CreateBenchwarpHandler());
			Subscribe(type, "OnDoorwarp", CreateDoorwarpHandler(type.GetEvent("OnDoorwarp")));
		}

		public static bool TryCreate(PositionTracker tracker, ManualLogSource log, out BenchwarpIntegration? integration)
		{
			integration = null;
			if (!Chainloader.PluginInfos.TryGetValue("io.github.homothetyhk.benchwarp", out var value))
			{
				log.LogInfo((object)"Benchwarp not installed; position tracking uses movement heuristics for warp.");
				return false;
			}
			try
			{
				integration = new BenchwarpIntegration(tracker, value, log);
				log.LogInfo((object)"Benchwarp detected; bench_warp and door_warp events enabled.");
				return true;
			}
			catch (Exception ex)
			{
				log.LogWarning((object)("Benchwarp is installed but Stratos Tweaks could not hook ModEvents: " + ex.Message));
				return false;
			}
		}

		private Action CreateBenchwarpHandler()
		{
			return delegate
			{
				_tracker.NotifyPendingWarp(PositionTracker.WarpSource.Bench);
			};
		}

		private Delegate CreateDoorwarpHandler(EventInfo doorEvent)
		{
			ParameterInfo[] parameters = (doorEvent.EventHandlerType.GetMethod("Invoke") ?? throw new InvalidOperationException("OnDoorwarp invoke method not found.")).GetParameters();
			if (parameters.Length != 2)
			{
				throw new InvalidOperationException("OnDoorwarp has unexpected signature.");
			}
			MethodInfo method = typeof(BenchwarpIntegration).GetMethod("HandleDoorwarp", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(parameters[0].ParameterType, parameters[1].ParameterType);
			return Delegate.CreateDelegate(doorEvent.EventHandlerType, this, method);
		}

		private void HandleDoorwarp<TRoom, TDoor>(TRoom room, TDoor gate)
		{
			_tracker.NotifyPendingWarp(PositionTracker.WarpSource.Door);
		}

		private void Subscribe(Type modEvents, string eventName, Delegate handler)
		{
			EventInfo eventInfo = modEvents.GetEvent(eventName, BindingFlags.Static | BindingFlags.Public) ?? throw new InvalidOperationException("Benchwarp event " + eventName + " not found.");
			eventInfo.AddEventHandler(null, handler);
			_subscriptions.Add((eventInfo, handler));
		}

		public void Dispose()
		{
			foreach (var (eventInfo, handler) in _subscriptions)
			{
				eventInfo.RemoveEventHandler(null, handler);
			}
			_subscriptions.Clear();
		}
	}
	internal interface ISceneHandler
	{
		string SceneName { get; }

		bool Enabled { get; }

		string CatalogRelativePath { get; }

		string PrefabAddress { get; }

		string SpawnedRootName { get; }

		IEnumerator TryBegin(Scene scene, SceneInjectSession session);
	}
	internal sealed class SceneInjectSession
	{
		public bool Proceed { get; set; } = true;

		public string? SkipReason { get; set; }

		public Action<GameObject>? AfterSpawn { get; set; }
	}
	internal sealed class PluginConfig
	{
		private static readonly string SharedCatalogRelativePath = Path.Combine("addressables", "catalog.bin");

		public ConfigEntry<bool> EnableSceneInjection { get; }

		public ConfigEntry<bool> EnablePositionTracking { get; }

		public ConfigEntry<float> TrackingSampleIntervalSeconds { get; }

		public ConfigEntry<float> TrackingFlushIntervalSeconds { get; }

		public ConfigEntry<string> TrackingCsvRelativePath { get; }

		public ConfigEntry<float> TrackingWarpDistance { get; }

		public ConfigEntry<float> TrackingMovementWindowSeconds { get; }

		public ConfigEntry<float> TrackingWalkThreshold { get; }

		public string BelltownPrefabAddress => "Assets/Mods/Noss2026/stratos_tweaks_root.prefab";

		public string BelltownSpawnedRootName => "stratos_tweaks_root";

		public string BelltownCatalogRelativePath => SharedCatalogRelativePath;

		public string BonetownPrefabAddress => "Assets/Mods/Noss2026/stratos_bowntown_root.prefab";

		public string BonetownSpawnedRootName => "stratos_bowntown_root";

		public string BonetownCatalogRelativePath => SharedCatalogRelativePath;

		public string SongEnclavePrefabAddress => "Assets/Mods/Noss2026/stratos_song_enclave_root.prefab";

		public string SongEnclaveSpawnedRootName => "stratos_song_enclave_root";

		public string SongEnclaveCatalogRelativePath => SharedCatalogRelativePath;

		public string Tut01PrefabAddress => "Assets/Mods/Noss2026/stratos_tut_root.prefab";

		public string Tut01SpawnedRootName => "stratos_tut_root";

		public string Tut01CatalogRelativePath => SharedCatalogRelativePath;

		public PluginConfig(ConfigFile file)
		{
			EnableSceneInjection = BindToggle(file, "General", "EnableSceneInjection", true, "Master switch for all scene injections.", "General");
			EnablePositionTracking = BindToggle(file, "General", "EnablePositionTracking", true, "Log Hornet's room-local position to a CSV (enter/exit always; samples on an interval).", "General");
			TrackingSampleIntervalSeconds = file.Bind<float>("Tracking", "SampleIntervalSeconds", 1f, "Seconds between sample rows while in a gameplay room. Enter/exit ignore this.");
			TrackingFlushIntervalSeconds = file.Bind<float>("Tracking", "FlushIntervalSeconds", 60f, "Seconds between writing buffered sample rows to disk. Enter/exit flush immediately.");
			TrackingCsvRelativePath = file.Bind<string>("Tracking", "CsvRelativePath", Path.Combine("logs", "positions.csv"), "CSV path template relative to this plugin DLL. Each session writes positions_yyyyMMdd_HHmmss_<id>.csv.");
			TrackingWarpDistance = file.Bind<float>("Tracking", "WarpDistance", 15f, "Same-room position jump (room-local units) logged as warp instead of sample.");
			TrackingMovementWindowSeconds = file.Bind<float>("Tracking", "MovementWindowSeconds", 2f, "Seconds before a room change to measure walking; little movement implies warp (e.g. Benchwarp).");
			TrackingWalkThreshold = file.Bind<float>("Tracking", "WalkThreshold", 3f, "Max room-local travel in the movement window below which a room arrival is logged as warp.");
		}

		private static ConfigEntry<bool> BindToggle(ConfigFile file, string section, string key, bool defaultValue, string description, params string[] menuPath)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			object[] array;
			if (menuPath.Length == 0)
			{
				array = Array.Empty<object>();
			}
			else if (menuPath.Length == 1)
			{
				array = new object[1] { (object)new ConfigEntrySubgroup(LocalizedText.op_Implicit(menuPath[0]), Array.Empty<LocalizedText>()) };
			}
			else
			{
				LocalizedText[] array2 = (LocalizedText[])(object)new LocalizedText[menuPath.Length - 1];
				for (int i = 1; i < menuPath.Length; i++)
				{
					array2[i - 1] = LocalizedText.op_Implicit(menuPath[i]);
				}
				array = new object[1] { (object)new ConfigEntrySubgroup(LocalizedText.op_Implicit(menuPath[0]), array2) };
			}
			return file.Bind<bool>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, array));
		}
	}
	internal sealed class PositionTracker
	{
		internal enum WarpSource
		{
			None,
			Bench,
			Door
		}

		private const string Header = "utc,event,scene,x,y,z";

		private readonly PluginConfig _config;

		private readonly ManualLogSource _log;

		private readonly string _pluginDir;

		private readonly StringBuilder _buffer = new StringBuilder();

		private string _sessionId = "";

		private string _csvPath = "";

		private string _room = "";

		private Vector3 _lastPos;

		private bool _inRoom;

		private float _nextSampleAt;

		private float _nextFlushAt;

		private float _movementWindowStart;

		private Vector3 _movementWindowOrigin;

		private float _movementInWindow;

		private WarpSource _pendingWarp;

		private float _pendingWarpUntil;

		public PositionTracker(PluginConfig config, ManualLogSource log, string pluginDir)
		{
			_config = config;
			_log = log;
			_pluginDir = pluginDir;
		}

		public void NotifyPendingWarp(WarpSource source)
		{
			_pendingWarp = source;
			_pendingWarpUntil = Time.unscaledTime + PendingWarpTimeoutSeconds();
		}

		public void Tick()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			if (!_config.EnablePositionTracking.Value)
			{
				if (_inRoom)
				{
					Write("exit", _room, _lastPos, flush: true);
					_inRoom = false;
					_room = "";
				}
				return;
			}
			EnsureSession();
			GameManager instance = GameManager.instance;
			HeroController instance2 = HeroController.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || !instance.IsGameplayScene())
			{
				if (_inRoom)
				{
					Write("exit", _room, _lastPos, flush: true);
					_inRoom = false;
					_room = "";
				}
				return;
			}
			string sceneNameString = instance.GetSceneNameString();
			if (string.IsNullOrEmpty(sceneNameString))
			{
				return;
			}
			Vector3 position = instance2.transform.position;
			float unscaledTime = Time.unscaledTime;
			if (_inRoom && string.Equals(sceneNameString, _room, StringComparison.Ordinal) && IsSameRoomWarp(position, _lastPos))
			{
				Write("warp", sceneNameString, position, flush: true);
				_lastPos = position;
				ResetMovementWindow(position, unscaledTime);
				_nextSampleAt = unscaledTime + SampleInterval();
				return;
			}
			if (_inRoom && !string.Equals(sceneNameString, _room, StringComparison.Ordinal))
			{
				Write("exit", _room, _lastPos, flush: true);
				_inRoom = false;
			}
			if (!_inRoom)
			{
				_room = sceneNameString;
				_inRoom = true;
				string eventName = ConsumePendingWarpEvent() ?? (LikelyTeleportArrival() ? "warp" : "enter");
				Write(eventName, sceneNameString, position, flush: true);
				_nextSampleAt = unscaledTime + SampleInterval();
				_lastPos = position;
				ResetMovementWindow(position, unscaledTime);
				return;
			}
			UpdateMovementWindow(position, unscaledTime);
			_lastPos = position;
			if (unscaledTime >= _nextSampleAt)
			{
				Write("sample", sceneNameString, position, flush: false);
				_nextSampleAt = unscaledTime + SampleInterval();
			}
			if (unscaledTime >= _nextFlushAt)
			{
				Flush();
			}
		}

		public void Shutdown()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (_inRoom)
			{
				Write("exit", _room, _lastPos, flush: false);
				_inRoom = false;
				_room = "";
			}
			Flush();
		}

		private float SampleInterval()
		{
			return Mathf.Max(0.1f, _config.TrackingSampleIntervalSeconds.Value);
		}

		private float FlushInterval()
		{
			return Mathf.Max(1f, _config.TrackingFlushIntervalSeconds.Value);
		}

		private float WarpDistance()
		{
			return Mathf.Max(1f, _config.TrackingWarpDistance.Value);
		}

		private float MovementWindowSeconds()
		{
			return Mathf.Max(0.5f, _config.TrackingMovementWindowSeconds.Value);
		}

		private float WalkThreshold()
		{
			return Mathf.Max(0.5f, _config.TrackingWalkThreshold.Value);
		}

		private float PendingWarpTimeoutSeconds()
		{
			return Mathf.Max(5f, _config.TrackingMovementWindowSeconds.Value * 15f);
		}

		private string? ConsumePendingWarpEvent()
		{
			if (_pendingWarp == WarpSource.None || Time.unscaledTime > _pendingWarpUntil)
			{
				_pendingWarp = WarpSource.None;
				return null;
			}
			object result = _pendingWarp switch
			{
				WarpSource.Bench => "bench_warp", 
				WarpSource.Door => "door_warp", 
				_ => null, 
			};
			_pendingWarp = WarpSource.None;
			return (string?)result;
		}

		private bool IsSameRoomWarp(Vector3 pos, Vector3 previous)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return Vector3.Distance(pos, previous) >= WarpDistance();
		}

		private bool LikelyTeleportArrival()
		{
			return _movementInWindow < WalkThreshold();
		}

		private void ResetMovementWindow(Vector3 pos, float now)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			_movementWindowStart = now;
			_movementWindowOrigin = pos;
			_movementInWindow = 0f;
		}

		private void UpdateMovementWindow(Vector3 pos, float now)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (now - _movementWindowStart > MovementWindowSeconds())
			{
				ResetMovementWindow(pos, now);
			}
			else
			{
				_movementInWindow = Mathf.Max(_movementInWindow, Vector3.Distance(pos, _movementWindowOrigin));
			}
		}

		private void EnsureSession()
		{
			if (string.IsNullOrEmpty(_sessionId))
			{
				byte[] array = new byte[16];
				using (RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create())
				{
					randomNumberGenerator.GetBytes(array);
				}
				DateTime utcNow = DateTime.UtcNow;
				_sessionId = ToHex(array);
				_csvPath = BuildSessionCsvPath(_pluginDir, _config.TrackingCsvRelativePath.Value, utcNow, _sessionId);
				_nextFlushAt = Time.unscaledTime + FlushInterval();
				_log.LogInfo((object)("Position tracking session " + _sessionId + " -> " + _csvPath));
			}
		}

		private static string BuildSessionCsvPath(string pluginDir, string relativeTemplate, DateTime utc, string sessionId)
		{
			string fullPath = Path.GetFullPath(Path.Combine(pluginDir, relativeTemplate));
			string path = Path.GetDirectoryName(fullPath) ?? pluginDir;
			string text = Path.GetFileNameWithoutExtension(fullPath);
			if (string.IsNullOrEmpty(text))
			{
				text = "positions";
			}
			string text2 = Path.GetExtension(fullPath);
			if (string.IsNullOrEmpty(text2))
			{
				text2 = ".csv";
			}
			string text3 = utc.ToString("yyyyMMdd_HHmmss", CultureInfo.InvariantCulture);
			string text4 = ((sessionId.Length >= 8) ? sessionId.Substring(0, 8) : sessionId);
			return Path.Combine(path, text + "_" + text3 + "_" + text4 + text2);
		}

		private void Write(string eventName, string scene, Vector3 pos, bool flush)
		{
			if (!string.IsNullOrEmpty(_sessionId) && !string.IsNullOrEmpty(scene))
			{
				DateTime utcNow = DateTime.UtcNow;
				CultureInfo invariantCulture = CultureInfo.InvariantCulture;
				_buffer.Append(utcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", invariantCulture)).Append(',').Append(eventName)
					.Append(',')
					.Append(scene)
					.Append(',')
					.Append(pos.x.ToString("0.###", invariantCulture))
					.Append(',')
					.Append(pos.y.ToString("0.###", invariantCulture))
					.Append(',')
					.Append(pos.z.ToString("0.###", invariantCulture))
					.Append('\n');
				if (flush)
				{
					Flush();
				}
			}
		}

		private void Flush()
		{
			if (_buffer.Length == 0 || string.IsNullOrEmpty(_csvPath))
			{
				_nextFlushAt = Time.unscaledTime + FlushInterval();
				return;
			}
			try
			{
				string directoryName = Path.GetDirectoryName(_csvPath);
				if (!string.IsNullOrEmpty(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				if (!File.Exists(_csvPath))
				{
					File.WriteAllText(_csvPath, "utc,event,scene,x,y,z\n");
				}
				File.AppendAllText(_csvPath, _buffer.ToString());
				_buffer.Clear();
				_nextFlushAt = Time.unscaledTime + FlushInterval();
			}
			catch (Exception arg)
			{
				_log.LogError((object)$"Position CSV write failed: {arg}");
			}
		}

		private static string ToHex(byte[] bytes)
		{
			StringBuilder stringBuilder = new StringBuilder(bytes.Length * 2);
			foreach (byte b in bytes)
			{
				stringBuilder.Append(b.ToString("x2", CultureInfo.InvariantCulture));
			}
			return stringBuilder.ToString();
		}
	}
	internal static class SceneHierarchy
	{
		public static GameObject? FindDescendant(Scene scene, string parentName, string childName)
		{
			if (!((Scene)(ref scene)).IsValid() || !((Scene)(ref scene)).isLoaded)
			{
				return null;
			}
			GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
			for (int i = 0; i < rootGameObjects.Length; i++)
			{
				Transform val = FindByName(rootGameObjects[i].transform, parentName);
				if (!((Object)(object)val == (Object)null))
				{
					Transform val2 = FindNamedChild(val, childName);
					if ((Object)(object)val2 != (Object)null)
					{
						return ((Component)val2).gameObject;
					}
				}
			}
			return null;
		}

		public static void FindAllByName(Scene scene, string name, List<GameObject> results)
		{
			if (((Scene)(ref scene)).IsValid() && ((Scene)(ref scene)).isLoaded)
			{
				GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
				for (int i = 0; i < rootGameObjects.Length; i++)
				{
					CollectByName(rootGameObjects[i].transform, name, results);
				}
			}
		}

		private static void CollectByName(Transform root, string name, List<GameObject> results)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			if (((Object)root).name == name)
			{
				results.Add(((Component)root).gameObject);
			}
			foreach (Transform item in root)
			{
				CollectByName(item, name, results);
			}
		}

		private static Transform? FindByName(Transform root, string name)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			if (((Object)root).name == name)
			{
				return root;
			}
			foreach (Transform item in root)
			{
				Transform val = FindByName(item, name);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static Transform? FindNamedChild(Transform parent, string name)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			foreach (Transform item in parent)
			{
				Transform val = item;
				if (((Object)val).name == name)
				{
					return val;
				}
			}
			foreach (Transform item2 in parent)
			{
				Transform val2 = FindNamedChild(item2, name);
				if ((Object)(object)val2 != (Object)null)
				{
					return val2;
				}
			}
			return null;
		}
	}
	internal sealed class SceneInjector
	{
		private readonly AddressableCatalog _catalog;

		private readonly ManualLogSource _log;

		private readonly Dictionary<string, GameObject> _spawnedByScene = new Dictionary<string, GameObject>();

		public SceneInjector(AddressableCatalog catalog, ManualLogSource log)
		{
			_catalog = catalog;
			_log = log;
		}

		public IEnumerator Inject(ISceneHandler handler, Scene loadedScene = default(Scene))
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (!handler.Enabled)
			{
				yield break;
			}
			Scene scene = ResolveTargetScene(loadedScene, handler.SceneName);
			if (!((Scene)(ref scene)).IsValid() || !((Scene)(ref scene)).isLoaded)
			{
				yield break;
			}
			if (_spawnedByScene.ContainsKey(handler.SceneName))
			{
				_log.LogInfo((object)("Skip inject: already spawned in '" + handler.SceneName + "'."));
				yield break;
			}
			SceneInjectSession session = new SceneInjectSession();
			yield return handler.TryBegin(scene, session);
			if (!session.Proceed)
			{
				_log.LogInfo((object)("Skip inject '" + handler.SceneName + "': " + session.SkipReason));
				yield break;
			}
			_catalog.InstallIdTransform();
			_log.LogInfo((object)("Injecting '" + handler.PrefabAddress + "' into '" + ((Scene)(ref scene)).name + "'."));
			AsyncOperationHandle<GameObject> handle = Addressables.InstantiateAsync((object)handler.PrefabAddress, Vector3.zero, Quaternion.identity, (Transform)null, true);
			yield return handle;
			if ((int)handle.Status != 1 || (Object)(object)handle.Result == (Object)null)
			{
				_log.LogError((object)$"InstantiateAsync('{handler.PrefabAddress}') failed: {handle.OperationException}");
				yield break;
			}
			GameObject result = handle.Result;
			if (!string.IsNullOrWhiteSpace(handler.SpawnedRootName))
			{
				((Object)result).name = handler.SpawnedRootName;
			}
			SceneManager.MoveGameObjectToScene(result, scene);
			LogSceneShaders(scene, result, "grass_03");
			_spawnedByScene[handler.SceneName] = result;
			session.AfterSpawn?.Invoke(result);
			_log.LogInfo((object)("Spawned '" + handler.PrefabAddress + "' as '" + ((Object)result).name + "' in '" + ((Scene)(ref scene)).name + "'."));
		}

		private void LogSceneShaders(Scene scene, GameObject spawned, string objectName)
		{
			GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
			for (int i = 0; i < rootGameObjects.Length; i++)
			{
				Renderer[] componentsInChildren = rootGameObjects[i].GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val in componentsInChildren)
				{
					if (!((Object)(object)((Component)val).transform.root == (Object)(object)spawned.transform) && !(((Object)val).name != objectName))
					{
						string text = (((Object)(object)((Component)val).transform.parent != (Object)null) ? ((Object)((Component)val).transform.parent).name : "");
						Material sharedMaterial = val.sharedMaterial;
						string text2 = (((Object)(object)sharedMaterial == (Object)null) ? "(null material)" : (((Object)(object)sharedMaterial.shader != (Object)null) ? ((Object)sharedMaterial.shader).name : "(null shader)"));
						_log.LogInfo((object)("Scene " + text + "/" + ((Object)val).name + ": mat='" + ((sharedMaterial != null) ? ((Object)sharedMaterial).name : null) + "' shader='" + text2 + "'"));
					}
				}
			}
		}

		public void OnSceneUnloaded(Scene scene)
		{
			if (_spawnedByScene.TryGetValue(((Scene)(ref scene)).name, out GameObject value))
			{
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)value);
				}
				_spawnedByScene.Remove(((Scene)(ref scene)).name);
			}
		}

		private static Scene ResolveTargetScene(Scene loadedScene, string targetName)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (((Scene)(ref loadedScene)).IsValid() && ((Scene)(ref loadedScene)).isLoaded && string.Equals(((Scene)(ref loadedScene)).name, targetName, StringComparison.Ordinal))
			{
				return loadedScene;
			}
			return SceneManager.GetSceneByName(targetName);
		}
	}
	internal sealed class Belltown : ISceneHandler
	{
		public const string Scene = "Belltown";

		private const string NormalWorld = "Normal World";

		private const string Fountain = "fountain";

		private const string BlackThreadWorld = "Black Thread World";

		private const string BlackThreadFountain = "fountain (1)";

		private const float WaitSeconds = 10f;

		private readonly PluginConfig _config;

		private readonly ManualLogSource _log;

		public string SceneName => "Belltown";

		public bool Enabled => _config.EnableSceneInjection.Value;

		public string CatalogRelativePath => _config.BelltownCatalogRelativePath;

		public string PrefabAddress => _config.BelltownPrefabAddress;

		public string SpawnedRootName => _config.BelltownSpawnedRootName;

		public Belltown(PluginConfig config, ManualLogSource log)
		{
			_config = config;
			_log = log;
		}

		public IEnumerator TryBegin(Scene scene, SceneInjectSession session)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			float deadline = Time.realtimeSinceStartup + 10f;
			while (((Scene)(ref scene)).isLoaded && Time.realtimeSinceStartup < deadline)
			{
				GameObject val = SceneHierarchy.FindDescendant(scene, "Normal World", "fountain");
				GameObject val2 = SceneHierarchy.FindDescendant(scene, "Black Thread World", "fountain (1)");
				if (ShouldReplace(val, val2))
				{
					_log.LogInfo((object)("Belltown: replacing vanilla fountain parent " + $"(fountain={(Object)(object)val != (Object)null}, fountain (1)={(Object)(object)val2 != (Object)null})."));
					session.AfterSpawn = delegate(GameObject spawned)
					{
						//IL_0008: Unknown result type (might be due to invalid IL or missing references)
						//IL_0022: Unknown result type (might be due to invalid IL or missing references)
						spawned.SetActive(true);
						Disable(SceneHierarchy.FindDescendant(spawned.scene, "Normal World", "fountain"));
						Disable(SceneHierarchy.FindDescendant(spawned.scene, "Black Thread World", "fountain (1)"));
					};
					yield break;
				}
				yield return null;
			}
			session.Proceed = false;
			session.SkipReason = "no active 'Normal World/fountain' or 'Black Thread World/fountain (1)'";
		}

		private static bool ShouldReplace(GameObject? normalFountain, GameObject? blackFountain)
		{
			if (!IsActive(normalFountain))
			{
				return IsActive(blackFountain);
			}
			return true;
		}

		private static bool IsActive(GameObject? go)
		{
			if ((Object)(object)go != (Object)null)
			{
				return go.activeInHierarchy;
			}
			return false;
		}

		private static void Disable(GameObject? go)
		{
			if ((Object)(object)go != (Object)null)
			{
				go.SetActive(false);
			}
		}
	}
	internal sealed class Bonetown : ISceneHandler
	{
		public const string Scene = "Bonetown";

		private readonly PluginConfig _config;

		private readonly ManualLogSource _log;

		public string SceneName => "Bonetown";

		public bool Enabled => _config.EnableSceneInjection.Value;

		public string CatalogRelativePath => _config.BonetownCatalogRelativePath;

		public string PrefabAddress => _config.BonetownPrefabAddress;

		public string SpawnedRootName => _config.BonetownSpawnedRootName;

		public Bonetown(PluginConfig config, ManualLogSource log)
		{
			_config = config;
			_log = log;
		}

		public IEnumerator TryBegin(Scene scene, SceneInjectSession session)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			_log.LogInfo((object)("Bonetown: injecting '" + PrefabAddress + "' into '" + ((Scene)(ref scene)).name + "'."));
			yield break;
		}
	}
	internal sealed class SongEnclave : ISceneHandler
	{
		public const string Scene = "Song_Enclave";

		private readonly PluginConfig _config;

		private readonly ManualLogSource _log;

		public string SceneName => "Song_Enclave";

		public bool Enabled => _config.EnableSceneInjection.Value;

		public string CatalogRelativePath => _config.SongEnclaveCatalogRelativePath;

		public string PrefabAddress => _config.SongEnclavePrefabAddress;

		public string SpawnedRootName => _config.SongEnclaveSpawnedRootName;

		public SongEnclave(PluginConfig config, ManualLogSource log)
		{
			_config = config;
			_log = log;
		}

		public IEnumerator TryBegin(Scene scene, SceneInjectSession session)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			_log.LogInfo((object)("Song_Enclave: injecting '" + PrefabAddress + "' into '" + ((Scene)(ref scene)).name + "'."));
			yield break;
		}
	}
	internal sealed class Tut01 : ISceneHandler
	{
		public const string Scene = "Tut_01";

		private readonly PluginConfig _config;

		private readonly ManualLogSource _log;

		public string SceneName => "Tut_01";

		public bool Enabled => _config.EnableSceneInjection.Value;

		public string CatalogRelativePath => _config.Tut01CatalogRelativePath;

		public string PrefabAddress => _config.Tut01PrefabAddress;

		public string SpawnedRootName => _config.Tut01SpawnedRootName;

		public Tut01(PluginConfig config, ManualLogSource log)
		{
			_config = config;
			_log = log;
		}

		public IEnumerator TryBegin(Scene scene, SceneInjectSession session)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			_log.LogInfo((object)("Tut_01: injecting '" + PrefabAddress + "' into '" + ((Scene)(ref scene)).name + "'."));
			yield break;
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("io.github.absolutestratos.stratostweaks", "StratosTweaks", "0.1.0")]
	public class StratosTweaksPlugin : BaseUnityPlugin, IModMenuNestedMenu, IModMenuInterface
	{
		private PluginConfig _config;

		private AddressableCatalog _catalog;

		private SceneInjector _injector;

		private ISceneHandler[] _scenes;

		private PositionTracker? _tracker;

		private BenchwarpIntegration? _benchwarp;

		private string _pluginDir = "";

		public const string Id = "io.github.absolutestratos.stratostweaks";

		public static string Name => "StratosTweaks";

		public static string Version => "0.1.0";

		public string ModMenuName()
		{
			return "Stratos Tweaks";
		}

		public int MinSubgroupSize()
		{
			return 1;
		}

		private void Awake()
		{
			_config = new PluginConfig(((BaseUnityPlugin)this).Config);
			_catalog = new AddressableCatalog(((BaseUnityPlugin)this).Logger);
			_injector = new SceneInjector(_catalog, ((BaseUnityPlugin)this).Logger);
			_scenes = new ISceneHandler[4]
			{
				new Belltown(_config, ((BaseUnityPlugin)this).Logger),
				new Bonetown(_config, ((BaseUnityPlugin)this).Logger),
				new SongEnclave(_config, ((BaseUnityPlugin)this).Logger),
				new Tut01(_config, ((BaseUnityPlugin)this).Logger)
			};
			_pluginDir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? "";
			_tracker = new PositionTracker(_config, ((BaseUnityPlugin)this).Logger, _pluginDir);
			BenchwarpIntegration.TryCreate(_tracker, ((BaseUnityPlugin)this).Logger, out _benchwarp);
			SceneManager.sceneLoaded += OnSceneLoaded;
			SceneManager.sceneUnloaded += _injector.OnSceneUnloaded;
			if (_config.EnableSceneInjection.Value)
			{
				((MonoBehaviour)this).StartCoroutine(BootInject());
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Plugin " + Name + " (io.github.absolutestratos.stratostweaks) loaded."));
		}

		private void Update()
		{
			_tracker?.Tick();
		}

		private void OnDestroy()
		{
			_benchwarp?.Dispose();
			_tracker?.Shutdown();
			SceneManager.sceneLoaded -= OnSceneLoaded;
			SceneManager.sceneUnloaded -= _injector.OnSceneUnloaded;
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Scene loaded: '" + ((Scene)(ref scene)).name + "'"));
			foreach (ISceneHandler item in HandlersFor(((Scene)(ref scene)).name))
			{
				((MonoBehaviour)this).StartCoroutine(RunHandler(item, scene));
			}
		}

		private IEnumerator BootInject()
		{
			while ((Object)(object)GameManager.instance == (Object)null)
			{
				yield return null;
			}
			ISceneHandler[] scenes = _scenes;
			foreach (ISceneHandler sceneHandler in scenes)
			{
				Scene sceneByName = SceneManager.GetSceneByName(sceneHandler.SceneName);
				if (((Scene)(ref sceneByName)).IsValid() && ((Scene)(ref sceneByName)).isLoaded)
				{
					yield return RunHandler(sceneHandler, sceneByName);
				}
			}
		}

		private IEnumerator RunHandler(ISceneHandler handler, Scene scene)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (!_config.EnableSceneInjection.Value || !handler.Enabled)
			{
				yield break;
			}
			if (string.IsNullOrEmpty(_pluginDir))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Could not resolve plugin directory.");
				yield break;
			}
			string text = Path.GetFullPath(Path.Combine(_pluginDir, handler.CatalogRelativePath));
			string text2 = text.Replace($"{Path.DirectorySeparatorChar}BellTown{Path.DirectorySeparatorChar}", $"{Path.DirectorySeparatorChar}");
			if (text2 != text && File.Exists(text2))
			{
				if (text != text2)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Catalog path migrated: '" + text + "' -> '" + text2 + "'"));
				}
				text = text2;
			}
			yield return _catalog.Load(text);
			if (_catalog.IsReady)
			{
				yield return _injector.Inject(handler, scene);
			}
		}

		private IEnumerable<ISceneHandler> HandlersFor(string sceneName)
		{
			ISceneHandler[] scenes = _scenes;
			foreach (ISceneHandler sceneHandler in scenes)
			{
				if (string.Equals(sceneHandler.SceneName, sceneName, StringComparison.Ordinal))
				{
					yield return sceneHandler;
				}
			}
		}
	}
}