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;
}
}
}