Decompiled source of Sailwind Fast Forward v1.2.1

plugins/SailwindFastForward/SailwindFastForward.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("SailwindFastForward")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.1.0")]
[assembly: AssemblyInformationalVersion("1.2.1+4cadf9d1358124a8f1456ac5156d884e4be73f5f")]
[assembly: AssemblyProduct("SailwindFastForward")]
[assembly: AssemblyTitle("SailwindFastForward")]
[assembly: AssemblyVersion("1.2.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 SailwindFastForward
{
	internal static class AutosavePatch
	{
		private sealed class Branch
		{
			internal readonly int Offset;

			internal Branch(int offset)
			{
				Offset = offset;
			}
		}

		private sealed class Shape
		{
			internal readonly int Offset;

			internal readonly OpCode Opcode;

			internal readonly object Operand;

			internal Shape(int offset, OpCode opcode, object operand)
			{
				Offset = offset;
				Opcode = opcode;
				Operand = operand;
			}
		}

		private const BindingFlags Members = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static FieldInfo Field(Type type, string name)
		{
			return type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		}

		internal static IEnumerable<CodeInstruction> Rewrite(IEnumerable<CodeInstruction> instructions, MethodInfo saveMethod, MethodInfo autosaveMethod)
		{
			string failure;
			return Rewrite(instructions, saveMethod, autosaveMethod, out failure);
		}

		internal static IEnumerable<CodeInstruction> Rewrite(IEnumerable<CodeInstruction> instructions, MethodInfo saveMethod, MethodInfo autosaveMethod, out string failure)
		{
			List<CodeInstruction> list = instructions.ToList();
			try
			{
				IEnumerable<CodeInstruction> result = RewriteVerified(list, saveMethod, autosaveMethod);
				failure = null;
				return result;
			}
			catch (Exception ex)
			{
				failure = ex.Message;
				return list;
			}
		}

		private static IEnumerable<CodeInstruction> RewriteVerified(IEnumerable<CodeInstruction> instructions, MethodInfo saveMethod, MethodInfo autosaveMethod)
		{
			if (saveMethod == null || autosaveMethod == null)
			{
				throw new MissingMethodException("Save wrapper methods are unavailable.");
			}
			List<CodeInstruction> code = instructions.Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction instruction) => new CodeInstruction(instruction))).ToList();
			Shape[] array = ExpectedUpdate(saveMethod);
			if (code.Count != array.Length)
			{
				throw new InvalidOperationException("Unexpected SaveLoadManager.Update instruction count.");
			}
			int i;
			for (i = 0; i < code.Count; i++)
			{
				Shape shape = array[i];
				object operand = shape.Operand;
				Branch branch = operand as Branch;
				bool flag;
				if (branch != null)
				{
					int num = Array.FindIndex(array, (Shape entry) => entry.Offset == branch.Offset);
					IEnumerable enumerable = ((num < 0) ? null : ((IEnumerable)typeof(CodeInstruction).GetField("labels").GetValue(code[num])));
					flag = enumerable != null && code[i].operand?.GetType().FullName == "System.Reflection.Emit.Label" && enumerable.Cast<object>().Any((object label) => object.Equals(label, code[i].operand));
				}
				else
				{
					flag = object.Equals(code[i].operand, shape.Operand);
				}
				if ((!(code[i].opcode == shape.Opcode) && (!(shape.Operand is Branch) || !(CanonicalBranch(code[i].opcode) == CanonicalBranch(shape.Opcode)))) || !flag)
				{
					throw new InvalidOperationException($"Unexpected SaveLoadManager.Update shape at IL_{shape.Offset:x4}.");
				}
			}
			int index = Array.FindIndex(array, (Shape entry) => entry.Offset == 141);
			code[index].opcode = OpCodes.Call;
			code[index].operand = autosaveMethod;
			return code;
		}

		private static OpCode CanonicalBranch(OpCode opcode)
		{
			if (opcode == OpCodes.Brfalse_S)
			{
				return OpCodes.Brfalse;
			}
			if (opcode == OpCodes.Brtrue_S)
			{
				return OpCodes.Brtrue;
			}
			if (opcode == OpCodes.Ble_S)
			{
				return OpCodes.Ble;
			}
			if (opcode == OpCodes.Bgt_Un_S)
			{
				return OpCodes.Bgt_Un;
			}
			return opcode;
		}

		private static Shape[] ExpectedUpdate(MethodInfo save)
		{
			FieldInfo operand = Field(typeof(SaveLoadManager), "save");
			FieldInfo operand2 = Field(typeof(SaveLoadManager), "saveCompressed");
			FieldInfo operand3 = Field(typeof(SaveLoadManager), "load");
			FieldInfo operand4 = Field(typeof(SaveLoadManager), "gctimer");
			FieldInfo operand5 = Field(typeof(Settings), "autosaveInterval");
			return new Shape[60]
			{
				S(0, OpCodes.Ldarg_0),
				S(1, OpCodes.Ldfld, operand),
				S(6, OpCodes.Brfalse_S, new Branch(22)),
				S(8, OpCodes.Ldarg_0),
				S(9, OpCodes.Ldc_I4_0),
				S(10, OpCodes.Stfld, operand),
				S(15, OpCodes.Ldarg_0),
				S(16, OpCodes.Ldc_I4_0),
				S(17, OpCodes.Call, save),
				S(22, OpCodes.Ldarg_0),
				S(23, OpCodes.Ldfld, operand2),
				S(28, OpCodes.Brfalse_S, new Branch(44)),
				S(30, OpCodes.Ldarg_0),
				S(31, OpCodes.Ldc_I4_0),
				S(32, OpCodes.Stfld, operand2),
				S(37, OpCodes.Ldarg_0),
				S(38, OpCodes.Ldc_I4_1),
				S(39, OpCodes.Call, save),
				S(44, OpCodes.Ldarg_0),
				S(45, OpCodes.Ldfld, operand3),
				S(50, OpCodes.Brfalse_S, new Branch(66)),
				S(52, OpCodes.Ldarg_0),
				S(53, OpCodes.Ldc_I4_0),
				S(54, OpCodes.Stfld, operand3),
				S(59, OpCodes.Ldarg_0),
				S(60, OpCodes.Ldc_I4_0),
				S(61, OpCodes.Call, typeof(SaveLoadManager).GetMethod("LoadGame", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(int) }, null)),
				S(66, OpCodes.Ldsfld, operand5),
				S(71, OpCodes.Ldc_I4_0),
				S(72, OpCodes.Ble_S, new Branch(164)),
				S(74, OpCodes.Ldarg_0),
				S(75, OpCodes.Ldfld, operand4),
				S(80, OpCodes.Ldc_R4, 0f),
				S(85, OpCodes.Bgt_Un_S, new Branch(146)),
				S(87, OpCodes.Ldsfld, Field(typeof(CrateInventoryUI), "instance")),
				S(92, OpCodes.Ldfld, Field(typeof(CrateInventoryUI), "showingUI")),
				S(97, OpCodes.Brtrue_S, new Branch(146)),
				S(99, OpCodes.Ldarg_0),
				S(100, OpCodes.Ldsfld, operand5),
				S(105, OpCodes.Conv_R4),
				S(106, OpCodes.Ldc_R4, 60f),
				S(111, OpCodes.Mul),
				S(112, OpCodes.Stfld, operand4),
				S(117, OpCodes.Ldarg_0),
				S(118, OpCodes.Ldfld, Field(typeof(SaveLoadManager), "enableAutosave")),
				S(123, OpCodes.Brfalse_S, new Branch(146)),
				S(125, OpCodes.Ldsfld, Field(typeof(GameState), "sleeping")),
				S(130, OpCodes.Brtrue_S, new Branch(146)),
				S(132, OpCodes.Ldsfld, Field(typeof(GameState), "recovering")),
				S(137, OpCodes.Brtrue_S, new Branch(146)),
				S(139, OpCodes.Ldarg_0),
				S(140, OpCodes.Ldc_I4_1),
				S(141, OpCodes.Call, save),
				S(146, OpCodes.Ldarg_0),
				S(147, OpCodes.Ldarg_0),
				S(148, OpCodes.Ldfld, operand4),
				S(153, OpCodes.Call, typeof(Time).GetProperty("deltaTime", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetGetMethod(nonPublic: true)),
				S(158, OpCodes.Sub),
				S(159, OpCodes.Stfld, operand4),
				S(164, OpCodes.Ret)
			};
		}

		private static Shape S(int offset, OpCode opcode, object operand = null)
		{
			return new Shape(offset, opcode, operand);
		}
	}
	internal sealed class AutosaveState
	{
		private enum Phase
		{
			Idle,
			AwaitingPrefix,
			AwaitingCancelledPrefix,
			Saving,
			WaitingForCompletion,
			ManualHoldSave
		}

		private Phase phase;

		internal bool TryEnterSave(bool allowManualHold = false, bool busy = false)
		{
			if (phase == Phase.AwaitingPrefix)
			{
				phase = Phase.Saving;
				return true;
			}
			if (phase == Phase.Idle && allowManualHold && !busy)
			{
				phase = Phase.ManualHoldSave;
				return true;
			}
			Reset();
			return false;
		}

		internal void Begin(bool keepFastForward)
		{
			phase = ((keepFastForward && phase == Phase.Idle) ? Phase.AwaitingPrefix : Phase.AwaitingCancelledPrefix);
		}

		internal void End(bool busy)
		{
			phase = ((phase == Phase.Saving && busy) ? Phase.WaitingForCompletion : Phase.Idle);
		}

		internal bool BlocksBusySave(bool busy, bool cancelOnAutosave = false, bool eligibleManualHold = false)
		{
			if (!busy || ((phase == Phase.ManualHoldSave) ? (!eligibleManualHold) : cancelOnAutosave))
			{
				Reset();
			}
			if (busy && phase != Phase.WaitingForCompletion)
			{
				return phase != Phase.ManualHoldSave;
			}
			return false;
		}

		internal void Reset()
		{
			phase = Phase.Idle;
		}
	}
	internal sealed class BackgroundExecution
	{
		private readonly Func<bool> read;

		private readonly Action<bool> write;

		private bool changed;

		internal BackgroundExecution(Func<bool> read, Action<bool> write)
		{
			this.read = read;
			this.write = write;
		}

		internal void Enable()
		{
			if (!changed && !read())
			{
				write(obj: true);
				changed = true;
			}
		}

		internal void Restore()
		{
			if (changed)
			{
				changed = false;
				if (read())
				{
					write(obj: false);
				}
			}
		}
	}
	internal sealed class ConfigurationManagerWindow
	{
		internal static readonly string PluginId = string.Join(".", "com", "bepis", "bepinex", "configurationmanager");

		private readonly Func<object> findPlugin;

		private readonly Action<string> warning;

		private bool resolved;

		private Func<bool> displayingWindow;

		internal ConfigurationManagerWindow(Func<object> findPlugin, Action<string> warning)
		{
			this.findPlugin = findPlugin;
			this.warning = warning;
		}

		internal bool IsOpen()
		{
			try
			{
				if (!resolved)
				{
					resolved = true;
					object obj = findPlugin();
					if (obj == null)
					{
						return false;
					}
					PropertyInfo property = obj.GetType().GetProperty("DisplayingWindow", BindingFlags.Instance | BindingFlags.Public);
					MethodInfo methodInfo = property?.GetGetMethod();
					if (property == null || property.PropertyType != typeof(bool) || property.GetIndexParameters().Length != 0 || methodInfo == null)
					{
						throw new MissingMemberException(obj.GetType().FullName, "DisplayingWindow");
					}
					displayingWindow = (Func<bool>)Delegate.CreateDelegate(typeof(Func<bool>), obj, methodInfo);
				}
				return displayingWindow != null && displayingWindow();
			}
			catch (Exception ex)
			{
				displayingWindow = null;
				warning?.Invoke("Configuration Manager window could not be read. Cursor menus will still cancel fast-forward. " + ex.Message);
				return false;
			}
		}

		internal static bool BlocksCursorMenu(bool cursorMenu, bool inventoryOpen, bool configurationOpen, bool nativeCursorMenu)
		{
			if (cursorMenu && !inventoryOpen)
			{
				return !configurationOpen || nativeCursorMenu;
			}
			return false;
		}
	}
	internal sealed class HoldInput
	{
		private enum Phase
		{
			Idle,
			Active,
			Suppressed
		}

		private Phase phase;

		private bool configured;

		private KeyboardShortcut previousHold;

		private int previousSpeed;

		private bool sampledMainHeld;

		internal bool Active => phase == Phase.Active;

		internal void Invalidate()
		{
			phase = Phase.Suppressed;
		}

		internal bool HasIntent(float current, float selected, bool focused, KeyboardShortcut hold, Func<KeyCode, bool> getKeyDown, Func<KeyCode, bool> getKey)
		{
			//IL_0006: 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)
			if (current == selected && HotkeyInput.IsHeld(((KeyboardShortcut)(ref hold)).MainKey, ((KeyboardShortcut)(ref hold)).Modifiers, getKey))
			{
				if (!Active)
				{
					if (phase == Phase.Idle && focused)
					{
						return getKeyDown(((KeyboardShortcut)(ref hold)).MainKey);
					}
					return false;
				}
				return true;
			}
			return false;
		}

		internal SpeedInputAction Dispatch(SpeedOwnership speed, float current, int maximum, int activityMaximum, int effectiveMaximum, bool focused, string blocked, KeyboardShortcut cycle, KeyboardShortcut reset, KeyboardShortcut hold, int holdSpeed, Func<KeyCode, bool> getKeyDown, Func<KeyCode, bool> getKey, Action<string> cancel, Action resetAction)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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)
			//IL_0078: 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_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			bool flag = (sampledMainHeld = (int)((KeyboardShortcut)(ref hold)).MainKey != 0 && getKey(((KeyboardShortcut)(ref hold)).MainKey));
			bool flag2 = HotkeyInput.IsHeld(((KeyboardShortcut)(ref hold)).MainKey, ((KeyboardShortcut)(ref hold)).Modifiers, getKey);
			int num;
			if (configured)
			{
				ref KeyboardShortcut reference = ref previousHold;
				object obj = hold;
				num = ((!((object)Unsafe.As<KeyboardShortcut, KeyboardShortcut>(ref reference)/*cast due to .constrained prefix*/).Equals(obj) || previousSpeed != holdSpeed) ? 1 : 0);
			}
			else
			{
				num = 0;
			}
			bool flag3 = (byte)num != 0;
			configured = true;
			previousHold = hold;
			previousSpeed = holdSpeed;
			if (phase == Phase.Suppressed && !flag)
			{
				phase = Phase.Idle;
			}
			if (focused && HotkeyInput.IsDown(((KeyboardShortcut)(ref reset)).MainKey, ((KeyboardShortcut)(ref reset)).Modifiers, getKeyDown, getKey))
			{
				Invalidate();
				resetAction();
				RearmAfterObservedRelease();
				return SpeedInputAction.Reset;
			}
			if (blocked != null)
			{
				return Cancel(cancel, blocked);
			}
			if (speed.Active && current != speed.SelectedSpeed)
			{
				return Cancel(cancel, "timescale changed externally");
			}
			if (!Active && speed.SelectedSpeed > (float)maximum)
			{
				return Cancel(cancel, "configured maximum lowered");
			}
			if (flag3 && (Active || flag))
			{
				return Cancel(cancel, "hold settings changed");
			}
			if (Active)
			{
				if (!flag2)
				{
					return Cancel(cancel, "hold released");
				}
				if (!speed.TryLimit(current, holdSpeed))
				{
					return SpeedInputAction.None;
				}
				return SpeedInputAction.Limit;
			}
			if (!focused)
			{
				if (flag)
				{
					Invalidate();
				}
				if (!speed.TryLimit(current, effectiveMaximum))
				{
					return SpeedInputAction.None;
				}
				return SpeedInputAction.Limit;
			}
			if (flag)
			{
				if (phase == Phase.Idle && flag2 && getKeyDown(((KeyboardShortcut)(ref hold)).MainKey))
				{
					if (speed.TrySelect(current, holdSpeed))
					{
						phase = Phase.Active;
						return SpeedInputAction.Hold;
					}
					Invalidate();
					if (!speed.TryLimit(current, effectiveMaximum))
					{
						return SpeedInputAction.Unavailable;
					}
					return SpeedInputAction.Limit;
				}
				Invalidate();
				if (flag2)
				{
					if (!speed.TryLimit(current, effectiveMaximum))
					{
						return SpeedInputAction.None;
					}
					return SpeedInputAction.Limit;
				}
			}
			return HotkeyInput.Dispatch(speed, current, effectiveMaximum, focused, cycle, reset, getKeyDown, getKey, resetAction);
		}

		private SpeedInputAction Cancel(Action<string> cancel, string reason)
		{
			Invalidate();
			cancel(reason);
			RearmAfterObservedRelease();
			return SpeedInputAction.Cancel;
		}

		private void RearmAfterObservedRelease()
		{
			if (!sampledMainHeld)
			{
				phase = Phase.Idle;
			}
		}
	}
	internal enum HoldBoundary
	{
		SleepOrBed,
		Recovery,
		Shipyard,
		Economy,
		CursorMenu,
		Pause,
		Loading,
		OutsideGameplay,
		SaveUnavailable
	}
	internal static class HoldPolicy
	{
		internal static bool NativeSleepOwnsSpeed(bool sleeping, bool eyesFullyClosed)
		{
			return sleeping && eyesFullyClosed;
		}

		internal static bool CanContinue(HoldInput hold, SpeedOwnership speed, float current, bool chordHeld, bool hardBlocked)
		{
			return !hardBlocked && hold.Active && speed.Active && current == speed.SelectedSpeed && chordHeld;
		}

		internal static bool Blocks(HoldBoundary boundary, bool intentionalHold)
		{
			if ((uint)boundary <= 4u)
			{
				return !intentionalHold;
			}
			return true;
		}
	}
	internal enum SpeedInputAction
	{
		None,
		Reset,
		Limit,
		Cycle,
		Hold,
		Cancel,
		Unavailable
	}
	internal static class HotkeyInput
	{
		internal static bool IsHeld(KeyCode mainKey, IEnumerable<KeyCode> modifiers, Func<KeyCode, bool> getKey)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//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_001f: Unknown result type (might be due to invalid IL or missing references)
			if ((int)mainKey == 0 || !getKey(mainKey))
			{
				return false;
			}
			foreach (KeyCode modifier in modifiers)
			{
				if (!getKey(modifier))
				{
					return false;
				}
			}
			return true;
		}

		internal static SpeedInputAction Dispatch(SpeedOwnership speed, float current, int effectiveMaximum, bool focused, KeyboardShortcut cycle, KeyboardShortcut reset, Func<KeyCode, bool> getKeyDown, Func<KeyCode, bool> getKey, Action cancel)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (focused && IsDown(((KeyboardShortcut)(ref reset)).MainKey, ((KeyboardShortcut)(ref reset)).Modifiers, getKeyDown, getKey))
			{
				cancel();
				return SpeedInputAction.Reset;
			}
			if (speed.TryLimit(current, effectiveMaximum))
			{
				return SpeedInputAction.Limit;
			}
			if (!focused || !IsDown(((KeyboardShortcut)(ref cycle)).MainKey, ((KeyboardShortcut)(ref cycle)).Modifiers, getKeyDown, getKey) || effectiveMaximum == 1)
			{
				return SpeedInputAction.None;
			}
			if (!speed.TryCycle(current, effectiveMaximum))
			{
				return SpeedInputAction.Unavailable;
			}
			return SpeedInputAction.Cycle;
		}

		internal static bool IsDown(KeyCode mainKey, IEnumerable<KeyCode> modifiers, Func<KeyCode, bool> getKeyDown, Func<KeyCode, bool> getKey)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//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_001f: Unknown result type (might be due to invalid IL or missing references)
			if ((int)mainKey == 0 || !getKeyDown(mainKey))
			{
				return false;
			}
			foreach (KeyCode modifier in modifiers)
			{
				if (!getKey(modifier))
				{
					return false;
				}
			}
			return true;
		}
	}
	internal sealed class IndicatorScaleRange : AcceptableValueRange<float>
	{
		internal IndicatorScaleRange()
			: base(0.5f, 5f)
		{
		}

		public override object Clamp(object value)
		{
			float num = (float)value;
			if (float.IsNaN(num))
			{
				return 1f;
			}
			num = Math.Max(((AcceptableValueRange<float>)this).MinValue, Math.Min(((AcceptableValueRange<float>)this).MaxValue, num));
			return (float)(Math.Round(num * 2f, MidpointRounding.AwayFromZero) / 2.0);
		}

		public override bool IsValid(object value)
		{
			if (value is float num)
			{
				return num.Equals(((AcceptableValueBase)this).Clamp((object)num));
			}
			return false;
		}

		public override string ToDescriptionString()
		{
			return base.ToDescriptionString() + " in steps of 0.5";
		}
	}
	internal enum IndicatorBackground
	{
		Simple,
		Scroll,
		None
	}
	[BepInPlugin("local.sailwind.fastforward", "Sailwind Fast Forward", "1.2.1")]
	[BepInProcess("Sailwind.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal const string Id = "local.sailwind.fastforward";

		private readonly SpeedOwnership speed = new SpeedOwnership();

		private readonly HoldInput holdInput = new HoldInput();

		private SaveContinuation autosave;

		private readonly BackgroundExecution background = new BackgroundExecution(() => Application.runInBackground, delegate(bool value)
		{
			Application.runInBackground = value;
		});

		private PluginSettings settings;

		private Action resetHotkey;

		private Action<string> cancelInput;

		private Action saveError;

		private Harmony harmony;

		private FieldInfo saveBusy;

		private bool ready;

		private SpeedIndicator indicator;

		private ConfigurationManagerWindow configurationManager;

		private static Plugin instance;

		private void Awake()
		{
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			instance = this;
			resetHotkey = delegate
			{
				Cancel("reset hotkey");
			};
			cancelInput = Cancel;
			saveError = delegate
			{
				Cancel("save error");
			};
			autosave = new SaveContinuation(delegate
			{
				Cancel("save continuation unavailable");
			});
			settings = new PluginSettings(((BaseUnityPlugin)this).Config, delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)message);
			});
			indicator = new SpeedIndicator(delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)message);
			});
			configurationManager = new ConfigurationManagerWindow(() => (!Chainloader.PluginInfos.TryGetValue(ConfigurationManagerWindow.PluginId, out var value) || !Object.op_Implicit((Object)(object)value.Instance)) ? null : value.Instance, delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)message);
			});
			try
			{
				saveBusy = AccessTools.Field(typeof(SaveLoadManager), "busy");
				if (saveBusy == null || saveBusy.FieldType != typeof(bool))
				{
					throw new MissingFieldException("SaveLoadManager.busy");
				}
				harmony = new Harmony("local.sailwind.fastforward");
				PatchBoundary(typeof(StartMenu), "GameToSettings", Type.EmptyTypes);
				PatchBoundary(typeof(SaveLoadManager), "SaveGame", new Type[1] { typeof(bool) });
				PatchBoundary(typeof(SaveLoadManager), "LoadGame", new Type[1] { typeof(int) });
				PatchBoundary(typeof(Sleep), "FallAsleep", Type.EmptyTypes);
				bool timerInstalled = SaveContinuation.TryInstall("Timer autosave hook", delegate
				{
					//IL_0048: Unknown result type (might be due to invalid IL or missing references)
					//IL_0054: Expected O, but got Unknown
					MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(SaveLoadManager), "Update", Type.EmptyTypes, (Type[])null);
					if (methodInfo == null)
					{
						throw new MissingMethodException("SaveLoadManager.Update");
					}
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "MarkAutosave", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
				}, delegate(string message)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)message);
				});
				autosave.SetTimerInstalled(timerInstalled);
				autosave.SetCoroutineSupport(SaveContinuation.TryInstall("Save coroutine error hook", delegate
				{
					//IL_0028: Unknown result type (might be due to invalid IL or missing references)
					//IL_0033: Expected O, but got Unknown
					harmony.Patch((MethodBase)SaveCoroutine.FindMoveNext(typeof(SaveLoadManager)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "AfterSaveError", (Type[])null), (HarmonyMethod)null);
				}, delegate(string message)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)message);
				}));
				SceneManager.activeSceneChanged += OnActiveSceneChanged;
				SceneManager.sceneLoaded += OnSceneLoaded;
				ready = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Ready. {settings.Hotkey.Value} to cycle. {settings.ResetHotkey.Value} to return to 1x. Hold {settings.HoldHotkey.Value} for {settings.HoldSpeed.Value}x.");
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Initialization failed. Fast-forward disabled: {arg}");
				Harmony obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				((Behaviour)this).enabled = false;
			}
		}

		private void PatchBoundary(Type type, string name, Type[] arguments)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			MethodInfo methodInfo = AccessTools.DeclaredMethod(type, name, arguments, (Type[])null);
			if (methodInfo == null)
			{
				throw new MissingMethodException(type.FullName, name);
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "BeforeBoundary", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (!(type == typeof(SaveLoadManager)) || !(name == "SaveGame")) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Plugin), "AfterSaveError", (Type[])null), (HarmonyMethod)null);
		}

		private static void BeforeBoundary(MethodBase __originalMethod, object __instance)
		{
			if (!((Object)(object)instance == (Object)null) && instance.ready && instance.settings != null && (!(__originalMethod.DeclaringType == typeof(SaveLoadManager)) || !(__originalMethod.Name == "SaveGame") || !instance.autosave.TryEnterSave(instance.CanContinueHold(), (bool)instance.saveBusy.GetValue(__instance))) && (!(__originalMethod.DeclaringType == typeof(Sleep)) || !(__originalMethod.Name == "FallAsleep") || HoldPolicy.Blocks(HoldBoundary.SleepOrBed, instance.HoldIntent())))
			{
				instance.Cancel(__originalMethod.DeclaringType.Name + "." + __originalMethod.Name);
			}
		}

		private static IEnumerable<CodeInstruction> MarkAutosave(IEnumerable<CodeInstruction> instructions)
		{
			string failure;
			IEnumerable<CodeInstruction> result = AutosavePatch.Rewrite(instructions, AccessTools.DeclaredMethod(typeof(SaveLoadManager), "SaveGame", new Type[1] { typeof(bool) }, (Type[])null), AccessTools.DeclaredMethod(typeof(Plugin), "SaveAutosave", (Type[])null, (Type[])null), out failure);
			instance?.autosave.SetTimerSupport(failure == null);
			if (failure != null)
			{
				Plugin plugin = instance;
				if (plugin == null)
				{
					return result;
				}
				((BaseUnityPlugin)plugin).Logger.LogWarning((object)("Timer autosave layout changed (" + failure + "). Fast-forward remains available. All saves will cancel fast-forward, including held fast-forward. Report this warning if save continuation is needed."));
			}
			return result;
		}

		private static Exception AfterSaveError(Exception __exception)
		{
			return SaveErrorBoundary.Handle(__exception, instance?.saveError);
		}

		private static void SaveAutosave(SaveLoadManager manager, bool compressed)
		{
			Plugin plugin = instance;
			if ((Object)(object)plugin == (Object)null || !plugin.ready)
			{
				manager.SaveGame(compressed);
				return;
			}
			bool flag = (bool)plugin.saveBusy.GetValue(manager);
			if (plugin.settings.CancelOnAutosave.Value)
			{
				plugin.Cancel("timer autosave");
			}
			plugin.autosave.Begin(!plugin.settings.CancelOnAutosave.Value && plugin.speed.Active && !flag);
			try
			{
				manager.SaveGame(compressed);
			}
			catch (Exception error)
			{
				SaveErrorBoundary.Handle(error, plugin.saveError);
				throw;
			}
			finally
			{
				plugin.autosave.End((bool)plugin.saveBusy.GetValue(manager));
			}
		}

		private static string HardBlockReason()
		{
			if (HoldPolicy.NativeSleepOwnsSpeed(GameState.sleeping, GameState.eyesFullyClosed))
			{
				return "native sleep";
			}
			if (!GameState.playing)
			{
				return "outside gameplay";
			}
			if (GameState.currentlyLoading)
			{
				return "save loading";
			}
			if (GameState.justStarted || GameState.changingStartRegion)
			{
				return "world transition";
			}
			if (GameState.wasInSettingsMenu)
			{
				return "settings menu";
			}
			return null;
		}

		private string BlockReason()
		{
			string text = HardBlockReason();
			if (text != null)
			{
				return text;
			}
			bool intentionalHold = HoldIntent();
			if ((GameState.sleeping || GameState.eyesFullyClosed || Sleep.timeskipSleep || GameState.justWokeUp || Object.op_Implicit((Object)(object)GameState.inBed)) && HoldPolicy.Blocks(HoldBoundary.SleepOrBed, intentionalHold))
			{
				return "sleep/bed";
			}
			if (GameState.recovering && HoldPolicy.Blocks(HoldBoundary.Recovery, intentionalHold))
			{
				return "recovery";
			}
			if (Object.op_Implicit((Object)(object)GameState.currentShipyard) && HoldPolicy.Blocks(HoldBoundary.Shipyard, intentionalHold))
			{
				return "shipyard";
			}
			if (Object.op_Implicit((Object)(object)EconomyUI.instance) && EconomyUI.instance.uiActive && HoldPolicy.Blocks(HoldBoundary.Economy, intentionalHold))
			{
				return "economy menu";
			}
			if (GameState.inCursorMenu && ConfigurationManagerWindow.BlocksCursorMenu(cursorMenu: true, InventoryOpen(), configurationManager.IsOpen(), NativeCursorMenuOpen()) && HoldPolicy.Blocks(HoldBoundary.CursorMenu, intentionalHold))
			{
				return "cursor menu";
			}
			if (!Object.op_Implicit((Object)(object)SaveLoadManager.instance))
			{
				return "save unavailable";
			}
			if (autosave.BlocksBusySave((bool)saveBusy.GetValue(SaveLoadManager.instance), settings.CancelOnAutosave.Value, CanContinueHold()))
			{
				return "save busy";
			}
			return null;
		}

		private bool HoldIntent()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (settings != null && HardBlockReason() == null)
			{
				return holdInput.HasIntent(Time.timeScale, speed.SelectedSpeed, Application.isFocused, settings.HoldHotkey.Value, (Func<KeyCode, bool>)Input.GetKeyDown, (Func<KeyCode, bool>)Input.GetKey);
			}
			return false;
		}

		private bool CanContinueHold()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			HoldInput hold = holdInput;
			SpeedOwnership speedOwnership = speed;
			float timeScale = Time.timeScale;
			KeyboardShortcut value = settings.HoldHotkey.Value;
			KeyCode mainKey = ((KeyboardShortcut)(ref value)).MainKey;
			value = settings.HoldHotkey.Value;
			return HoldPolicy.CanContinue(hold, speedOwnership, timeScale, HotkeyInput.IsHeld(mainKey, ((KeyboardShortcut)(ref value)).Modifiers, (Func<KeyCode, bool>)Input.GetKey), HardBlockReason() != null);
		}

		private static bool InventoryOpen()
		{
			if (Object.op_Implicit((Object)(object)PlayerNeedsUI.instance))
			{
				return PlayerNeedsUI.instance.IsActive();
			}
			return false;
		}

		private static bool NativeCursorMenuOpen()
		{
			if (!GameState.inPortMissionList && (!Object.op_Implicit((Object)(object)MapTableCamera.instance) || !Object.op_Implicit((Object)(object)MapTableCamera.instance.currentMap)))
			{
				if (Object.op_Implicit((Object)(object)CurrencyExchangeUI.instance))
				{
					return CurrencyExchangeUI.instance.uiActive;
				}
				return false;
			}
			return true;
		}

		private static bool MovementRequested()
		{
			OVRPlayerController ovrController = Refs.ovrController;
			if (!Application.isFocused || !Object.op_Implicit((Object)(object)ovrController) || !((Behaviour)ovrController).isActiveAndEnabled || !ovrController.EnableLinearMovement)
			{
				return false;
			}
			bool flag = false;
			ovrController.GetHaltUpdateMovement(ref flag);
			if (flag)
			{
				return false;
			}
			if (!GameInput.GetKey((InputName)0) && !GameInput.GetKey((InputName)1) && !GameInput.GetKey((InputName)2) && !GameInput.GetKey((InputName)3) && !GameInput.GetKey((InputName)4) && !(Mathf.Abs(GameInput.GetPrimaryHorizontal()) > 0.1f))
			{
				return Mathf.Abs(GameInput.GetPrimaryVertical()) > 0.1f;
			}
			return true;
		}

		private void Update()
		{
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: 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 (!ready)
			{
				return;
			}
			indicator.Update(!GameState.playing || GameState.currentlyLoading || GameState.justStarted || GameState.changingStartRegion);
			try
			{
				string text = BlockReason();
				bool flag = text == null && InventoryOpen();
				bool flag2 = text == null && !flag && MovementRequested();
				int effectiveMaximum = ((flag || flag2) ? Math.Min(settings.MaxSpeed.Value, settings.MovementInventoryMaxSpeed.Value) : settings.MaxSpeed.Value);
				float timeScale = Time.timeScale;
				switch (holdInput.Dispatch(speed, timeScale, settings.MaxSpeed.Value, settings.MovementInventoryMaxSpeed.Value, effectiveMaximum, Application.isFocused, text, settings.Hotkey.Value, settings.ResetHotkey.Value, settings.HoldHotkey.Value, settings.HoldSpeed.Value, (Func<KeyCode, bool>)Input.GetKeyDown, (Func<KeyCode, bool>)Input.GetKey, cancelInput, resetHotkey))
				{
				case SpeedInputAction.Limit:
					ApplySelectedSpeed(timeScale, flag ? "inventory speed limit" : "movement speed limit");
					break;
				case SpeedInputAction.Cycle:
					ApplySelectedSpeed(timeScale, "hotkey");
					break;
				case SpeedInputAction.Hold:
					ApplySelectedSpeed(timeScale, "hold hotkey");
					break;
				case SpeedInputAction.Unavailable:
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Fast-forward unavailable at native/external scale {Time.timeScale}.");
					break;
				}
			}
			catch (Exception arg)
			{
				Cancel("runtime error");
				((BaseUnityPlugin)this).Logger.LogError((object)$"Fast-forward disabled: {arg}");
				((Behaviour)this).enabled = false;
			}
		}

		private void ApplySelectedSpeed(float previous, string reason)
		{
			Time.timeScale = speed.SelectedSpeed;
			if (speed.Active)
			{
				background.Enable();
			}
			else
			{
				background.Restore();
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"{previous}x -> {Time.timeScale}x ({reason})");
			LogNeeds();
		}

		private void Cancel(string reason)
		{
			holdInput.Invalidate();
			autosave?.Reset();
			if (speed.Active)
			{
				float timeScale = Time.timeScale;
				if (speed.Release(timeScale, HoldPolicy.NativeSleepOwnsSpeed(GameState.sleeping, GameState.eyesFullyClosed)))
				{
					Time.timeScale = 1f;
				}
				background.Restore();
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Fast-forward off ({reason}). Scale {timeScale} -> {Time.timeScale}.");
				LogNeeds();
			}
		}

		private void LogNeeds()
		{
			if (GameState.playing && Object.op_Implicit((Object)(object)Sun.sun) && Object.op_Implicit((Object)(object)PlayerNeeds.instance))
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)($"Needs: realSeconds={Time.realtimeSinceStartup:F1}, day={GameState.day}, hour={Sun.sun.globalTime:F3}, " + $"food={PlayerNeeds.food:F2}, water={PlayerNeeds.water:F2}, rest={PlayerNeeds.sleep:F2}, " + $"sunRate={Sun.sun.timescale:F5}, godMode={PlayerNeeds.instance.godMode}."));
			}
		}

		private void OnGUI()
		{
			if (ready && speed.Active && Time.timeScale == speed.SelectedSpeed && settings.ShowIndicator.Value)
			{
				indicator.Draw(speed.SelectedSpeed, settings.IndicatorScale.Value, settings.IndicatorBackground.Value);
			}
		}

		private void OnActiveSceneChanged(Scene previous, Scene next)
		{
			indicator?.Invalidate();
			Cancel("active scene changed");
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if ((int)mode == 0)
			{
				indicator?.Invalidate();
				Cancel("world scene loaded");
			}
		}

		private void OnApplicationQuit()
		{
			Cancel("quit");
		}

		private void OnDisable()
		{
			Cancel("plugin disabled");
		}

		private void OnDestroy()
		{
			ready = false;
			indicator?.Dispose();
			Cancel("plugin destroyed");
			SceneManager.activeSceneChanged -= OnActiveSceneChanged;
			SceneManager.sceneLoaded -= OnSceneLoaded;
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if ((Object)(object)instance == (Object)(object)this)
			{
				instance = null;
			}
		}
	}
	internal sealed class PluginSettings
	{
		private const string ShortcutHelp = " Add modifiers with +, for example LeftShift + Mouse4. Names ignore capitalization and accept spaces, such as left shift + mouse 4. Leave the value blank to disable this shortcut.";

		internal ShortcutSetting Hotkey { get; }

		internal ShortcutSetting ResetHotkey { get; }

		internal ShortcutSetting HoldHotkey { get; }

		internal ConfigEntry<bool> ShowIndicator { get; }

		internal ConfigEntry<float> IndicatorScale { get; }

		internal ConfigEntry<IndicatorBackground> IndicatorBackground { get; }

		internal ConfigEntry<int> MaxSpeed { get; }

		internal ConfigEntry<int> HoldSpeed { get; }

		internal ConfigEntry<int> MovementInventoryMaxSpeed { get; }

		internal ConfigEntry<bool> CancelOnAutosave { get; }

		internal PluginSettings(ConfigFile config, Action<string> warning = null)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Invalid comparison between Unknown and I4
			//IL_006b: 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_0089: 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_0096: Invalid comparison between Unknown and I4
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Invalid comparison between Unknown and I4
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Expected O, but got Unknown
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Expected O, but got Unknown
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Expected O, but got Unknown
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Expected O, but got Unknown
			Hotkey = new ShortcutSetting(config, "Controls", "Hotkey", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Cycle through the allowed fast-forward speeds, then return to normal speed. Add modifiers with +, for example LeftShift + Mouse4. Names ignore capitalization and accept spaces, such as left shift + mouse 4. Leave the value blank to disable this shortcut.", warning);
			KeyboardShortcut value = Hotkey.Value;
			KeyboardShortcut defaultValue = default(KeyboardShortcut);
			((KeyboardShortcut)(ref defaultValue))..ctor((KeyCode)(((int)((KeyboardShortcut)(ref value)).MainKey != 289) ? 289 : 0), Array.Empty<KeyCode>());
			ResetHotkey = new ShortcutSetting(config, "Controls", "ResetHotkey", defaultValue, "Return immediately to normal speed. This takes priority over the cycle and hold shortcuts. Add modifiers with +, for example LeftShift + Mouse4. Names ignore capitalization and accept spaces, such as left shift + mouse 4. Leave the value blank to disable this shortcut.", warning);
			value = Hotkey.Value;
			int num;
			if ((int)((KeyboardShortcut)(ref value)).MainKey != 290)
			{
				value = ResetHotkey.Value;
				if ((int)((KeyboardShortcut)(ref value)).MainKey != 290)
				{
					num = 290;
					goto IL_00ba;
				}
			}
			num = 0;
			goto IL_00ba;
			IL_00ba:
			KeyboardShortcut defaultValue2 = default(KeyboardShortcut);
			((KeyboardShortcut)(ref defaultValue2))..ctor((KeyCode)num, Array.Empty<KeyCode>());
			HoldHotkey = new ShortcutSetting(config, "Controls", "HoldHotkey", defaultValue2, "Fast-forward at HoldSpeed while this key is held. Release it to return to normal speed. Add modifiers with +, for example LeftShift + Mouse4. Names ignore capitalization and accept spaces, such as left shift + mouse 4. Leave the value blank to disable this shortcut.", warning);
			ShowIndicator = config.Bind<bool>("Display", "ShowIndicator", true, "Show the active fast-forward speed.");
			IndicatorScale = config.Bind<float>("Display", "IndicatorScale", 1f, new ConfigDescription("Size of the speed indicator. 1 keeps the original size. Values snap to the nearest 0.5 step.", (AcceptableValueBase)(object)new IndicatorScaleRange(), Array.Empty<object>()));
			IndicatorBackground = BindIndicatorBackground(config);
			MaxSpeed = config.Bind<int>("Simulation", "MaxSpeed", 4, new ConfigDescription("Maximum fast-forward speed for cycling. HoldSpeed is independent of this limit.", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[5] { 2, 4, 8, 16, 32 }), Array.Empty<object>()));
			HoldSpeed = config.Bind<int>("Simulation", "HoldSpeed", 4, new ConfigDescription("Fast-forward speed while holding HoldHotkey, independent of MaxSpeed. Holding ignores movement and inventory limits.", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[5] { 2, 4, 8, 16, 32 }), Array.Empty<object>()));
			MovementInventoryMaxSpeed = config.Bind<int>("Simulation", "MovementInventoryMaxSpeed", 2, new ConfigDescription("Cycle-mode speed limit while moving or viewing inventory, also bounded by MaxSpeed. Holding ignores this limit. 1 turns cycle-mode fast-forward off. 32 adds no lower limit. Ending the activity does not raise speed automatically.", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[6] { 1, 2, 4, 8, 16, 32 }), Array.Empty<object>()));
			CancelOnAutosave = config.Bind<bool>("Simulation", "CancelOnAutosave", false, "Turn off fast-forward when an autosave starts, including while holding. When disabled, active fast-forward continues during autosaves. Loading always cancels fast-forward. Manual saves cancel cycle mode but allow an active hold to continue.");
		}

		private static ConfigEntry<IndicatorBackground> BindIndicatorBackground(ConfigFile config)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			ConfigDefinition val = new ConfigDefinition("Display", "IndicatorBackground");
			ConfigEntry<IndicatorBackground> result = default(ConfigEntry<IndicatorBackground>);
			if (config.TryGetEntry<IndicatorBackground>(val, ref result))
			{
				return result;
			}
			bool saveOnConfigSet = config.SaveOnConfigSet;
			try
			{
				config.SaveOnConfigSet = false;
				string value = config.Bind<string>(val, "Simple", (ConfigDescription)null).Value;
				config.Remove(val);
				ConfigEntry<IndicatorBackground> obj = config.Bind<IndicatorBackground>(val, SailwindFastForward.IndicatorBackground.Simple, new ConfigDescription("Background for the speed indicator. Simple keeps the original box. Scroll uses Sailwind's parchment. None shows only the speed.", (AcceptableValueBase)null, Array.Empty<object>()));
				((ConfigEntryBase)obj).SetSerializedValue(string.Equals(value.Trim(), "Current", StringComparison.OrdinalIgnoreCase) ? "Simple" : value);
				return obj;
			}
			finally
			{
				config.SaveOnConfigSet = saveOnConfigSet;
				if (saveOnConfigSet)
				{
					config.Save();
				}
			}
		}
	}
	internal sealed class SaveContinuation
	{
		private readonly AutosaveState state = new AutosaveState();

		private readonly Action cancel;

		private bool timerInstalled;

		private bool timerSupported;

		private bool coroutineSupported;

		internal bool Supported
		{
			get
			{
				if (timerInstalled && timerSupported)
				{
					return coroutineSupported;
				}
				return false;
			}
		}

		internal SaveContinuation(Action cancel)
		{
			this.cancel = cancel;
		}

		internal void SetTimerInstalled(bool installed)
		{
			bool supported = Supported;
			timerInstalled = installed;
			if (!Supported)
			{
				state.Reset();
			}
			if (supported && !Supported)
			{
				cancel?.Invoke();
			}
		}

		internal void SetTimerSupport(bool supported)
		{
			SetSupport(supported, coroutineSupported);
		}

		internal void SetCoroutineSupport(bool supported)
		{
			SetSupport(timerSupported, supported);
		}

		private void SetSupport(bool timer, bool coroutine)
		{
			bool supported = Supported;
			timerSupported = timer;
			coroutineSupported = coroutine;
			if (!Supported)
			{
				state.Reset();
			}
			if (supported && !Supported)
			{
				cancel?.Invoke();
			}
		}

		internal bool TryEnterSave(bool allowManualHold = false, bool busy = false)
		{
			if (Supported)
			{
				return state.TryEnterSave(allowManualHold, busy);
			}
			return false;
		}

		internal void Begin(bool keepFastForward)
		{
			state.Begin(Supported && keepFastForward);
		}

		internal void End(bool busy)
		{
			state.End(Supported && busy);
		}

		internal bool BlocksBusySave(bool busy, bool cancelOnAutosave = false, bool eligibleManualHold = false)
		{
			if (!Supported)
			{
				return busy;
			}
			return state.BlocksBusySave(busy, cancelOnAutosave, eligibleManualHold);
		}

		internal void Reset()
		{
			state.Reset();
		}

		internal static bool TryInstall(string name, Action install, Action<string> warn)
		{
			try
			{
				install();
				return true;
			}
			catch (Exception ex)
			{
				warn(name + " unavailable (" + ex.GetType().Name + ": " + ex.Message + "). Fast-forward remains available. All saves will cancel fast-forward, including held fast-forward. Report this warning if save continuation is needed.");
				return false;
			}
		}
	}
	internal static class SaveCoroutine
	{
		private const BindingFlags Declared = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly Dictionary<short, OpCode> Opcodes = (from field in typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public)
			where field.FieldType == typeof(OpCode)
			select (OpCode)field.GetValue(null)).ToDictionary((OpCode opcode) => opcode.Value);

		internal static MethodInfo FindMoveNext(Type manager)
		{
			MethodInfo[] array = (from method in manager.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
				where method.Name == "DoSaveGame"
				select method).ToArray();
			if (array.Length != 1 || !typeof(IEnumerator).IsAssignableFrom(array[0].ReturnType))
			{
				throw new MissingMethodException("Expected one IEnumerator DoSaveGame factory.");
			}
			MethodInfo obj = array[0];
			ConstructorInfo[] array2 = ReadConstructors(obj).ToArray();
			if (array2.Length != 1)
			{
				throw new NotSupportedException("DoSaveGame must construct exactly one iterator.");
			}
			Type declaringType = array2[0].DeclaringType;
			IteratorStateMachineAttribute customAttribute = obj.GetCustomAttribute<IteratorStateMachineAttribute>();
			if (declaringType.DeclaringType != manager || !typeof(IEnumerator).IsAssignableFrom(declaringType) || !declaringType.IsDefined(typeof(CompilerGeneratedAttribute), inherit: false) || (customAttribute != null && customAttribute.StateMachineType != declaringType))
			{
				throw new NotSupportedException("DoSaveGame iterator mapping is not a unique nested compiler-generated state machine.");
			}
			InterfaceMapping interfaceMap = declaringType.GetInterfaceMap(typeof(IEnumerator));
			MethodInfo methodInfo = interfaceMap.TargetMethods[Array.FindIndex(interfaceMap.InterfaceMethods, (MethodInfo method) => method.Name == "MoveNext")];
			if (methodInfo.DeclaringType != declaringType || methodInfo.IsStatic || methodInfo.ReturnType != typeof(bool) || methodInfo.GetParameters().Length != 0 || methodInfo.GetMethodBody() == null)
			{
				throw new MissingMethodException("DoSaveGame iterator has no patchable IEnumerator.MoveNext.");
			}
			return methodInfo;
		}

		private static IEnumerable<ConstructorInfo> ReadConstructors(MethodInfo factory)
		{
			byte[] array = factory.GetMethodBody()?.GetILAsByteArray();
			if (array == null)
			{
				throw new NotSupportedException("DoSaveGame has no readable factory body.");
			}
			Stack<bool> stack = new Stack<bool>();
			ConstructorInfo constructorInfo = null;
			bool flag = false;
			int num2;
			for (int i = 0; i < array.Length; i += num2)
			{
				short num = array[i++];
				if (num == 254)
				{
					if (i == array.Length)
					{
						throw new NotSupportedException("Truncated factory opcode.");
					}
					num = (short)(0xFE00 | array[i++]);
				}
				if (!Opcodes.TryGetValue(num, out var value))
				{
					throw new NotSupportedException("Unknown factory opcode.");
				}
				switch (value.OperandType)
				{
				case OperandType.InlineNone:
					num2 = 0;
					break;
				case OperandType.ShortInlineBrTarget:
				case OperandType.ShortInlineI:
				case OperandType.ShortInlineVar:
					num2 = 1;
					break;
				case OperandType.InlineVar:
					num2 = 2;
					break;
				case OperandType.InlineI8:
				case OperandType.InlineR:
					num2 = 8;
					break;
				case OperandType.InlineSwitch:
				{
					if (array.Length - i < 4)
					{
						throw new NotSupportedException("Truncated factory switch.");
					}
					int num3 = BitConverter.ToInt32(array, i);
					if (num3 < 0 || num3 > (array.Length - i - 4) / 4)
					{
						throw new NotSupportedException("Invalid factory switch.");
					}
					num2 = 4 + num3 * 4;
					break;
				}
				default:
					num2 = 4;
					break;
				}
				if (num2 > array.Length - i)
				{
					throw new NotSupportedException("Truncated factory operand.");
				}
				if (flag && value != OpCodes.Nop)
				{
					throw new NotSupportedException("DoSaveGame has instructions after its iterator return.");
				}
				if (value == OpCodes.Newobj)
				{
					if (constructorInfo != null)
					{
						throw new NotSupportedException("DoSaveGame constructs multiple objects.");
					}
					constructorInfo = (ConstructorInfo)factory.Module.ResolveMethod(BitConverter.ToInt32(array, i), factory.DeclaringType.GetGenericArguments(), factory.GetGenericArguments());
					ParameterInfo[] parameters = constructorInfo.GetParameters();
					for (int j = 0; j < parameters.Length; j++)
					{
						_ = parameters[j];
						stack.Pop();
					}
					stack.Push(item: true);
				}
				else if (value == OpCodes.Dup)
				{
					stack.Push(stack.Peek());
				}
				else if (value == OpCodes.Stfld)
				{
					bool num4 = stack.Pop();
					bool flag2 = stack.Pop();
					FieldInfo fieldInfo = factory.Module.ResolveField(BitConverter.ToInt32(array, i), factory.DeclaringType.GetGenericArguments(), factory.GetGenericArguments());
					if (num4 || !flag2 || fieldInfo.DeclaringType != constructorInfo?.DeclaringType)
					{
						throw new NotSupportedException("DoSaveGame writes outside its iterator captures.");
					}
				}
				else if (value == OpCodes.Ret)
				{
					if (stack.Count != 1 || !stack.Pop())
					{
						throw new NotSupportedException("DoSaveGame does not return its constructed iterator.");
					}
					flag = true;
				}
				else if (value == OpCodes.Ldarg || value == OpCodes.Ldarg_S || value == OpCodes.Ldarg_0 || value == OpCodes.Ldarg_1 || value == OpCodes.Ldarg_2 || value == OpCodes.Ldarg_3 || value == OpCodes.Ldc_I4 || value == OpCodes.Ldc_I4_S || value == OpCodes.Ldc_I4_M1 || value == OpCodes.Ldc_I4_0 || value == OpCodes.Ldc_I4_1 || value == OpCodes.Ldc_I4_2 || value == OpCodes.Ldc_I4_3 || value == OpCodes.Ldc_I4_4 || value == OpCodes.Ldc_I4_5 || value == OpCodes.Ldc_I4_6 || value == OpCodes.Ldc_I4_7 || value == OpCodes.Ldc_I4_8 || value == OpCodes.Ldnull)
				{
					stack.Push(item: false);
				}
				else if (value != OpCodes.Nop)
				{
					throw new NotSupportedException("DoSaveGame is no longer a direct iterator factory.");
				}
			}
			if (!flag || constructorInfo == null)
			{
				throw new NotSupportedException("DoSaveGame has no iterator return.");
			}
			yield return constructorInfo;
		}
	}
	internal static class SaveErrorBoundary
	{
		internal static Exception Handle(Exception error, Action cancel)
		{
			if (error == null)
			{
				return null;
			}
			try
			{
				cancel?.Invoke();
			}
			catch
			{
			}
			return error;
		}
	}
	internal static class ShortcutParser
	{
		private static readonly Dictionary<string, KeyCode> Names = ((IEnumerable<string>)Enum.GetNames(typeof(KeyCode))).ToDictionary((Func<string, string>)((string name) => name), (Func<string, KeyCode>)((string name) => (KeyCode)Enum.Parse(typeof(KeyCode), name)), (IEqualityComparer<string>?)StringComparer.OrdinalIgnoreCase);

		internal static bool TryParse(string raw, out KeyboardShortcut shortcut, out string error)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: 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_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			shortcut = KeyboardShortcut.Empty;
			error = null;
			if (string.IsNullOrWhiteSpace(raw))
			{
				return true;
			}
			List<KeyCode> list = new List<KeyCode>();
			string[] array = raw.Split(new char[4] { '+', ',', ';', '|' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				if (string.IsNullOrWhiteSpace(text))
				{
					continue;
				}
				if (TryKey(text, out var key))
				{
					list.Add(key);
					continue;
				}
				string[] array2 = text.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
				foreach (string text2 in array2)
				{
					if (!TryKey(text2, out key))
					{
						error = "Unknown key name '" + text2 + "'. Use names such as O, LeftShift or Mouse4. Separate keys with +.";
						return false;
					}
					list.Add(key);
				}
			}
			if (list.Count == 0)
			{
				error = "No key name was provided. Leave the value blank to disable this shortcut.";
				return false;
			}
			if (list.Contains((KeyCode)0))
			{
				if (list.Count == 1)
				{
					return true;
				}
				error = "None must be used alone to disable a shortcut.";
				return false;
			}
			list = list.Distinct().ToList();
			KeyCode[] array3 = list.Where((KeyCode key2) => !IsModifier(key2)).ToArray();
			KeyCode main = (KeyCode)((array3.Length == 1) ? ((int)array3[0]) : ((int)list[0]));
			shortcut = new KeyboardShortcut(main, list.Where((KeyCode val) => val != main).ToArray());
			return true;
		}

		private static bool TryKey(string text, out KeyCode key)
		{
			return Names.TryGetValue(string.Concat(text.Where((char character) => !char.IsWhiteSpace(character))), out key);
		}

		private static bool IsModifier(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			if (key - 303 <= 10)
			{
				return true;
			}
			return false;
		}
	}
	internal sealed class ShortcutSetting
	{
		private readonly ConfigEntry<string> entry;

		private readonly Action<string> warning;

		private KeyboardShortcut parsed;

		private string lastRaw;

		private bool initialized;

		internal KeyboardShortcut Value
		{
			get
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				return parsed;
			}
			set
			{
				entry.Value = ((KeyboardShortcut)(ref value)).Serialize();
			}
		}

		internal ShortcutSetting(ConfigFile config, string section, string key, KeyboardShortcut defaultValue, string description, Action<string> warning)
		{
			this.warning = warning;
			entry = config.Bind<string>(section, key, ((KeyboardShortcut)(ref defaultValue)).Serialize(), description);
			entry.SettingChanged += delegate
			{
				Refresh();
			};
			Refresh();
		}

		private void Refresh()
		{
			string value = entry.Value;
			if (!initialized || !string.Equals(value, lastRaw, StringComparison.Ordinal))
			{
				initialized = true;
				lastRaw = value;
				if (!ShortcutParser.TryParse(value, out parsed, out var error))
				{
					warning?.Invoke("[" + ((ConfigEntryBase)entry).Definition.Section + "] " + ((ConfigEntryBase)entry).Definition.Key + " is disabled because its shortcut is invalid. The value '" + value + "' was kept unchanged. " + error);
				}
			}
		}
	}
	internal sealed class SpeedIndicator : IDisposable
	{
		private readonly Action<string> warning;

		private Font font;

		private FontStyle fontStyle;

		private Color ink = Color.black;

		private string multiplier = "×";

		private RenderTexture scroll;

		private bool scrollAttempted;

		private bool warned;

		private float nextLookup;

		private GUIStyle boxStyle;

		private GUIStyle textStyle;

		private float styleScale;

		internal SpeedIndicator(Action<string> warning)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			this.warning = warning;
		}

		internal void Update(bool canPrepareScroll)
		{
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (scroll != null && (!Object.op_Implicit((Object)(object)scroll) || !scroll.IsCreated()))
				{
					if (Object.op_Implicit((Object)(object)scroll))
					{
						scroll.Release();
						Object.Destroy((Object)(object)scroll);
					}
					scroll = null;
					scrollAttempted = false;
				}
				if ((Object.op_Implicit((Object)(object)font) && (!canPrepareScroll || (Object.op_Implicit((Object)(object)scroll) && scroll.IsCreated()) || scrollAttempted)) || Time.unscaledTime < nextLookup)
				{
					return;
				}
				nextLookup = Time.unscaledTime + 2f;
				StartMenu val = Object.FindObjectOfType<StartMenu>();
				if (!Object.op_Implicit((Object)(object)val))
				{
					return;
				}
				object? obj = typeof(StartMenu).GetField("chooseIslandUI", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(val);
				object? obj2 = ((obj is GameObject) ? obj : null);
				Transform val2 = ((obj2 != null) ? ((GameObject)obj2).transform : null);
				if (!Object.op_Implicit((Object)(object)val2))
				{
					Warn("Sailwind's menu templates were not found. Using the original display style.");
					return;
				}
				Transform obj3 = val2.Find("button confirm/text");
				TextMesh val3 = ((obj3 != null) ? ((Component)obj3).GetComponent<TextMesh>() : null);
				if (Object.op_Implicit((Object)(object)val3) && Object.op_Implicit((Object)(object)val3.font))
				{
					font = val3.font;
					fontStyle = (FontStyle)0;
					ink = val3.color;
					ink.a = 1f;
					multiplier = (font.HasCharacter('×') ? "×" : "x");
					boxStyle = null;
				}
				if (canPrepareScroll && !Object.op_Implicit((Object)(object)scroll) && !scrollAttempted)
				{
					Transform val4 = val2.Find("bg");
					if (Object.op_Implicit((Object)(object)val4))
					{
						scrollAttempted = true;
						scroll = RenderScroll(val4);
					}
				}
			}
			catch (Exception ex)
			{
				Warn("Native indicator styling unavailable. Using the original display style. " + ex.Message);
			}
		}

		internal void Draw(float speed, float scale, IndicatorBackground background)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: 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_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Expected O, but got Unknown
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (boxStyle == null || styleScale != scale)
				{
					styleScale = scale;
					boxStyle = new GUIStyle(GUI.skin.box)
					{
						font = (Object.op_Implicit((Object)(object)font) ? font : GUI.skin.box.font),
						fontStyle = (Object.op_Implicit((Object)(object)font) ? fontStyle : GUI.skin.box.fontStyle),
						fontSize = Mathf.RoundToInt(16f * scale),
						alignment = (TextAnchor)4,
						padding = Scale(GUI.skin.box.padding, scale)
					};
					textStyle = new GUIStyle(GUI.skin.label)
					{
						font = boxStyle.font,
						fontStyle = boxStyle.fontStyle,
						fontSize = boxStyle.fontSize,
						alignment = boxStyle.alignment,
						padding = boxStyle.padding
					};
				}
				Rect val = default(Rect);
				((Rect)(ref val))..ctor((float)(Screen.width - 16) - 60f * scale, 16f, 60f * scale, 28f * scale);
				string text = speed + " " + multiplier;
				switch (background)
				{
				case IndicatorBackground.None:
					textStyle.normal.textColor = boxStyle.normal.textColor;
					GUI.Label(val, text, textStyle);
					return;
				case IndicatorBackground.Scroll:
					if (Object.op_Implicit((Object)(object)scroll) && scroll.IsCreated())
					{
						GUI.DrawTexture(val, (Texture)(object)scroll, (ScaleMode)0, true);
						textStyle.normal.textColor = ink;
						GUI.Label(val, text, textStyle);
						return;
					}
					break;
				}
				GUI.Box(val, text, boxStyle);
			}
			catch (Exception ex)
			{
				Warn("Speed indicator could not be drawn. " + ex.Message);
			}
		}

		private static RectOffset Scale(RectOffset source, float scale)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			return new RectOffset(Mathf.RoundToInt((float)source.left * scale), Mathf.RoundToInt((float)source.right * scale), Mathf.RoundToInt((float)source.top * scale), Mathf.RoundToInt((float)source.bottom * scale));
		}

		private static RenderTexture RenderScroll(Transform donor)
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Expected O, but got Unknown
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Expected O, but got Unknown
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Expected O, but got Unknown
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b1: Expected O, but got Unknown
			//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f4: Expected O, but got Unknown
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_0329: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			MeshFilter component = ((Component)donor).GetComponent<MeshFilter>();
			Mesh val = ((component != null) ? component.sharedMesh : null);
			MeshRenderer component2 = ((Component)donor).GetComponent<MeshRenderer>();
			Material val2 = ((component2 != null) ? ((Renderer)component2).sharedMaterial : null);
			if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val2) || !Object.op_Implicit((Object)(object)val2.mainTexture) || !Object.op_Implicit((Object)(object)val2.shader) || ((Object)val2.shader).name != "Legacy Shaders/VertexLit" || !val2.shader.isSupported || !val2.HasProperty("_Emission") || !val2.HasProperty("_SpecColor"))
			{
				throw new InvalidOperationException("Native scroll mesh, texture or expected shader was not available.");
			}
			Material val3 = null;
			RenderTexture val4 = null;
			GameObject val5 = null;
			CommandBuffer val6 = null;
			Texture2D val7 = null;
			RenderTexture active = RenderTexture.active;
			try
			{
				Quaternion localRotation = donor.localRotation;
				Bounds bounds = val.bounds;
				Bounds val8 = default(Bounds);
				((Bounds)(ref val8))..ctor(localRotation * ((Bounds)(ref bounds)).center, Vector3.zero);
				Bounds bounds2 = val.bounds;
				for (int i = 0; i < 8; i++)
				{
					Vector3 val9 = ((Bounds)(ref bounds2)).center + Vector3.Scale(((Bounds)(ref bounds2)).extents, new Vector3((float)(((i & 1) != 0) ? 1 : (-1)), (float)(((i & 2) != 0) ? 1 : (-1)), (float)(((i & 4) != 0) ? 1 : (-1))));
					((Bounds)(ref val8)).Encapsulate(localRotation * val9);
				}
				val3 = new Material(val2)
				{
					color = Color.black,
					shaderKeywords = new string[0]
				};
				val3.SetColor("_SpecColor", Color.black);
				val3.SetColor("_Emission", val2.color * 0.5f);
				val4 = new RenderTexture(600, 280, 24, (RenderTextureFormat)0)
				{
					name = "Fast Forward scroll indicator",
					hideFlags = (HideFlags)61,
					filterMode = (FilterMode)1,
					wrapMode = (TextureWrapMode)1
				};
				if (!val4.Create())
				{
					throw new InvalidOperationException("Indicator render texture could not be created.");
				}
				float orthographicSize = Mathf.Max(((Bounds)(ref val8)).extents.y, ((Bounds)(ref val8)).extents.x * 28f / 60f) * 1.01f;
				val5 = new GameObject("Fast Forward scroll cache")
				{
					hideFlags = (HideFlags)61
				};
				Camera obj = val5.AddComponent<Camera>();
				((Behaviour)obj).enabled = false;
				obj.cullingMask = 0;
				obj.clearFlags = (CameraClearFlags)2;
				obj.backgroundColor = Color.clear;
				obj.orthographic = true;
				obj.orthographicSize = orthographicSize;
				obj.aspect = 2.142857f;
				obj.nearClipPlane = 0.01f;
				obj.farClipPlane = 20f;
				((Component)obj).transform.position = new Vector3(0f, 0f, -10f);
				obj.targetTexture = val4;
				obj.allowHDR = false;
				obj.allowMSAA = false;
				val6 = new CommandBuffer
				{
					name = "Fast Forward native scroll"
				};
				val6.DrawMesh(val, Matrix4x4.TRS(-((Bounds)(ref val8)).center, localRotation, Vector3.one), val3, 0, 0);
				obj.AddCommandBuffer((CameraEvent)20, val6);
				obj.Render();
				RenderTexture.active = val4;
				val7 = new Texture2D(1, 1, (TextureFormat)4, false);
				val7.ReadPixels(new Rect((float)(((Texture)val4).width / 2), (float)(((Texture)val4).height / 2), 1f, 1f), 0, 0);
				val7.Apply();
				if (val7.GetPixel(0, 0).a < 0.01f)
				{
					throw new InvalidOperationException("Native scroll render was empty.");
				}
				return val4;
			}
			catch
			{
				if (Object.op_Implicit((Object)(object)val4))
				{
					val4.Release();
					Object.Destroy((Object)(object)val4);
				}
				throw;
			}
			finally
			{
				RenderTexture.active = active;
				if (Object.op_Implicit((Object)(object)val5))
				{
					Camera component3 = val5.GetComponent<Camera>();
					component3.targetTexture = null;
					component3.RemoveAllCommandBuffers();
					Object.Destroy((Object)(object)val5);
				}
				if (val6 != null)
				{
					val6.Release();
				}
				if (Object.op_Implicit((Object)(object)val7))
				{
					Object.Destroy((Object)(object)val7);
				}
				if (Object.op_Implicit((Object)(object)val3))
				{
					Object.Destroy((Object)(object)val3);
				}
			}
		}

		private void Warn(string message)
		{
			if (!warned)
			{
				warned = true;
				warning(message);
			}
		}

		internal void Invalidate()
		{
			font = null;
			boxStyle = null;
			scrollAttempted = false;
			nextLookup = 0f;
		}

		public void Dispose()
		{
			if (Object.op_Implicit((Object)(object)scroll))
			{
				scroll.Release();
				Object.Destroy((Object)(object)scroll);
			}
			scroll = null;
			Invalidate();
		}
	}
	internal sealed class SpeedOwnership
	{
		internal float SelectedSpeed { get; private set; } = 1f;

		internal bool Active => SelectedSpeed > 1f;

		internal bool TrySelect(float current, int requested)
		{
			if ((requested != 2 && requested != 4 && requested != 8 && requested != 16 && requested != 32) || current != SelectedSpeed)
			{
				return false;
			}
			SelectedSpeed = requested;
			return true;
		}

		internal bool TryCycle(float current, int maximum)
		{
			if ((maximum != 2 && maximum != 4 && maximum != 8 && maximum != 16 && maximum != 32) || current != SelectedSpeed)
			{
				return false;
			}
			SelectedSpeed = ((SelectedSpeed >= (float)maximum) ? 1f : (SelectedSpeed * 2f));
			return true;
		}

		internal bool Release(float current, bool externallyOwned = false)
		{
			bool result = Active && !externallyOwned && current == SelectedSpeed;
			SelectedSpeed = 1f;
			return result;
		}

		internal bool TryLimit(float current, int maximum)
		{
			if ((maximum != 1 && maximum != 2 && maximum != 4 && maximum != 8 && maximum != 16 && maximum != 32) || !Active || current != SelectedSpeed || SelectedSpeed <= (float)maximum)
			{
				return false;
			}
			SelectedSpeed = maximum;
			return true;
		}
	}
}