Decompiled source of Custom Levels v1.0.0

BepInEx/plugins/bro-CustomLevels/fred_plugin_2.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using FishNet.Connection;
using FishNet.Managing.Scened;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Scripting;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("fred_plugin_2")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("fred_plugin_2")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("a0650051-5c69-4a23-b28a-c815791fa0dc")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace FredLevelPlugin;

[Serializable]
[Preserve]
public class ScenesJson
{
	public BundleSpec[] bundles;
}
[Serializable]
[Preserve]
public class BundleSpec
{
	public string name;

	public string[] scenes;

	public string displayPrefix;
}
[BepInPlugin("com.fred.fredlevel", "Fred Level Plugin", "2.0.0")]
public class Plugin : BaseUnityPlugin
{
	internal class SceneEntry
	{
		public string SceneName;

		public string BundleDiskPath;

		public string SourcePack;

		public string DisplayPrefix;

		public string ScenePathInBundle;

		public bool TriedResolvePath;
	}

	public const string PluginGuid = "com.fred.fredlevel";

	public const string PluginName = "Fred Level Plugin";

	public const string PluginVersion = "2.0.0";

	internal static ManualLogSource LogSrc;

	internal static Harmony Harmony;

	internal static readonly Dictionary<string, SceneEntry> SceneRegistry = new Dictionary<string, SceneEntry>(StringComparer.OrdinalIgnoreCase);

	internal static readonly Dictionary<string, AssetBundle> LoadedBundles = new Dictionary<string, AssetBundle>(StringComparer.OrdinalIgnoreCase);

	internal static int NetSceneLoadDepth = 0;

	internal static string AssetBundlesRoot;

	private void Awake()
	{
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Expected O, but got Unknown
		LogSrc = ((BaseUnityPlugin)this).Logger;
		try
		{
			string path = Directory.GetParent(Paths.PluginPath)?.FullName ?? Paths.PluginPath;
			AssetBundlesRoot = Path.Combine(path, "Assets", "AssetBundles");
		}
		catch
		{
			AssetBundlesRoot = Path.Combine(Application.dataPath, "Assets", "AssetBundles");
		}
		ScanScenePacks();
		DebugDumpRegistry(tryResolve: true);
		Harmony = new Harmony("com.fred.fredlevel");
		Harmony.PatchAll(typeof(Plugin).Assembly);
		Log($"Initialized. Registered {SceneRegistry.Count} scene(s).");
	}

	private static void DebugDumpRegistry(bool tryResolve)
	{
		Log($"[Debug] SceneRegistry has {SceneRegistry.Count} item(s).");
		foreach (KeyValuePair<string, SceneEntry> item in SceneRegistry)
		{
			SceneEntry value = item.Value;
			bool flag = (tryResolve ? EnsureScenePathResolved(value) : (!string.IsNullOrEmpty(value.ScenePathInBundle)));
			string text = (string.IsNullOrEmpty(value.BundleDiskPath) ? "(none)" : Path.GetFileName(value.BundleDiskPath));
			Log("[Debug] '" + value.SceneName + "' from pack '" + value.SourcePack + "' -> bundle '" + text + "' -> " + (flag ? value.ScenePathInBundle : "UNRESOLVED"));
		}
	}

	internal static void Log(string msg)
	{
		if (LogSrc != null)
		{
			LogSrc.LogInfo((object)("[FredLevel] " + msg));
		}
		else
		{
			Debug.Log((object)("[FredLevel] " + msg));
		}
	}

	internal static void Warn(string msg)
	{
		if (LogSrc != null)
		{
			LogSrc.LogWarning((object)("[FredLevel] " + msg));
		}
		else
		{
			Debug.LogWarning((object)("[FredLevel] " + msg));
		}
	}

	internal static void Error(string msg, Exception e = null)
	{
		if (LogSrc != null)
		{
			LogSrc.LogError((object)("[FredLevel] " + msg + ((e != null) ? (" :: " + e) : "")));
		}
		else
		{
			Debug.LogError((object)("[FredLevel] " + msg + ((e != null) ? (" :: " + e) : "")));
		}
	}

	private static void ScanScenePacks()
	{
		try
		{
			if (!Directory.Exists(Paths.PluginPath))
			{
				Warn("Plugins path not found: " + Paths.PluginPath);
				return;
			}
			int num = 0;
			string text = Path.Combine(Paths.PluginPath, "scenes.json");
			if (File.Exists(text))
			{
				num++;
				ParseScenesJson(text, "(plugins root)");
			}
			string[] directories = Directory.GetDirectories(Paths.PluginPath, "*", SearchOption.TopDirectoryOnly);
			string[] array = directories;
			foreach (string text2 in array)
			{
				string text3 = FindScenesJson(text2);
				if (text3 != null)
				{
					num++;
					ParseScenesJson(text3, new DirectoryInfo(text2).Name);
				}
			}
			Log($"Scan complete. Found {num} scenes.json file(s).");
		}
		catch (Exception e)
		{
			Error("ScanScenePacks failed.", e);
		}
	}

	private static void ParseScenesJson(string scenesJsonPath, string sourcePackName)
	{
		try
		{
			string text = File.ReadAllText(scenesJsonPath, Encoding.UTF8);
			ScenesJson scenesJson = JsonUtility.FromJson<ScenesJson>(text);
			if (scenesJson == null || scenesJson.bundles == null || scenesJson.bundles.Length == 0)
			{
				if (scenesJson == null)
				{
					Warn($"FromJson returned null for {scenesJsonPath} (length={text.Length}). Trying fallback parser.");
				}
				else
				{
					Warn("Parsed but no bundles array in " + scenesJsonPath + ". Trying fallback parser.");
				}
				scenesJson = FallbackParseScenes(text);
				if (scenesJson == null || scenesJson.bundles == null || scenesJson.bundles.Length == 0)
				{
					Warn("Fallback JSON parse also found no bundles in " + scenesJsonPath + ".");
					return;
				}
				Log($"[Fallback JSON] Parsed {scenesJson.bundles.Length} bundle(s) from {scenesJsonPath}.");
			}
			BundleSpec[] bundles = scenesJson.bundles;
			foreach (BundleSpec bundleSpec in bundles)
			{
				if (bundleSpec == null)
				{
					continue;
				}
				if (string.IsNullOrWhiteSpace(bundleSpec.name))
				{
					Warn("A bundle entry in " + scenesJsonPath + " has no 'name'. Skipping.");
					continue;
				}
				if (bundleSpec.scenes == null || bundleSpec.scenes.Length == 0)
				{
					Warn("Bundle '" + bundleSpec.name + "' in " + scenesJsonPath + " has no 'scenes'. Skipping.");
					continue;
				}
				string text2 = ResolveBundleOnDisk(bundleSpec.name);
				if (string.IsNullOrEmpty(text2) || !File.Exists(text2))
				{
					Warn("Bundle file not found for '" + bundleSpec.name + "'. Looking under: " + AssetBundlesRoot);
					continue;
				}
				string[] scenes = bundleSpec.scenes;
				foreach (string text3 in scenes)
				{
					if (!string.IsNullOrWhiteSpace(text3))
					{
						SceneEntry sceneEntry = new SceneEntry
						{
							SceneName = text3.Trim(),
							BundleDiskPath = text2,
							SourcePack = sourcePackName,
							DisplayPrefix = (bundleSpec.displayPrefix ?? "")
						};
						if (!SceneRegistry.ContainsKey(sceneEntry.SceneName))
						{
							SceneRegistry[sceneEntry.SceneName] = sceneEntry;
							continue;
						}
						Warn("Duplicate scene name '" + sceneEntry.SceneName + "' encountered (pack '" + sceneEntry.SourcePack + "'). Keeping the first registration.");
					}
				}
			}
		}
		catch (Exception e)
		{
			Error("Failed parsing " + scenesJsonPath, e);
		}
	}

	private static string FindScenesJson(string subdir)
	{
		try
		{
			foreach (string item in Directory.EnumerateFiles(subdir, "*", SearchOption.TopDirectoryOnly))
			{
				string fileName = Path.GetFileName(item);
				if (string.Equals(fileName, "scenes.json", StringComparison.OrdinalIgnoreCase))
				{
					return item;
				}
			}
		}
		catch
		{
		}
		return null;
	}

	private static string ResolveBundleOnDisk(string nameFromJson)
	{
		string text = nameFromJson.Trim();
		if (Path.IsPathRooted(text))
		{
			return text;
		}
		string text2 = Path.Combine(AssetBundlesRoot, text);
		if (File.Exists(text2))
		{
			return text2;
		}
		string[] array = new string[2] { ".bundle", ".unity3d" };
		string[] array2 = array;
		foreach (string text3 in array2)
		{
			text2 = Path.Combine(AssetBundlesRoot, text + text3);
			if (File.Exists(text2))
			{
				return text2;
			}
		}
		try
		{
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text);
			text2 = Path.Combine(AssetBundlesRoot, fileNameWithoutExtension);
			if (File.Exists(text2))
			{
				return text2;
			}
			string[] array3 = array;
			foreach (string text4 in array3)
			{
				text2 = Path.Combine(AssetBundlesRoot, fileNameWithoutExtension + text4);
				if (File.Exists(text2))
				{
					return text2;
				}
			}
		}
		catch
		{
		}
		return null;
	}

	internal static bool EnsureScenePathResolved(SceneEntry entry)
	{
		if (entry == null)
		{
			return false;
		}
		if (entry.TriedResolvePath && !string.IsNullOrEmpty(entry.ScenePathInBundle))
		{
			return true;
		}
		entry.TriedResolvePath = true;
		try
		{
			if (string.IsNullOrEmpty(entry.BundleDiskPath) || !File.Exists(entry.BundleDiskPath))
			{
				Warn("Bundle missing for scene '" + entry.SceneName + "'. Path: " + entry.BundleDiskPath);
				return false;
			}
			if (!LoadedBundles.TryGetValue(entry.BundleDiskPath, out var value) || (Object)(object)value == (Object)null)
			{
				value = AssetBundle.LoadFromFile(entry.BundleDiskPath);
				if ((Object)(object)value == (Object)null)
				{
					Warn("Failed to load AssetBundle: " + entry.BundleDiskPath);
					return false;
				}
				LoadedBundles[entry.BundleDiskPath] = value;
			}
			string[] allScenePaths = value.GetAllScenePaths();
			if (allScenePaths == null || allScenePaths.Length == 0)
			{
				Warn("No scenes in bundle: " + entry.BundleDiskPath);
				return false;
			}
			foreach (string text in allScenePaths)
			{
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text);
				if (string.Equals(fileNameWithoutExtension, entry.SceneName, StringComparison.OrdinalIgnoreCase))
				{
					entry.ScenePathInBundle = text;
					Log("Resolved '" + entry.SceneName + "' -> '" + entry.ScenePathInBundle + "' (bundle: " + Path.GetFileName(entry.BundleDiskPath) + ")");
					return true;
				}
			}
			Warn("Bundle '" + Path.GetFileName(entry.BundleDiskPath) + "' does not contain a scene named '" + entry.SceneName + "'. Available: " + string.Join(", ", allScenePaths.Select((string s) => Path.GetFileNameWithoutExtension(s))));
		}
		catch (Exception e)
		{
			Error("EnsureScenePathResolved failed for '" + entry.SceneName + "'.", e);
		}
		return false;
	}

	internal static void InjectAllMaps(MapsManager mm)
	{
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: 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_0104: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Expected O, but got Unknown
		//IL_015c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0161: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)mm == (Object)null)
		{
			return;
		}
		try
		{
			if (SceneRegistry.Count == 0)
			{
				return;
			}
			List<Map> list = new List<Map>();
			if (mm.allMaps != null)
			{
				list.AddRange(mm.allMaps);
			}
			if (mm.allMapsDict == null)
			{
				mm.allMapsDict = new Dictionary<string, Map>();
			}
			object? value = AccessTools.Field(typeof(MapsManager), "mapInstance").GetValue(mm);
			GameObject val = (GameObject)((value is GameObject) ? value : null);
			object? value2 = AccessTools.Field(typeof(MapsManager), "standardMapParent").GetValue(mm);
			Transform val2 = (Transform)((value2 is Transform) ? value2 : null);
			foreach (KeyValuePair<string, SceneEntry> item in SceneRegistry)
			{
				string key = item.Key;
				if (mm.allMapsDict.ContainsKey(key))
				{
					continue;
				}
				Map val3 = new Map
				{
					mapName = key,
					index = list.Count,
					isDlcExclusive = false,
					isAltMap = false,
					isUnlocked = true,
					isSelected = false,
					mapInstance = null
				};
				list.Add(val3);
				mm.allMapsDict[val3.mapName] = val3;
				if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null)
				{
					GameObject val4 = Object.Instantiate<GameObject>(val, val2.position, Quaternion.identity, val2);
					MapInstance component = val4.GetComponent<MapInstance>();
					if ((Object)(object)component != (Object)null)
					{
						val3.mapInstance = component;
						component.name = val3.mapName;
						component.selected = false;
						val4.SetActive(true);
						component.UpdateUI();
					}
				}
				else
				{
					Warn("Could not access 'mapInstance' prefab or 'standardMapParent' via reflection. UI entry may not appear.");
				}
			}
			mm.allMaps = list.ToArray();
			List<int> list2 = new List<int>();
			if (mm.unlockedMaps != null)
			{
				list2.AddRange(mm.unlockedMaps);
			}
			for (int i = 0; i < list.Count; i++)
			{
				if (!list2.Contains(i))
				{
					list2.Add(i);
				}
			}
			mm.unlockedMaps = list2.ToArray();
			mm.SortMapsFromMapInstanceName();
			Log($"Injected {SceneRegistry.Count} custom map(s) into MapsManager.");
		}
		catch (Exception e)
		{
			Error("InjectAllMaps failed.", e);
		}
	}

	private static ScenesJson FallbackParseScenes(string json)
	{
		try
		{
			if (!(SimpleJson.Parse(json) is Dictionary<string, object> dictionary))
			{
				return null;
			}
			if (!dictionary.TryGetValue("bundles", out var value))
			{
				ScenesJson scenesJson = new ScenesJson();
				scenesJson.bundles = new BundleSpec[0];
				return scenesJson;
			}
			if (!(value is List<object> list))
			{
				ScenesJson scenesJson = new ScenesJson();
				scenesJson.bundles = new BundleSpec[0];
				return scenesJson;
			}
			List<BundleSpec> list2 = new List<BundleSpec>();
			foreach (object item in list)
			{
				if (!(item is Dictionary<string, object> dictionary2))
				{
					continue;
				}
				object value2;
				string text = (dictionary2.TryGetValue("name", out value2) ? (value2 as string) : null);
				object value3;
				List<object> list3 = (dictionary2.TryGetValue("scenes", out value3) ? (value3 as List<object>) : null);
				object value4;
				string displayPrefix = (dictionary2.TryGetValue("displayPrefix", out value4) ? (value4 as string) : null);
				if (string.IsNullOrWhiteSpace(text) || list3 == null || list3.Count == 0)
				{
					continue;
				}
				List<string> list4 = new List<string>();
				foreach (object item2 in list3)
				{
					string text2 = item2 as string;
					if (!string.IsNullOrWhiteSpace(text2))
					{
						list4.Add(text2);
					}
				}
				if (list4.Count != 0)
				{
					list2.Add(new BundleSpec
					{
						name = text,
						scenes = list4.ToArray(),
						displayPrefix = displayPrefix
					});
				}
			}
			return new ScenesJson
			{
				bundles = list2.ToArray()
			};
		}
		catch (Exception e)
		{
			Error("FallbackParseScenes failed.", e);
			return null;
		}
	}
}
[HarmonyPatch(typeof(MapsManager), "InitMaps")]
internal static class MapsManager_InitMaps_Postfix
{
	public static void Postfix(MapsManager __instance)
	{
		try
		{
			Plugin.InjectAllMaps(__instance);
		}
		catch (Exception e)
		{
			Plugin.Error("InitMaps postfix failed.", e);
		}
	}
}
[HarmonyPatch(typeof(SceneLoader), "LoadScene", new Type[] { typeof(string) })]
internal static class SceneLoader_LoadScene_Prefix
{
	public static bool Prefix(ref string sceneName)
	{
		try
		{
			if (string.IsNullOrEmpty(sceneName))
			{
				return true;
			}
			if (Plugin.SceneRegistry.TryGetValue(sceneName, out var value))
			{
				if (Plugin.NetSceneLoadDepth > 0)
				{
					Plugin.Log("Skipping duplicate local LoadScene('" + sceneName + "') because FishNet is already loading it.");
					return false;
				}
				if (Plugin.EnsureScenePathResolved(value))
				{
					Plugin.Log("Redirecting SceneLoader.LoadScene from '" + sceneName + "' to bundle path '" + value.ScenePathInBundle + "'.");
					sceneName = value.ScenePathInBundle;
				}
				else
				{
					Plugin.Warn("Bundle scene path not found for '" + sceneName + "'. Falling back; load may fail.");
				}
			}
		}
		catch (Exception e)
		{
			Plugin.Error("SceneLoader_LoadScene_Prefix failed.", e);
		}
		return true;
	}
}
internal static class FishNetSceneRedirect
{
	private static bool _reentry;

	private static List<string> GetRequestedSceneNames(SceneLoadData data)
	{
		List<string> list = new List<string>();
		if (data == null)
		{
			return list;
		}
		try
		{
			PropertyInfo property = ((object)data).GetType().GetProperty("Scenes", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				object value = property.GetValue(data, null);
				if (value is string[] array && array.Length != 0)
				{
					list.AddRange(array);
				}
			}
		}
		catch
		{
		}
		try
		{
			PropertyInfo property2 = ((object)data).GetType().GetProperty("SceneNames", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property2 != null)
			{
				object value2 = property2.GetValue(data, null);
				if (value2 is IEnumerable<string> collection)
				{
					list.AddRange(collection);
				}
			}
		}
		catch
		{
		}
		try
		{
			FieldInfo field = ((object)data).GetType().GetField("SceneLookupDatas", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null && field.GetValue(data) is IEnumerable enumerable)
			{
				foreach (object item in enumerable)
				{
					PropertyInfo property3 = item.GetType().GetProperty("Name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (property3 != null)
					{
						string text = property3.GetValue(item, null) as string;
						if (!string.IsNullOrEmpty(text))
						{
							list.Add(text);
						}
					}
				}
			}
		}
		catch
		{
		}
		return list;
	}

	private static SceneLoadData CloneWithReplacedScenes(SceneLoadData original, List<string> newScenePaths)
	{
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Expected O, but got Unknown
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Expected O, but got Unknown
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Expected O, but got Unknown
		//IL_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_0186: Expected O, but got Unknown
		SceneLoadData val = null;
		try
		{
			ConstructorInfo constructor = typeof(SceneLoadData).GetConstructor(new Type[1] { typeof(string[]) });
			if (constructor != null)
			{
				val = (SceneLoadData)constructor.Invoke(new object[1] { newScenePaths.ToArray() });
			}
			else
			{
				ConstructorInfo constructor2 = typeof(SceneLoadData).GetConstructor(new Type[1] { typeof(string) });
				if (constructor2 != null)
				{
					val = (SceneLoadData)constructor2.Invoke(new object[1] { newScenePaths.FirstOrDefault() ?? string.Empty });
				}
			}
			if (val == null)
			{
				throw new MissingMethodException("SceneLoadData ctor(string[]) and ctor(string) not found.");
			}
		}
		catch
		{
			ConstructorInfo[] constructors = ((object)original).GetType().GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			ConstructorInfo[] array = constructors;
			foreach (ConstructorInfo constructorInfo in array)
			{
				ParameterInfo[] parameters = constructorInfo.GetParameters();
				if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string[]))
				{
					val = (SceneLoadData)constructorInfo.Invoke(new object[1] { newScenePaths.ToArray() });
					break;
				}
				if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string))
				{
					val = (SceneLoadData)constructorInfo.Invoke(new object[1] { newScenePaths.FirstOrDefault() ?? string.Empty });
					break;
				}
			}
			if (val == null)
			{
				throw;
			}
		}
		try
		{
			PropertyInfo property = ((object)original).GetType().GetProperty("ReplaceScenes", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				object value = property.GetValue(original, null);
				PropertyInfo property2 = ((object)val).GetType().GetProperty("ReplaceScenes", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property2 != null)
				{
					property2.SetValue(val, value, null);
				}
			}
			PropertyInfo property3 = ((object)original).GetType().GetProperty("Options", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property3 != null)
			{
				object value2 = property3.GetValue(original, null);
				PropertyInfo property4 = ((object)val).GetType().GetProperty("Options", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property4 != null)
				{
					property4.SetValue(val, value2, null);
				}
			}
			PropertyInfo property5 = ((object)original).GetType().GetProperty("PreferredActiveScene", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property5 != null)
			{
				object value3 = property5.GetValue(original, null);
				PropertyInfo property6 = ((object)val).GetType().GetProperty("PreferredActiveScene", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property6 != null)
				{
					property6.SetValue(val, value3, null);
				}
			}
			PropertyInfo property7 = ((object)original).GetType().GetProperty("MovedNetworkObjects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property7 != null)
			{
				object value4 = property7.GetValue(original, null);
				PropertyInfo property8 = ((object)val).GetType().GetProperty("MovedNetworkObjects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property8 != null)
				{
					property8.SetValue(val, value4, null);
				}
			}
		}
		catch
		{
		}
		return val;
	}

	private static bool TryBuildReplacement(SceneLoadData sceneLoadData, out SceneLoadData replacement)
	{
		replacement = null;
		List<string> requestedSceneNames = GetRequestedSceneNames(sceneLoadData);
		if (requestedSceneNames.Count == 0)
		{
			return false;
		}
		bool flag = false;
		List<string> list = new List<string>(requestedSceneNames.Count);
		foreach (string item in requestedSceneNames)
		{
			if (Plugin.SceneRegistry.TryGetValue(item, out var value) && Plugin.EnsureScenePathResolved(value))
			{
				list.Add(value.ScenePathInBundle);
				flag = true;
			}
			else
			{
				list.Add(item);
			}
		}
		if (!flag)
		{
			return false;
		}
		replacement = CloneWithReplacedScenes(sceneLoadData, list);
		return replacement != null;
	}

	internal static bool TryRedirect(SceneManager manager, SceneLoadData data, NetworkConnection ownerOrNull)
	{
		if (_reentry)
		{
			return false;
		}
		if (!TryBuildReplacement(data, out var replacement))
		{
			return false;
		}
		_reentry = true;
		Plugin.NetSceneLoadDepth++;
		try
		{
			if (ownerOrNull == (NetworkConnection)null)
			{
				Plugin.Log("Redirecting FishNet LoadGlobalScenes to paths from bundles.");
				manager.LoadGlobalScenes(replacement);
			}
			else
			{
				Plugin.Log("Redirecting FishNet LoadConnectionScenes to paths from bundles.");
				manager.LoadConnectionScenes(ownerOrNull, replacement);
			}
		}
		finally
		{
			Plugin.NetSceneLoadDepth--;
			_reentry = false;
		}
		return true;
	}
}
[HarmonyPatch]
internal static class FishNetSceneManagerPatches
{
	[HarmonyPatch(typeof(SceneManager), "LoadGlobalScenes", new Type[] { typeof(SceneLoadData) })]
	[HarmonyPrefix]
	private static bool LoadGlobalScenes_Prefix(SceneManager __instance, [HarmonyArgument(0)] SceneLoadData sceneLoadData)
	{
		try
		{
			if (FishNetSceneRedirect.TryRedirect(__instance, sceneLoadData, null))
			{
				return false;
			}
		}
		catch (Exception e)
		{
			Plugin.Error("FishNet LoadGlobalScenes prefix failed.", e);
		}
		return true;
	}

	[HarmonyPatch(typeof(SceneManager), "LoadConnectionScenes", new Type[]
	{
		typeof(NetworkConnection),
		typeof(SceneLoadData)
	})]
	[HarmonyPrefix]
	private static bool LoadConnectionScenes_Prefix(SceneManager __instance, [HarmonyArgument(0)] NetworkConnection connection, [HarmonyArgument(1)] SceneLoadData sceneLoadData)
	{
		try
		{
			if (FishNetSceneRedirect.TryRedirect(__instance, sceneLoadData, connection))
			{
				return false;
			}
		}
		catch (Exception e)
		{
			Plugin.Error("FishNet LoadConnectionScenes prefix failed.", e);
		}
		return true;
	}
}
internal static class SimpleJson
{
	private class Parser
	{
		private readonly string _s;

		private int _i;

		private char Peek => _s[_i];

		private bool End => _i >= _s.Length;

		public Parser(string s)
		{
			_s = s ?? "";
			_i = 0;
		}

		public object ParseValue()
		{
			SkipWs();
			if (End)
			{
				return null;
			}
			switch (Peek)
			{
			case '{':
				return ParseObject();
			case '[':
				return ParseArray();
			case '"':
				return ParseString();
			default:
				if (Match("true"))
				{
					return true;
				}
				if (Match("false"))
				{
					return false;
				}
				if (Match("null"))
				{
					return null;
				}
				return ParseNumberOrBare();
			}
		}

		private Dictionary<string, object> ParseObject()
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
			Expect('{');
			SkipWs();
			if (Try('}'))
			{
				return dictionary;
			}
			while (true)
			{
				SkipWs();
				string key = ParseString();
				SkipWs();
				Expect(':');
				object value = ParseValue();
				dictionary[key] = value;
				SkipWs();
				if (Try('}'))
				{
					break;
				}
				Expect(',');
			}
			return dictionary;
		}

		private List<object> ParseArray()
		{
			List<object> list = new List<object>();
			Expect('[');
			SkipWs();
			if (Try(']'))
			{
				return list;
			}
			while (true)
			{
				object item = ParseValue();
				list.Add(item);
				SkipWs();
				if (Try(']'))
				{
					break;
				}
				Expect(',');
			}
			return list;
		}

		private string ParseString()
		{
			Expect('"');
			StringBuilder stringBuilder = new StringBuilder();
			while (!End)
			{
				char c = Next();
				switch (c)
				{
				case '\\':
					if (End)
					{
						break;
					}
					c = Next();
					switch (c)
					{
					case '"':
						stringBuilder.Append('"');
						break;
					case '\\':
						stringBuilder.Append('\\');
						break;
					case '/':
						stringBuilder.Append('/');
						break;
					case 'b':
						stringBuilder.Append('\b');
						break;
					case 'f':
						stringBuilder.Append('\f');
						break;
					case 'n':
						stringBuilder.Append('\n');
						break;
					case 'r':
						stringBuilder.Append('\r');
						break;
					case 't':
						stringBuilder.Append('\t');
						break;
					case 'u':
						if (_i + 4 <= _s.Length)
						{
							string s = _s.Substring(_i, 4);
							if (ushort.TryParse(s, NumberStyles.HexNumber, null, out var result))
							{
								stringBuilder.Append((char)result);
								_i += 4;
							}
						}
						break;
					default:
						stringBuilder.Append(c);
						break;
					}
					continue;
				default:
					stringBuilder.Append(c);
					continue;
				case '"':
					break;
				}
				break;
			}
			return stringBuilder.ToString();
		}

		private object ParseNumberOrBare()
		{
			int i = _i;
			while (!End)
			{
				char peek = Peek;
				if (char.IsWhiteSpace(peek) || peek == ',' || peek == ']' || peek == '}')
				{
					break;
				}
				_i++;
			}
			string text = _s.Substring(i, _i - i);
			if (double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			return text;
		}

		private void SkipWs()
		{
			while (!End && char.IsWhiteSpace(Peek))
			{
				_i++;
			}
		}

		private bool Match(string lit)
		{
			SkipWs();
			if (_i + lit.Length > _s.Length)
			{
				return false;
			}
			for (int i = 0; i < lit.Length; i++)
			{
				if (_s[_i + i] != lit[i])
				{
					return false;
				}
			}
			_i += lit.Length;
			return true;
		}

		private void Expect(char ch)
		{
			SkipWs();
			if (End || _s[_i] != ch)
			{
				throw new Exception($"Expected '{ch}' at {_i}");
			}
			_i++;
		}

		private bool Try(char ch)
		{
			SkipWs();
			if (!End && _s[_i] == ch)
			{
				_i++;
				return true;
			}
			return false;
		}

		private char Next()
		{
			return _s[_i++];
		}
	}

	public static object Parse(string json)
	{
		return new Parser(json).ParseValue();
	}
}