Decompiled source of Replay v1.0.0

LCReplay.dll

Decompiled 10 hours ago
using System;
using System.Buffers;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using LCReplay.Core;
using LCReplay.Core.Archive;
using LCReplay.Core.Lifecycle;
using LCReplay.Plugin.Capture;
using LCReplay.Plugin.Library;
using LCReplay.Plugin.Playback;
using LCReplay.Plugin.UI;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using TMPro;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.Playables;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.TextCore;
using UnityEngine.TextCore.LowLevel;
using UnityEngine.UI;
using UnityEngine.VFX;

[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("LCReplay")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Local gameplay recorder and isolated free-camera replay for Lethal Company.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+31677f6cc6ace662863f1d3840f84ade19c50bbc")]
[assembly: AssemblyProduct("LCReplay")]
[assembly: AssemblyTitle("LCReplay")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 LCReplay.Plugin
{
	internal static class AnimationAssetRegistry
	{
		private sealed class Candidate
		{
			internal RuntimeAnimatorController Controller;

			internal Avatar? Avatar;

			internal string Kind = "";
		}

		private static readonly List<Candidate> Candidates = new List<Candidate>();

		private static object? scannedNetworkConfig;

		internal static void Remember(Animator animator, string kind = "")
		{
			if (Object.op_Implicit((Object)(object)animator) && Object.op_Implicit((Object)(object)animator.runtimeAnimatorController))
			{
				if (kind.Length == 0)
				{
					kind = Classify(((Component)animator).gameObject);
				}
				Add(animator.runtimeAnimatorController, animator.avatar, kind);
			}
		}

		private static string Classify(GameObject obj)
		{
			Type type = GameAccess.Type("GameNetcodeStuff.PlayerControllerB");
			if (type != null && (Object.op_Implicit((Object)(object)obj.GetComponentInParent(type)) || Object.op_Implicit((Object)(object)obj.GetComponentInChildren(type, true))))
			{
				return "player";
			}
			Type type2 = GameAccess.Type("EnemyAI");
			if (!(type2 != null) || (!Object.op_Implicit((Object)(object)obj.GetComponentInParent(type2)) && !Object.op_Implicit((Object)(object)obj.GetComponentInChildren(type2, true))))
			{
				return "";
			}
			return "enemy";
		}

		private static void Add(RuntimeAnimatorController controller, Avatar? avatar, string kind)
		{
			if (!Candidates.Any((Candidate candidate) => (Object)(object)candidate.Controller == (Object)(object)controller && (Object)(object)candidate.Avatar == (Object)(object)avatar && candidate.Kind == kind))
			{
				Candidates.Add(new Candidate
				{
					Controller = controller,
					Avatar = avatar,
					Kind = kind
				});
			}
		}

		private static void RememberInstalledPrefabs()
		{
			object obj = GameAccess.Read(GameAccess.Singleton("Unity.Netcode.NetworkManager"), "NetworkConfig");
			if (obj == null || obj == scannedNetworkConfig)
			{
				return;
			}
			object? obj2 = GameAccess.Read(obj, "PlayerPrefab");
			GameObject val = (GameObject)((obj2 is GameObject) ? obj2 : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				object? obj3 = GameAccess.Read(GameAccess.Singleton("StartOfRound"), "playerPrefab");
				val = (GameObject)((obj3 is GameObject) ? obj3 : null);
			}
			if (Object.op_Implicit((Object)(object)val))
			{
				Animator[] componentsInChildren = val.GetComponentsInChildren<Animator>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					Remember(componentsInChildren[i], "player");
				}
			}
			if (!(GameAccess.Read(GameAccess.Read(obj, "Prefabs"), "Prefabs") is IEnumerable enumerable))
			{
				return;
			}
			foreach (object item in enumerable)
			{
				object? obj4 = GameAccess.Read(item, "Prefab");
				GameObject val2 = (GameObject)((obj4 is GameObject) ? obj4 : null);
				if (Object.op_Implicit((Object)(object)val2))
				{
					string kind = Classify(val2);
					Animator[] componentsInChildren = val2.GetComponentsInChildren<Animator>(true);
					for (int i = 0; i < componentsInChildren.Length; i++)
					{
						Remember(componentsInChildren[i], kind);
					}
				}
			}
			scannedNetworkConfig = obj;
		}

		internal static (RuntimeAnimatorController? Controller, Avatar? Avatar) Resolve(string controllerName, string avatarName, string actorKind, IEnumerable<string> recordedClips)
		{
			if (controllerName.Length == 0)
			{
				return (Controller: null, Avatar: null);
			}
			RememberInstalledPrefabs();
			Animator[] array = Resources.FindObjectsOfTypeAll<Animator>();
			foreach (Animator val in array)
			{
				if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.runtimeAnimatorController) && ((Object)val.runtimeAnimatorController).name == controllerName)
				{
					Remember(val);
				}
			}
			RuntimeAnimatorController[] array2 = Resources.FindObjectsOfTypeAll<RuntimeAnimatorController>();
			foreach (RuntimeAnimatorController controller in array2)
			{
				if (Object.op_Implicit((Object)(object)controller) && ((Object)controller).name == controllerName && !Candidates.Any((Candidate candidate) => (Object)(object)candidate.Controller == (Object)(object)controller))
				{
					Add(controller, null, "");
				}
			}
			Candidate[] array3 = Candidates.Where((Candidate candidate) => Object.op_Implicit((Object)(object)candidate.Controller) && ((Object)candidate.Controller).name == controllerName).ToArray();
			if (actorKind == "player" || actorKind == "enemy")
			{
				Candidate[] array4 = array3.Where((Candidate candidate) => candidate.Kind == actorKind).ToArray();
				array3 = ((array4.Length != 0) ? array4 : ((actorKind == "player") ? Array.Empty<Candidate>() : array3.Where((Candidate candidate) => candidate.Kind.Length == 0).ToArray()));
			}
			if (array3.Length == 0)
			{
				return (Controller: null, Avatar: null);
			}
			if (avatarName.Length != 0)
			{
				Candidate[] array5 = array3.Where((Candidate candidate) => Object.op_Implicit((Object)(object)candidate.Avatar) && ((Object)candidate.Avatar).name == avatarName).ToArray();
				if (array5.Length == 0)
				{
					Avatar[] avatars = (from avatar in Resources.FindObjectsOfTypeAll<Avatar>()
						where Object.op_Implicit((Object)(object)avatar) && ((Object)avatar).name == avatarName
						select avatar).Take(2).ToArray();
					if (avatars.Length != 1)
					{
						return (Controller: null, Avatar: null);
					}
					array3 = array3.Where((Candidate candidate) => (Object)(object)candidate.Avatar == (Object)null || (Object)(object)candidate.Avatar == (Object)(object)avatars[0]).ToArray();
					if (array3.Length == 0)
					{
						return (Controller: null, Avatar: null);
					}
					return Select(array3, recordedClips, avatars[0]);
				}
				array3 = array5;
			}
			return Select(array3, recordedClips, null);
		}

		private static (RuntimeAnimatorController? Controller, Avatar? Avatar) Select(Candidate[] candidates, IEnumerable<string> recordedClips, Avatar? fallbackAvatar)
		{
			string[] clips = recordedClips.Where((string name) => !string.IsNullOrEmpty(name)).Distinct<string>(StringComparer.Ordinal).Take(32)
				.ToArray();
			if (candidates.Select((Candidate item) => item.Controller).Distinct().Count() > 1 && clips.Length != 0)
			{
				candidates = candidates.Where(delegate(Candidate candidate2)
				{
					try
					{
						HashSet<string> hashSet = new HashSet<string>(from clip in candidate2.Controller.animationClips
							where Object.op_Implicit((Object)(object)clip)
							select ((Object)clip).name, StringComparer.Ordinal);
						return clips.All(hashSet.Contains);
					}
					catch
					{
						return false;
					}
				}).ToArray();
			}
			RuntimeAnimatorController[] controllers = candidates.Select((Candidate item) => item.Controller).Distinct().ToArray();
			if (controllers.Length != 1)
			{
				return (Controller: null, Avatar: null);
			}
			Candidate candidate = candidates.First((Candidate item) => (Object)(object)item.Controller == (Object)(object)controllers[0]);
			return (Controller: candidate.Controller, Avatar: Object.op_Implicit((Object)(object)candidate.Avatar) ? candidate.Avatar : fallbackAvatar);
		}
	}
	internal static class ItemAssetRegistry
	{
		private sealed class Candidate
		{
			internal string ItemId = "";

			internal AudioClip Clip;
		}

		private sealed class HandCandidate
		{
			internal string Name = "";

			internal bool TwoHandedAnimation;

			internal Vector3 PositionOffset;

			internal Vector3 RotationOffset;
		}

		private static readonly List<Candidate> Candidates = new List<Candidate>();

		private static readonly List<HandCandidate> HandCandidates = new List<HandCandidate>();

		private static readonly Dictionary<string, bool> HandCache = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, float> NextHandScan = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, (Vector3 Position, Vector3 Rotation)> HeldPoseCache = new Dictionary<string, (Vector3, Vector3)>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, float> NextHeldPoseScan = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static object? scannedConfig;

		private static void Remember(object? properties)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: 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_00f8: 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)
			if (properties == null)
			{
				return;
			}
			string name = GameAccess.Read(properties, "itemName") as string;
			if (!string.IsNullOrWhiteSpace(name))
			{
				object obj = GameAccess.Read(properties, "twoHandedAnimation");
				if (obj is bool)
				{
					bool twoHanded = (bool)obj;
					obj = GameAccess.Read(properties, "positionOffset");
					if (obj is Vector3)
					{
						Vector3 positionOffset = (Vector3)obj;
						obj = GameAccess.Read(properties, "rotationOffset");
						if (obj is Vector3)
						{
							Vector3 rotationOffset = (Vector3)obj;
							if (GameAccess.Finite(positionOffset) && GameAccess.Finite(rotationOffset) && !HandCandidates.Any((HandCandidate candidate) => candidate.Name == name && candidate.TwoHandedAnimation == twoHanded && candidate.PositionOffset == positionOffset && candidate.RotationOffset == rotationOffset))
							{
								HandCandidates.Add(new HandCandidate
								{
									Name = name,
									TwoHandedAnimation = twoHanded,
									PositionOffset = positionOffset,
									RotationOffset = rotationOffset
								});
							}
						}
					}
				}
			}
			AudioClip clip = default(AudioClip);
			ref AudioClip reference = ref clip;
			object? obj2 = GameAccess.Read(properties, "dropSFX");
			reference = (AudioClip)((obj2 is AudioClip) ? obj2 : null);
			if (Object.op_Implicit((Object)(object)clip))
			{
				string id = GameAccess.Scalar(GameAccess.Read(properties, "itemId")) ?? "";
				if (!Candidates.Any((Candidate candidate) => candidate.ItemId == id && (Object)(object)candidate.Clip == (Object)(object)clip))
				{
					Candidates.Add(new Candidate
					{
						ItemId = id,
						Clip = clip
					});
				}
			}
		}

		private static void Scan()
		{
			object obj = GameAccess.Read(GameAccess.Singleton("Unity.Netcode.NetworkManager"), "NetworkConfig");
			if (obj != null && obj != scannedConfig)
			{
				Type type = GameAccess.Type("GrabbableObject");
				IEnumerable enumerable = GameAccess.Read(GameAccess.Read(obj, "Prefabs"), "Prefabs") as IEnumerable;
				if (type != null && enumerable != null)
				{
					foreach (object item in enumerable)
					{
						object? obj2 = GameAccess.Read(item, "Prefab");
						GameObject val = (GameObject)((obj2 is GameObject) ? obj2 : null);
						if (Object.op_Implicit((Object)(object)val))
						{
							Component componentInChildren = val.GetComponentInChildren(type, true);
							if (Object.op_Implicit((Object)(object)componentInChildren))
							{
								Remember(GameAccess.Read(componentInChildren, "itemProperties"));
							}
						}
					}
				}
				scannedConfig = obj;
			}
			Type type2 = GameAccess.Type("Item");
			if (type2 != null)
			{
				Object[] array = Resources.FindObjectsOfTypeAll(type2);
				for (int i = 0; i < array.Length; i++)
				{
					Remember(array[i]);
				}
			}
		}

		internal static AudioClip? Resolve(string itemId, string clipName)
		{
			if (itemId.Length == 0 && clipName.Length == 0)
			{
				return null;
			}
			AudioClip val = Match(itemId, clipName);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			Scan();
			return Match(itemId, clipName);
		}

		internal static bool? ResolveTwoHandedAnimation(string itemName)
		{
			if (string.IsNullOrWhiteSpace(itemName))
			{
				return null;
			}
			if (HandCache.TryGetValue(itemName, out var value))
			{
				return value;
			}
			if (NextHandScan.TryGetValue(itemName, out var value2) && Time.realtimeSinceStartup < value2)
			{
				return null;
			}
			Scan();
			bool[] array = (from candidate in HandCandidates
				where string.Equals(candidate.Name, itemName, StringComparison.OrdinalIgnoreCase)
				select candidate.TwoHandedAnimation).Distinct().Take(2).ToArray();
			if (array.Length == 1)
			{
				Dictionary<string, bool> handCache = HandCache;
				string key = itemName;
				bool num = array[0];
				bool value3 = num;
				handCache[key] = num;
				return value3;
			}
			NextHandScan[itemName] = Time.realtimeSinceStartup + 1f;
			return null;
		}

		internal static bool TryResolveHeldPose(string itemName, out Vector3 position, out Quaternion rotation)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: 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)
			position = Vector3.zero;
			rotation = Quaternion.identity;
			if (string.IsNullOrWhiteSpace(itemName))
			{
				return false;
			}
			if (!HeldPoseCache.TryGetValue(itemName, out (Vector3, Vector3) value))
			{
				if (NextHeldPoseScan.TryGetValue(itemName, out var value2) && Time.realtimeSinceStartup < value2)
				{
					return false;
				}
				Scan();
				(Vector3, Vector3)[] array = (from candidate in HandCandidates
					where string.Equals(candidate.Name, itemName, StringComparison.OrdinalIgnoreCase)
					select (PositionOffset: candidate.PositionOffset, RotationOffset: candidate.RotationOffset)).Distinct().Take(2).ToArray();
				if (array.Length != 1)
				{
					NextHeldPoseScan[itemName] = Time.realtimeSinceStartup + 1f;
					return false;
				}
				value = array[0];
				HeldPoseCache[itemName] = value;
			}
			position = value.Item1;
			rotation = Quaternion.Euler(value.Item2);
			return true;
		}

		private static AudioClip? Match(string itemId, string clipName)
		{
			AudioClip[] array = (from candidate in Candidates
				where Object.op_Implicit((Object)(object)candidate.Clip) && (itemId.Length == 0 || candidate.ItemId == itemId) && (clipName.Length == 0 || ((Object)candidate.Clip).name == clipName)
				select candidate.Clip).Distinct().Take(2).ToArray();
			if (array.Length != 1)
			{
				return null;
			}
			return array[0];
		}
	}
	internal sealed class LiveReplayInput : IDisposable
	{
		private readonly Dictionary<InputAction, bool> actions = new Dictionary<InputAction, bool>();

		private readonly HashSet<InputActionAsset> assets = new HashSet<InputActionAsset>();

		private object? hudOwner;

		private bool disposed;

		internal void Block(Component player)
		{
			if (disposed)
			{
				return;
			}
			Add(InputSystem.actions);
			object value = GameAccess.Read(player, "playerActions");
			object? obj = GameAccess.Read(value, "asset");
			Add((InputActionAsset?)((obj is InputActionAsset) ? obj : null));
			object obj2 = GameAccess.Singleton("HUDManager");
			if (hudOwner != obj2)
			{
				hudOwner = obj2;
				ResetSpectatorVoteHold(obj2);
			}
			object value2 = GameAccess.Read(obj2, "playerActions");
			object? obj3 = GameAccess.Read(value2, "asset");
			Add((InputActionAsset?)((obj3 is InputActionAsset) ? obj3 : null));
			foreach (InputAction key in actions.Keys)
			{
				if (key.enabled)
				{
					key.Disable();
				}
			}
		}

		private void Add(InputActionAsset? asset)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)asset) || !assets.Add(asset))
			{
				return;
			}
			Enumerator<InputActionMap> enumerator = asset.actionMaps.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					Enumerator<InputAction> enumerator2 = enumerator.Current.actions.GetEnumerator();
					try
					{
						while (enumerator2.MoveNext())
						{
							InputAction current = enumerator2.Current;
							actions[current] = current.enabled;
						}
					}
					finally
					{
						((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
					}
				}
			}
			finally
			{
				((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
			}
		}

		public void Dispose()
		{
			if (disposed)
			{
				return;
			}
			disposed = true;
			ResetSpectatorVoteHold(hudOwner);
			object obj = GameAccess.Singleton("HUDManager");
			if (obj != hudOwner)
			{
				ResetSpectatorVoteHold(obj);
			}
			foreach (KeyValuePair<InputAction, bool> action in actions)
			{
				try
				{
					if (action.Value)
					{
						action.Key.Enable();
					}
				}
				catch (Exception)
				{
				}
			}
			actions.Clear();
			assets.Clear();
			hudOwner = null;
		}

		private static void ResetSpectatorVoteHold(object? hud)
		{
			if (hud == null)
			{
				return;
			}
			try
			{
				hud.GetType().GetField("holdButtonToEndGameEarlyHoldTime", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(hud, 0f);
			}
			catch (Exception)
			{
			}
		}
	}
	internal static class NativeSoundAssets
	{
		private static readonly Dictionary<string, Dictionary<string, AudioClip>> Catalogs = new Dictionary<string, Dictionary<string, AudioClip>>(StringComparer.Ordinal);

		private static readonly Dictionary<string, WeakReference<AudioClip>?> UniqueClips = new Dictionary<string, WeakReference<AudioClip>>(StringComparer.Ordinal);

		private static readonly Dictionary<string, AudioSource?> Sources = new Dictionary<string, AudioSource>(StringComparer.Ordinal);

		private static float nextScan;

		private static Component? playerPrefab;

		private static Component? Prefab(string key)
		{
			if (key != "player")
			{
				return PrefabAssetRegistry.ResolvePrefab(key);
			}
			if (Object.op_Implicit((Object)(object)playerPrefab))
			{
				return playerPrefab;
			}
			Type type = GameAccess.Type("GameNetcodeStuff.PlayerControllerB");
			if (type == null)
			{
				return null;
			}
			object? obj = GameAccess.Read(GameAccess.Singleton("StartOfRound"), "playerPrefab");
			GameObject val = (GameObject)((obj is GameObject) ? obj : null);
			playerPrefab = (Component?)(Object.op_Implicit((Object)(object)val) ? ((object)val.GetComponentInChildren(type, true)) : ((object)Resources.FindObjectsOfTypeAll(type).OfType<Component>().FirstOrDefault((Func<Component, bool>)delegate(Component component)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				if (Object.op_Implicit((Object)(object)component))
				{
					Scene scene = component.gameObject.scene;
					return !((Scene)(ref scene)).IsValid();
				}
				return false;
			})));
			return playerPrefab;
		}

		internal static string Identity(Component owner, AudioClip clip)
		{
			return Catalog(owner).FirstOrDefault<KeyValuePair<string, AudioClip>>((KeyValuePair<string, AudioClip> pair) => (Object)(object)pair.Value == (Object)(object)clip).Key ?? "";
		}

		internal static Dictionary<string, AudioClip> Catalog(Component owner)
		{
			Dictionary<string, AudioClip> dictionary = new Dictionary<string, AudioClip>(StringComparer.Ordinal);
			MonoBehaviour[] componentsInChildren = owner.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val) && !(((object)val).GetType().Namespace ?? "").StartsWith("UnityEngine", StringComparison.Ordinal))
				{
					string path = EntityTracker.RelativePath(owner.transform, ((Component)val).transform) + "|" + ((object)val).GetType().FullName;
					Fields(val, path, dictionary, 0);
				}
			}
			AudioSource[] componentsInChildren2 = owner.GetComponentsInChildren<AudioSource>(true);
			foreach (AudioSource val2 in componentsInChildren2)
			{
				if (Object.op_Implicit((Object)(object)val2.clip))
				{
					dictionary[EntityTracker.RelativePath(owner.transform, ((Component)val2).transform) + "|AudioSource|clip"] = val2.clip;
				}
			}
			return dictionary;
		}

		internal static Dictionary<string, AudioClip>? InstalledCatalog(string key)
		{
			if (Catalogs.TryGetValue(key, out Dictionary<string, AudioClip> value))
			{
				return value;
			}
			Component val = Prefab(key);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			value = Catalog(val);
			if (Catalogs.Count < 1024)
			{
				Catalogs[key] = value;
			}
			return value;
		}

		private static void Fields(object obj, string path, Dictionary<string, AudioClip> clips, int depth)
		{
			Type type = obj.GetType();
			while (type != null && type != typeof(MonoBehaviour) && type != typeof(ScriptableObject))
			{
				FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (fieldInfo.IsStatic)
					{
						continue;
					}
					string text = path + "|" + fieldInfo.Name;
					if (fieldInfo.FieldType == typeof(AudioClip))
					{
						object? value = fieldInfo.GetValue(obj);
						AudioClip val = (AudioClip)((value is AudioClip) ? value : null);
						if (val != null && Object.op_Implicit((Object)(object)val))
						{
							clips[text] = val;
						}
						continue;
					}
					if (fieldInfo.FieldType == typeof(AudioClip[]) || fieldInfo.FieldType == typeof(List<AudioClip>))
					{
						if (!(fieldInfo.GetValue(obj) is IEnumerable enumerable))
						{
							continue;
						}
						int num = 0;
						foreach (object item in enumerable)
						{
							AudioClip val2 = (AudioClip)((item is AudioClip) ? item : null);
							if (val2 != null && Object.op_Implicit((Object)(object)val2))
							{
								clips[text + "|" + num] = val2;
							}
							if (++num >= 1024)
							{
								break;
							}
						}
						continue;
					}
					if (depth == 0 && typeof(ScriptableObject).IsAssignableFrom(fieldInfo.FieldType))
					{
						object? value2 = fieldInfo.GetValue(obj);
						ScriptableObject val3 = (ScriptableObject)((value2 is ScriptableObject) ? value2 : null);
						if (val3 != null && Object.op_Implicit((Object)(object)val3))
						{
							Fields(val3, text, clips, depth + 1);
							continue;
						}
					}
					if (depth >= 2 || !fieldInfo.FieldType.IsArray || !(fieldInfo.FieldType.GetElementType()?.Name == "EnemyBehaviourState") || !(fieldInfo.GetValue(obj) is IEnumerable enumerable2))
					{
						continue;
					}
					int num2 = 0;
					foreach (object item2 in enumerable2)
					{
						if (item2 != null)
						{
							Fields(item2, text + "|" + num2, clips, depth + 1);
						}
						if (++num2 >= 128)
						{
							break;
						}
					}
				}
				type = type.BaseType;
			}
		}

		internal static AudioClip? Resolve(string prefabKey, string identity, string name, out Component? prefab)
		{
			prefab = ((prefabKey.Length == 0) ? null : Prefab(prefabKey));
			if (!Object.op_Implicit((Object)(object)prefab) && prefabKey.StartsWith("enemy:", StringComparison.Ordinal))
			{
				return null;
			}
			if (Object.op_Implicit((Object)(object)prefab))
			{
				Dictionary<string, AudioClip> dictionary = InstalledCatalog(prefabKey);
				if (identity.Length != 0 && dictionary.TryGetValue(identity, out var value) && Object.op_Implicit((Object)(object)value) && ((Object)value).name == name)
				{
					return value;
				}
				AudioClip[] array = dictionary.Values.Where((AudioClip clip) => Object.op_Implicit((Object)(object)clip) && ((Object)clip).name == name).Distinct().Take(2)
					.ToArray();
				if (array.Length == 1)
				{
					return array[0];
				}
				if (array.Length > 1 || prefabKey.StartsWith("enemy:", StringComparison.Ordinal))
				{
					return null;
				}
			}
			WeakReference<AudioClip> value2;
			bool num = UniqueClips.TryGetValue(name, out value2);
			AudioClip target = null;
			value2?.TryGetTarget(out target);
			if ((!num || (value2 != null && !Object.op_Implicit((Object)(object)target))) && Time.realtimeSinceStartup >= nextScan)
			{
				nextScan = Time.realtimeSinceStartup + 2f;
				UniqueClips.Clear();
				AudioClip[] array2 = Resources.FindObjectsOfTypeAll<AudioClip>();
				foreach (AudioClip val in array2)
				{
					if (!Object.op_Implicit((Object)(object)val) || ((Object)val).name.StartsWith("LCReplay", StringComparison.Ordinal) || ((Object)val).name.StartsWith("LC Replay", StringComparison.Ordinal) || ((Object)val).name.StartsWith("Dissonance", StringComparison.OrdinalIgnoreCase))
					{
						continue;
					}
					if (UniqueClips.TryGetValue(((Object)val).name, out WeakReference<AudioClip> value3))
					{
						if (value3 == null || !value3.TryGetTarget(out var target2) || (Object)(object)target2 != (Object)(object)val)
						{
							UniqueClips[((Object)val).name] = null;
						}
					}
					else
					{
						UniqueClips[((Object)val).name] = new WeakReference<AudioClip>(val);
					}
				}
				target = null;
				if (UniqueClips.TryGetValue(name, out value2))
				{
					value2?.TryGetTarget(out target);
				}
			}
			if (!Object.op_Implicit((Object)(object)target) || !(((Object)target).name == name))
			{
				return null;
			}
			return target;
		}

		internal static AudioSource? Source(Component? prefab, string path, int index)
		{
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				return null;
			}
			string key = ((Object)prefab).GetInstanceID() + "\n" + index + "\n" + path;
			if (Sources.TryGetValue(key, out AudioSource value) && Object.op_Implicit((Object)(object)value))
			{
				return value;
			}
			Transform val = ((IEnumerable<Transform>)prefab.GetComponentsInChildren<Transform>(true)).FirstOrDefault((Func<Transform, bool>)((Transform child) => EntityTracker.RelativePath(prefab.transform, child) == path));
			AudioSource val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponents<AudioSource>().ElementAtOrDefault(Math.Max(0, index)) : null);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				string leaf = path.Substring(path.LastIndexOf('/') + 1);
				AudioSource[] array = (from audio in (from field in ((object)prefab).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
						where field.FieldType == typeof(AudioSource)
						select field).Select(delegate(FieldInfo field)
					{
						object? value2 = field.GetValue(prefab);
						return (AudioSource)((value2 is AudioSource) ? value2 : null);
					})
					where Object.op_Implicit((Object)(object)audio) && EntityTracker.RelativePath(prefab.transform, ((Component)audio).transform).Split('/').Last() == leaf
					select audio).Distinct().Take(2).ToArray();
				if (array.Length == 1)
				{
					val2 = array[0];
				}
			}
			if (Object.op_Implicit((Object)(object)val2) && Sources.Count < 4096)
			{
				Sources[key] = val2;
			}
			return val2;
		}
	}
	internal static class PrefabAssetRegistry
	{
		private static object? scannedConfig;

		private static float nextScan;

		private static readonly Dictionary<string, Component> Prefabs = new Dictionary<string, Component>(StringComparer.Ordinal);

		private static readonly Dictionary<string, Renderer> Renderers = new Dictionary<string, Renderer>(StringComparer.Ordinal);

		private static readonly Dictionary<string, Animator> Animators = new Dictionary<string, Animator>(StringComparer.Ordinal);

		private static readonly Dictionary<string, GeometrySnapshot> Meshes = new Dictionary<string, GeometrySnapshot>(StringComparer.Ordinal);

		private static readonly Queue<string> meshOrder = new Queue<string>();

		private static long meshBytes;

		internal static RuntimeAnimatorController? LocalPlayerController { get; private set; }

		internal static RuntimeAnimatorController? RemotePlayerController { get; private set; }

		internal static bool CachePlayerAssets(Component prefab, RuntimeAnimatorController? local, RuntimeAnimatorController? remote)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Type type = GameAccess.Type("GameNetcodeStuff.PlayerControllerB");
			if (Object.op_Implicit((Object)(object)prefab))
			{
				Scene scene = prefab.gameObject.scene;
				if (!((Scene)(ref scene)).IsValid() && !(type == null) && type.IsInstanceOfType(prefab))
				{
					Add(prefab, "player");
					if (Object.op_Implicit((Object)(object)local))
					{
						LocalPlayerController = local;
					}
					if (Object.op_Implicit((Object)(object)remote))
					{
						RemotePlayerController = remote;
					}
					return true;
				}
			}
			return false;
		}

		internal static string Key(Component component, string kind)
		{
			if (kind == "player")
			{
				return "player";
			}
			if (kind == "hazard")
			{
				return "hazard:" + ((object)component).GetType().Name;
			}
			string text = GameAccess.Scalar(GameAccess.Read(GameAccess.Read(component, (kind == "enemy") ? "enemyType" : "itemProperties"), (kind == "enemy") ? "enemyName" : "itemId"));
			if (!string.IsNullOrEmpty(text))
			{
				return kind + ":" + text;
			}
			return "";
		}

		internal static void Warm(bool force = false)
		{
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0343: Unknown result type (might be due to invalid IL or missing references)
			//IL_0348: Unknown result type (might be due to invalid IL or missing references)
			object? obj = GameAccess.Singleton("StartOfRound");
			Component val = (Component)((obj is Component) ? obj : null);
			if (val != null && Object.op_Implicit((Object)(object)val))
			{
				ReplayParticleAssets.CacheShipMagnet(val);
			}
			object obj2 = GameAccess.Read(GameAccess.Singleton("Unity.Netcode.NetworkManager"), "NetworkConfig");
			Scene scene;
			if (!Prefabs.TryGetValue("player", out Component value) || !Object.op_Implicit((Object)(object)value))
			{
				Type type = GameAccess.Type("GameNetcodeStuff.PlayerControllerB");
				object value2 = GameAccess.Singleton("StartOfRound");
				object? obj3 = GameAccess.Read(value2, "playerPrefab");
				GameObject val2 = (GameObject)((obj3 is GameObject) ? obj3 : null);
				if (!Object.op_Implicit((Object)(object)val2))
				{
					object? obj4 = GameAccess.Read(obj2, "PlayerPrefab");
					val2 = (GameObject)((obj4 is GameObject) ? obj4 : null);
				}
				if (!Object.op_Implicit((Object)(object)val2))
				{
					Type type2 = GameAccess.Type("StartOfRound");
					if ((object)type2 != null)
					{
						Object[] array = Resources.FindObjectsOfTypeAll(type2);
						foreach (Object val3 in array)
						{
							object? obj5 = GameAccess.Read(val3, "playerPrefab");
							GameObject val4 = (GameObject)((obj5 is GameObject) ? obj5 : null);
							if (val4 != null && Object.op_Implicit((Object)(object)val4))
							{
								scene = val4.scene;
								if (!((Scene)(ref scene)).IsValid())
								{
									val2 = val4;
									value2 = val3;
									break;
								}
							}
						}
					}
				}
				if (type != null && Object.op_Implicit((Object)(object)val2))
				{
					scene = val2.scene;
					if (!((Scene)(ref scene)).IsValid())
					{
						Component componentInChildren = val2.GetComponentInChildren(type, true);
						if (componentInChildren != null)
						{
							object local = (object)/*isinst with value type is only supported in some contexts*/;
							object? obj6 = GameAccess.Read(value2, "otherClientsAnimatorController");
							CachePlayerAssets(componentInChildren, (RuntimeAnimatorController?)local, (RuntimeAnimatorController?)((obj6 is RuntimeAnimatorController) ? obj6 : null));
						}
					}
				}
				if (type != null && (!Prefabs.TryGetValue("player", out value) || !Object.op_Implicit((Object)(object)value)))
				{
					foreach (Component item in Resources.FindObjectsOfTypeAll(type).OfType<Component>())
					{
						if (Object.op_Implicit((Object)(object)item))
						{
							scene = item.gameObject.scene;
							if (!((Scene)(ref scene)).IsValid())
							{
								Add(item, "player");
								break;
							}
						}
					}
				}
			}
			if ((!force && obj2 != null && obj2 == scannedConfig) || (!force && obj2 == null && Time.realtimeSinceStartup < nextScan))
			{
				return;
			}
			nextScan = Time.realtimeSinceStartup + 2f;
			string[] array2 = new string[2] { "Turret", "Landmine" };
			foreach (string text in array2)
			{
				Type type3 = GameAccess.Type(text);
				if (type3 == null)
				{
					continue;
				}
				Component val5 = Resources.FindObjectsOfTypeAll(type3).OfType<Component>().FirstOrDefault((Func<Component, bool>)delegate(Component component)
				{
					//IL_000e: Unknown result type (might be due to invalid IL or missing references)
					//IL_0013: Unknown result type (might be due to invalid IL or missing references)
					if (Object.op_Implicit((Object)(object)component))
					{
						Scene scene2 = component.gameObject.scene;
						return !((Scene)(ref scene2)).IsValid();
					}
					return false;
				});
				if (Object.op_Implicit((Object)(object)val5))
				{
					Prefabs["hazard:" + text] = val5;
				}
			}
			array2 = new string[2] { "enemy", "item" };
			foreach (string text2 in array2)
			{
				Type type4 = GameAccess.Type((text2 == "enemy") ? "EnemyAI" : "GrabbableObject");
				if (type4 == null)
				{
					continue;
				}
				foreach (Component item2 in Resources.FindObjectsOfTypeAll(type4).OfType<Component>())
				{
					if (Object.op_Implicit((Object)(object)item2))
					{
						scene = item2.gameObject.scene;
						if (!((Scene)(ref scene)).IsValid())
						{
							Add(item2, text2);
						}
					}
				}
			}
			if (!(GameAccess.Read(GameAccess.Read(obj2, "Prefabs"), "Prefabs") is IEnumerable enumerable))
			{
				return;
			}
			foreach (object item3 in enumerable)
			{
				object? obj7 = GameAccess.Read(item3, "Prefab");
				GameObject val6 = (GameObject)((obj7 is GameObject) ? obj7 : null);
				if (!Object.op_Implicit((Object)(object)val6))
				{
					continue;
				}
				array2 = new string[2] { "enemy", "item" };
				foreach (string text3 in array2)
				{
					Type type5 = GameAccess.Type((text3 == "enemy") ? "EnemyAI" : "GrabbableObject");
					if (!(type5 == null))
					{
						Component componentInChildren2 = val6.GetComponentInChildren(type5, true);
						if (Object.op_Implicit((Object)(object)componentInChildren2))
						{
							Add(componentInChildren2, text3);
						}
					}
				}
			}
			scannedConfig = obj2;
		}

		private static void Add(Component component, string kind)
		{
			string text = Key(component, kind);
			if (text.Length == 0 || (Prefabs.TryGetValue(text, out Component value) && Object.op_Implicit((Object)(object)value)))
			{
				return;
			}
			Prefabs[text] = component;
			Renderer[] componentsInChildren = component.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (val is MeshRenderer || val is SkinnedMeshRenderer)
				{
					Renderers[text + "\n" + EntityTracker.RelativePath(component.transform, ((Component)val).transform)] = val;
				}
			}
			Animator[] componentsInChildren2 = component.GetComponentsInChildren<Animator>(true);
			foreach (Animator val2 in componentsInChildren2)
			{
				Animators[text + "\n" + EntityTracker.RelativePath(component.transform, ((Component)val2).transform)] = val2;
				AnimationAssetRegistry.Remember(val2, kind);
			}
		}

		internal static bool Reference(EntityTracker.Entry owner, Renderer renderer, GeometrySnapshot geometry)
		{
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			if (owner.Kind != "enemy" && owner.Kind != "item" && owner.Kind != "player")
			{
				return false;
			}
			Warm();
			string text = Key(owner.Component, owner.Kind);
			if (owner.Kind == "enemy" && (!Prefabs.TryGetValue(text, out Component value) || !Object.op_Implicit((Object)(object)value)))
			{
				object? obj = GameAccess.Read(GameAccess.Read(owner.Component, "enemyType"), "enemyPrefab");
				GameObject val = (GameObject)((obj is GameObject) ? obj : null);
				Type type = GameAccess.Type("EnemyAI");
				if (Object.op_Implicit((Object)(object)val))
				{
					Scene scene = val.scene;
					if (!((Scene)(ref scene)).IsValid() && type != null)
					{
						Component componentInChildren = val.GetComponentInChildren(type, true);
						if (componentInChildren != null)
						{
							Add(componentInChildren, "enemy");
						}
					}
				}
			}
			string text2 = EntityTracker.RelativePath(owner.Component.transform, ((Component)renderer).transform);
			if (text2.Length == 0 || !Renderers.TryGetValue(text + "\n" + text2, out Renderer value2) || !Object.op_Implicit((Object)(object)value2) || (Object)(object)Mesh(renderer) != (Object)(object)Mesh(value2))
			{
				return false;
			}
			geometry.PrefabKey = text;
			geometry.PrefabRendererPath = text2;
			geometry.MeshName = ((Object)Mesh(value2)).name;
			geometry.IsBoundsProxy = false;
			return true;
		}

		internal static bool NativeMaterials(Renderer renderer, GeometrySnapshot geometry)
		{
			if (!Renderers.TryGetValue(geometry.PrefabKey + "\n" + geometry.PrefabRendererPath, out Renderer value) || !Object.op_Implicit((Object)(object)value))
			{
				return false;
			}
			Material[] sharedMaterials = renderer.sharedMaterials;
			Material[] source = value.sharedMaterials;
			if (sharedMaterials.Length == source.Length)
			{
				return !sharedMaterials.Where((Material material, int index) => (Object)(object)material != (Object)(object)source[index]).Any();
			}
			return false;
		}

		internal static void Restore(WorldSnapshot world, ReplaySession session)
		{
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Expected O, but got Unknown
			//IL_0514: Unknown result type (might be due to invalid IL or missing references)
			//IL_0519: Unknown result type (might be due to invalid IL or missing references)
			//IL_0573: Unknown result type (might be due to invalid IL or missing references)
			//IL_057a: Expected O, but got Unknown
			//IL_05b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0556: Unknown result type (might be due to invalid IL or missing references)
			//IL_055b: Unknown result type (might be due to invalid IL or missing references)
			Warm();
			Dictionary<string, EntitySnapshot> dictionary = (from actor in session.Frames.SelectMany((ReplayFrame frame) => frame.Entities)
				where actor.Kind == "enemy"
				group actor by actor.Id).ToDictionary((IGrouping<string, EntitySnapshot> group) => group.Key, (IGrouping<string, EntitySnapshot> group) => group.First());
			foreach (GeometrySnapshot geometry in world.Geometry)
			{
				bool flag = geometry.PrefabKey.Length != 0 && geometry.MaterialIds.Count != 0;
				string text = geometry.PrefabKey;
				string text2 = geometry.PrefabRendererPath;
				if (text.Length == 0 && dictionary.TryGetValue(geometry.EntityId, out var value))
				{
					text = "enemy:" + value.Name;
					if (!Prefabs.TryGetValue(text, out Component value2) || !Object.op_Implicit((Object)(object)value2))
					{
						continue;
					}
					Renderer[] array = (from renderer in value2.GetComponentsInChildren<Renderer>(true)
						where Object.op_Implicit((Object)(object)renderer) && ((Object)renderer).name == geometry.Name && Object.op_Implicit((Object)(object)Mesh(renderer)) && (((Object)Mesh(renderer)).name == geometry.MeshName || geometry.IsBoundsProxy)
						select renderer).ToArray();
					if (array.Length != 1)
					{
						continue;
					}
					text2 = EntityTracker.RelativePath(value2.transform, ((Component)array[0]).transform);
				}
				string text3 = text + "\n" + text2;
				if (!Renderers.TryGetValue(text3, out Renderer value3) || !Object.op_Implicit((Object)(object)value3) || !Prefabs.TryGetValue(text, out Component value4) || !Object.op_Implicit((Object)(object)value4))
				{
					continue;
				}
				if (!Meshes.TryGetValue(text3, out GeometrySnapshot value5))
				{
					value5 = new GeometrySnapshot();
					Mesh val = Mesh(value3);
					if (!Object.op_Implicit((Object)(object)val) || !MeshSnapshotReader.Read(val, value5, 1000000, 6000000))
					{
						continue;
					}
					SkinnedMeshRenderer val2 = (SkinnedMeshRenderer)(object)((value3 is SkinnedMeshRenderer) ? value3 : null);
					if (val2 != null && !MeshSnapshotReader.Skin(val2, value4.transform, value5))
					{
						continue;
					}
					long num = Bytes(value5);
					if (num <= 67108864)
					{
						while (meshBytes + num > 67108864 && meshOrder.Count != 0)
						{
							string key = meshOrder.Dequeue();
							if (Meshes.TryGetValue(key, out GeometrySnapshot value6))
							{
								meshBytes -= Bytes(value6);
								Meshes.Remove(key);
							}
						}
						Meshes[text3] = value5;
						meshOrder.Enqueue(text3);
						meshBytes += num;
					}
				}
				geometry.Vertices = value5.Vertices;
				geometry.Normals = value5.Normals;
				geometry.Uvs = value5.Uvs;
				geometry.Uvs1 = value5.Uvs1;
				geometry.Uvs2 = value5.Uvs2;
				geometry.Uvs3 = value5.Uvs3;
				geometry.Tangents = value5.Tangents;
				geometry.Triangles = value5.Triangles;
				geometry.SubmeshTriangles = value5.SubmeshTriangles;
				geometry.BonePaths = value5.BonePaths;
				geometry.BindPoses = value5.BindPoses;
				geometry.BoneIndices = value5.BoneIndices;
				geometry.BoneWeights = value5.BoneWeights;
				geometry.RigBones = value5.RigBones;
				geometry.RootBonePath = value5.RootBonePath;
				geometry.AnimatorPath = value5.AnimatorPath;
				geometry.AnimatorController = value5.AnimatorController;
				geometry.AnimatorAvatar = value5.AnimatorAvatar;
				geometry.IsBoundsProxy = false;
				geometry.PrefabKey = text;
				geometry.PrefabRendererPath = text2;
				if (flag)
				{
					continue;
				}
				geometry.MaterialIds = new List<string>();
				Material[] sharedMaterials = value3.sharedMaterials;
				foreach (Material val3 in sharedMaterials)
				{
					if (!Object.op_Implicit((Object)(object)val3) || !Object.op_Implicit((Object)(object)val3.shader))
					{
						geometry.MaterialIds.Add("");
						continue;
					}
					string id = "native-prefab:" + ((Object)val3).GetInstanceID();
					if (!world.Materials.Any((MaterialSnapshot snapshot) => snapshot.Id == id))
					{
						Color val4 = Color.white;
						string[] array2 = new string[3] { "_BaseColor", "_UnlitColor", "_Color" };
						foreach (string text4 in array2)
						{
							if (val3.HasProperty(text4))
							{
								val4 = val3.GetColor(text4);
								break;
							}
						}
						List<MaterialSnapshot> materials = world.Materials;
						MaterialSnapshot val5 = new MaterialSnapshot();
						val5.Id = id;
						val5.Name = ((Object)val3).name;
						val5.ShaderName = ((Object)val3.shader).name;
						val5.Color = new float[4] { val4.r, val4.g, val4.b, val4.a };
						materials.Add(val5);
					}
					geometry.MaterialIds.Add(id);
				}
			}
		}

		private static Mesh? Mesh(Renderer renderer)
		{
			SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null);
			if (val == null)
			{
				MeshFilter component = ((Component)renderer).GetComponent<MeshFilter>();
				if (component == null)
				{
					return null;
				}
				return component.sharedMesh;
			}
			return val.sharedMesh;
		}

		internal static Mesh? ResolveMesh(GeometrySnapshot geometry)
		{
			if (geometry.PrefabKey.Length == 0 || geometry.PrefabRendererPath.Length == 0 || !Renderers.TryGetValue(geometry.PrefabKey + "\n" + geometry.PrefabRendererPath, out Renderer value) || !Object.op_Implicit((Object)(object)value))
			{
				return null;
			}
			Mesh val = Mesh(value);
			if (!Object.op_Implicit((Object)(object)val) || val.vertexCount != geometry.Vertices.Length / 3)
			{
				return null;
			}
			return val;
		}

		internal static Animator? ResolveAnimator(string key, string path)
		{
			if (!Animators.TryGetValue(key + "\n" + path, out Animator value) || !Object.op_Implicit((Object)(object)value))
			{
				return null;
			}
			return value;
		}

		internal static Component? ResolvePrefab(string key)
		{
			Warm();
			if (!Prefabs.TryGetValue(key, out Component value) || !Object.op_Implicit((Object)(object)value))
			{
				return null;
			}
			return value;
		}

		private static long Bytes(GeometrySnapshot geometry)
		{
			return 4 * (geometry.Vertices.Length + geometry.Normals.Length + geometry.Uvs.Length + geometry.Uvs1.Length + geometry.Uvs2.Length + geometry.Uvs3.Length + geometry.Tangents.Length + geometry.Triangles.Length + geometry.BoneWeights.Length + geometry.BoneIndices.Length + geometry.BindPoses.Length + ((IEnumerable<int[]>)geometry.SubmeshTriangles).Sum((Func<int[], long>)((int[] indices) => indices.Length)));
		}
	}
	internal static class RenderVisibilityPolicy
	{
		private static readonly string[] VegetationNames = new string[13]
		{
			"tree", "grass", "leaf", "leaves", "bush", "flower", "forest", "foliage", "vine", "plant",
			"fern", "shrub", "moss"
		};

		internal static bool IsGroundSurface(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			if (name.IndexOf("terrain", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("ground", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("landscape", StringComparison.OrdinalIgnoreCase) < 0)
			{
				return name.IndexOf("splatmap", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return true;
		}

		internal static bool IsRockSurface(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			if (name.IndexOf("rock", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("stone", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("boulder", StringComparison.OrdinalIgnoreCase) < 0)
			{
				return name.IndexOf("cliff", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return true;
		}

		internal static bool IsArchitectureSurface(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			if (name.IndexOf("wall", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("floor", StringComparison.OrdinalIgnoreCase) < 0)
			{
				return name.IndexOf("ceiling", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return true;
		}

		internal static bool IsVegetation(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			string[] vegetationNames = VegetationNames;
			foreach (string value in vegetationNames)
			{
				if (name.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		internal static bool IsDebugMaterial(string name)
		{
			if (!name.StartsWith("testTrigger", StringComparison.OrdinalIgnoreCase))
			{
				return name.StartsWith("wireframe_", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		internal static bool IsDebugObject(string name)
		{
			if (!name.StartsWith("ImpVis_", StringComparison.Ordinal) && !name.StartsWith("ImpGizmo_", StringComparison.Ordinal))
			{
				switch (name)
				{
				default:
					if (!string.Equals(name, "ScanSphere", StringComparison.OrdinalIgnoreCase) && !(name == "MapDot"))
					{
						return name.StartsWith("MapDot (", StringComparison.Ordinal);
					}
					break;
				case "VehicleBounds":
				case "InsideTruckNavBounds":
				case "OntopOfTruckBounds":
				case "WindshieldInteractBlocker":
					break;
				}
			}
			return true;
		}

		internal static bool IsRadarPath(string path)
		{
			if (!path.StartsWith("MapDot[", StringComparison.Ordinal) && !path.StartsWith("MapDot (", StringComparison.Ordinal) && path.IndexOf("/MapDot[", StringComparison.Ordinal) < 0)
			{
				return path.IndexOf("/MapDot (", StringComparison.Ordinal) >= 0;
			}
			return true;
		}

		internal static bool IsFirstPersonOverlay(string name)
		{
			if (!string.Equals(name, "ScavengerHelmet", StringComparison.OrdinalIgnoreCase) && !string.Equals(name, "ScreenHelmetGoopPreload", StringComparison.OrdinalIgnoreCase))
			{
				return string.Equals(name, "VisorCracks", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		internal static bool IsDebugGeometry(GeometrySnapshot geometry, ISet<string> debugMaterials)
		{
			if (IsDebugObject(geometry.Name) || IsFirstPersonOverlay(geometry.Name))
			{
				return true;
			}
			if (geometry.MaterialIds.Count == 0)
			{
				return false;
			}
			foreach (string materialId in geometry.MaterialIds)
			{
				if (materialId.Length == 0 || !debugMaterials.Contains(materialId))
				{
					return false;
				}
			}
			return true;
		}
	}
	public static class ReplayApi
	{
		private static readonly Dictionary<string, Func<IDictionary<string, string>>> Providers = new Dictionary<string, Func<IDictionary<string, string>>>();

		internal static Action<ReplayEvent>? EventSink;

		public static bool IsRecording => EventSink != null;

		public static void RegisterStateProvider(string id, Func<IDictionary<string, string>> provider)
		{
			if (string.IsNullOrWhiteSpace(id) || id.Length > 128)
			{
				throw new ArgumentException("Provider id must contain 1 to 128 characters.", "id");
			}
			if (provider == null)
			{
				throw new ArgumentNullException("provider");
			}
			lock (Providers)
			{
				if (Providers.Count >= 64 && !Providers.ContainsKey(id))
				{
					throw new InvalidOperationException("Replay supports at most 64 state providers.");
				}
				Providers[id] = provider;
			}
		}

		public static void UnregisterStateProvider(string id)
		{
			lock (Providers)
			{
				Providers.Remove(id);
			}
		}

		public static void LogEvent(string category, string name, IDictionary<string, string>? data = null)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			Action<ReplayEvent> eventSink = EventSink;
			if (eventSink == null)
			{
				return;
			}
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			if (data != null)
			{
				foreach (KeyValuePair<string, string> datum in data)
				{
					if (dictionary.Count >= 128)
					{
						dictionary["$truncated"] = "128 event fields";
						break;
					}
					dictionary[Limit(datum.Key, 128)] = Limit(datum.Value, 1024);
				}
			}
			eventSink(new ReplayEvent
			{
				Category = Limit(category ?? "custom", 128),
				Name = Limit(name ?? "event", 256),
				Data = dictionary
			});
		}

		private static string Limit(string? value, int length)
		{
			if (value != null)
			{
				if (value.Length <= length)
				{
					return value;
				}
				return value.Substring(0, length);
			}
			return "";
		}

		internal static void CaptureProviders(Dictionary<string, string> state, Action<string> log)
		{
			KeyValuePair<string, Func<IDictionary<string, string>>>[] array;
			lock (Providers)
			{
				array = new KeyValuePair<string, Func<IDictionary<string, string>>>[Providers.Count];
				((ICollection<KeyValuePair<string, Func<IDictionary<string, string>>>>)Providers).CopyTo(array, 0);
			}
			KeyValuePair<string, Func<IDictionary<string, string>>>[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				KeyValuePair<string, Func<IDictionary<string, string>>> keyValuePair = array2[i];
				if (state.Count >= 1000)
				{
					state["$providersTruncated"] = "frame field limit";
					break;
				}
				try
				{
					int num = 0;
					foreach (KeyValuePair<string, string> item in keyValuePair.Value())
					{
						if (++num > 128 || state.Count >= 1000)
						{
							state["mod:" + keyValuePair.Key + ":$truncated"] = "provider/frame field limit";
							break;
						}
						string value = item.Value ?? "";
						state["mod:" + keyValuePair.Key + ":" + Limit(item.Key, 128)] = Limit(value, 1024);
					}
				}
				catch (Exception ex)
				{
					log("State provider disabled after error: " + keyValuePair.Key + " / " + ex.Message);
					lock (Providers)
					{
						if (Providers.TryGetValue(keyValuePair.Key, out Func<IDictionary<string, string>> value2) && value2 == keyValuePair.Value)
						{
							Providers.Remove(keyValuePair.Key);
						}
					}
				}
			}
		}
	}
	internal sealed class ReplayBookmarkInput : IDisposable
	{
		internal const string InputUtilsGuid = "com.rune580.LethalCompanyInputUtils";

		private readonly ConfigEntry<KeyCode> fallback;

		private Func<bool>? pressed;

		private Action? disable;

		internal ReplayBookmarkInput(ConfigEntry<KeyCode> fallback, Action<string> log)
		{
			this.fallback = fallback;
			if (!Chainloader.PluginInfos.ContainsKey("com.rune580.LethalCompanyInputUtils"))
			{
				return;
			}
			try
			{
				AttachInputUtils();
				log("Replay bookmark key registered with InputUtils's game settings.");
			}
			catch (Exception ex)
			{
				log("Replay bookmark uses the configured fallback key: " + ex.Message);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private void AttachInputUtils()
		{
			BaseUnityPlugin instance = Chainloader.PluginInfos["com.rune580.LethalCompanyInputUtils"].Instance;
			BepInPlugin metadata = Chainloader.PluginInfos["pasta.replay"].Metadata;
			object actions = ReplayKeybinds.Create(((object)instance).GetType().Assembly, metadata);
			PropertyInfo property = actions.GetType().GetProperty("AddBookmark");
			InputAction action = null;
			pressed = delegate
			{
				InputAction obj = action ?? (action = (InputAction)/*isinst with value type is only supported in some contexts*/);
				return obj != null && obj.WasPerformedThisFrame();
			};
			disable = delegate
			{
				actions.GetType().GetMethod("Disable").Invoke(actions, null);
			};
		}

		internal bool WasPressed()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (pressed == null)
			{
				return ReplayInput.WasPressed(((object)fallback.Value/*cast due to .constrained prefix*/).ToString());
			}
			return pressed();
		}

		public void Dispose()
		{
			disable?.Invoke();
			pressed = null;
			disable = null;
		}
	}
	internal static class ReplayIsolation
	{
		private static readonly HashSet<int> scenes;

		private static object? networkSceneManager;

		private static Func<Scene, bool>? networkFilter;

		internal static bool PlaybackActive { get; set; }

		static ReplayIsolation()
		{
			scenes = new HashSet<int>();
			SceneManager.sceneUnloaded += delegate(Scene scene)
			{
				scenes.Remove(((Scene)(ref scene)).handle);
			};
		}

		internal static void Register(Scene scene)
		{
			if (((Scene)(ref scene)).IsValid())
			{
				scenes.Add(((Scene)(ref scene)).handle);
			}
		}

		internal static void Unregister(Scene scene)
		{
			if (((Scene)(ref scene)).IsValid() && !((Scene)(ref scene)).isLoaded)
			{
				scenes.Remove(((Scene)(ref scene)).handle);
			}
		}

		internal static void Unloaded(Scene scene)
		{
			scenes.Remove(((Scene)(ref scene)).handle);
		}

		internal static bool IsReplayScene(Scene scene)
		{
			if (((Scene)(ref scene)).IsValid())
			{
				return scenes.Contains(((Scene)(ref scene)).handle);
			}
			return false;
		}

		internal static bool EnsureNetworkExclusion()
		{
			object obj = GameAccess.Read(GameAccess.Singleton("Unity.Netcode.NetworkManager"), "SceneManager");
			if (obj == null)
			{
				return false;
			}
			FieldInfo field = obj.GetType().GetField("ExcludeSceneFromSychronization", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field?.FieldType != typeof(Func<Scene, bool>))
			{
				return false;
			}
			try
			{
				Func<Scene, bool> current = field.GetValue(obj) as Func<Scene, bool>;
				if (obj == networkSceneManager && (object)current == networkFilter)
				{
					return true;
				}
				Func<Scene, bool> value = (Scene scene) => !IsReplayScene(scene) && (current?.Invoke(scene) ?? true);
				field.SetValue(obj, value);
				networkSceneManager = obj;
				networkFilter = value;
				return true;
			}
			catch (Exception)
			{
				return false;
			}
		}
	}
	internal static class ReplayKeybinds
	{
		internal static object Create(Assembly sdk, BepInPlugin metadata)
		{
			Type type = sdk.GetType("LethalCompanyInputUtils.Api.LcInputActions", throwOnError: true);
			Type type2 = sdk.GetType("LethalCompanyInputUtils.Api.InputActionAttribute", throwOnError: true);
			TypeBuilder typeBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("LCReplay.OptionalInputUtils"), AssemblyBuilderAccess.Run).DefineDynamicModule("Keybinds").DefineType("LCReplay.BookmarkActions", TypeAttributes.Public | TypeAttributes.Sealed, type);
			ILGenerator iLGenerator = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[1] { typeof(BepInPlugin) }).GetILGenerator();
			iLGenerator.Emit(OpCodes.Ldarg_0);
			iLGenerator.Emit(OpCodes.Ldarg_1);
			iLGenerator.Emit(OpCodes.Call, type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[1] { typeof(BepInPlugin) }, null));
			iLGenerator.Emit(OpCodes.Ret);
			MethodInfo getMethod = type.GetProperty("MapName", BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(nonPublic: true);
			MethodBuilder methodBuilder = typeBuilder.DefineMethod("get_MapName", MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.SpecialName, typeof(string), Type.EmptyTypes);
			iLGenerator = methodBuilder.GetILGenerator();
			iLGenerator.Emit(OpCodes.Ldstr, "Replay");
			iLGenerator.Emit(OpCodes.Ret);
			typeBuilder.DefineMethodOverride(methodBuilder, getMethod);
			FieldBuilder field = typeBuilder.DefineField("bookmark", typeof(InputAction), FieldAttributes.Private);
			PropertyBuilder propertyBuilder = typeBuilder.DefineProperty("AddBookmark", PropertyAttributes.None, typeof(InputAction), null);
			MethodBuilder methodBuilder2 = typeBuilder.DefineMethod("get_AddBookmark", MethodAttributes.Public | MethodAttributes.SpecialName, typeof(InputAction), Type.EmptyTypes);
			iLGenerator = methodBuilder2.GetILGenerator();
			iLGenerator.Emit(OpCodes.Ldarg_0);
			iLGenerator.Emit(OpCodes.Ldfld, field);
			iLGenerator.Emit(OpCodes.Ret);
			MethodBuilder methodBuilder3 = typeBuilder.DefineMethod("set_AddBookmark", MethodAttributes.Public | MethodAttributes.SpecialName, typeof(void), new Type[1] { typeof(InputAction) });
			iLGenerator = methodBuilder3.GetILGenerator();
			iLGenerator.Emit(OpCodes.Ldarg_0);
			iLGenerator.Emit(OpCodes.Ldarg_1);
			iLGenerator.Emit(OpCodes.Stfld, field);
			iLGenerator.Emit(OpCodes.Ret);
			propertyBuilder.SetGetMethod(methodBuilder2);
			propertyBuilder.SetSetMethod(methodBuilder3);
			propertyBuilder.SetCustomAttribute(new CustomAttributeBuilder(type2.GetConstructor(new Type[1] { typeof(string) }), new object[1] { "<Keyboard>/backquote" }, new PropertyInfo[2]
			{
				type2.GetProperty("Name"),
				type2.GetProperty("ActionId")
			}, new object[2] { "Add replay bookmark", "AddBookmark" }));
			MethodBuilder methodBuilder4 = typeBuilder.DefineMethod("OnAssetLoaded", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), Type.EmptyTypes);
			iLGenerator = methodBuilder4.GetILGenerator();
			iLGenerator.Emit(OpCodes.Ldarg_0);
			iLGenerator.Emit(OpCodes.Call, type.GetMethod("Enable"));
			iLGenerator.Emit(OpCodes.Ret);
			typeBuilder.DefineMethodOverride(methodBuilder4, type.GetMethod("OnAssetLoaded"));
			return Activator.CreateInstance(typeBuilder.CreateType(), metadata);
		}
	}
	internal static class ReplayLightProperties
	{
		private static readonly Dictionary<string, (float Min, float Max)> Floats = new Dictionary<string, (float, float)>(StringComparer.Ordinal)
		{
			["shapeRadius"] = (0f, 10000f),
			["shapeWidth"] = (0f, 10000f),
			["shapeHeight"] = (0f, 10000f),
			["aspectRatio"] = (0.01f, 1000f),
			["innerSpotPercent"] = (0f, 100f),
			["volumetricDimmer"] = (0f, 16f),
			["volumetricShadowDimmer"] = (0f, 16f),
			["fadeDistance"] = (0f, 100000f),
			["volumetricFadeDistance"] = (0f, 100000f),
			["maxSmoothness"] = (0f, 1f),
			["angularDiameter"] = (0f, 90f),
			["normalBias"] = (0f, 64f),
			["slopeBias"] = (0f, 64f),
			["shadowNearPlane"] = (0f, 1000f)
		};

		private static readonly HashSet<string> Bools = new HashSet<string>(StringComparer.Ordinal) { "applyRangeAttenuation", "affectDiffuse", "affectSpecular", "enableSpotReflector", "interactsWithSky" };

		private static readonly HashSet<string> Enums = new HashSet<string>(StringComparer.Ordinal) { "type", "spotLightShape", "areaLightShape" };

		internal static List<EnvironmentParameterSnapshot> Capture(Component? data)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Expected O, but got Unknown
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Expected O, but got Unknown
			List<EnvironmentParameterSnapshot> list = new List<EnvironmentParameterSnapshot>();
			if (!Object.op_Implicit((Object)(object)data))
			{
				return list;
			}
			foreach (KeyValuePair<string, (float, float)> @float in Floats)
			{
				if (GameAccess.Read(data, @float.Key) is float num && GameAccess.Finite(num))
				{
					EnvironmentParameterSnapshot val = new EnvironmentParameterSnapshot();
					val.Name = @float.Key;
					val.Kind = "float";
					val.Values = new float[1] { Mathf.Clamp(num, @float.Value.Item1, @float.Value.Item2) };
					list.Add(val);
				}
			}
			foreach (string @bool in Bools)
			{
				if (GameAccess.Read(data, @bool) is bool flag)
				{
					EnvironmentParameterSnapshot val = new EnvironmentParameterSnapshot();
					val.Name = @bool;
					val.Kind = "bool";
					val.Values = new float[1] { flag ? 1f : 0f };
					list.Add(val);
				}
			}
			foreach (string @enum in Enums)
			{
				object obj = GameAccess.Read(data, @enum);
				if (obj != null && obj.GetType().IsEnum)
				{
					list.Add(new EnvironmentParameterSnapshot
					{
						Name = @enum,
						Kind = "enum",
						Text = obj.ToString()
					});
				}
			}
			return list;
		}

		internal static void Apply(Component? data, IReadOnlyList<EnvironmentParameterSnapshot> parameters, Light light, float intensity)
		{
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Invalid comparison between Unknown and I4
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Invalid comparison between Unknown and I4
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Invalid comparison between Unknown and I4
			if (!Object.op_Implicit((Object)(object)data))
			{
				light.intensity = intensity;
				return;
			}
			foreach (EnvironmentParameterSnapshot parameter in parameters)
			{
				if (parameter.Name == "type" && parameter.Kind == "enum")
				{
					SetEnum(data, "type", parameter.Text);
				}
			}
			foreach (EnvironmentParameterSnapshot parameter2 in parameters)
			{
				if (parameter2.Name != "type" && parameter2.Kind == "enum" && Enums.Contains(parameter2.Name))
				{
					SetEnum(data, parameter2.Name, parameter2.Text);
				}
			}
			foreach (EnvironmentParameterSnapshot parameter3 in parameters)
			{
				if (parameter3.Values.Length != 1)
				{
					continue;
				}
				float num = parameter3.Values[0];
				if (GameAccess.Finite(num))
				{
					if (parameter3.Kind == "float" && Floats.TryGetValue(parameter3.Name, out (float, float) value))
					{
						Set(data, parameter3.Name, Mathf.Clamp(num, value.Item1, value.Item2));
					}
					else if (parameter3.Kind == "bool" && Bools.Contains(parameter3.Name))
					{
						Set(data, parameter3.Name, num != 0f);
					}
				}
			}
			bool flag = (int)light.type == 0 && GameAccess.Read(data, "spotLightShape")?.ToString() == "Box";
			bool flag2 = GameAccess.Read(data, "type")?.ToString() == "Area";
			string value2 = (((int)light.type == 1 || flag) ? "Lux" : ((flag2 || (int)light.type == 3 || (int)light.type == 4) ? "Nits" : "Candela"));
			SetEnum(data, "lightUnit", value2);
			Set(data, "intensity", intensity);
			light.intensity = intensity;
		}

		private static void Set(Component data, string name, object value)
		{
			try
			{
				PropertyInfo property = ((object)data).GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
				if ((object)property != null && property.CanWrite)
				{
					property.SetValue(data, value, null);
				}
				else
				{
					((object)data).GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public)?.SetValue(data, value);
				}
			}
			catch
			{
			}
		}

		private static void SetEnum(Component data, string name, string value)
		{
			PropertyInfo property = ((object)data).GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
			if ((object)property != null && property.CanWrite && property.PropertyType.IsEnum && Enum.IsDefined(property.PropertyType, value))
			{
				Set(data, name, Enum.Parse(property.PropertyType, value));
			}
		}
	}
	[BepInPlugin("pasta.replay", "LC Replay", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class ReplayPlugin : BaseUnityPlugin
	{
		private sealed class SaveResult
		{
			internal readonly string Path;

			internal readonly double Duration;

			internal readonly Exception? Error;

			internal readonly bool MetadataError;

			internal SaveResult(string path, double duration, Exception? error, bool metadataError)
			{
				Path = path;
				Duration = duration;
				Error = error;
				MetadataError = metadataError;
			}
		}

		public const string Guid = "pasta.replay";

		public const string Version = "1.0.0";

		private GUIStyle? overlayLabel;

		private GUIStyle? errorNoticeLabel;

		private ConfigEntry<bool> bones;

		private ConfigEntry<bool> world;

		private ConfigEntry<bool> chat;

		private ConfigEntry<bool> noShadow;

		private ConfigEntry<bool> showDebugOverlay;

		private ConfigEntry<bool> cinematicMove;

		private ConfigEntry<bool> showFog;

		private ConfigEntry<int> rate;

		private ConfigEntry<int> maxFields;

		private ConfigEntry<int> maxObjects;

		private ConfigEntry<int> maxVertices;

		private ConfigEntry<float> replayResolution;

		private ConfigEntry<float> replayGamma;

		private ConfigEntry<float> cameraSpeed;

		private ConfigEntry<string> folder;

		private ConfigEntry<string> extraTypes;

		private ReplayBookmarkInput? bookmarkInput;

		private EventHooks? hooks;

		private Recorder? recorder;

		private ReplayViewer? viewer;

		private ReplayViewer? cachedViewer;

		private string? viewerPath;

		private string? cachedViewerPath;

		private int viewerSceneHandle;

		private bool viewerConnected;

		private int cachedSceneHandle;

		private bool cachedConnected;

		private List<(string Path, long Length, DateTime ModifiedUtc)>? cachedFiles;

		private ReplayLibraryWindow? library;

		private ReplayMenuButton? menuButton;

		private ReplayPauseMenuButton? pauseMenuButton;

		private Harmony? inputHarmony;

		private static ReplayPlugin? activeInputGuard;

		private ReplayRuntime? runtime;

		private ReplayArchive? archive;

		private ArchiveIndex index = new ArchiveIndex();

		private ArchiveRun? run;

		private ArchiveSession? session;

		private ArchiveDay? day;

		private ArchiveSegment? segment;

		private IReadOnlyList<WorldSnapshot>? reusableWorld;

		private readonly List<Task<SaveResult>> pendingSaves = new List<Task<SaveResult>>();

		private readonly List<Task> pendingGroupSaves = new List<Task>();

		private DayLifecycle dayLifecycle = new DayLifecycle();

		private Component? sessionRound;

		private Component? deletedLobbyRound;

		private Task<ArchiveIndex>? scanning;

		private Task<LoadedRecording>? loading;

		private IEnumerator<float>? loadingPlayerAssets;

		private CancellationTokenSource? loadingCancellation;

		private bool rescanRequested;

		private bool recordingBlocked;

		private bool wasConnected;

		private bool quitting;

		private bool checkpointRequested;

		private bool dayHadRecoveryGap;

		private int recordingRetryFailures;

		private float nextRecordingRetryAt;

		private bool loadCancelled;

		private bool closeLibraryAfterSceneCleanup;

		private bool shutdownCompleted;

		private bool inputReported;

		private bool inputReadyReported;

		private Component? playbackPlayer;

		private bool savedMoveInput;

		private bool savedLookInput;

		private LiveReplayInput? liveReplayInput;

		private int part;

		private float nextMemberScan;

		private string status = "Gameplay is recorded and saved automatically.";

		private string recordingFailure = "";

		private string errorNotice = "";

		private float errorNoticeUntil;

		private float errorNoticeStarted;

		private bool errorNoticePending;

		private string? loadingPath;

		private float loadingSpeed = 1f;

		private int loadingPercent;

		private string loadingStage = "";

		private string ReplayDirectory => Path.GetFullPath(Path.IsPathRooted(folder.Value) ? folder.Value : Path.Combine(Paths.BepInExRootPath, folder.Value));

		private void Awake()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Expected O, but got Unknown
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Expected O, but got Unknown
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Expected O, but got Unknown
			//IL_0609: Unknown result type (might be due to invalid IL or missing references)
			//IL_060e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Expected O, but got Unknown
			//IL_0307: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Expected O, but got Unknown
			//IL_03fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0405: Expected O, but got Unknown
			//IL_0467: Unknown result type (might be due to invalid IL or missing references)
			//IL_0471: Expected O, but got Unknown
			string text = Path.Combine(Paths.ConfigPath, "io.lcreplay.recorder.cfg");
			if (!File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath) && File.Exists(text))
			{
				try
				{
					File.Copy(text, ((BaseUnityPlugin)this).Config.ConfigFilePath, overwrite: false);
					((BaseUnityPlugin)this).Config.Reload();
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not migrate legacy replay settings: " + ex.Message));
				}
			}
			activeInputGuard = this;
			rate = ((BaseUnityPlugin)this).Config.Bind<int>("Recording", "SampleRate", 10, new ConfigDescription("Automatic recording snapshots per second.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 60), Array.Empty<object>()));
			bones = ((BaseUnityPlugin)this).Config.Bind<bool>("Recording", "CaptureBones", false, "Also record player and item bone poses on each sample. Enemy visual poses are always recorded after animation and procedural movement; other actors use installed Animator controllers when off.");
			world = ((BaseUnityPlugin)this).Config.Bind<bool>("Recording", "CaptureWorld", true, "Save bounded geometry, textures and actor appearance for offline replay.");
			chat = ((BaseUnityPlugin)this).Config.Bind<bool>("Recording", "CaptureChat", false, "Capture displayed text chat. Restart required.");
			maxFields = ((BaseUnityPlugin)this).Config.Bind<int>("Limits", "FieldsPerEntity", 192, new ConfigDescription("Primitive fields per component.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(32, 512), Array.Empty<object>()));
			maxObjects = ((BaseUnityPlugin)this).Config.Bind<int>("Limits", "WorldObjects", 4000, new ConfigDescription("Render object budget per world snapshot.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(100, 20000), Array.Empty<object>()));
			maxVertices = ((BaseUnityPlugin)this).Config.Bind<int>("Limits", "WorldVertices", 500000, new ConfigDescription("Unique mesh vertex budget per world snapshot; repeated room meshes share data.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1000, 1000000), Array.Empty<object>()));
			ConfigEntry<int> val = ((BaseUnityPlugin)this).Config.Bind<int>("Compatibility", "CaptureSettingsVersion", 0, "Internal capture settings migration version.");
			if (val.Value < 4)
			{
				if (maxVertices.Value == 120000)
				{
					maxVertices.Value = 500000;
				}
				val.Value = 4;
			}
			if (val.Value < 5)
			{
				bones.Value = false;
				val.Value = 5;
			}
			if (val.Value < 6)
			{
				val.Value = 6;
			}
			folder = ((BaseUnityPlugin)this).Config.Bind<string>("Storage", "ReplayDirectory", "replays", "Absolute folder, or relative to BepInEx. Restart after changing.");
			extraTypes = ((BaseUnityPlugin)this).Config.Bind<string>("Compatibility", "ExtraTrackedTypes", "", "Comma-separated full Unity Component names supplied by other mods.");
			replayResolution = ((BaseUnityPlugin)this).Config.Bind<float>("Playback", "ResolutionMultiplier", 1f, new ConfigDescription("Replay resolution relative to the HDLethalCompany vanilla baseline: 1 = 860x520. Independent of window size; UI stays sharp. Replaces RenderResolutionScale.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 4.5f), Array.Empty<object>()));
			ConfigEntry<int> val2 = ((BaseUnityPlugin)this).Config.Bind<int>("Compatibility", "PlaybackSettingsVersion", 0, "Internal playback defaults migration version.");
			replayGamma = ((BaseUnityPlugin)this).Config.Bind<float>("Playback", "Gamma", 1f, new ConfigDescription("Replay image gamma; 1.0 is neutral.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			noShadow = ((BaseUnityPlugin)this).Config.Bind<bool>("Playback", "NoShadow", false, "Keep outside illumination inside the facility and disable replay light shadows.");
			showFog = ((BaseUnityPlugin)this).Config.Bind<bool>("Playback", "ShowFog", true, "Render recorded HDRP fog, local fog and fog-named scenery in replay.");
			if (val2.Value < 3)
			{
				replayResolution.Value = 1f;
				replayGamma.Value = 1f;
				noShadow.Value = false;
				showFog.Value = true;
				val2.Value = 3;
				((BaseUnityPlugin)this).Config.Save();
			}
			cinematicMove = ((BaseUnityPlugin)this).Config.Bind<bool>("Playback", "CinematicMove", false, "Use smooth acceleration and deceleration for the replay free camera. Press C in replay to toggle.");
			cameraSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Playback", "CameraSpeed", 8f, new ConfigDescription("Free camera speed in replay.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>()));
			showDebugOverlay = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "ShowOverlay", false, "Show the recording status in the top-left corner during gameplay. Errors appear briefly in the top-right corner regardless of this setting.");
			ConfigEntry<KeyCode> fallback = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Input", "BookmarkKey", (KeyCode)96, "Recording bookmark key when InputUtils is absent. With InputUtils, change Add replay bookmark in the game's Controls menu.");
			bookmarkInput = new ReplayBookmarkInput(fallback, delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			});
			try
			{
				archive = new ReplayArchive(ReplayDirectory);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Replay archive directory: " + archive.RootDirectory));
				library = new ReplayLibraryWindow();
				menuButton = new ReplayMenuButton(OpenLibrary, delegate(string message)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)message);
				}, delegate(string message)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)message);
				});
				pauseMenuButton = new ReplayPauseMenuButton(OpenLibrary, delegate(string message)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)message);
				});
				InstallInputGuards();
				library.RefreshRequested += RefreshArchive;
				library.PlayRequested += delegate(string path)
				{
					LoadReplay(path);
				};
				library.OpenFolderRequested += OpenFolder;
				library.DeleteRequested += DeleteRecording;
				library.CloseRequested += CloseLibrary;
				hooks = new EventHooks(delegate(ReplayEvent evt)
				{
					recorder?.Event(evt);
					if (evt.Name.Contains("OnShipLanded"))
					{
						recorder?.RefreshEnvironmentAfterLanding();
					}
					else if (evt.Name.Contains("StartGame"))
					{
						recorder?.MarkWorldDirty();
					}
					else if (evt.Name == "SandSpiderAI.SpawnWebTrapClientRpc")
					{
						recorder?.NoticeSpiderWeb();
					}
				}, chat.Value, delegate(string message)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)message);
				});
				((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("LC Replay {0}: automatic quota/deadline archive; {1} event hooks active.", "1.0.0", hooks.Installed.Count));
			}
			catch (Exception error)
			{
				ReportRecordingFailure("Initialization failed", error);
			}
			SceneManager.sceneLoaded += SceneLoaded;
			Application.quitting += ApplicationQuitting;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Replay bootstrap ready; waiting for a loaded scene before creating the runtime.");
			Scene activeScene = SceneManager.GetActiveScene();
			if (((Scene)(ref activeScene)).isLoaded)
			{
				EnsureRuntime();
			}
		}

		private void SceneLoaded(Scene scene, LoadSceneMode mode)
		{
			EnsureRuntime();
		}

		private void EnsureRuntime()
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			if (quitting || shutdownCompleted || Object.op_Implicit((Object)(object)runtime))
			{
				return;
			}
			try
			{
				runtime = ReplayRuntime.Create(RunFrame, DrawUi, RunLateFrame, delegate
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Replay runtime active: independent persistent frame and UI driver.");
				}, delegate(string stage, Exception ex)
				{
					ShowErrorNotice("Replay runtime " + stage + " failed: " + ex.Message);
					((BaseUnityPlugin)this).Logger.LogError((object)("Replay runtime " + stage + " failed: " + ex));
				});
				ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
				Scene activeScene = SceneManager.GetActiveScene();
				logger.LogInfo((object)("Replay runtime created after scene load: " + ((Scene)(ref activeScene)).name + "."));
			}
			catch (Exception error)
			{
				ReportRecordingFailure("Replay runtime initialization failed", error);
			}
		}

		private void RunFrame()
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			if (quitting)
			{
				return;
			}
			NativePlayerAssetLoader.Tick();
			if (viewer != null || cachedViewer != null || loading != null || SceneAssetReplay.HasPendingLoads || ReplayViewer.HasPendingSceneCleanup || NativePlayerAssetLoader.RequiresIsolation)
			{
				ReplayIsolation.EnsureNetworkExclusion();
			}
			Scene activeScene;
			if (cachedViewer != null)
			{
				if (cachedConnected == GameAccess.Connected)
				{
					int num = cachedSceneHandle;
					activeScene = SceneManager.GetActiveScene();
					if (num == ((Scene)(ref activeScene)).handle)
					{
						goto IL_0071;
					}
				}
				DiscardCachedViewer();
			}
			goto IL_0071;
			IL_01d1:
			object replayPauseMenuButton;
			int allowed;
			((ReplayPauseMenuButton)replayPauseMenuButton).Tick((byte)allowed != 0);
			goto IL_01d6;
			IL_01d6:
			if (viewer != null)
			{
				BlockLocalPlayerInput();
				if (viewer.CloseRequested || ReplayInput.WasPressed("Escape") || ReplayInput.WasPressed("F11"))
				{
					CloseViewer(reopenLibrary: true);
					return;
				}
				try
				{
					viewer.Tick(Time.unscaledDeltaTime);
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)ex);
					CloseViewer(reopenLibrary: true, allowCache: false);
					status = "Playback error: " + ex.Message;
					ShowErrorNotice(status);
				}
				if (viewer?.Error != null)
				{
					Exception error = viewer.Error;
					((BaseUnityPlugin)this).Logger.LogError((object)error);
					CloseViewer(reopenLibrary: true, allowCache: false);
					status = "A recording part could not be loaded: " + error.Message;
					ShowErrorNotice(status);
				}
				return;
			}
			bool connected;
			CompleteLoad(connected);
			if (viewer != null)
			{
				return;
			}
			if (ReplayInput.WasPressed("F9"))
			{
				ReplayLibraryWindow? replayLibraryWindow = library;
				if (replayLibraryWindow != null && replayLibraryWindow.IsOpen)
				{
					if (!library.DismissDialog())
					{
						CloseLibrary();
					}
				}
				else
				{
					OpenLibrary();
				}
			}
			ReplayLibraryWindow? replayLibraryWindow2 = library;
			if (replayLibraryWindow2 != null && replayLibraryWindow2.IsOpen)
			{
				library.Tick();
				BlockLocalPlayerInput();
				ReplayLibraryWindow? replayLibraryWindow3 = library;
				string text = status;
				bool networkStateKnown = GameAccess.NetworkStateKnown;
				bool isLoading = loading != null || scanning != null;
				string text2 = recordingFailure;
				float loadProgress = ((loading != null) ? ((float)Volatile.Read(in loadingPercent) / 100f) : 0f);
				string loadStage = ((loading != null) ? Volatile.Read(in loadingStage) : ((scanning != null) ? "Scanning archive" : ""));
				ArchiveDay? obj = day;
				replayLibraryWindow3.DrawGui(text, networkStateKnown, isLoading, text2, loadProgress, loadStage, ((obj != null) ? ((ArchiveFolder)obj).Id : null) ?? "");
				if (ReplayInput.WasPressed("Escape") && !library.DismissDialog())
				{
					CloseLibrary();
				}
			}
			if (ReplayInput.WasPressed("F8") && GameAccess.CanRecord && archive != null && hooks != null)
			{
				try
				{
					if (recordingBlocked && day != null)
					{
						archive.EndDay(day, "interrupted");
						day = null;
						segment = null;
						part = 0;
						reusableWorld = null;
					}
					recordingBlocked = false;
					nextRecordingRetryAt = 0f;
					recordingRetryFailures = 0;
					if (recorder != null)
					{
						checkpointRequested = true;
					}
				}
				catch (Exception error2)
				{
					RecoverRecording("Save checkpoint failed", error2);
				}
			}
			ReplayBookmarkInput? replayBookmarkInput = bookmarkInput;
			if (replayBookmarkInput != null && replayBookmarkInput.WasPressed())
			{
				AddRecordingBookmark();
			}
			if (ReplayInput.WasPressed("F11"))
			{
				OpenLibrary();
			}
			return;
			IL_0197:
			replayPauseMenuButton = pauseMenuButton;
			if (replayPauseMenuButton == null)
			{
				goto IL_01d6;
			}
			if (connected && viewer == null)
			{
				ReplayLibraryWindow? replayLibraryWindow4 = library;
				if (replayLibraryWindow4 == null || !replayLibraryWindow4.IsOpen)
				{
					allowed = ((loading == null) ? 1 : 0);
					goto IL_01d1;
				}
			}
			allowed = 0;
			goto IL_01d1;
			IL_0071:
			if (closeLibraryAfterSceneCleanup && !SceneAssetReplay.HasPendingLoads && !ReplayViewer.HasPendingSceneCleanup)
			{
				CloseLibrary();
			}
			bool available = ReplayInput.Available;
			if (!inputReported || (available && !inputReadyReported))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)(available ? "Replay keyboard input ready (F9 opens the archive)." : "Replay keyboard input is not ready; waiting for an Input System keyboard."));
				inputReported = true;
				inputReadyReported |= available;
			}
			connected = GameAccess.Connected;
			if (viewer != null)
			{
				if (viewerConnected == connected)
				{
					int num2 = viewerSceneHandle;
					activeScene = SceneManager.GetActiveScene();
					if (num2 == ((Scene)(ref activeScene)).handle)
					{
						goto IL_0115;
					}
				}
				CloseViewer(reopenLibrary: false, allowCache: false);
				status = "Replay closed after the live game changed scenes.";
				return;
			}
			goto IL_0115;
			IL_0115:
			if (!connected && wasConnected)
			{
				EndSession();
				recordingBlocked = false;
				recordingRetryFailures = 0;
				nextRecordingRetryAt = 0f;
			}
			wasConnected = connected;
			CompleteSaves();
			ResumeRecordingWhenReady();
			CompleteScan();
			ReplayMenuButton? replayMenuButton = menuButton;
			if (replayMenuButton == null)
			{
				goto IL_0197;
			}
			int allowed2;
			if (!connected && viewer == null)
			{
				ReplayLibraryWindow? replayLibraryWindow5 = library;
				if (replayLibraryWindow5 == null || !replayLibraryWindow5.IsOpen)
				{
					allowed2 = ((loading == null) ? 1 : 0);
					goto IL_0192;
				}
			}
			allowed2 = 0;
			goto IL_0192;
			IL_0192:
			replayMenuButton.Tick((byte)allowed2 != 0);
			goto IL_0197;
		}

		private void RunLateFrame()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			if (quitting)
			{
				return;
			}
			viewer?.RefreshCursor();
			if (hooks != null && archive != null && GameAccess.CanRecord)
			{
				try
				{
					if (checkpointRequested)
					{
						recorder?.Event(new ReplayEvent
						{
							Category = "capture",
							Name = "manual-checkpoint"
						});
					}
					RecordAutomatically(viewer != null);
				}
				catch (Exception error)
				{
					RecoverRecording("Automatic recording stopped", error);
				}
			}
			checkpointRequested = false;
		}

		private unsafe void RecordAutomatically(bool playbackActive = false)
		{
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Invalid comparison between Unknown and I4
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Expected O, but got Unknown
			//IL_0428: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Expected O, but got Unknown
			//IL_049d: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f9: Expected O, but got Unknown
			//IL_0507: Unknown result type (might be due to invalid IL or missing references)
			//IL_050a: Invalid comparison between Unknown and I4
			if (recordingBlocked)
			{
				return;
			}
			object obj = GameAccess.Singleton("StartOfRound");
			if (session != null)
			{
				Component val = (Component)((obj is Component) ? obj : null);
				if (val != null && (Object)(object)val != (Object)(object)sessionRound)
				{
					EndSession();
				}
			}
			if (recordingBlocked)
			{
				return;
			}
			if (!(GameAccess.Read(obj, "inShipPhase") is bool flag))
			{
				throw new InvalidOperationException("This game version does not expose round phase (inShipPhase).");
			}
			if ((Object)(object)deletedLobbyRound != (Object)null)
			{
				if (obj == deletedLobbyRound && flag)
				{
					return;
				}
				deletedLobbyRound = null;
			}
			ArchiveQuotaSnapshot val2 = CurrentQuota();
			if (session == null && (!val2.Target.HasValue || val2.Target.Value <= 0 || !val2.DeadlineDaysRemaining.HasValue || (val2.DeadlineDaysRemaining.Value == 0 && (!(GameAccess.Read(GameAccess.Singleton("TimeOfDay"), "timeUntilDeadline") is float num) || !(num > 0f)))))
			{
				return;
			}
			if (session == null)
			{
				object value = GameAccess.Singleton("GameNetworkManager");
				string text = (GameAccess.Read(value, "steamLobbyName") as string)?.Trim();
				if (string.IsNullOrWhiteSpace(text) && GameAccess.IsHost)
				{
					text = (GameAccess.Read(GameAccess.Read(value, "lobbyHostSettings"), "lobbyName") as string)?.Trim();
				}
				if (string.IsNullOrWhiteSpace(text))
				{
					text = "Unknown lobby";
				}
				if (run != null && !string.Equals(((ArchiveFolder)run).Label, text, StringComparison.Ordinal))
				{
					archive.EndRun(run);
					run = null;
				}
				run = run ?? archive.BeginRun(DateTimeOffset.UtcNow, text);
				session = archive.BeginQuota(run, DateTimeOffset.UtcNow, val2, GameAccess.IsHost ? "host-observed" : "client-observed");
				sessionRound = (Component?)((obj is Component) ? obj : null);
				dayLifecycle = new DayLifecycle();
			}
			DayTransition val3 = dayLifecycle.Observe(flag);
			if (day != null && (int)val3 == 2)
			{
				recorder?.Event(new ReplayEvent
				{
					Category = "day",
					Name = ((object)(*(DayTransition*)(&val3))/*cast due to .constrained prefix*/).ToString(),
					Data = new Dictionary<string, string> { ["day"] = dayLifecycle.DayNumber.ToString() }
				});
				recorder?.Tick(forceFrame: true, playbackActive);
				StopSegment();
				ArchiveDay val4 = day;
				day = null;
				part = 0;
				archive.EndDay(val4, (recordingBlocked || dayHadRecoveryGap) ? "interrupted" : "saved");
				dayHadRecoveryGap = false;
			}
			else
			{
				if (day == null && dayLifecycle.HasReturned)
				{
					return;
				}
				if (day == null && session.QuotaRemaining != val2.Remaining)
				{
					archive.EndSession(session);
					session = archive.BeginQuota(run, DateTimeOffset.UtcNow, val2, GameAccess.IsHost ? "host-observed" : "client-observed");
				}
				if (day == null)
				{
					BeginDay(obj, val2);
				}
				if (day == null)
				{
					return;
				}
				if (Time.realtimeSinceStartup >= nextMemberScan)
				{
					nextMemberScan = Time.realtimeSinceStartup + 2f;
					if (archive.UpdateMembers(day, CurrentMembers(obj)))
					{
						RefreshArchiveIfOpen();
					}
				}
				string text2 = CurrentMoon(obj);
				if ((!dayLifecycle.HasReturned && text2 != day.Moon) || (int)val3 != 0)
				{
					archive.UpdateDay(day, dayLifecycle.HasReturned ? day.Moon : text2, dayLifecycle.HasReturned ? "returned" : (dayLifecycle.HasDeparted ? "exploring" : "preparing"));
				}
				if (recorder == null)
				{
					StartSegment();
				}
				if (recorder != null)
				{
					if ((int)val3 != 0)
					{
						recorder.Event(new ReplayEvent
						{
							Category = "day",
							Name = ((object)(*(DayTransition*)(&val3))/*cast due to .constrained prefix*/).ToString(),
							Data = new Dictionary<string, string> { ["day"] = dayLifecycle.DayNumber.ToString() }
						});
					}
					recorder.Tick(checkpointRequested || (int)val3 == 2, playbackActive);
					if (recorder.Error != null)
					{
						Exception error = recorder.Error;
						RecoverRecording("Automatic saving stopped", error);
					}
					else if (recorder.Duration >= 30.0)
					{
						recordingRetryFailures = 0;
					}
				}
			}
		}

		private void BeginDay(object? round, ArchiveQuotaSnapshot quota)
		{
			int? num = null;
			if (GameAccess.IsHost && GameAccess.Read(GameAccess.Read(round, "gameStats"), "daysSpent") is int num2 && num2 >= 0 && num2 < 1000000)
			{
				num = num2 + 1;
			}
			day = archive.BeginQuotaDay(session, dayLifecycle.DayNumber, DateTimeOffset.UtcNow, CurrentMoon(round), quota, num);
			nextMemberScan = 0f;
			dayHadRecoveryGap = false;
			reusableWorld = null;
			archive.UpdateDay(day, day.Moon, dayLifecycle.HasReturned ? "returned" : (dayLifecycle.HasDeparted ? "exploring" : "preparing"));
			part = 0;
			RefreshArchiveIfOpen();
		}

		private static string CurrentMoon(object? round)
		{
			return GameAccess.Scalar(GameAccess.Read(GameAccess.Read(round, "currentLevel"), "PlanetName")) ?? "Unknown moon";
		}

		private static ArchiveQuotaSnapshot CurrentQuota()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Expected O, but got Unknown
			object value = GameAccess.Singleton("TimeOfDay");
			int? num = NonnegativeInt(GameAccess.Read(value, "daysUntilDeadline"));
			object obj = GameAccess.Read(value, "timeUntilDeadline");
			object obj2 = GameAccess.Read(value, "totalTime");
			if (obj is float num2 && obj2 is float num3)
			{
				num = QuotaDay.Remaining(num2, num3, num);
			}
			return new ArchiveQuotaSnapshot
			{
				Target = NonnegativeInt(GameAccess.Read(value, "profitQuota")),
				Fulfilled = NonnegativeInt(GameAccess.Read(value, "quotaFulfilled")),
				DeadlineDaysRemaining = num,
				DeadlineDaysTotal = NonnegativeInt(GameAccess.Read(GameAccess.Read(value, "quotaVariables"), "deadlineDaysAmount")),
				QuotaCycle = NonnegativeInt(GameAccess.Read(value, "timesFulfilledQuota"))
			};
		}

		private static int? NonnegativeInt(object? value)
		{
			if (value == null)
			{
				return null;
			}
			try
			{
				return Math.Max(0, Convert.ToInt32(value, CultureInfo.InvariantCulture));
			}
			catch
			{
				return null;
			}
		}

		private static IEnumerable<string> CurrentMembers(object? round)
		{
			if (!(GameAccess.Read(round, "allPlayerScripts") is IEnumerable enumerable))
			{
				yield break;
			}
			foreach (object item in enumerable)
			{
				if (item != null && GameAccess.Bool(item, "isPlayerControlled") && GameAccess.Read(item, "playerUsername") is string text && !string.IsNullOrWhiteSpace(text))
				{
					yield return text;
				}
			}
		}

		private void StartSegment()
		{
			if (recorder != null || recordingBlocked || day == null || hooks == null)
			{
				return;
			}
			segment = archive.AllocateSegment(day, ++part);
			if (day.Members.Count != 0)
			{
				segment.Metadata["members"] = string.Join(", ", day.Members);
			}
			part = segment.Part;
			try
			{
				recorder = new Recorder(((ArchiveFolder)day).DirectoryPath, rate.Value, bones.Value, world.Value, maxFields.Value, maxObjects.Value, maxVertices.Value, extraTypes.Value, hooks, chat.Value, delegate(string message)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)message);
				}, ((ArchiveFolder)day).Id, part, segment.FilePath, segment.Metadata, reusableWorld);
				ReplayApi.EventSink = recorder.Event;
				recordingFailure = "";
				library?.SetRecordingPath(segment.FilePath);
				status = "Quota " + (day.QuotaRemaining?.ToString() ?? "unknown") + " / Deadline " + (day.DeadlineDaysRemaining?.ToString() ?? "unknown") + " days - recording and saving automatically";
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Recording " + segment.FilePath));
				RefreshArchiveIfOpen();
			}
			catch (Exception ex)
			{
				try
				{
					archive.CompleteSegment(segment, 0.0, false, ex.Message, (int?)

LCReplay.Core.dll

Decompiled 10 hours ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.Versioning;
using System.Security;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("LCReplay.Core.Tests")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LCReplay.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+31677f6cc6ace662863f1d3840f84ade19c50bbc")]
[assembly: AssemblyProduct("LCReplay.Core")]
[assembly: AssemblyTitle("LCReplay.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 LCReplay.Core
{
	public sealed class ReplayHeader
	{
		public int SchemaVersion { get; set; } = 1;

		public string SessionId { get; set; } = "";

		public string GameVersion { get; set; } = "";

		public string UnityVersion { get; set; } = "";

		public string RecorderVersion { get; set; } = "";

		public string StartedUtc { get; set; } = "";

		public string Perspective { get; set; } = "";

		public int SampleRate { get; set; } = 10;

		public Dictionary<string, string> Metadata { get; set; } = new Dictionary<string, string>();

		public List<string> Capabilities { get; set; } = new List<string>();

		public List<string> Warnings { get; set; } = new List<string>();
	}
	public sealed class ReplayRecord
	{
		public string Kind { get; set; } = "";

		public double Time { get; set; }

		[JsonIgnore]
		public double? SourceTime { get; set; }

		public ReplayHeader? Header { get; set; }

		public ReplayFrame? Frame { get; set; }

		public ReplayEvent? Event { get; set; }

		public WorldSnapshot? World { get; set; }
	}
	public sealed class ReplayFrame
	{
		public double Time { get; set; }

		public List<EntitySnapshot> Entities { get; set; } = new List<EntitySnapshot>();

		public List<AnchorPose> Anchors { get; set; } = new List<AnchorPose>();

		public List<RenderPose> SceneRenderers { get; set; } = new List<RenderPose>();

		public List<ParticlePose> Particles { get; set; } = new List<ParticlePose>();

		public List<ParticleStyleSnapshot> ParticleStyles { get; set; } = new List<ParticleStyleSnapshot>();

		public List<LinePose> Lines { get; set; } = new List<LinePose>();

		public Dictionary<string, string> State { get; set; } = new Dictionary<string, string>();
	}
	public sealed class ParticlePose
	{
		public string EmitterId { get; set; } = "";

		public Vec3 Position { get; set; }

		public Vec3 Velocity { get; set; }

		public Vec3 Size3D { get; set; }

		public Vec3 Rotation3D { get; set; }

		public float Lifetime { get; set; } = 1f;

		public float RemainingLifetime { get; set; } = 1f;

		public uint RandomSeed { get; set; }

		public float Size { get; set; }

		public float Rotation { get; set; }

		public float[] Color { get; set; } = new float[4] { 1f, 1f, 1f, 1f };

		public bool IsInterior { get; set; }
	}
	public sealed class LinePose
	{
		public string Id { get; set; } = "";

		public string MaterialName { get; set; } = "";

		public string ShaderName { get; set; } = "";

		public int TextureMode { get; set; }

		public int Alignment { get; set; }

		public float[] Positions { get; set; } = Array.Empty<float>();

		public float[] StartColor { get; set; } = new float[4] { 1f, 1f, 1f, 1f };

		public float[] EndColor { get; set; } = new float[4] { 1f, 1f, 1f, 1f };

		public float StartWidth { get; set; }

		public float EndWidth { get; set; }

		public bool IsInterior { get; set; }
	}
	public sealed class AnchorPose
	{
		public string Id { get; set; } = "";

		public Vec3 Position { get; set; }

		public Quat Rotation { get; set; } = Quat.Identity;

		public Vec3 Scale { get; set; } = Vec3.One;
	}
	public sealed class EntitySnapshot
	{
		public string Id { get; set; } = "";

		public string Kind { get; set; } = "";

		public string Name { get; set; } = "";

		public Vec3 Position { get; set; }

		public Vec3 Scale { get; set; } = Vec3.One;

		public Quat Rotation { get; set; } = Quat.Identity;

		public bool Active { get; set; } = true;

		public Quat? ViewRotation { get; set; }

		public Vec3? ViewPosition { get; set; }

		public bool PoseFromItemEvents { get; set; }

		public Dictionary<string, string> State { get; set; } = new Dictionary<string, string>();

		public List<BonePose> Bones { get; set; } = new List<BonePose>();

		public List<RenderPose> Renderers { get; set; } = new List<RenderPose>();

		public bool ShouldSerializeViewRotation()
		{
			return ViewRotation.HasValue;
		}

		public bool ShouldSerializeViewPosition()
		{
			return ViewPosition.HasValue;
		}

		public bool ShouldSerializePoseFromItemEvents()
		{
			return PoseFromItemEvents;
		}

		public bool ShouldSerializePosition()
		{
			return !PoseFromItemEvents;
		}

		public bool ShouldSerializeRotation()
		{
			return !PoseFromItemEvents;
		}

		public bool ShouldSerializeScale()
		{
			return !PoseFromItemEvents;
		}
	}
	public struct Vec3
	{
		public float X { get; set; }

		public float Y { get; set; }

		public float Z { get; set; }

		public static Vec3 One => new Vec3(1f, 1f, 1f);

		public Vec3(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}
	}
	public struct Quat
	{
		public float X { get; set; }

		public float Y { get; set; }

		public float Z { get; set; }

		public float W { get; set; }

		public static Quat Identity => new Quat(0f, 0f, 0f, 1f);

		public Quat(float x, float y, float z, float w)
		{
			X = x;
			Y = y;
			Z = z;
			W = w;
		}
	}
	public sealed class BonePose
	{
		public string Path { get; set; } = "";

		public Vec3 Position { get; set; }

		public Quat Rotation { get; set; } = Quat.Identity;

		public Vec3 Scale { get; set; } = Vec3.One;
	}
	public sealed class RenderPose
	{
		public string Id { get; set; } = "";

		public Vec3 Position { get; set; }

		public Quat Rotation { get; set; } = Quat.Identity;

		public Vec3 Scale { get; set; } = Vec3.One;

		public bool Active { get; set; } = true;
	}
	public sealed class ReplayEvent
	{
		public double Time { get; set; }

		public string Category { get; set; } = "";

		public string Name { get; set; } = "";

		public string EntityId { get; set; } = "";

		public Dictionary<string, string> Data { get; set; } = new Dictionary<string, string>();

		public AnimationTrackSnapshot? AnimationTrack { get; set; }

		public ItemMotionSnapshot? ItemMotion { get; set; }

		public EnvironmentComponentSnapshot? PostProcess { get; set; }
	}
	public sealed class ItemMotionSnapshot
	{
		public string Mode { get; set; } = "";

		public string AnchorId { get; set; } = "";

		public Vec3 Position { get; set; }

		public Quat Rotation { get; set; } = Quat.Identity;

		public Vec3 Scale { get; set; } = Vec3.One;

		public Vec3 Target { get; set; }

		public Quat TargetRotation { get; set; } = Quat.Identity;

		public float FallTime { get; set; }

		public float FallRate { get; set; }

		public float[] Curve { get; set; } = new float[0];

		public bool ShouldSerializeAnchorId()
		{
			return AnchorId.Length != 0;
		}

		public bool ShouldSerializeTarget()
		{
			return Mode == "fall";
		}

		public bool ShouldSerializeTargetRotation()
		{
			return Mode == "fall";
		}

		public bool ShouldSerializeFallTime()
		{
			return Mode == "fall";
		}

		public bool ShouldSerializeFallRate()
		{
			return Mode == "fall";
		}

		public bool ShouldSerializeCurve()
		{
			return Mode == "fall";
		}
	}
	public sealed class AnimationTrackSnapshot
	{
		public string Clip { get; set; } = "";

		public string AnimatorPath { get; set; } = "";

		public int StateHash { get; set; }

		public int Layer { get; set; }

		public bool Looping { get; set; }

		public List<string> BonePaths { get; set; } = new List<string>();

		public float[] Phases { get; set; } = new float[0];

		public float[] Positions { get; set; } = new float[0];

		public float[] Rotations { get; set; } = new float[0];
	}
	public sealed class WorldSnapshot
	{
		public string Scene { get; set; } = "";

		public string CaptureSetId { get; set; } = "";

		public string Layer { get; set; } = "";

		public double CaptureCompletedAt { get; set; }

		public string AssetScene { get; set; } = "";

		public string AssetGameVersion { get; set; } = "";

		public int AssetBuildIndex { get; set; } = -1;

		public int MapSeed { get; set; }

		public int LevelId { get; set; } = -1;

		public int DungeonSeed { get; set; }

		public int DungeonFlow { get; set; } = -1;

		public List<string> AssetRendererPaths { get; set; } = new List<string>();

		public List<string> AssetTerrainPaths { get; set; } = new List<string>();

		public List<GeometrySnapshot> Geometry { get; set; } = new List<GeometrySnapshot>();

		public List<TextureSnapshot> Textures { get; set; } = new List<TextureSnapshot>();

		public List<MaterialSnapshot> Materials { get; set; } = new List<MaterialSnapshot>();

		public List<LightSnapshot> Lights { get; set; } = new List<LightSnapshot>();

		public List<ParticleEmitterSnapshot> ParticleEmitters { get; set; } = new List<ParticleEmitterSnapshot>();

		public List<LocalFogSnapshot> LocalFogs { get; set; } = new List<LocalFogSnapshot>();

		public List<RoomSnapshot> Rooms { get; set; } = new List<RoomSnapshot>();

		public EnvironmentSnapshot? Environment { get; set; }

		public bool ShouldSerializeCaptureCompletedAt()
		{
			return CaptureCompletedAt > 0.0;
		}
	}
	public sealed class RoomSnapshot
	{
		public string Id { get; set; } = "";

		public Vec3 Center { get; set; }

		public Vec3 Size { get; set; }

		public List<RoomVolumeSnapshot> AdditionalVolumes { get; set; } = new List<RoomVolumeSnapshot>();
	}
	public sealed class RoomVolumeSnapshot
	{
		public Vec3 Center { get; set; }

		public Vec3 Size { get; set; }
	}
	public sealed class EnvironmentSnapshot
	{
		public float[] AmbientSkyColor { get; set; } = new float[4] { 0.15f, 0.2f, 0.28f, 1f };

		public List<EnvironmentComponentSnapshot> Components { get; set; } = new List<EnvironmentComponentSnapshot>();

		public List<TextureSnapshot> SkyFaces { get; set; } = new List<TextureSnapshot>();

		public string SkyFaceEncoding { get; set; } = "";

		public List<PostProcessPassSnapshot> CustomPasses { get; set; } = new List<PostProcessPassSnapshot>();

		public bool CustomPassCaptureComplete { get; set; }

		public bool ShouldSerializeSkyFaceEncoding()
		{
			return SkyFaceEncoding.Length != 0;
		}

		public bool ShouldSerializeCustomPasses()
		{
			return CustomPasses.Count != 0;
		}

		public bool ShouldSerializeCustomPassCaptureComplete()
		{
			return CustomPassCaptureComplete;
		}
	}
	public sealed class PostProcessPassSnapshot
	{
		public string Name { get; set; } = "";

		public string ShaderName { get; set; } = "";

		public string MaterialName { get; set; } = "";

		public string InjectionPoint { get; set; } = "";

		public string MaterialPassName { get; set; } = "";

		public bool? FetchColorBuffer { get; set; }

		public List<EnvironmentParameterSnapshot> Properties { get; set; } = new List<EnvironmentParameterSnapshot>();

		public bool ShouldSerializeMaterialName()
		{
			return MaterialName.Length != 0;
		}

		public bool ShouldSerializeFetchColorBuffer()
		{
			return FetchColorBuffer.HasValue;
		}
	}
	public sealed class EnvironmentComponentSnapshot
	{
		public string Type { get; set; } = "";

		public List<EnvironmentParameterSnapshot> Parameters { get; set; } = new List<EnvironmentParameterSnapshot>();
	}
	public sealed class EnvironmentParameterSnapshot
	{
		public string Name { get; set; } = "";

		public string Kind { get; set; } = "";

		public float[] Values { get; set; } = new float[0];

		public string Text { get; set; } = "";

		public float[] CurveKeys { get; set; } = new float[0];

		public bool ShouldSerializeText()
		{
			return Text.Length != 0;
		}

		public bool ShouldSerializeCurveKeys()
		{
			return CurveKeys.Length != 0;
		}
	}
	public sealed class LightSnapshot
	{
		public string Id { get; set; } = "";

		public string Name { get; set; } = "";

		public string AnchorId { get; set; } = "";

		public string EntityId { get; set; } = "";

		public bool IsInterior { get; set; }

		public string Type { get; set; } = "Point";

		public Vec3 Position { get; set; }

		public Quat Rotation { get; set; } = Quat.Identity;

		public float[] Color { get; set; } = new float[4] { 1f, 1f, 1f, 1f };

		public bool UseColorTemperature { get; set; }

		public float ColorTemperature { get; set; } = 6500f;

		public float Intensity { get; set; } = 1f;

		public float LightDimmer { get; set; } = 1f;

		public float Range { get; set; } = 10f;

		public float SpotAngle { get; set; } = 30f;

		public bool Shadows { get; set; }

		public float ShadowStrength { get; set; } = 1f;

		public float ShadowDimmer { get; set; } = 1f;

		public string BakeType { get; set; } = "";

		public List<EnvironmentParameterSnapshot> LightPipelineParameters { get; set; } = new List<EnvironmentParameterSnapshot>();

		public bool ShouldSerializeName()
		{
			return Name.Length != 0;
		}

		public bool ShouldSerializeLightPipelineParameters()
		{
			return LightPipelineParameters.Count != 0;
		}

		public bool ShouldSerializeShadowStrength()
		{
			return ShadowStrength != 1f;
		}

		public bool ShouldSerializeLightDimmer()
		{
			return LightDimmer != 1f;
		}

		public bool ShouldSerializeShadowDimmer()
		{
			return ShadowDimmer != 1f;
		}

		public bool ShouldSerializeBakeType()
		{
			return BakeType.Length != 0;
		}
	}
	public sealed class ParticleEmitterSnapshot
	{
		public string Id { get; set; } = "";

		public string Name { get; set; } = "";

		public string EntityId { get; set; } = "";

		public string MaterialId { get; set; } = "";

		public ParticleStyleSnapshot? Style { get; set; }

		public bool IsInterior { get; set; }

		public string RoomId { get; set; } = "";

		public Vec3 Position { get; set; }

		public Quat Rotation { get; set; } = Quat.Identity;

		public float[] Color { get; set; } = new float[4] { 1f, 1f, 1f, 1f };

		public float Rate { get; set; } = 10f;

		public float Lifetime { get; set; } = 1f;

		public float Speed { get; set; }

		public float Size { get; set; } = 0.1f;

		public float Radius { get; set; } = 0.1f;
	}
	public sealed class ParticleStyleSnapshot
	{
		public string Id { get; set; } = "";

		public string Name { get; set; } = "";

		public string ParentName { get; set; } = "";

		public string MaterialName { get; set; } = "";

		public string ShaderName { get; set; } = "";

		public string MeshName { get; set; } = "";

		public bool Simulate { get; set; }

		public bool IsInterior { get; set; }

		public Vec3 Position { get; set; }

		public Quat Rotation { get; set; } = Quat.Identity;

		public float Time { get; set; }

		public uint RandomSeed { get; set; }

		public int RenderMode { get; set; }

		public int Alignment { get; set; }

		public int[] VertexStreams { get; set; } = Array.Empty<int>();

		public float LengthScale { get; set; } = 2f;

		public float VelocityScale { get; set; }

		public float CameraVelocityScale { get; set; }

		public Vec3 Scale { get; set; } = Vec3.One;

		public Vec3 Pivot { get; set; }
	}
	public sealed class LocalFogSnapshot
	{
		public string Id { get; set; } = "";

		public string Name { get; set; } = "";

		public bool IsInterior { get; set; }

		public string RoomId { get; set; } = "";

		public Vec3 Position { get; set; }

		public Quat Rotation { get; set; } = Quat.Identity;

		public Vec3 Size { get; set; } = new Vec3(1f, 1f, 1f);

		public Vec3 PositiveFade { get; set; } = new Vec3(0.1f, 0.1f, 0.1f);

		public Vec3 NegativeFade { get; set; } = new Vec3(0.1f, 0.1f, 0.1f);

		public float[] Albedo { get; set; } = new float[4] { 1f, 1f, 1f, 1f };

		public float MeanFreePath { get; set; } = 10f;

		public float Anisotropy { get; set; }

		public float DistanceFadeStart { get; set; } = 10000f;

		public float DistanceFadeEnd { get; set; } = 10000f;

		public int Priority { get; set; }

		public int BlendingMode { get; set; }

		public int FalloffMode { get; set; }

		public bool InvertFade { get; set; }

		public int MaskMode { get; set; }

		public Vec3 TextureTiling { get; set; } = Vec3.One;

		public Vec3 TextureScrollingSpeed { get; set; }

		public int MaskWidth { get; set; }

		public int MaskHeight { get; set; }

		public int MaskDepth { get; set; }

		public byte[] MaskRgba { get; set; } = new byte[0];
	}
	public sealed class TextureSnapshot
	{
		public string Id { get; set; } = "";

		public string Name { get; set; } = "";

		public int Width { get; set; }

		public int Height { get; set; }

		public bool Linear { get; set; }

		public int FilterMode { get; set; } = -1;

		public byte[] Png { get; set; } = new byte[0];

		public bool ShouldSerializeName()
		{
			return Name.Length != 0;
		}

		public bool ShouldSerializeFilterMode()
		{
			return FilterMode >= 0;
		}
	}
	public sealed class MaterialSnapshot
	{
		public string Id { get; set; } = "";

		public string Name { get; set; } = "";

		public string ShaderName { get; set; } = "";

		public float[] Color { get; set; } = new float[4] { 1f, 1f, 1f, 1f };

		public string TextureId { get; set; } = "";

		public float[] TextureScaleOffset { get; set; } = new float[4] { 1f, 1f, 0f, 0f };

		public bool AlphaClip { get; set; }

		public bool Transparent { get; set; }

		public float Cutoff { get; set; } = 0.5f;

		public int RenderQueue { get; set; } = -1;

		public List<string> Keywords { get; set; } = new List<string>();

		public bool? KeywordsComplete { get; set; }

		public List<MaterialPropertySnapshot> Properties { get; set; } = new List<MaterialPropertySnapshot>();

		public bool ShouldSerializeKeywordsComplete()
		{
			return KeywordsComplete.HasValue;
		}
	}
	public sealed class MaterialPropertySnapshot
	{
		public string Name { get; set; } = "";

		public string Kind { get; set; } = "";

		public float[] Values { get; set; } = new float[0];

		public string TextureId { get; set; } = "";

		public float[] TextureScaleOffset { get; set; } = new float[4] { 1f, 1f, 0f, 0f };
	}
	public sealed class GeometrySnapshot
	{
		public string Id { get; set; } = "";

		public string Name { get; set; } = "";

		public Vec3 Position { get; set; }

		public Vec3 Scale { get; set; } = Vec3.One;

		public Quat Rotation { get; set; } = Quat.Identity;

		public Vec3 BoundsCenter { get; set; }

		public Vec3 BoundsSize { get; set; }

		public float[] Vertices { get; set; } = new float[0];

		public int[] Triangles { get; set; } = new int[0];

		public float[] Color { get; set; } = new float[4] { 0.6f, 0.6f, 0.6f, 1f };

		public bool IsBoundsProxy { get; set; }

		public int ShadowCastingMode { get; set; } = -1;

		public bool ReceiveShadows { get; set; } = true;

		public string EntityId { get; set; } = "";

		public string AnchorId { get; set; } = "";

		public string MeshName { get; set; } = "";

		public string MeshSourceId { get; set; } = "";

		public string PrefabKey { get; set; } = "";

		public string PrefabRendererPath { get; set; } = "";

		public bool IsInterior { get; set; }

		public string RoomId { get; set; } = "";

		public bool IsMovingSceneRenderer { get; set; }

		public bool Active { get; set; } = true;

		public string LodGroupId { get; set; } = "";

		public int LodLevel { get; set; }

		public Vec3 LodCenter { get; set; }

		public float LodSwitchDistance { get; set; }

		public float[] Uvs { get; set; } = new float[0];

		public float[] Uvs1 { get; set; } = new float[0];

		public float[] Uvs2 { get; set; } = new float[0];

		public float[] Uvs3 { get; set; } = new float[0];

		public float[] Tangents { get; set; } = new float[0];

		public float[] Normals { get; set; } = new float[0];

		public float[] Instances { get; set; } = new float[0];

		public List<int[]> SubmeshTriangles { get; set; } = new List<int[]>();

		public List<string> MaterialIds { get; set; } = new List<string>();

		public List<string> BonePaths { get; set; } = new List<string>();

		public string AttachedBonePath { get; set; } = "";

		public string RootBonePath { get; set; } = "";

		public float[] BindPoses { get; set; } = new float[0];

		public int[] BoneIndices { get; set; } = new int[0];

		public float[] BoneWeights { get; set; } = new float[0];

		public List<BonePose> RigBones { get; set; } = new List<BonePose>();

		public string AnimatorPath { get; set; } = "";

		public string AnimatorController { get; set; } = "";

		public string AnimatorAvatar { get; set; } = "";

		public bool ShouldSerializeShadowCastingMode()
		{
			return ShadowCastingMode != -1;
		}

		public bool ShouldSerializeReceiveShadows()
		{
			return !ReceiveShadows;
		}

		public bool ShouldSerializePrefabKey()
		{
			return PrefabKey.Length != 0;
		}

		public bool ShouldSerializePrefabRendererPath()
		{
			return PrefabRendererPath.Length != 0;
		}

		public bool ShouldSerializeUvs1()
		{
			return Uvs1.Length != 0;
		}

		public bool ShouldSerializeUvs2()
		{
			return Uvs2.Length != 0;
		}

		public bool ShouldSerializeUvs3()
		{
			return Uvs3.Length != 0;
		}

		public bool ShouldSerializeTangents()
		{
			return Tangents.Length != 0;
		}
	}
	public sealed class ReplaySession
	{
		public ReplayHeader Header { get; set; } = new ReplayHeader();

		public List<ReplayFrame> Frames { get; set; } = new List<ReplayFrame>();

		public List<ReplayEvent> Events { get; set; } = new List<ReplayEvent>();

		public List<ReplayRecord> Worlds { get; set; } = new List<ReplayRecord>();

		[JsonIgnore]
		public Dictionary<string, List<EntitySnapshot>> DoorPoseReferences { get; set; } = new Dictionary<string, List<EntitySnapshot>>(StringComparer.Ordinal);

		public List<string> Warnings { get; set; } = new List<string>();

		public double Duration { get; set; }

		public bool IsComplete { get; set; }
	}
	public sealed class ReplayReadLimits
	{
		public long MaxFileBytes { get; set; } = 1073741824L;

		public int MaxCompressedRecordBytes { get; set; } = 50331648;

		public int MaxUncompressedRecordBytes { get; set; } = 100663296;

		public long MaxTotalUncompressedBytes { get; set; } = 536870912L;

		public int MaxRecords { get; set; } = 500000;

		public int MaxFrames { get; set; } = 432000;

		public int MaxEvents { get; set; } = 100000;

		public int MaxWorlds { get; set; } = 256;

		public int MaxEntitiesPerFrame { get; set; } = 4096;

		public int MaxTotalEntitySnapshots { get; set; } = 1000000;

		public int MaxBonesPerEntity { get; set; } = 512;

		public int MaxRenderersPerEntity { get; set; } = 512;

		public int MaxGeometryPerWorld { get; set; } = 50000;

		public int MaxParticleEmittersPerWorld { get; set; } = 256;

		public int MaxLocalFogsPerWorld { get; set; } = 128;

		public int MaxVerticesPerGeometry { get; set; } = 1000000;

		public int MaxTriangleIndicesPerGeometry { get; set; } = 6000000;

		public int MaxInstancesPerGeometry { get; set; } = 8192;

		public int MaxTexturesPerWorld { get; set; } = 256;

		public int MaxTextureDimension { get; set; } = 2048;

		public int MaxTextureBytesPerWorld { get; set; } = 33554432;

		public int MaxMaterialsPerWorld { get; set; } = 8192;

		public int MaxMaterialSlotsPerGeometry { get; set; } = 16;

		public int MaxDictionaryEntries { get; set; } = 1024;

		public int MaxStringLength { get; set; } = 32768;

		public double MaxDurationSeconds { get; set; } = 604800.0;
	}
	public static class ReplayAlphaCoverage
	{
		public static float SelectScale(ReadOnlySpan<int> histogram, int pixelCount, int threshold, float targetCoverage)
		{
			if (histogram.Length != 256)
			{
				throw new ArgumentException("Expected one count per alpha byte.", "histogram");
			}
			if (pixelCount <= 0)
			{
				throw new ArgumentOutOfRangeException("pixelCount");
			}
			if (threshold < 0 || threshold > 255)
			{
				throw new ArgumentOutOfRangeException("threshold");
			}
			Span<int> span = stackalloc int[256];
			int num = 0;
			for (int num2 = 255; num2 >= 0; num2--)
			{
				num += histogram[num2];
				span[num2] = num;
			}
			float result = 1f;
			float num3 = float.MaxValue;
			for (float num4 = 1f; num4 <= 2.5f; num4 += 0.125f)
			{
				float num5 = Math.Abs((float)span[(int)Math.Ceiling((double)threshold / (double)num4)] / (float)pixelCount - targetCoverage);
				if (num5 < num3)
				{
					num3 = num5;
					result = num4;
				}
			}
			return result;
		}
	}
	public static class ReplayAnimationClock
	{
		public static bool TrySample(ReplayEvent state, double time, out float normalized, ReplayEvent? next = null)
		{
			normalized = 0f;
			if (!Number(state, "normalizedTime", out var value) || !Finite(time))
			{
				return false;
			}
			double num = Math.Max(0.0, time - state.Time);
			double num2;
			if (Number(state, "normalizedRate", out var value2))
			{
				num2 = value + num * value2;
			}
			else
			{
				double value3;
				double num3 = (Number(state, "speed", out value3) ? value3 : 1.0);
				num2 = ((next == null || !(next.Time > state.Time) || !(time <= next.Time) || !Same(state, next, "hash") || !Same(state, next, "clip") || !Same(state, next, "controller") || !Number(next, "normalizedTime", out var value4) || ((!(num3 > 0.0) || !(value4 >= value)) && (!(num3 < 0.0) || !(value4 <= value)) && (num3 != 0.0 || value4 != value))) ? (value + ((Number(state, "duration", out var value5) && value5 > 0.001) ? (num * (double)Math.Sign(num3) / value5) : 0.0)) : (value + (value4 - value) * num / (next.Time - state.Time)));
			}
			if (!Finite(num2) || num2 < -3.4028234663852886E+38 || num2 > 3.4028234663852886E+38)
			{
				return false;
			}
			normalized = (float)num2;
			return true;
		}

		private static bool Same(ReplayEvent a, ReplayEvent b, string key)
		{
			if (a.Data.TryGetValue(key, out string value) == b.Data.TryGetValue(key, out string value2))
			{
				return value == value2;
			}
			return false;
		}

		private static bool Number(ReplayEvent evt, string key, out double value)
		{
			value = 0.0;
			if (evt.Data.TryGetValue(key, out string value2) && double.TryParse(value2, NumberStyles.Float, CultureInfo.InvariantCulture, out value))
			{
				return Finite(value);
			}
			return false;
		}

		private static bool Finite(double value)
		{
			if (!double.IsNaN(value))
			{
				return !double.IsInfinity(value);
			}
			return false;
		}
	}
	public static class ReplayAudioCodec
	{
		public const int SampleRate = 22050;

		public const int MaxSamplesPerBlock = 8192;

		private static readonly int[] Steps = new int[89]
		{
			7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
			19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
			50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
			130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
			337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
			876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
			2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
			5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
			15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
		};

		private static readonly int[] IndexChange = new int[16]
		{
			-1, -1, -1, -1, 2, 4, 6, 8, -1, -1,
			-1, -1, 2, 4, 6, 8
		};

		public static byte[] Encode(short[] samples, int count)
		{
			if (samples == null || count < 1 || count > samples.Length || count > 8192)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			byte[] array = new byte[2 + count / 2];
			int num = samples[0];
			int num2 = 0;
			array[0] = (byte)num;
			array[1] = (byte)(num >> 8);
			for (int i = 1; i < count; i++)
			{
				int num3 = Steps[num2];
				int num4 = samples[i] - num;
				int num5 = ((num4 < 0) ? 8 : 0);
				if (num4 < 0)
				{
					num4 = -num4;
				}
				int num6 = num3 >> 3;
				if (num4 >= num3)
				{
					num5 |= 4;
					num4 -= num3;
					num6 += num3;
				}
				if (num4 >= num3 >> 1)
				{
					num5 |= 2;
					num4 -= num3 >> 1;
					num6 += num3 >> 1;
				}
				if (num4 >= num3 >> 2)
				{
					num5 |= 1;
					num6 += num3 >> 2;
				}
				num = Math.Max(-32768, Math.Min(32767, num + (((num5 & 8) != 0) ? (-num6) : num6)));
				num2 = Math.Max(0, Math.Min(88, num2 + IndexChange[num5]));
				int num7 = 2 + (i - 1) / 2;
				if ((i & 1) != 0)
				{
					array[num7] = (byte)num5;
				}
				else
				{
					array[num7] |= (byte)(num5 << 4);
				}
			}
			return array;
		}

		public static float[] Decode(byte[] data, int count)
		{
			if (data == null || count < 1 || count > 8192 || data.Length != 2 + count / 2)
			{
				throw new InvalidDataException("Invalid replay audio block size.");
			}
			float[] array = new float[count];
			int num = (short)(data[0] | (data[1] << 8));
			int num2 = 0;
			array[0] = (float)num / 32768f;
			for (int i = 1; i < count; i++)
			{
				byte b = data[2 + (i - 1) / 2];
				int num3 = (((i & 1) != 0) ? (b & 0xF) : (b >> 4));
				int num4 = Steps[num2];
				int num5 = num4 >> 3;
				if ((num3 & 4) != 0)
				{
					num5 += num4;
				}
				if ((num3 & 2) != 0)
				{
					num5 += num4 >> 1;
				}
				if ((num3 & 1) != 0)
				{
					num5 += num4 >> 2;
				}
				num = Math.Max(-32768, Math.Min(32767, num + (((num3 & 8) != 0) ? (-num5) : num5)));
				num2 = Math.Max(0, Math.Min(88, num2 + IndexChange[num3]));
				array[i] = (float)num / 32768f;
			}
			return array;
		}
	}
	internal static class ReplayBlendShapes
	{
		internal sealed class Cache
		{
			private Dictionary<string, float[]?>? values;

			internal float[]? Parse(string text)
			{
				if (values != null && values.TryGetValue(text, out float[] value))
				{
					return value;
				}
				float[] array = ParseWeights(text);
				if (values == null)
				{
					values = new Dictionary<string, float[]>(StringComparer.Ordinal);
				}
				if (values.Count >= 256)
				{
					values.Clear();
				}
				values[text] = array;
				return array;
			}
		}

		internal const string Prefix = "$blendshape:";

		internal static Dictionary<string, string>? Interpolate(EntitySnapshot left, EntitySnapshot right, double amount, Cache? cache)
		{
			if (!left.Active || !right.Active || left.Kind != right.Kind || amount <= 0.0 || amount >= 1.0)
			{
				return null;
			}
			Dictionary<string, string> dictionary = null;
			int num = 0;
			int num2 = 0;
			bool flag = false;
			foreach (KeyValuePair<string, string> item in left.State)
			{
				if (!item.Key.StartsWith("$blendshape:", StringComparison.Ordinal))
				{
					continue;
				}
				if (++num > 32)
				{
					break;
				}
				if (!right.State.TryGetValue(item.Key, out string value) || item.Value == value)
				{
					continue;
				}
				if (!flag)
				{
					if (!SameForms(left, right))
					{
						return null;
					}
					flag = true;
				}
				float[] array = ((cache == null) ? ParseWeights(item.Value) : cache.Parse(item.Value));
				float[] array2 = ((cache == null) ? ParseWeights(value) : cache.Parse(value));
				if (array == null || array2 == null || array.Length != array2.Length)
				{
					continue;
				}
				num2 += array.Length;
				if (num2 > 128)
				{
					break;
				}
				StringBuilder stringBuilder = new StringBuilder(array.Length * 6);
				for (int i = 0; i < array.Length; i++)
				{
					if (i != 0)
					{
						stringBuilder.Append(',');
					}
					stringBuilder.Append(((float)((double)array[i] + ((double)array2[i] - (double)array[i]) * amount)).ToString("R", CultureInfo.InvariantCulture));
				}
				if (dictionary == null)
				{
					dictionary = new Dictionary<string, string>(left.State, StringComparer.Ordinal);
				}
				dictionary[item.Key] = stringBuilder.ToString();
			}
			return dictionary;
		}

		private static bool SameForms(EntitySnapshot left, EntitySnapshot right)
		{
			if (left.Renderers.Count != right.Renderers.Count)
			{
				return false;
			}
			for (int i = 0; i < left.Renderers.Count; i++)
			{
				RenderPose renderPose = left.Renderers[i];
				if (renderPose.Id == right.Renderers[i].Id)
				{
					if (renderPose.Active != right.Renderers[i].Active)
					{
						return false;
					}
					continue;
				}
				bool flag = false;
				foreach (RenderPose renderer in right.Renderers)
				{
					if (renderPose.Id == renderer.Id)
					{
						if (renderPose.Active != renderer.Active)
						{
							return false;
						}
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					return false;
				}
			}
			return true;
		}

		private static float[]? ParseWeights(string text)
		{
			if (text.Length == 0 || text.Length > 4096)
			{
				return null;
			}
			string[] array = text.Split(new char[1] { ',' }, 129);
			if (array.Length > 128)
			{
				return null;
			}
			float[] array2 = new float[array.Length];
			for (int i = 0; i < array.Length; i++)
			{
				if (!float.TryParse(array[i], NumberStyles.Float, CultureInfo.InvariantCulture, out array2[i]) || float.IsNaN(array2[i]) || float.IsInfinity(array2[i]))
				{
					return null;
				}
			}
			return array2;
		}
	}
	public sealed class ReplayCaptureBuffer
	{
		private readonly ReplayWriter writer;

		private readonly int capacity;

		private readonly long maxBytes;

		private readonly Queue<(ReplayRecord Record, long Bytes)> pending = new Queue<(ReplayRecord, long)>();

		private long pendingBytes;

		private bool pendingLargeWorld;

		private bool completing;

		private bool throttled;

		private bool drainingBulkTail;

		public Exception? Error { get; private set; }

		public int PendingCount => pending.Count;

		public long PendingBytes => pendingBytes;

		public bool ShouldPauseCapture
		{
			get
			{
				long bulkWorldBytes = writer.BulkWorldBytes;
				if (bulkWorldBytes != 0L)
				{
					drainingBulkTail = true;
				}
				else if (pending.Count == 0 && writer.QueuedCount <= 12 && writer.QueuedBytes <= 1048576)
				{
					drainingBulkTail = false;
				}
				if (drainingBulkTail)
				{
					long num = pendingBytes + Math.Max(0L, writer.QueuedBytes - bulkWorldBytes);
					int num2 = pending.Count + writer.QueuedCount;
					if (num2 >= Math.Max(1, capacity * 3 / 4) || num >= maxBytes * 3 / 4)
					{
						throttled = true;
					}
					else if (num2 <= capacity / 4 && num <= maxBytes / 4)
					{
						throttled = false;
					}
					return throttled;
				}
				if (pending.Count != 0 || writer.QueuedCount >= 48 || writer.QueuedBytes >= 4194304)
				{
					throttled = true;
				}
				else if (writer.QueuedCount <= 12 && writer.QueuedBytes <= 1048576)
				{
					throttled = false;
				}
				return throttled;
			}
		}

		public ReplayCaptureBuffer(ReplayWriter writer, int capacity = 4096, long maxBytes = 8388608L)
		{
			if (capacity < 1 || maxBytes < 1024)
			{
				throw new ArgumentOutOfRangeException("capacity");
			}
			this.writer = writer ?? throw new ArgumentNullException("writer");
			this.capacity = capacity;
			this.maxBytes = maxBytes;
		}

		public bool TryWrite(ReplayRecord record)
		{
			if (completing || Error != null || writer.Error != null)
			{
				return false;
			}
			long num = ReplayRecordMemory.Estimate(record);
			if (pending.Count == 0 && writer.TryWrite(record, num))
			{
				return true;
			}
			bool flag = record.Kind == "world" && num > maxBytes;
			if (pending.Count >= capacity || pendingLargeWorld || (!flag && pendingBytes != 0L && num > maxBytes - pendingBytes))
			{
				Error = new IOException("Capture backlog exceeded its bounded memory budget; previously accepted recording data is preserved.");
				return false;
			}
			pending.Enqueue((record, num));
			pendingBytes += num;
			if (flag)
			{
				pendingLargeWorld = true;
			}
			return true;
		}

		public void Drain(int maxRecords = 16, double maxMilliseconds = 0.75)
		{
			if (completing)
			{
				return;
			}
			long timestamp = Stopwatch.GetTimestamp();
			while (maxRecords-- > 0 && pending.Count != 0)
			{
				(ReplayRecord, long) tuple = pending.Peek();
				if (writer.TryWrite(tuple.Item1, tuple.Item2))
				{
					(ReplayRecord, long) tuple2 = pending.Dequeue();
					pendingBytes -= tuple2.Item2;
					if (tuple2.Item2 > maxBytes && tuple2.Item1.Kind == "world")
					{
						pendingLargeWorld = false;
					}
					if ((double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency >= maxMilliseconds)
					{
						break;
					}
					continue;
				}
				break;
			}
		}

		public Task CompleteAsync()
		{
			return CompleteAsync(null);
		}

		public async Task CompleteAsync(IEnumerable<ReplayRecord>? trailingRecords)
		{
			completing = true;
			try
			{
				while (pending.Count != 0)
				{
					(ReplayRecord, long) tuple = pending.Peek();
					if (!(await writer.WriteAsync(tuple.Item1, tuple.Item2).ConfigureAwait(continueOnCapturedContext: false)))
					{
						break;
					}
					(ReplayRecord, long) tuple2 = pending.Dequeue();
					pendingBytes -= tuple2.Item2;
					if (tuple2.Item2 > maxBytes && tuple2.Item1.Kind == "world")
					{
						pendingLargeWorld = false;
					}
				}
				if (writer.Error != null || trailingRecords == null)
				{
					return;
				}
				foreach (ReplayRecord trailingRecord in trailingRecords)
				{
					if (!(await writer.WriteAsync(trailingRecord).ConfigureAwait(continueOnCapturedContext: false)))
					{
						break;
					}
				}
			}
			finally
			{
				pending.Clear();
				pendingBytes = 0L;
				pendingLargeWorld = false;
				await writer.CompleteAsync().ConfigureAwait(continueOnCapturedContext: false);
			}
		}
	}
	public sealed class ReplayEnemyLife
	{
		public string EntityId { get; set; } = "";

		public string Name { get; set; } = "";

		public double SpawnTime { get; set; }

		public Vec3 SpawnPosition { get; set; }

		public double? DeathTime { get; set; }

		public Vec3 DeathPosition { get; set; }
	}
	public static class ReplayReader
	{
		private sealed class PendingRecord
		{
			internal ReplayFileIndex.Entry Entry;

			internal byte[]? Payload;

			internal ReplayRecord? Record;
		}

		private const long MaxSingleFileBytes = 17179869184L;

		private const long MaxSingleFileExpandedBytes = 25769803776L;

		public static IReadOnlyList<ReplayEnemyLife> ReadEnemyHistory(ReplayFileIndex index, CancellationToken cancellation = default(CancellationToken))
		{
			FileStream input = new FileStream(index.FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete, 65536, FileOptions.SequentialScan);
			BinaryReader reader;
			try
			{
				reader = new BinaryReader(input, Encoding.UTF8, leaveOpen: true);
				try
				{
					if (input.Length != index.FileLength || File.GetLastWriteTimeUtc(index.FilePath).Ticks != index.LastWriteUtcTicks)
					{
						throw new InvalidDataException("Replay changed after its index was created.");
					}
					return EnemyHistory(Records());
				}
				finally
				{
					if (reader != null)
					{
						((IDisposable)reader).Dispose();
					}
				}
			}
			finally
			{
				if (input != null)
				{
					((IDisposable)input).Dispose();
				}
			}
			IEnumerable<ReplayRecord> Records()
			{
				ReplayReadLimits limits = new ReplayReadLimits();
				foreach (ReplayFileIndex.Entry entry in index.Entries)
				{
					cancellation.ThrowIfCancellationRequested();
					if (!(entry.Kind != "frame") || (!(entry.Kind != "event") && entry.EventCategory.Length == 0))
					{
						input.Position = entry.Offset;
						if (reader.ReadInt32() != entry.Compressed || reader.ReadInt32() != entry.Expanded)
						{
							throw new InvalidDataException("Replay changed after its index was created.");
						}
						byte[] array = reader.ReadBytes(entry.Compressed);
						if (array.Length != entry.Compressed)
						{
							throw new InvalidDataException("Truncated replay record.");
						}
						ReplayRecord replayRecord = ReplayFormat.Decode(array, entry.Expanded, cancellation);
						ReplayValidation.Record(replayRecord, limits);
						yield return replayRecord;
					}
				}
			}
		}

		public static IReadOnlyList<ReplayEnemyLife> EnemyHistory(IEnumerable<ReplayRecord> records)
		{
			Dictionary<string, ReplayEnemyLife> dictionary = new Dictionary<string, ReplayEnemyLife>();
			Dictionary<string, Vec3> dictionary2 = new Dictionary<string, Vec3>();
			foreach (ReplayRecord record in records)
			{
				if (record.Frame != null)
				{
					foreach (EntitySnapshot entity in record.Frame.Entities)
					{
						if (entity.Kind != "enemy")
						{
							continue;
						}
						if (!dictionary.TryGetValue(entity.Id, out var value))
						{
							if (!entity.Active)
							{
								continue;
							}
							value = (dictionary[entity.Id] = new ReplayEnemyLife
							{
								EntityId = entity.Id,
								Name = entity.Name,
								SpawnTime = record.Frame.Time,
								SpawnPosition = entity.Position
							});
						}
						dictionary2[entity.Id] = entity.Position;
						if (!value.DeathTime.HasValue && entity.State.TryGetValue("isEnemyDead", out string value2) && string.Equals(value2, "True", StringComparison.OrdinalIgnoreCase))
						{
							value.DeathTime = record.Frame.Time;
							value.DeathPosition = entity.Position;
						}
					}
				}
				ReplayEvent replayEvent = record.Event;
				if (replayEvent != null && replayEvent.Category == "state" && replayEvent.Name == "isEnemyDead" && replayEvent.Data.TryGetValue("to", out string value3) && string.Equals(value3, "True", StringComparison.OrdinalIgnoreCase) && dictionary.TryGetValue(replayEvent.EntityId, out var value4) && (!value4.DeathTime.HasValue || replayEvent.Time < value4.DeathTime))
				{
					value4.DeathTime = replayEvent.Time;
					value4.DeathPosition = dictionary2[replayEvent.EntityId];
				}
			}
			return dictionary.Values.OrderBy((ReplayEnemyLife life) => life.SpawnTime).ToArray();
		}

		public static IReadOnlyList<ReplayPlayerDeath> ReadPlayerDeaths(ReplayFileIndex index, CancellationToken cancellation = default(CancellationToken))
		{
			if (index == null)
			{
				throw new ArgumentNullException("index");
			}
			List<ReplayPlayerDeath> list = new List<ReplayPlayerDeath>();
			ReplayReadLimits limits = new ReplayReadLimits();
			FileStream input = new FileStream(index.FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete, 65536, FileOptions.RandomAccess);
			BinaryReader reader;
			try
			{
				reader = new BinaryReader(input, Encoding.UTF8, leaveOpen: true);
				try
				{
					if (input.Length != index.FileLength || File.GetLastWriteTimeUtc(index.FilePath).Ticks != index.LastWriteUtcTicks)
					{
						throw new InvalidDataException("Replay changed after its index was created.");
					}
					List<ReplayFileIndex.Entry> entries = index.Entries;
					int num = -1;
					for (int i = 0; i < entries.Count; i++)
					{
						ReplayFileIndex.Entry entry = entries[i];
						cancellation.ThrowIfCancellationRequested();
						if (entry.Kind == "frame")
						{
							num = i;
						}
						else
						{
							if (entry.Kind != "event" || entry.EventCategory != "death")
							{
								continue;
							}
							ReplayEvent evt = Decode(entry).Event;
							if (evt?.Category != "state" || evt.Name != "isPlayerDead" || !evt.Data.TryGetValue("to", out string value) || !string.Equals(value, "True", StringComparison.OrdinalIgnoreCase) || num < 0)
							{
								continue;
							}
							EntitySnapshot entitySnapshot = null;
							int num2 = 0;
							int num3 = num;
							while (num3 >= 0 && num2 < 64)
							{
								if (!(entries[num3].Kind != "frame"))
								{
									num2++;
									EntitySnapshot entitySnapshot2 = Decode(entries[num3]).Frame?.Entities.Find((EntitySnapshot entity) => entity.Id == evt.EntityId && entity.Kind == "player");
									if (entitySnapshot2 != null)
									{
										if (entitySnapshot == null)
										{
											entitySnapshot = entitySnapshot2;
										}
										if (!entitySnapshot2.State.TryGetValue("isPlayerDead", out string value2) || !string.Equals(value2, "True", StringComparison.OrdinalIgnoreCase))
										{
											entitySnapshot = entitySnapshot2;
											break;
										}
									}
								}
								num3--;
							}
							if (entitySnapshot != null)
							{
								list.Add(new ReplayPlayerDeath
								{
									Time = evt.Time,
									EntityId = evt.EntityId,
									Name = ((entitySnapshot.Name.Length != 0) ? entitySnapshot.Name : (evt.Data.TryGetValue("entity", out string value3) ? value3 : "")),
									Position = entitySnapshot.Position
								});
							}
						}
					}
					return list;
				}
				finally
				{
					if (reader != null)
					{
						((IDisposable)reader).Dispose();
					}
				}
			}
			finally
			{
				if (input != null)
				{
					((IDisposable)input).Dispose();
				}
			}
			ReplayRecord Decode(ReplayFileIndex.Entry entry2)
			{
				cancellation.ThrowIfCancellationRequested();
				input.Position = entry2.Offset;
				if (reader.ReadInt32() != entry2.Compressed || reader.ReadInt32() != entry2.Expanded)
				{
					throw new InvalidDataException("Replay changed after its index was created.");
				}
				byte[] array = reader.ReadBytes(entry2.Compressed);
				if (array.Length != entry2.Compressed)
				{
					throw new InvalidDataException("Indexed replay record was truncated.");
				}
				ReplayRecord replayRecord = ReplayFormat.Decode(array, entry2.Expanded, cancellation);
				ReplayValidation.Record(replayRecord, limits);
				return replayRecord;
			}
		}

		public static ReplayFileIndex IndexSingleFile(string path, CancellationToken cancellationToken = default(CancellationToken), long windowExpandedBytes = 50331648L, Action<double>? progress = null, long initialWindowExpandedBytes = 0L, bool deferPayloadValidation = false)
		{
			if (windowExpandedBytes < 1024 || windowExpandedBytes > 377487360)
			{
				throw new ArgumentOutOfRangeException("windowExpandedBytes");
			}
			if (initialWindowExpandedBytes < 0 || initialWindowExpandedBytes > windowExpandedBytes || (initialWindowExpandedBytes != 0L && initialWindowExpandedBytes < 1024))
			{
				throw new ArgumentOutOfRangeException("initialWindowExpandedBytes");
			}
			long num = ((initialWindowExpandedBytes == 0L) ? windowExpandedBytes : initialWindowExpandedBytes);
			ReplayFileIndex replayFileIndex = TryReadSidecar(path, cancellationToken, windowExpandedBytes, num, progress);
			if (replayFileIndex != null)
			{
				return replayFileIndex;
			}
			ReplayReadLimits replayReadLimits = new ReplayReadLimits();
			ReplayFileIndex index = new ReplayFileIndex
			{
				FilePath = Path.GetFullPath(path),
				LastWriteUtcTicks = File.GetLastWriteTimeUtc(path).Ticks
			};
			List<double> list = new List<double> { 0.0 };
			long num2 = 0L;
			long num3 = 0L;
			int num4 = 0;
			double num5 = -1.0;
			using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete, 65536, FileOptions.SequentialScan))
			{
				using BinaryReader binaryReader = new BinaryReader(fileStream, Encoding.UTF8, leaveOpen: true);
				index.FileLength = fileStream.Length;
				if (fileStream.Length > 17179869184L || !((ReadOnlySpan<byte>)binaryReader.ReadBytes(8)).SequenceEqual((ReadOnlySpan<byte>)ReplayFormat.Magic))
				{
					throw new InvalidDataException("Invalid or oversized single-file replay.");
				}
				while (fileStream.Position < index.FileLength)
				{
					cancellationToken.ThrowIfCancellationRequested();
					if (index.Entries.Count >= 2000000)
					{
						throw new InvalidDataException("Single-file replay record limit exceeded.");
					}
					long position = fileStream.Position;
					if (index.FileLength - position < 8)
					{
						break;
					}
					int num6 = binaryReader.ReadInt32();
					int num7 = binaryReader.ReadInt32();
					if (num6 <= 0 || num6 > replayReadLimits.MaxCompressedRecordBytes || num7 <= 0 || num7 > replayReadLimits.MaxUncompressedRecordBytes)
					{
						throw new InvalidDataException("Invalid single-file record size at byte " + position + ".");
					}
					num3 += num7;
					if (num3 > 25769803776L)
					{
						throw new InvalidDataException("Single-file replay expanded-size limit exceeded.");
					}
					if (index.FileLength - fileStream.Position < num6)
					{
						break;
					}
					ReplayRecord replayRecord;
					if (deferPayloadValidation)
					{
						replayRecord = ReplayFormat.ReadIndexRecord(fileStream, num6, num7, cancellationToken);
						fileStream.Position = position + 8 + num6;
					}
					else
					{
						byte[] array = binaryReader.ReadBytes(num6);
						if (array.Length != num6)
						{
							break;
						}
						replayRecord = ReplayFormat.Decode(array, num7, cancellationToken);
					}
					ReplayValidation.Record(replayRecord, replayReadLimits);
					if (index.Entries.Count == 0)
					{
						if (replayRecord.Kind != "header")
						{
							throw new InvalidDataException("Single-file replay has no header.");
						}
						index.Header = replayRecord.Header;
					}
					else if (replayRecord.Kind == "header")
					{
						throw new InvalidDataException("Duplicate replay header.");
					}
					ReplayFileIndex.Entry item = new ReplayFileIndex.Entry
					{
						Offset = position,
						Compressed = num6,
						Expanded = num7,
						Kind = replayRecord.Kind,
						Time = replayRecord.Time,
						CaptureSetId = (replayRecord.World?.CaptureSetId ?? ""),
						EventCategory = ReplayEventIndex.Category(replayRecord.Event),
						EventKey = ReplayEventIndex.Key(replayRecord.Event)
					};
					if (replayRecord.Kind == "frame")
					{
						if (replayRecord.Time < num5)
						{
							throw new InvalidDataException("Frame timestamps are out of order.");
						}
						num5 = replayRecord.Time;
						if (++num4 > 1000000)
						{
							throw new InvalidDataException("Single-file frame limit exceeded.");
						}
						if (num2 >= ((list.Count == 1) ? num : windowExpandedBytes) && replayRecord.Time > list[list.Count - 1])
						{
							list.Add(replayRecord.Time);
							num2 = 0L;
						}
					}
					if (replayRecord.Kind == "frame" || replayRecord.Kind == "event")
					{
						num2 += num7;
					}
					if (replayRecord.Kind == "end")
					{
						if (fileStream.Position != index.FileLength)
						{
							throw new InvalidDataException("Unexpected data after replay end marker.");
						}
						index.IsComplete = true;
					}
					index.Duration = Math.Max(index.Duration, replayRecord.Time);
					index.Entries.Add(item);
					if ((index.Entries.Count & 0xFF) == 0)
					{
						progress?.Invoke(Math.Min(1.0, (double)fileStream.Position / (double)index.FileLength));
					}
				}
			}
			if (index.Entries.Count == 0)
			{
				throw new InvalidDataException("Single-file replay has no complete header.");
			}
			list.RemoveAll((double time) => time >= index.Duration && time > 0.0);
			List<ReplayFileWindow> list2 = new List<ReplayFileWindow>();
			for (int num8 = 0; num8 < list.Count; num8++)
			{
				list2.Add(new ReplayFileWindow
				{
					Index = index,
					Number = num8,
					Start = list[num8],
					End = ((num8 + 1 < list.Count) ? list[num8 + 1] : index.Duration)
				});
			}
			index.Windows = list2.AsReadOnly();
			TryPersistSidecar(index);
			progress?.Invoke(1.0);
			return index;
		}

		private static void TryPersistSidecar(ReplayFileIndex index)
		{
			if (!index.IsComplete)
			{
				return;
			}
			string text = Path.ChangeExtension(index.FilePath, ".lci");
			string text2 = text + "." + Guid.NewGuid().ToString("N") + ".tmp";
			try
			{
				using (FileStream output = new FileStream(text2, FileMode.CreateNew, FileAccess.Write, FileShare.None, 65536))
				{
					using BinaryWriter binaryWriter = new BinaryWriter(output, Encoding.UTF8, leaveOpen: true);
					binaryWriter.Write(ReplayFileIndex.SidecarMagic);
					foreach (ReplayFileIndex.Entry entry in index.Entries)
					{
						byte[] bytes = Encoding.UTF8.GetBytes(entry.CaptureSetId);
						byte[] bytes2 = Encoding.UTF8.GetBytes(entry.EventKey);
						if (bytes.Length > 96 || bytes2.Length > 65535)
						{
							return;
						}
						binaryWriter.Write(entry.Offset);
						binaryWriter.Write(entry.Compressed);
						binaryWriter.Write(entry.Expanded);
						binaryWriter.Write(entry.Time);
						binaryWriter.Write(ReplayFileIndex.KindCode(entry.Kind));
						binaryWriter.Write((byte)bytes.Length);
						binaryWriter.Write(bytes);
						binaryWriter.Write(ReplayEventIndex.Code(entry.EventCategory));
						binaryWriter.Write((ushort)bytes2.Length);
						binaryWriter.Write(bytes2);
					}
				}
				if (File.Exists(text))
				{
					File.Replace(text2, text, null, ignoreMetadataErrors: true);
				}
				else
				{
					File.Move(text2, text);
				}
			}
			catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException || ex is SecurityException)
			{
			}
			finally
			{
				try
				{
					if (File.Exists(text2))
					{
						File.Delete(text2);
					}
				}
				catch (IOException)
				{
				}
				catch (UnauthorizedAccessException)
				{
				}
			}
		}

		private static ReplayFileIndex? TryReadSidecar(string path, CancellationToken cancellationToken, long windowExpandedBytes, long firstBudget, Action<double>? progress)
		{
			string path2 = Path.ChangeExtension(path, ".lci");
			if (!File.Exists(path2))
			{
				return null;
			}
			try
			{
				ReplayFileIndex index = new ReplayFileIndex
				{
					FilePath = Path.GetFullPath(path),
					Header = ReadHeader(path),
					LastWriteUtcTicks = File.GetLastWriteTimeUtc(path).Ticks
				};
				index.FileLength = new FileInfo(path).Length;
				if (index.FileLength > 17179869184L)
				{
					throw new InvalidDataException("Single-file replay is too large.");
				}
				using (FileStream fileStream = new FileStream(path2, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete, 65536))
				{
					using BinaryReader binaryReader = new BinaryReader(fileStream, Encoding.UTF8, leaveOpen: true);
					if (fileStream.Length > 268435456 || !((ReadOnlySpan<byte>)binaryReader.ReadBytes(8)).SequenceEqual((ReadOnlySpan<byte>)ReplayFileIndex.SidecarMagic))
					{
						return null;
					}
					long num = 8L;
					long num2 = 0L;
					double num3 = -1.0;
					int num4 = 0;
					while (fileStream.Position < fileStream.Length)
					{
						cancellationToken.ThrowIfCancellationRequested();
						if (index.Entries.Count >= 2000000 || fileStream.Length - fileStream.Position < 28)
						{
							return null;
						}
						long num5 = binaryReader.ReadInt64();
						int num6 = binaryReader.ReadInt32();
						int num7 = binaryReader.ReadInt32();
						double num8 = binaryReader.ReadDouble();
						string text = ReplayFileIndex.KindName(binaryReader.ReadByte());
						byte b = binaryReader.ReadByte();
						if (b > 96 || fileStream.Length - fileStream.Position < b + 3)
						{
							return null;
						}
						string captureSetId = Encoding.UTF8.GetString(binaryReader.ReadBytes(b));
						byte b2 = binaryReader.ReadByte();
						ushort num9 = binaryReader.ReadUInt16();
						if (b2 > 8 || (b2 != 0 && text != "event") || fileStream.Length - fileStream.Position < num9 || (((b2 >= 1 && b2 <= 5) || b2 == 8) && num9 == 0) || ((b2 == 0 || b2 == 6 || b2 == 7) && num9 != 0))
						{
							return null;
						}
						string eventKey = Encoding.UTF8.GetString(binaryReader.ReadBytes(num9));
						if (num5 != num || num6 <= 0 || num6 > 50331648 || num7 <= 0 || num7 > 100663296 || double.IsNaN(num8) || double.IsInfinity(num8) || num8 < 0.0 || num8 > 604800.0 || num5 > index.FileLength - 8 - num6 || (index.Entries.Count == 0 && text != "header") || (index.Entries.Count != 0 && text == "header"))
						{
							return null;
						}
						if (text == "frame")
						{
							if (num8 < num3 || ++num4 > 1000000)
							{
								return null;
							}
							num3 = num8;
						}
						num = num5 + 8 + num6;
						num2 += num7;
						if (num2 > 25769803776L)
						{
							return null;
						}
						index.Duration = Math.Max(index.Duration, num8);
						index.Entries.Add(new ReplayFileIndex.Entry
						{
							Offset = num5,
							Compressed = num6,
							Expanded = num7,
							Time = num8,
							Kind = text,
							CaptureSetId = captureSetId,
							EventKey = eventKey,
							EventCategory = ReplayEventIndex.Name(b2)
						});
						if ((index.Entries.Count & 0xFF) == 0)
						{
							progress?.Invoke(Math.Min(1.0, (double)fileStream.Position / (double)fileStream.Length));
						}
					}
					index.IsComplete = index.Entries.Count != 0 && index.Entries[index.Entries.Count - 1].Kind == "end" && num == index.FileLength;
					if (!index.IsComplete)
					{
						return null;
					}
				}
				List<double> list = new List<double> { 0.0 };
				long num10 = 0L;
				foreach (ReplayFileIndex.Entry entry in index.Entries)
				{
					if (entry.Kind == "frame" && num10 >= ((list.Count == 1) ? firstBudget : windowExpandedBytes) && entry.Time > list[list.Count - 1])
					{
						list.Add(entry.Time);
						num10 = 0L;
					}
					if (entry.Kind == "frame" || entry.Kind == "event")
					{
						num10 += entry.Expanded;
					}
				}
				list.RemoveAll((double time) => time >= index.Duration && time > 0.0);
				List<ReplayFileWindow> list2 = new List<ReplayFileWindow>();
				for (int num11 = 0; num11 < list.Count; num11++)
				{
					list2.Add(new ReplayFileWindow
					{
						Index = index,
						Number = num11,
						Start = list[num11],
						End = ((num11 + 1 < list.Count) ? list[num11 + 1] : index.Duration)
					});
				}
				index.Windows = list2.AsReadOnly();
				progress?.Invoke(1.0);
				return index;
			}
			catch (Exception ex) when (ex is IOException || ex is InvalidDataException || ex is UnauthorizedAccessException || ex is ArgumentException || ex is EndOfStreamException)
			{
				cancellationToken.ThrowIfCancellationRequested();
				return null;
			}
		}

		public static ReplaySession ReadWindow(ReplayFileWindow window, CancellationToken cancellationToken = default(CancellationToken), Action<double>? progress = null, bool preloadInitialMap = false)
		{
			if (window == null || window.Index == null || window.Number < 0 || window.Number >= window.Index.Windows.Count || window.Index.Windows[window.Number] != window)
			{
				throw new ArgumentException("The replay window is not part of its file index.", "window");
			}
			ReplayFileIndex index = window.Index;
			List<ReplayFileIndex.Entry> entries = index.Entries;
			HashSet<int> hashSet = new HashSet<int>();
			int num = -1;
			int num2 = -1;
			for (int i = 0; i < entries.Count; i++)
			{
				ReplayFileIndex.Entry entry = entries[i];
				if (entry.Time < window.Start)
				{
					if (entry.Kind == "frame")
					{
						num = i;
					}
					if (entry.Kind == "world")
					{
						num2 = i;
					}
				}
				else if ((entry.Time < window.End || (window.Number == index.Windows.Count - 1 && entry.Time <= window.End)) && (entry.Kind == "frame" || entry.Kind == "event" || entry.Kind == "world"))
				{
					hashSet.Add(i);
				}
			}
			if (num >= 0)
			{
				hashSet.Add(num);
			}
			double num3 = 0.0;
			if (num2 >= 0)
			{
				string captureSetId = entries[num2].CaptureSetId;
				num3 = entries[num2].Time;
				for (int j = 0; j <= num2; j++)
				{
					if (entries[j].Kind == "world" && ((captureSetId.Length == 0) ? (j == num2) : (entries[j].CaptureSetId == captureSetId)))
					{
						hashSet.Add(j);
						num3 = Math.Min(num3, entries[j].Time);
					}
				}
			}
			ReplayReadLimits replayReadLimits = new ReplayReadLimits();
			ReplaySession replaySession = new ReplaySession
			{
				Header = index.Header,
				Duration = window.Duration,
				IsComplete = (index.IsComplete && window.Number == index.Windows.Count - 1)
			};
			replaySession.Warnings.AddRange(index.Header.Warnings);
			if (!index.IsComplete)
			{
				replaySession.Warnings.Add("Recording has no end marker; recovered data may omit its final moments.");
			}
			long num4 = 0L;
			long num5 = 0L;
			HashSet<string> hashSet2 = null;
			if (index.TrimInactiveActorState)
			{
				hashSet2 = new HashSet<string>(StringComparer.Ordinal);
				int[] array = (from position in hashSet
					where entries[position].Kind == "frame"
					orderby position
					select position).ToArray();
				int num6 = 0;
				foreach (ReplayRecord item in ReadWindowRecords(index, array, replayReadLimits, cancellationToken))
				{
					item.Time = Math.Max(0.0, item.Time - window.Start);
					item.Frame.Time = item.Time;
					num4 += item.Frame.Entities.Count;
					if (replaySession.Frames.Count >= replayReadLimits.MaxFrames || num4 > replayReadLimits.MaxTotalEntitySnapshots)
					{
						throw new InvalidDataException("Replay playback window exceeds entity budget.");
					}
					replaySession.Frames.Add(item.Frame);
					foreach (EntitySnapshot entity in item.Frame.Entities)
					{
						hashSet2.Add(entity.Id);
					}
					if ((++num6 & 0xF) == 0)
					{
						progress?.Invoke(0.5 * (double)num6 / (double)Math.Max(1, array.Length));
					}
				}
				num5 = ((IEnumerable<int>)array).Sum((Func<int, long>)((int position) => entries[position].Expanded));
				hashSet.ExceptWith(array);
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
			for (int num7 = 0; num7 < entries.Count; num7++)
			{
				if ((num7 & 0xFF) == 0)
				{
					cancellationToken.ThrowIfCancellationRequested();
				}
				if (!(entries[num7].Time < window.Start) || (!(entries[num7].EventCategory != "visual") && !(entries[num7].Time >= num3)))
				{
					continue;
				}
				foreach (string item2 in ReplayEventIndex.CarryKeys(entries[num7].EventCategory, entries[num7].EventKey, hashSet2))
				{
					if (!dictionary.TryGetValue(item2, out var value) || entries[num7].Time >= entries[value].Time)
					{
						if (entries[num7].EventCategory == "animation" && entries[num7].Time >= window.Start - 0.5 && value >= 0 && dictionary.ContainsKey(item2))
						{
							hashSet.Add(value);
						}
						dictionary[item2] = num7;
					}
				}
			}
			foreach (int value2 in dictionary.Values)
			{
				hashSet.Add(value2);
			}
			long num8 = num5 + ((IEnumerable<int>)hashSet).Sum((Func<int, long>)((int position) => entries[position].Expanded));
			if (num8 > replayReadLimits.MaxTotalUncompressedBytes)
			{
				throw new InvalidDataException("Replay playback window exceeds memory budget.");
			}
			int num9 = 0;
			foreach (ReplayRecord item3 in ReadWindowRecords(index, hashSet.OrderBy((int result) => result), replayReadLimits, cancellationToken))
			{
				cancellationToken.ThrowIfCancellationRequested();
				double time = item3.Time;
				item3.Time = ((item3.Kind == "event") ? (item3.Time - window.Start) : Math.Max(0.0, item3.Time - window.Start));
				if (item3.Kind == "world")
				{
					item3.SourceTime = time;
				}
				if (item3.Kind == "frame")
				{
					item3.Frame.Time = item3.Time;
					num4 += item3.Frame.Entities.Count;
					if (replaySession.Frames.Count >= replayReadLimits.MaxFrames || num4 > replayReadLimits.MaxTotalEntitySnapshots)
					{
						throw new InvalidDataException("Replay playback window exceeds entity budget.");
					}
					replaySession.Frames.Add(item3.Frame);
				}
				else if (item3.Kind == "event")
				{
					item3.Event.Time = item3.Time;
					if (replaySession.Events.Count >= replayReadLimits.MaxEvents)
					{
						throw new InvalidDataException("Replay playback window exceeds event budget.");
					}
					replaySession.Events.Add(item3.Event);
				}
				else if (item3.Kind == "world")
				{
					if (replaySession.Worlds.Count >= replayReadLimits.MaxWorlds)
					{
						throw new InvalidDataException("Replay playback window exceeds world budget.");
					}
					replaySession.Worlds.Add(item3);
				}
				if ((++num9 & 0xF) == 0)
				{
					progress?.Invoke((hashSet2 == null) ? ((double)num9 / (double)Math.Max(1, hashSet.Count)) : (0.5 + 0.5 * (double)num9 / (double)Math.Max(1, hashSet.Count)));
				}
			}
			if (preloadInitialMap && window.Number == 0)
			{
				int num10 = -1;
				for (int num11 = 0; num11 < entries.Count; num11++)
				{
					if (hashSet.Contains(num11) && entries[num11].Kind == "world")
					{
						num10 = num11;
						break;
					}
				}
				if (num10 >= 0)
				{
					string initialSet = entries[num10].CaptureSetId;
					bool flag = replaySession.Worlds.Any((ReplayRecord record) => record.World?.CaptureSetId == initialSet && record.World.Layer == "interior");
					bool flag2 = replaySession.Worlds.Any((ReplayRecord record) => record.World?.CaptureSetId != initialSet && record.World?.Layer == "interior");
					if (initialSet.Length != 0 && !flag2)
					{
						int num12 = 0;
						long num13 = 0L;
						List<ReplayRecord> list = new List<ReplayRecord>();
						string text = "";
						for (int num14 = num10 + 1; num14 < entries.Count; num14++)
						{
							cancellationToken.ThrowIfCancellationRequested();
							ReplayFileIndex.Entry entry2 = entries[num14];
							if (entry2.Kind != "world")
							{
								continue;
							}
							if (entry2.CaptureSetId.Length == 0)
							{
								break;
							}
							if (entry2.CaptureSetId != initialSet)
							{
								if (text.Length == 0)
								{
									text = entry2.CaptureSetId;
									list.Clear();
								}
								else if (entry2.CaptureSetId != text)
								{
									break;
								}
							}
							else if (text.Length != 0)
							{
								break;
							}
							if (hashSet.Contains(num14) || (entry2.CaptureSetId == initialSet && flag))
							{
								continue;
							}
							if (num12 >= 8 || num13 + entry2.Expanded > 402653184 || num8 + num13 + entry2.Expanded > replayReadLimits.MaxTotalUncompressedBytes || replaySession.Worlds.Count + list.Count >= replayReadLimits.MaxWorlds)
							{
								break;
							}
							ReplayRecord replayRecord = ReadWindowRecords(index, new int[1] { num14 }, replayReadLimits, cancellationToken).Single();
							replayRecord.SourceTime = replayRecord.Time;
							replayRecord.Time = Math.Max(0.0, replayRecord.Time - window.Start);
							list.Add(replayRecord);
							num12++;
							num13 += entry2.Expanded;
							if (replayRecord.World.Layer == "interior")
							{
								replaySession.Worlds.AddRange(list);
								list.Clear();
								if (entry2.CaptureSetId != initialSet)
								{
									break;
								}
								flag = true;
							}
						}
					}
				}
			}
			foreach (ReplayRecord item4 in (from @group in replaySession.Worlds.Where((ReplayRecord record) => record.World?.Layer == "interior" && record.World.Geometry.Any((GeometrySnapshot geometry) => geometry.IsMovingSceneRenderer && geometry.Name == "DoorMesh")).GroupBy<ReplayRecord, string>((ReplayRecord record) => record.World.CaptureSetId, StringComparer.Ordinal)
				select @group.First()).Take(2))
			{
				double num15 = ((item4.World.CaptureCompletedAt > 0.0) ? item4.World.CaptureCompletedAt : (item4.SourceTime ?? (item4.Time + window.Start)));
				ReplayFileIndex.Entry entry3 = null;
				double num16 = 5.0;
				foreach (ReplayFileIndex.Entry item5 in entries)
				{
					if (!(item5.Kind != "frame"))
					{
						double num17 = Math.Abs(item5.Time - num15);
						if (!(num17 >= num16))
						{
							num16 = num17;
							entry3 = item5;
						}
					}
				}
				if (entry3 != null)
				{
					int num18 = entries.IndexOf(entry3);
					ReplayFrame frame = ReadWindowRecords(index, new int[1] { num18 }, replayReadLimits, cancellationToken).Single().Frame;
					if (frame != null)
					{
						StoreDoorPoseReference(replaySession, item4.World.CaptureSetId, frame);
					}
				}
			}
			replaySession.Worlds = replaySession.Worlds.OrderBy((ReplayRecord record) => record.Time).ToList();
			replaySession.Events = replaySession.Events.OrderBy((ReplayEvent record) => record.Time).ToList();
			progress?.Invoke(1.0);
			return replaySession;
		}

		private static void StoreDoorPoseReference(ReplaySession session, string captureSetId, ReplayFrame frame)
		{
			session.DoorPoseReferences[captureSetId] = (from entity in frame.Entities.Where((EntitySnapshot entity) => entity.Kind == "door").Take(512)
				select new EntitySnapshot
				{
					Id = entity.Id,
					Kind = entity.Kind,
					Position = entity.Position,
					Rotation = entity.Rotation
				}).ToList();
		}

		public static ReplayHeader ReadHeader(string path, int maxHeaderBytes = 262144)
		{
			if (maxHeaderBytes < 256 || maxHeaderBytes > 16777216)
			{
				throw new ArgumentOutOfRangeException("maxHeaderBytes");
			}
			using FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
			using BinaryReader binaryReader = new BinaryReader(fileStream, Encoding.UTF8, leaveOpen: true);
			if (!((ReadOnlySpan<byte>)binaryReader.ReadBytes(ReplayFormat.Magic.Length)).SequenceEqual((ReadOnlySpan<byte>)ReplayFormat.Magic))
			{
				throw new InvalidDataException("Unrecognized or incomplete replay header.");
			}
			if (fileStream.Length - fileStream.Position < 8)
			{
				throw new InvalidDataException("Replay header length is incomplete.");
			}
			int num = binaryReader.ReadInt32();
			int num2 = binaryReader.ReadInt32();
			if (num <= 0 || num2 <= 0 || num > maxHeaderBytes || num2 > maxHeaderBytes)
			{
				throw new InvalidDataException("Replay header exceeds the bounded index read limit.");
			}
			if (fileStream.Length - fileStream.Position < num)
			{
				throw new InvalidDataException("Replay header is incomplete.");
			}
			byte[] array = binaryReader.ReadBytes(num);
			if (array.Length != num)
			{
				throw new InvalidDataException("Replay header was truncated while reading.");
			}
			ReplayRecord replayRecord = ReplayFormat.Decode(array, num2);
			ReplayValidation.Record(replayRecord, new ReplayReadLimits());
			if (replayRecord.Kind != "header")
			{
				throw new InvalidDataException("The first replay record must be a header.");
			}
			return replayRecord.Header;
		}

		public static ReplaySession Read(string path, ReplayReadLimits? limits = null, CancellationToken cancellationToken = default(CancellationToken), Action<double>? progress = null)
		{
			limits = limits ?? new ReplayReadLimits();
			ReplayValidation.Limits(limits);
			ReplaySession replaySession = new ReplaySession();
			cancellationToken.ThrowIfCancellationRequested();
			using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 65536, FileOptions.SequentialScan))
			{
				using BinaryReader binaryReader = new BinaryReader(fileStream, Encoding.UTF8, leaveOpen: true);
				long length = fileStream.Length;
				if (length > limits.MaxFileBytes)
				{
					throw new InvalidDataException("Replay file exceeds configured size limit.");
				}
				byte[] array = binaryReader.ReadBytes(ReplayFormat.Magic.Length);
				if (array.Length != ReplayFormat.Magic.Length)
				{
					throw new InvalidDataException("Replay header is incomplete.");
				}
				for (int i = 0; i < array.Length; i++)
				{
					if (array[i] != ReplayFormat.Magic[i])
					{
						throw new InvalidDataException("Unrecognized replay format.");
					}
				}
				int num = 0;
				long num2 = 0L;
				long num3 = 0L;
				bool flag = false;
				double num4 = -1.0;
				while (fileStream.Position < length)
				{
					cancellationToken.ThrowIfCancellationRequested();
					long position = fileStream.Position;
					if (length - fileStream.Position < 8)
					{
						replaySession.Warnings.Add("Recovered complete prefix: the final record length is truncated.");
						break;
					}
					int num5 = binaryReader.ReadInt32();
					int num6 = binaryReader.ReadInt32();
					if (num5 <= 0 || num5 > limits.MaxCompressedRecordBytes || num6 <= 0 || num6 > limits.MaxUncompressedRecordBytes)
					{
						throw new InvalidDataException("Invalid or excessive record size at byte " + position + ".");
					}
					num2 += num6;
					if (num2 > limits.MaxTotalUncompressedBytes || ++num > limits.MaxRecords)
					{
						throw new InvalidDataException("Replay exceeds configured aggregate read limits.");
					}
					if (length - fileStream.Position < num5)
					{
						replaySession.Warnings.Add("Recovered complete prefix: the final compressed record is truncated.");
						break;
					}
					byte[] array2 = binaryReader.ReadBytes(num5);
					if (array2.Length != num5)
					{
						replaySession.Warnings.Add("Recovered complete prefix: the file was truncated while reading.");
						break;
					}
					if ((num & 0x3F) == 0)
					{
						progress?.Invoke(Math.Min(1.0, (double)fileStream.Position / (double)length));
					}
					ReplayRecord replayRecord;
					try
					{
						replayRecord = ReplayFormat.Decode(array2, num6, cancellationToken);
						ReplayValidation.Record(replayRecord, limits);
					}
					catch (InvalidDataException ex)
					{
						throw new InvalidDataException("Invalid replay record at byte " + position + ": " + ex.Message, ex);
					}
					if (!flag)
					{
						if (replayRecord.Kind != "header")
						{
							throw new InvalidDataException("The first record must be a header.");
						}
						replaySession.Header = replayRecord.Header;
						replaySession.Warnings.AddRange(replaySession.Header.Warnings);
						flag = true;
						continue;
					}
					replaySession.Duration = Math.Max(replaySession.Duration, replayRecord.Time);
					switch (replayRecord.Kind)
					{
					case "header":
						throw new InvalidDataException("Duplicate replay header.");
					case "frame":
						if (replayRecord.Time < num4)
						{
							throw new InvalidDataException("Frame timestamps are out of order.");
						}
						num4 = replayRecord.Time;
						num3 += replayRecord.Frame.Entities.Count;
						if (replaySession.Frames.Count >= limits.MaxFrames || num3 > limits.MaxTotalEntitySnapshots)
						{
							throw new InvalidDataException("Replay exceeds configured frame/entity limits.");
						}
						replaySession.Frames.Add(replayRecord.Frame);
						break;
					case "event":
						if (replaySession.Events.Count >= limits.MaxEvents)
						{
							throw new InvalidDataException("Replay exceeds event limit.");
						}
						replaySession.Events.Add(replayRecord.Event);
						break;
					case "world":
						if (replaySession.Worlds.Count >= limits.MaxWorlds)
						{
							throw new InvalidDataException("Replay exceeds world snapshot limit.");
						}
						replaySession.Worlds.Add(replayRecord);
						break;
					case "end":
						if (fileStream.Position != length)
						{
							throw new InvalidDataException("Unexpected data after the replay end marker.");
						}
						replaySession.IsComplete = true;
						break;
					}
				}
				if (!flag)
				{
					throw new InvalidDataException("No complete replay header was found.");
				}
			}
			if (!replaySession.IsComplete)
			{
				replaySession.Warnings.Add("Recording has no end marker; recovered data may omit its final moments.");
			}
			replaySession.Events = replaySession.Events.OrderBy((ReplayEvent item) => item.Time).ToList();
			replaySession.Worlds = replaySession.Worlds.OrderBy((ReplayRecord item) => item.Time).ToList();
			foreach (ReplayRecord item in (from @group in replaySession.Worlds.Where((ReplayRecord record) => record.World?.Layer == "interior" && record.World.Geometry.Any((GeometrySnapshot geometry) => geometry.IsMovingSceneRenderer && geometry.Name == "DoorMesh")).GroupBy<ReplayRecord, string>((ReplayRecord record) => record.World.CaptureSetId, StringComparer.Ordinal)
				select @group.First()).Take(2))
			{
				ReplayFrame replayFrame = null;
				double num7 = 5.0;
				foreach (ReplayFrame frame in replaySession.Frames)
				{
					double num8 = ((item.World.CaptureCompletedAt > 0.0) ? item.World.CaptureCompletedAt : item.Time);
					double num9 = Math.Abs(frame.Time - num8);
					if (!(num9 >= num7))
					{
						replayFrame = frame;
						num7 = num9;
					}
				}
				if (replayFrame != null)
				{
					StoreDoorPoseReference(replaySession, item.World.CaptureSetId, replayFrame);
				}
			}
			progress?.Invoke(1.0);
			return replaySession;
		}

		public static double ReadDuration(string path, CancellationToken cancellationToken = default(CancellationToken))
		{
			cancellationToken.ThrowIfCancellationRequested();
			if (ReadHeader(path).Metadata.TryGetValue("singleFile", out string value) && value == "true")
			{
				return IndexSingleFile(path, cancellationToken, 50331648L, null, 0L, deferPayloadValidation: true).Duration;
			}
			ReplayReadLimits replayReadLimits = new ReplayReadLimits();
			using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096))
			{
				using BinaryReader binaryReader = new BinaryReader(fileStream, Encoding.UTF8, leaveOpen: true);
				if (fileStream.Length > replayReadLimits.MaxFileBytes || !((ReadOnlySpan<byte>)binaryReader.ReadBytes(8)).SequenceEqual((ReadOnlySpan<byte>)ReplayFormat.Magic))
				{
					throw new InvalidDataException("Invalid replay while indexing duration.");
				}
				long num = -1L;
				long num2 = 0L;
				int num3 = 0;
				int num4 = 0;
				int num5 = 0;
				while (fileStream.Length - fileStream.Position >= 8)
				{
					cancellationToken.ThrowIfCancellationRequested();
					num3 = binaryReader.ReadInt32();
					num4 = binaryReader.ReadInt32();
					if (num3 <= 0 || num3 > replayReadLimits.MaxCompressedRecordBytes || num4 <= 0 || num4 > replayReadLimits.MaxUncompressedRecordBytes)
					{
						throw new InvalidDataException("Invalid replay record size while indexing duration.");
					}
					num2 += num4;
					if (++num5 > replayReadLimits.MaxRecords || num2 > replayReadLimits.MaxTotalUncompressedBytes)
					{
						throw new InvalidDataException("Replay index exceeds read limits.");
					}
					if (fileStream.Length - fileStream.Position < num3)
					{
						num = -1L;
						break;
					}
					num = fileStream.Position;
					fileStream.Position += num3;
				}
				if (num >= 0 && fileStream.Position == fileStream.Length && num4 < 1024 && num3 < 1024)
				{
					fileStream.Position = num;
					ReplayRecord replayRecord = ReplayFormat.Decode(binaryReader.ReadBytes(num3), num4);
					if (replayRecord.Kind == "end")
					{
						ReplayValidation.Record(replayRecord, replayReadLimits);
						return replayRecord.Time;
					}
				}
			}
			return Read(path, replayReadLimits, cancellationToken).Duration;
		}

		private static IEnumerable<ReplayRecord> ReadWindowRecords(ReplayFileIndex index, IEnumerable<int> positions, ReplayReadLimits limits, CancellationToken cancellation)
		{
			List<PendingRecord> batch = new List<PendingRecord>(128);
			long expandedTotal = 0L;
			long num = 0L;
			using FileStream input = new FileStream(index.FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete, 65536, FileOptions.SequentialScan);
			using BinaryReader reader = new BinaryReader(input, Encoding.UTF8, leaveOpen: true);
			bool reuseWorlds = index.ReuseWorldPayloads && index.IsComplete;
			if (reuseWorlds && (input.Length != index.FileLength || File.GetLastWriteTimeUtc(index.FilePath).Ticks != index.LastWriteUtcTicks))
			{
				throw new InvalidDataException("Replay changed after its index was created.");
			}
			foreach (int position in positions)
			{
				cancellation.ThrowIfCancellationRequested();
				ReplayFileIndex.Entry entry = index.Entries[position];
				expandedTotal += entry.Expanded;
				if (expandedTotal > limits.MaxTotalUncompressedBytes)
				{
					throw new InvalidDataException("Replay playback window exceeds memory budget.");
				}
				if (batch.Count != 0 && (batch.Count >= 128 || num + entry.Expanded > 100663296))
				{
					DecodeBatch(batch, limits, reuseWorlds, cancellation);
					foreach (PendingRecord item in batch)
					{
						yield return item.Record;
					}
					batch.Clear();
					num = 0L;
				}
				input.Position = entry.Offset;
				if (reader.ReadInt32() != entry.Compressed || reader.ReadInt32() != entry.Expanded)
				{
					throw new InvalidDataException("Replay changed after its index was created.");
				}
				PendingRecord pendingRecord = new PendingRecord
				{
					Entry = entry
				};
				if (reuseWorlds && entry.Kind == "world" && entry.World != null && entry.World.TryGetTarget(out WorldSnapshot target))
				{
					pendingRecord.Record = new ReplayRecord
					{
						Kind = "world",
						Time = entry.Time,
						World = target
					};
				}
				else
				{
					pendingRecord.Payload = reader.ReadBytes(entry.Compressed);
					if (pendingRecord.Payload.Length != entry.Compressed)
					{
						throw new InvalidDataException("Indexed replay record was truncated.");
					}
					num += entry.Expanded;
				}
				batch.Add(pendingRecord);
			}
			DecodeBatch(batch, limits, reuseWorlds, cancellation);
			foreach (PendingRecord item2 in batch)
			{
				yield return item2.Record;
			}
		}

		private static void DecodeBatch(List<PendingRecord> batch, ReplayReadLimits limits, bool reuseWorlds, CancellationToken cancellation)
		{
			int num = Math.Min(4, Math.Max(1, Environment.ProcessorCount / 2));
			if (batch.Count < 2 || num == 1)
			{
				for (int i = 0; i < batch.Count; i++)
				{
					Decode(i);
				}
				return;
			}
			try
			{
				Parallel.For(0, batch.Count, new ParallelOptions
				{
					MaxDegreeOfParallelism = num,
					CancellationToken = cancellation
				}, Decode);
			}
			catch (AggregateException ex)
			{
				cancellation.ThrowIfCancellationRequested();
				ExceptionDispatchInfo.Capture(ex.Flatten().InnerExceptions[0]).Throw();
				throw;
			}
			void Decode(int number)
			{
				cancellation.ThrowIfCancellationRequested();
				PendingRecord pendingRecord = batch[number];
				if (pendingRecord.Record != null)
				{
					ReplayValidation.Record(pendingRecord.Record, limits);
				}
				else
				{
					ReplayRecord replayRecord = ReplayFormat.Decode(pendingRecord.Payload, pendingRecord.Entry.Expanded, cancellation);
					pendingRecord.Payload = null;
					ReplayValidation.Record(replayRecord, limits);
					if (replayRecord.Kind != pendingRecord.Entry.Kind || Math.Abs(replayRecord.Time - pendingRecord.Entry.Time) > 1E-09 || (replayRecord.World?.CaptureSetId ?? "") != pendingRecord.Entry.CaptureSetId || ReplayEventIndex.Category(replayRecord.Event) != pendingRecord.Entry.EventCategory || ReplayEventIndex.Key(replayRecord.Event) != pendingRecord.Entry.EventKey)
					{
						throw new InvalidDataException("Replay record disagrees with its index.");
					}
					pendingRecord.Record = replayRecord;
					if (reuseWorlds && replayRecord.World != null)
					{
						pendingRecord.Entry.World = new WeakReference<WorldSnapshot>(replayRecord.World);
					}
				}
			}
		}
	}
	internal static class ReplayEventIndex
	{
		internal static string Category(ReplayEvent? evt)
		{
			if (evt == null)
			{
				return "";
			}
			if (evt.Category == "marker" && evt.Name == "bookmark")
			{
				return "bookmark";
			}
			if (evt.Category == "visual" && (evt.Name == "renderer" || evt.Name == "spray"))
			{
				return "visual";
			}
			if (evt.Category == "animation" && (evt.Name == "state" || evt.Name == "parameters" || evt.Name == "track"))
			{
				return "animation";
			}
			if (evt.Category == "item" && evt.Name == "pose")
			{
				return "item";
			}
			if (evt.Category == "postfx" && evt.Name == "component")
			{
				return "postfx";
			}
			if (evt.Category == "lighting" && evt.Name == "sun")
			{
				return "lighting";
			}
			if (evt.Category == "sound" && (evt.Name == "stop" || evt.Data.GetValueOrDefault("loop") == "true"))
			{
				return "sound";
			}
			if (evt.Category == "state" && evt.Name == "isPlayerDead" && string.Equals(evt.Data.GetValueOrDefault("to"), "True", StringComparison.OrdinalIgnoreCase))
			{
				return "death";
			}
			return "";
		}

		internal static byte Code(string category)
		{
			return category switch
			{
				"lighting" => 8, 
				"bookmark" => 7, 
				"death" => 6, 
				"sound" => 5, 
				"postfx" => 4, 
				"item" => 3, 
				"animation" => 2, 
				"visual" => 1, 
				_ => 0, 
			};
		}

		internal static string Name(byte code)
		{
			return code switch
			{
				8 => "lighting", 
				7 => "bookmark", 
				6 => "death", 
				5 => "sound", 
				4 => "postfx", 
				3 => "item", 
				2 => "animation", 
				1 => "visual", 
				_ => "", 
			};
		}

		internal static string Key(ReplayEvent? evt)
		{
			string text = Category(evt);
			if (evt != null && text.Length != 0)
			{
				switch (text)
				{
				case "death":
				case "bookmark":
					break;
				case "postfx":
					return evt.PostProcess?.Type ?? "";
				default:
				{
					List<string> list = new List<string> { text, evt.Name, evt.EntityId };
					switch (text)
					{
					case "animation":
						list.Add(evt.AnimationTrack?.AnimatorPath ?? evt.Data.GetValueOrDefault("animatorPath", ""));
						if (evt.Name == "parameters")
						{
							list.AddRange(evt.Data.Keys.Where((string key) => key.Length > 1 && (key[0] == 'b' || key[0] == 'f' || key[0] == 'i') && int.TryParse(key.Substring(1), out var _)).OrderBy<string, string>((string key) => key, StringComparer.Ordinal));
							break;
						}
						list.Add(evt.AnimationTrack?.Layer.ToString(CultureInfo.InvariantCulture) ?? evt.Data.GetValueOrDefault("layer", "0"));
						if (evt.Name == "track")
						{
							list.Add(evt.AnimationTrack?.StateHash.ToString(CultureInfo.InvariantCulture) ?? "");
							list.Add(evt.AnimationTrack?.Clip ?? "");
						}
						break;
					case "visual":
						list.Add(evt.Data.GetValueOrDefault("set", ""));
						list.Add(evt.Data.GetValueOrDefault("asset", "false"));
						break;
					case "sound":
						list = new List<string>
						{
							text,
							"loop",
							evt.Data.GetValueOrDefault("source", ""),
							evt.EntityId
						};
						break;
					}
					return JsonConvert.SerializeObject((object)list);
				}
				}
			}
			return "";
		}

		internal static IEnumerable<string> CarryKeys(string category, string key, ISet<string>? actors = null)
		{
			if (category == "postfx")
			{
				yield return "postfx:" + key;
			}
			else
			{
				if (key.Length == 0 || category == "death" || category == "bookmark")
				{
					yield break;
				}
				string[] array = JsonConvert.DeserializeObject<string[]>(key) ?? Array.Empty<string>();
				if (actors != null)
				{
					string text = (((category == "animation" || category == "item") && array.Length >= 3) ? array[2] : ((category == "sound" && array.Length >= 4) ? array[3] : ""));
					if (text.Length != 0 && !actors.Contains(text))
					{
						yield break;
					}
				}
				if (array.Length >= 4 && array[0] == "animation" && array[1] == "parameters")
				{
					string binding = JsonConvert.SerializeObject((object)array.Take(4));
					foreach (string item in array.Skip(4))
					{
						yield return binding + ":" + item;
					}
				}
				else
				{
					yield return key;
				}
			}
		}
	}
	public sealed class ReplayFileIndex
	{
		internal sealed class Entry
		{
			internal long Offset;

			internal int Compressed;

			internal int Expanded;

			internal string Kind = "";

			internal string CaptureSetId = "";

			internal string EventCategory = "";

			internal string EventKey = "";

			internal double Time;

			internal WeakReference<WorldSnapshot>? World;
		}

		internal static readonly byte[] SidecarMagic = Encoding.ASCII.GetBytes("LCIX0007");

		internal readonly List<Entry> Entries = new List<Entry>();

		internal long FileLength;

		internal long LastWriteUtcTicks;

		public string FilePath { get; internal set; } = "";

		public ReplayHeader Header { get; internal set; } = new ReplayHeader();

		public IReadOnlyList<ReplayFileWindow> Windows { get; internal set; } = Array.Empty<ReplayFileWindow>();

		public double Duration { get; internal set; }

		public bool IsComplete { get; internal set; }

		public bool ReuseWorldPayloads { get; set; }

		public bool TrimInactiveActorState { get; set; }

		public int FrameCount => Entries.Count((Entry entry) => entry.Kind == "frame");

		public int EventCount => Entries.Count((Entry entry) => entry.Kind == "event");

		public int WorldCount => Entries.Count((Entry entry) => entry.Kind == "world");

		public IReadOnlyList<double> Bookmarks => (from entry in Entries
			where entry.EventCategory == "bookmark"
			select entry.Time into time
			orderby time
			select time).ToArray();

		internal static byte KindCode(string kind)
		{
			return kind switch
			{
				"header" => 0, 
				"frame" => 1, 
				"event" => 2, 
				"world" => 3, 
				"end" => 4, 
				_ => throw new InvalidDataException("Unknown indexed replay record kind."), 
			};
		}

		internal static string KindName(byte code)
		{
			return code switch
			{
				0 => "header", 
				1 => "frame", 
				2 => "event", 
				3 => "world", 
				4 => "end", 
				_ => throw new InvalidDataException("Unknown replay sidecar record kind."), 
			};
		}

		public long WorldRevisionAt(double time)
		{
			long result = -1L;
			foreach (Entry entry in Entries)
			{
				if (entry.Kind == "world" && entry.Time <= time)
				{
					result = entry.Offset;
				}
			}
			return result;
		}
	}
	public sealed class ReplayFileWindow
	{
		public ReplayFileIndex Index { get; internal set; }

		public double Start { get; internal set; }

		public double End { get; internal set; }

		public int Number { get; internal set; }

		public double Duration => End - Start;
	}
	public sealed class ReplayPlayerDeath
	{
		public double Time { get; set; }

		public string EntityId { get; set; } = "";

		public string Name { get; set; } = "";

		public Vec3 Position { get; set; }
	}
	internal static class ReplayFormat
	{
		private sealed class LimitedWriteStream : Stream
		{
			private readonly Stream target;

			private readonly long limit;

			private long length;

			public override bool CanRead => false;

			public override bool CanSeek => false;

			public override bool CanWrite => true;

			public override long Length => length;

			public override long Position
			{
				get
				{
					return length;
				}
				set
				{
					throw new NotSupportedException();
				}
			}

			internal LimitedWriteStream(Stream target, long limit)
			{
				this.target = target;
				this.limit = limit;
			}

			public override void Write(byte[] buffer, int offset, int count)
			{
				if (count > limit - length)
				{
					throw new InvalidDataException("Replay record exceeds size limit.");
				}
				target.Write(buffer, offset, count);
				length += count;
			}

			public override void Flush()
			{
				target.Flush();
			}

			public override int Read(byte[] buffer, int offset, int count)
			{
				throw new NotSupportedException();
			}

			public override long Seek(long offset, SeekOrigin origin)
			{
				throw new NotSupportedException();
			}

			public override void SetLength(long value)
			{
				throw new NotSupportedException();
			}
		}

		private sealed class RecordSliceStream : Stream
		{
			private readonly Stream input;

			private readonly int length;

			private int position;

			public override bool CanRead => true;

			public override bool CanSeek => false;

			public override bool CanWrite => false;

			public override long Length => length;

			public override long Position
			{
				get
				{
					return position;
				}
				set
				{
					throw new NotSupportedException();
				}
			}

			internal RecordSliceStream(Stream input, int length)
			{
				this.input = input;
				this.length = length;
			}

			public override int Read(byte[] buffer, int offset, int count)
			{
				int num = input.Read(buffer, offset, Math.Min(count, length - position));
				position += num;
				return num;
			}

			public override void Flush()
			{
			}

			public override long Seek(long offset, SeekOrigin origin)
			{
				throw new NotSupportedException();
			}

			public override void SetLength(long value)
			{
				throw new NotSupportedException();
			}

			public override void Write(byte[] buffer, int offset, int count)
			{
				throw new NotSupportedException();
			}
		}

		private sealed class ExpandedRecordStream : Stream
		{
			private readonly Stream input;

			private readonly int length;

			private readonly CancellationToken cancellation;

			private int position;

			public override bool CanRead => true;

			public override bool CanSeek => false;

			public override bool CanWrite => false;

			public override long Length => length;

			public override long Position
			{
				get
				{
					return position;
				}
				set
				{
					throw new NotSupportedException();
				}
			}

			internal ExpandedRecordStream(Stream input, int length, CancellationToken cancellation)
			{
				this.input = input;
				this.length = length;
				this.cancellation = cancellation;
			}

			public override int Read(byte[] buffer, int offset, int count)
			{
				cancellation.ThrowIfCancellationRequested();
				if (count == 0)
				{
					return 0;
				}
				if (position == length)
				{
					VerifyComplete();
					return 0;
				}
				int num = input.Read(buffer, offset, Math.Min(count, length - position));
				if (num == 0)
				{
					throw new InvalidDataException("Compressed record is shorter than its declared expanded length.");
				}
				position += num;
				return num;
			}

			internal void VerifyComplete()
			{
				cancellation.ThrowIfCancellationRequested();
				if (position != length)
				{
					throw new InvalidDataException("Compressed record is shorter than its declared expanded length.");
				}
				if (input.ReadByte() != -1)
				{
					throw new InvalidDataException("Compressed record exceeds its declared expanded length.");
				}
			}

			public override void Flush()
			{
			}

			public override long Seek(long offset, SeekOrigin origin)
			{
				throw new NotSupportedException();
			}

			public override void SetLength(long value)
			{
				throw new NotSupportedException();
			}

			public override void Write(byte[] buffer, int offset, int count)
			{
				throw new NotSupportedException();
			}
		}

		internal static readonly byte[] Magic = Encoding.ASCII.GetBytes("LCREPL01");

		private static readonly UTF8Encoding Utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);

		private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
		{
			TypeNameHandling = (TypeNameHandling)0,
			MetadataPropertyHandling = (MetadataPropertyHandling)2,
			DateParseHandling = (DateParseHandling)0,
			FloatParseHandling = (FloatParseHandling)0,
			MaxDepth = 64,
			CheckAdditionalContent = true,
			NullValueHandling = (NullValueHandling)1,
			Formatting = (Formatting)0
		};

		internal static int Write(Stream destination, ReplayRecord record, ReplayReadLimits limits)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			ReplayValidation.Record(record, limits);
			checked
			{
				using MemoryStream memoryStream = new MemoryStream();
				int num;
				using (LimitedWriteStream stream = new LimitedWriteStream(memoryStream, limits.MaxCompressedRecordBytes))
				{
					using GZipStream target = new GZipStream(stream, CompressionLevel.Fastest, leaveOpen: true);
					using LimitedWriteStream limitedWriteStream = new LimitedWriteStream(target, limits.MaxUncompressedRecordBytes);
					using (StreamWriter streamWriter = new StreamWriter(limitedWriteStream, Utf8, 16384, leaveOpen: true))
					{
						JsonTextWriter val = new JsonTextWriter((TextWriter)streamWriter)
						{
							CloseOutput = false
						};
						try
						{
							JsonSerializer.Create(Settings).Serialize((JsonWriter)(object)val, (object)record);
						}
						finally
						{
							((IDisposable)val)?.Dispose();
						}
					}
					num = (int)limitedWriteStream.Length;
				}
				using BinaryWriter binaryWriter = new BinaryWriter(destination, Utf8, leaveOpen: true);
				binaryWriter.Write((int)memoryStream.Length);
				binaryWriter.Write(num);
				binaryWriter.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
				return num;
			}
		}

		internal static ReplayRecord Decode(byte[] compressed, int expandedLength, CancellationToken cancellation = default(CancellationToken))
		{
			//IL_0093: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			try
			{
				using MemoryStream stream = new MemoryStream(compressed, writable: false);
				using GZipStream input = new GZipStream(stream, CompressionMode.Decompress);
				using ExpandedRecordStream expandedRecordStream = new ExpandedRecordStream(input, expandedLength, cancellation);
				using StreamReader streamReader = new StreamReader(expandedRecordStream, Utf8, detectEncodingFromByteOrderMarks: false, 16384);
				JsonTextReader val = new JsonTextReader((TextReader)streamReader);
				try
				{
					ReplayRecord result = JsonSerializer.Create(Settings).Deserialize<ReplayRecord>((JsonReader)(object)val) ?? throw new InvalidDataException("Null JSON record.");
					expandedRecordStream.VerifyComplete();
					return result;
				}
				finally
				{
					((IDisposable)val)?.Dispose();
				}
			}
			catch (JsonException ex)
			{
				JsonException innerException = ex;
				throw new InvalidDataException("Invalid replay JSON.", (Exception?)(object)innerException);
			}
			catch (DecoderFallbackException innerException2)
			{
				throw new InvalidDataException("Invalid replay UTF-8.", innerException2);
			}
		}

		internal static ReplayRecord ReadIndexRecord(Stream input, int compressedLength, int expandedLength, CancellationToken cancellation)
		{
			//IL_0336: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Invalid comparison between Unknown and I4
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Invalid comparison between Unknown and I4
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c7: Invalid comparison between Unknown and I4
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Invalid comparison between Unknown and I4
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Invalid comparison between Unknown and I4
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Invalid comparison between Unknown and I4
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Invalid comparison between Unknown and I4
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Invalid comparison between Unknown and I4
			try
			{
				using RecordSliceStream stream = new RecordSliceStream(input, compressedLength);
				using GZipStream input2 = new GZipStream(stream, CompressionMode.Decompress);
				using ExpandedRecordStream expandedRecordStream = new ExpandedRecordStream(input2, expandedLength, cancellation);
				using StreamReader streamReader = new StreamReader(expandedRecordStream, Utf8, detectEncodingFromByteOrderMarks: false, 1024);
				JsonTextReader val = new JsonTextReader((TextReader)streamReader)
				{
					MaxDepth = 64,
					DateParseHandling = (DateParseHandling)0,
					FloatParseHandling = (FloatParseHandling)0
				};
				try
				{
					JsonSerializer val2 = JsonSerializer.Create(Settings);
					val2.CheckAdditionalContent = false;
					ReplayRecord replayRecord = new ReplayRecord();
					bool flag = false;
					if (!((JsonReader)val).Read() || (int)((JsonReader)val).TokenType != 1)
					{
						throw new InvalidDataException("Invalid replay record envelope.");
					}
					while (((JsonReader)val).Read() && (int)((JsonReader)val).TokenType != 13)
					{
						cancellation.ThrowIfCancellationRequested();
						if ((int)((JsonReader)val).TokenType != 4 || ((JsonReader)val).Depth != 1)
						{
							throw new InvalidDataException("Invalid replay record envelope.");
						}
						string text = (string)((JsonReader)val).Value;
						if (!((JsonReader)val).Read())
						{
							throw new InvalidDataException("Incomplete replay record envelope.");
						}
						switch (text)
						{
						case "Kind":
							replayRecord.Kind = val2.Deserialize<string>((JsonReader)(object)val);
							break;
						case "Time"