Decompiled source of SceneSaverBL v2.0.0
Mods/SceneSaverBL.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using BoneLib; using BoneLib.BoneMenu; using BoneLib.BoneMenu.UI; using BoneLib.Notifications; using HarmonyLib; using Il2CppCysharp.Threading.Tasks; using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppOculus.Platform; using Il2CppOculus.Platform.Models; using Il2CppSLZ.Bonelab; using Il2CppSLZ.MLAgents; using Il2CppSLZ.Marrow; using Il2CppSLZ.Marrow.AI; using Il2CppSLZ.Marrow.Data; using Il2CppSLZ.Marrow.Interaction; using Il2CppSLZ.Marrow.Pool; using Il2CppSLZ.Marrow.PuppetMasta; using Il2CppSLZ.Marrow.SceneStreaming; using Il2CppSLZ.Marrow.Warehouse; using Il2CppSLZ.VFX; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppSystem.Threading; using Jevil; using Jevil.ModStats; using Jevil.Patching; using Jevil.PostProcessing; using Jevil.Prefs; using Jevil.Spawning; using Jevil.Tweening; using Jevil.Waiting; using LabFusion.Network; using LabFusion.RPC; using MelonLoader; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using SceneSaverBL; using SceneSaverBL.Exceptions; using SceneSaverBL.Interfaces; using SceneSaverBL.Versions; using SceneSaverBL.Versions.Version4; using SceneSaverBL.Versions.Version5; using SceneSaverBL.Versions.Version6; using SceneSaverRepo.Data; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("SceneSaverBL")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany(null)] [assembly: AssemblyProduct("SceneSaverBL")] [assembly: AssemblyCopyright("Created by extraes")] [assembly: AssemblyTrademark(null)] [assembly: ComVisible(false)] [assembly: AssemblyFileVersion("2.0.0")] [assembly: NeutralResourcesLanguage("en")] [assembly: MelonInfo(typeof(global::SceneSaverBL.SceneSaverBL), "SceneSaverBL", "2.0.0", "extraes", "https://bonelab.thunderstore.io/package/extraes/SceneSaverBL/")] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: Ungovernable(/*Could not decode attribute arguments.*/)] [assembly: MelonOptionalDependencies(new string[] { "LabFusion" })] [assembly: VerifyLoaderVersion(0, 7, 1, true)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsUnmanagedAttribute : Attribute { } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NativeIntegerAttribute : Attribute { public readonly bool[] TransformFlags; public NativeIntegerAttribute() { TransformFlags = new bool[1] { true }; } public NativeIntegerAttribute(bool[] P_0) { TransformFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 SceneSaverRepo.Data { public class SceneSaverEntryCollection { public TimeSpan TimeSinceLastUpdate { get; set; } public SceneSaverSaveEntry[] Saves { get; set; } } public class SceneSaverRepoInfo { public string name { get; set; } public Version version { get; set; } public string timeZone { get; set; } public int totalDownloads { get; set; } public int totalDownloadsWeek { get; set; } public int totalSaves { get; set; } public TimeSpan timeSinceLastUpdate { get; set; } } public class SceneSaverSaveEntry { public const string FILENAME = "ssblmeta"; public static readonly SceneSaverSaveEntry err = new SceneSaverSaveEntry { name = "error", hash = "0000000000000000", tag = "0000" }; public string name { get; set; } = ""; public string hash { get; set; } = ""; public string tag { get; set; } = ""; public string owner { get; set; } = ""; public int version { get; set; } public int downloadCount { get; set; } public int downloadCountWeek { get; set; } public DateTime? expire { get; set; } public override bool Equals(object obj) { if (obj is SceneSaverSaveEntry sceneSaverSaveEntry) { return sceneSaverSaveEntry == this; } return false; } public override int GetHashCode() { return (((((-1527566069 * -1521134295 + EqualityComparer<string>.Default.GetHashCode(name)) * -1521134295 + EqualityComparer<string>.Default.GetHashCode(hash)) * -1521134295 + EqualityComparer<string>.Default.GetHashCode(tag)) * -1521134295 + version.GetHashCode()) * -1521134295 + downloadCount.GetHashCode()) * -1521134295 + expire.GetHashCode(); } public TimeSpan? TimeUntilExpired() { if (!expire.HasValue) { return null; } return expire - DateTime.Now; } public static bool operator ==(SceneSaverSaveEntry lhs, SceneSaverSaveEntry rhs) { return lhs.hash == rhs.hash; } public static bool operator !=(SceneSaverSaveEntry lhs, SceneSaverSaveEntry rhs) { return !(lhs == rhs); } } } namespace SceneSaverBL { internal static class Assets { internal static class Prefabs { internal static readonly BundledAsset<GameObject> SelectionWire = new BundledAsset<GameObject>("Assets/SceneSaver/SelectionParticlePrefab.prefab", true); internal static readonly BundledAsset<GameObject> SelectionWireMusic = new BundledAsset<GameObject>("Assets/SceneSaver/SelectionWireMusic.prefab", true); internal static readonly BundledAsset<GameObject> SavingObjectBounds = new BundledAsset<GameObject>("Assets/SceneSaver/SavingBounds.prefab", true); internal static readonly BundledAsset<GameObject> SavingPhotographer = new BundledAsset<GameObject>("Assets/SceneSaver/Photographer.prefab", true); internal static readonly BundledAsset<GameObject> IdlePhotographer = new BundledAsset<GameObject>("Assets/SceneSaver/PhotographerIdle.prefab", true); internal static readonly BundledAsset<GameObject> CameraFlash = new BundledAsset<GameObject>("Assets/SceneSaver/CameraParticles.prefab", true); internal static readonly BundledAsset<GameObject> Polaroid = new BundledAsset<GameObject>("Assets/SceneSaver/PreviewPolaroid.prefab", true); internal static readonly BundledAsset<GameObject> FullsavePreviewBounds = new BundledAsset<GameObject>("Assets/SceneSaver/FullsaveBounds.prefab", true); internal static readonly BundledAsset<GameObject> ControllerTutorial = new BundledAsset<GameObject>("Assets/SceneSaver/Tutorial/ControllerTutorial.prefab", true); internal static readonly BundledAsset<GameObject> Laser = new BundledAsset<GameObject>("Assets/SceneSaver/Laser.prefab", true); internal static readonly BundledAsset<GameObject> GroundCircle = new BundledAsset<GameObject>("Assets/SceneSaver/GroundCircle.prefab", true); internal static readonly BundledAsset<GameObject> DupeTutorial = new BundledAsset<GameObject>("Assets/SceneSaver/Tutorial/DupeTutorial.prefab", true); } internal static class Materials { internal static readonly BundledAsset<Material> SavingObjectCompletedMaterial = new BundledAsset<Material>("Assets/SceneSaver/BoundsLinesSAVED.mat", true); } private static AssetBundle bundle; internal static async Task Init() { string text = "SceneSaverBL.Resources.Resources" + (Utilities.IsPlatformQuest() ? "Quest.bundle" : ".bundle"); byte[] bundleBytes = null; Extensions.UseEmbeddedResource(((MelonBase)SceneSaverBL.instance).MelonAssembly.Assembly, text, (Action<byte[]>)delegate(byte[] bytes) { bundleBytes = bytes; }); bundle = await AsyncExtensions.ToTask(AssetBundle.LoadFromMemoryAsync(Il2CppStructArray<byte>.op_Implicit(bundleBytes)), (PlayerLoopTiming)8); Extensions.Persist((Object)(object)bundle, true); await Prefabs.SelectionWire.BindAsync(bundle); await Prefabs.SelectionWireMusic.BindAsync(bundle); await Prefabs.SavingObjectBounds.BindAsync(bundle); await Prefabs.SavingPhotographer.BindAsync(bundle); await Prefabs.IdlePhotographer.BindAsync(bundle); await Prefabs.CameraFlash.BindAsync(bundle); await Prefabs.Polaroid.BindAsync(bundle); await Prefabs.FullsavePreviewBounds.BindAsync(bundle); await Prefabs.ControllerTutorial.BindAsync(bundle); await Prefabs.Laser.BindAsync(bundle); await Prefabs.GroundCircle.BindAsync(bundle); await Prefabs.DupeTutorial.BindAsync(bundle); await Materials.SavingObjectCompletedMaterial.BindAsync(bundle); } } public static class BonemenuStringInput { public const string ALL_NUMBERS_ALLOWED = "*"; public const string ALL_LETTERS_ALLOWED = "*"; public const string DEFAULT_SYMBOL_SELECTION = " !~@#$%^&*()_+-=/<>,.;':\"[]{}?"; private const string CATEGORY_NAME_BASE = "Input String: "; private static readonly string CharsNotForPath = new string(Path.GetInvalidFileNameChars()); private static readonly string SymbolsForPath = new string(" !~@#$%^&*()_+-=/<>,.;':\"[]{}?".Where((char c) => !CharsNotForPath.Contains(c)).ToArray()); private static Page basePage; private static BoolElement capsLockElement; private static Page letters; private static Page numbers; private static Page symbols; private static FunctionElement backspaceElement; private static FunctionElement confirmElement; private static int lastLetterSelHash; private static int lastNumSelHash; private static int lastSymbolSelHash; private static UniTaskCompletionSource<string>? uniTaskCompletion; private static readonly StringBuilder stringCollector = new StringBuilder(); private static bool capsLockActive; internal static void Init() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: 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_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) basePage = Page.Root.CreatePage("Input String: ", Color.white, 0, true); Element val = basePage.Parent.Elements.First((Element e) => e.ElementName == basePage.Name); basePage.Parent.Remove(val); basePage.RemoveAll(); capsLockElement = basePage.CreateBool("caps", Color.gray, false, (Action<bool>)SetCapsLock); letters = basePage.CreatePage("Letters", Color.white, 0, true); numbers = basePage.CreatePage("Numbers", Color.white, 0, true); symbols = basePage.CreatePage("Symbols/other", Color.white, 0, true); backspaceElement = basePage.CreateFunction("Backspace", Color.red, (Action)Backspace); confirmElement = basePage.CreateFunction("Confirm", Color.green, (Action)ConfirmSelection); } internal static Task<string> GetFileNameInput() { return GetStringInput("*", "*", SymbolsForPath); } internal static async Task<string> GetStringInput(string lettersSelection = "*", string numbersSelection = "*", string symbolSelection = " !~@#$%^&*()_+-=/<>,.;':\"[]{}?") { basePage.RemoveAll(); await SetSubpanelButtons(lettersSelection, numbersSelection, symbolSelection); RefreshBaseCategory(); uniTaskCompletion = new UniTaskCompletionSource<string>(); Page currentPage = Menu.CurrentPage; Menu.OpenPage(basePage); string result = await uniTaskCompletion.Task; uniTaskCompletion = null; Menu.OpenPage(currentPage); return result; } private static async Task SetSubpanelButtons(string lettersSelection, string numbersSelection, string symbolSelection) { SetCapsLock(capsLock: false); int hashCode = lettersSelection.GetHashCode(); string text; if (hashCode != lastLetterSelHash) { lastLetterSelHash = hashCode; letters.RemoveAll(); text = ((lettersSelection == "*") ? "abcdefghijklmnopqrstuvwxyz" : lettersSelection); for (int i = 0; i < text.Length; i++) { char c3 = text[i]; letters.CreateFunction(c3.ToString(), Color.white, (Action)delegate { InputCharacter(capsLockActive ? char.ToUpper(c3) : c3); }); } await UniTask.Yield(); } int hashCode2 = numbersSelection.GetHashCode(); if (hashCode2 != lastNumSelHash) { lastNumSelHash = hashCode2; numbers.RemoveAll(); text = ((numbersSelection == "*") ? "0123456789" : numbersSelection); for (int i = 0; i < text.Length; i++) { char c2 = text[i]; numbers.CreateFunction(c2.ToString(), Color.white, (Action)delegate { InputCharacter(c2); }); } await UniTask.Yield(); } int hashCode3 = symbolSelection.GetHashCode(); if (hashCode3 == lastSymbolSelHash) { return; } lastSymbolSelHash = hashCode3; symbols.RemoveAll(); text = symbolSelection; for (int i = 0; i < text.Length; i++) { char c = text[i]; string text2 = c switch { '\n' => "Newline", '\t' => "Tab", ' ' => "Space", '_' => "Underscore", '"' => "Double quote/Quotation mark", '\'' => "Single quote/apostraphe", _ => c.ToString(), }; symbols.CreateFunction(text2, Color.white, (Action)delegate { InputCharacter(c); }); } await UniTask.Yield(); } private static void RefreshBaseCategory() { if (letters.Elements.Count != 0) { basePage.Add((Element)(object)capsLockElement); basePage.CreatePageLink(letters); } if (numbers.Elements.Count != 0) { basePage.CreatePageLink(numbers); } if (symbols.Elements.Count != 0) { basePage.CreatePageLink(symbols); } basePage.Add((Element)(object)backspaceElement); basePage.Add((Element)(object)confirmElement); } private static void InputCharacter(char c) { stringCollector.Append(c); basePage.Name = "Input String: " + stringCollector.ToString(); } private static void SetCapsLock(bool capsLock) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) capsLockActive = capsLock; if (capsLock) { ((Element)capsLockElement).ElementColor = Color.white; ((Element)capsLockElement).ElementName = "CAPS"; } else { ((Element)capsLockElement).ElementColor = Color.gray; ((Element)capsLockElement).ElementName = "caps"; } } private static void Backspace() { stringCollector.Remove(stringCollector.Length - 1, 1); basePage.Name = "Input String: " + stringCollector.ToString(); } private static void ConfirmSelection() { uniTaskCompletion.TrySetResult(stringCollector.ToString()); } } internal static class CameraFella { private static GameObject cameraFella; private static CancellationTokenSource currentOperation; public static void PlayScreenshot() { currentOperation?.Cancel(); currentOperation = new CancellationTokenSource(); AsyncUtilities.WrapNoThrow<CancellationToken>((Func<CancellationToken, Task>)SavingStartedImpl, currentOperation.Token); } public static void MenuOpened() { currentOperation?.Cancel(); currentOperation = new CancellationTokenSource(); AsyncUtilities.WrapNoThrow<CancellationToken>((Func<CancellationToken, Task>)MenuOpenedImpl, currentOperation.Token); } public static void MenuClosed() { currentOperation?.Cancel(); currentOperation = new CancellationTokenSource(); AsyncUtilities.WrapNoThrow<CancellationToken>((Func<CancellationToken, Task>)MenuClosedImpl, currentOperation.Token); } public static void UpdatePosition() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (SelectionZone.Active && !((Object)(object)cameraFella == (Object)null)) { var (val, val2) = SelectionZone.Instance.GetCameraPosAndDir(); cameraFella.transform.SetPositionAndRotation(val, Quaternion.LookRotation(-val2)); } } private static async Task SavingStartedImpl(CancellationToken token) { if ((Object)(object)cameraFella == (Object)null) { await MenuOpenedImpl(token); } cameraFella.GetComponent<Animation>().Play(); } [MemberNotNull("cameraFella")] private static async Task MenuOpenedImpl(CancellationToken token) { GameObject val = await Assets.Prefabs.SavingPhotographer.GetAsync(); Object.Destroy((Object)(object)cameraFella); if (!token.IsCancellationRequested) { cameraFella = Object.Instantiate<GameObject>(val); cameraFella.SetActive(SelectionZone.Active); } } private static async Task MenuClosedImpl(CancellationToken token) { GameObject val = await Assets.Prefabs.IdlePhotographer.GetAsync(); Object.Destroy((Object)(object)cameraFella); if (!token.IsCancellationRequested) { cameraFella = Object.Instantiate<GameObject>(val); cameraFella.SetActive(SelectionZone.Active && Prefs.showPreviewLocation); } } } internal static class ConfigVars { internal static int previewSize = (Utilities.IsPlatformQuest() ? 64 : 256); internal static int timeSliceMs = 3; internal static bool fullsaveOverTime = true; } [RegisterTypeInIl2Cpp] public class ControllerTutorial : MonoBehaviour { private const string PLAYER_PREFS_KEY = "SSBL_Shown_Tutorial"; private static bool tutorialCurrentlyActive; private double startTime; private Transform handL; private Transform handR; public ControllerTutorial(nint ptr) : base((IntPtr)ptr) { } private void Start() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Invalid comparison between Unknown and I4 tutorialCurrentlyActive = !((Enum)((Object)this).hideFlags).HasFlag((Enum)(object)(HideFlags)32); startTime = Time.timeAsDouble; CallDelayed.CallAction((Action)Disappearize, 40f, false); handL = ((Component)Player.LeftHand).transform; handR = ((Component)Player.RightHand).transform; AutoAgroBoid component = ((Component)this).GetComponent<AutoAgroBoid>(); for (int i = 0; i < ((Il2CppArrayBase<GameObject>)(object)component.betweenAgroWaypoints).Length; i++) { ((Il2CppArrayBase<GameObject>)(object)component.betweenAgroWaypoints)[i].SetActive(false); } int num; int num2; if ((int)((ControllerRig)Player.ControllerRig).leftController.Type == 1) { num = component.bumperUpdateRate; num2 = component.bumperUpdateRate * 2; } else { num = 0; num2 = component.bumperUpdateRate; } for (int j = num; j < num2; j++) { ((Il2CppArrayBase<GameObject>)(object)component.betweenAgroWaypoints)[j].SetActive(true); } } private void Update() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: 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_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: 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_00db: Unknown result type (might be due to invalid IL or missing references) Transform transform = ((Component)Player.Head).transform; Vector3 val = Vector3.Lerp(handL.position, handR.position, 0.5f); Vector3 val2 = transform.forward + new Vector3(0f, 0.25f, 0f); Vector3 normalized = ((Vector3)(ref val2)).normalized; normalized.y /= 2f; Vector3 val3 = transform.position + 1.5f * normalized; Vector3 val4 = transform.position - ((Component)this).transform.position; val4.y = 0f; Quaternion val5 = Quaternion.LookRotation(val4); Vector3 val6 = Vector3.Lerp(val3, val, 0.25f); Vector3 val7 = Vector3.Lerp(((Component)this).transform.position, val6, Time.deltaTime * 5f); ((Component)this).transform.SetPositionAndRotation(val7, val5); } private void OnDestroy() { tutorialCurrentlyActive = false; } private void Disappearize() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) TweenTweenExtensions.RunOnFinish<ScaleTween>(TweenTweenExtensions.UseCustomInterpolator<ScaleTween>(TweenExtensions.TweenLocalScale(((Component)this).transform, Vector3.zero, 5f), (Func<float, float>)((float time) => Mathf.Pow(time, 4f))), (Action)delegate { Object.Destroy((Object)(object)((Component)this).gameObject); }); } public static void ShowIfUnseen() { if (!PlayerPrefs.HasKey("SSBL_Shown_Tutorial")) { Show(); } } public static void Show() { PlayerPrefs.TrySetInt("SSBL_Shown_Tutorial", 1); AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow((Func<Task>)ShowImpl), (Action<Exception>)SceneSaverBL.ErrIfNotNull); } private static async Task ShowImpl() { GameObject val = await Assets.Prefabs.ControllerTutorial.GetAsync(); if (!tutorialCurrentlyActive) { GameObject obj = Object.Instantiate<GameObject>(val); obj.AddComponent<ControllerTutorial>(); obj.SetActive(true); } } } internal static class DeserializationBroker { public static async Task<ISaveFile> CreateSaveFromFile(string path) { _ = 1; try { using FileStream fs = File.OpenRead(path); ISaveFile isf = CreateFileVersion(fs, await SaveUtils.CheckFormatIdentifier(fs)); await isf.SetFilePath(path); return isf; } catch (Exception ex) { SceneSaverBL.Warn("Failed to read save metadata from disk! " + ex); return new FailedSaveFile(ex); } } private static ISaveFile CreateFileVersion(FileStream fs, byte version) { return version switch { 4 => new SaveFile(), 5 => new SaveFile5(), 6 => new SaveFile6(), _ => throw new InvalidVersionException(version), }; } } [RegisterTypeInIl2Cpp] public class DupeTutorial : MonoBehaviour { private const string PLAYER_PREFS_KEY = "SSBL_Shown_DupeTutorial"; private static bool tutorialCurrentlyActive; private double startTime; private Transform handL; private Transform handR; public DupeTutorial(nint ptr) : base((IntPtr)ptr) { } private void Start() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) tutorialCurrentlyActive = !((Enum)((Object)this).hideFlags).HasFlag((Enum)(object)(HideFlags)32); ((Component)this).transform.position = Player.Head.position + Player.Head.forward * 50f; startTime = Time.timeAsDouble; CallDelayed.CallAction((Action)delegate { Object.Destroy((Object)(object)((Component)this).gameObject); }, 21f, false); handL = ((Component)Player.LeftHand).transform; handR = ((Component)Player.RightHand).transform; } private void Update() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: 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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) Transform head = Player.Head; Vector3 val = Vector3.Lerp(handL.position, handR.position, 0.5f); Vector3 val2 = head.forward + new Vector3(0f, 0.25f, 0f); Vector3 normalized = ((Vector3)(ref val2)).normalized; normalized.y /= 2f; Vector3 val3 = head.position + 1.5f * normalized; Vector3 val4 = head.position - ((Component)this).transform.position; val4.y = 0f; Quaternion val5 = Quaternion.LookRotation(val4); Vector3 val6 = Vector3.Lerp(val3, val, 0.25f); Vector3 val7 = Vector3.Lerp(((Component)this).transform.position, val6, Time.deltaTime * 5f); ((Component)this).transform.SetPositionAndRotation(val7, val5); } private void OnDestroy() { tutorialCurrentlyActive = false; } public static void ShowIfUnseen() { if (!PlayerPrefs.HasKey("SSBL_Shown_DupeTutorial")) { Show(); } } public static void Show() { PlayerPrefs.TrySetInt("SSBL_Shown_DupeTutorial", 1); AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow((Func<Task>)ShowImpl), (Action<Exception>)SceneSaverBL.ErrIfNotNull); } private static async Task ShowImpl() { GameObject val = await Assets.Prefabs.DupeTutorial.GetAsync(); if (!tutorialCurrentlyActive) { GameObject obj = Object.Instantiate<GameObject>(val); obj.AddComponent<DupeTutorial>(); obj.SetActive(true); } } } internal static class FingerOffset { private enum BlendModes { Alpha, PreMultiply, Additive, Multiply } private const float ANT_SIZE_DEFAULT = 1f; private static readonly ShaderProperty<float> antSize = ShaderProperty<float>.op_Implicit("_AntSize"); private static readonly ShaderProperty<int> blendMode = ShaderProperty<int>.op_Implicit("_Blend"); private static readonly Vector3[] directions = (Vector3[])(object)new Vector3[8] { new Vector3(-1f, 0f, -1f), new Vector3(-1f, 0f, 0f), new Vector3(-1f, 0f, 1f), new Vector3(0f, 0f, -1f), new Vector3(0f, 0f, 1f), new Vector3(1f, 0f, -1f), new Vector3(1f, 0f, 0f), new Vector3(1f, 0f, 1f) }; private static readonly float[] directionDistances = new float[directions.Length]; private static readonly Vector3[] scratchDirections = (Vector3[])(object)new Vector3[directions.Length]; private static readonly float[] scratchDistances = new float[directions.Length]; private static readonly float[] distanceHitPoints = new float[directions.Length]; private static readonly byte[] directionComplements = directions.Select((Vector3 dir) => (byte)Array.IndexOf(directions, -dir)).ToArray(); private static float timerFollowSpeed = 50f; private static Transform timer; private static Animation timerAnim; private static Renderer timerRend; private static Il2CppStructArray<Vector3> timerDirScratch = new Il2CppStructArray<Vector3>(1L); private static Il2CppStructArray<Color> timerColScratch = new Il2CppStructArray<Color>(1L); private static GameObject line; private static Transform lineTransform; private static Transform indexFinger; private static Material dupeMat; private static BlendModes? currBlendMode; public static Vector2 wsSaveExtents = Vector2.one; private static bool forceFadeoutTimer = false; private static Transform Timer { get { return timer; } set { timer = value; timerAnim = ((Component)value).GetComponent<Animation>(); timerRend = ((Component)value.GetChild(0)).GetComponent<Renderer>(); } } private static Animation TimerAnim { get { if ((Object)(object)timerAnim == (Object)null && (Object)(object)Timer != (Object)null) { timerAnim = ((Component)Timer).GetComponent<Animation>(); } return timerAnim; } } private static Renderer TimerRend { get { if ((Object)(object)timerRend == (Object)null && (Object)(object)Timer != (Object)null) { timerRend = ((Component)Timer.GetChild(0)).GetComponent<Renderer>(); } return timerRend; } } private static Il2CppStructArray<Vector3> TimerDirScratch { get { if (timerDirScratch == null || ((Il2CppObjectBase)timerDirScratch).WasCollected) { timerDirScratch = new Il2CppStructArray<Vector3>(1L); } return timerDirScratch; } } private static Il2CppStructArray<Color> TimerColScratch { get { if (timerColScratch == null || ((Il2CppObjectBase)timerColScratch).WasCollected) { timerColScratch = new Il2CppStructArray<Color>(1L); } return timerColScratch; } } private static GameObject Line { get { return line; } set { line = value; lineTransform = value.transform; } } private static Transform LineTransform { get { if ((Object)(object)lineTransform == (Object)null && (Object)(object)Line != (Object)null) { lineTransform = Line.transform; } return lineTransform; } } private static Transform IndexFinger { get { if ((Object)(object)indexFinger == (Object)null) { indexFinger = GetIndex(); } return indexFinger; } } private static Material DupeMat { get { if ((Object)(object)dupeMat == (Object)null && (Object)(object)Line != (Object)null) { dupeMat = ((Renderer)((Component)LineTransform.GetChild(0)).GetComponent<MeshRenderer>()).sharedMaterial; } return dupeMat; } } private static void CalculateDirectionDistances() { float x = wsSaveExtents.x; float y = wsSaveExtents.y; float num = Mathf.Sqrt(x * x + y * y); directionDistances[0] = num; directionDistances[1] = x; directionDistances[2] = num; directionDistances[3] = y; directionDistances[4] = y; directionDistances[5] = num; directionDistances[6] = x; directionDistances[7] = num; } public static void Init() { Hooking.OnLevelLoading += delegate { currBlendMode = null; }; } public static void Begin() { forceFadeoutTimer = false; if ((Object)(object)Line != (Object)null) { Line.SetActive(true); return; } AsyncExtensions.RunOnFinish<GameObject>(Assets.Prefabs.Laser.GetAsync(), (Action<GameObject>)delegate(GameObject go) { Line = Object.Instantiate<GameObject>(go); Line.SetActive(true); }); AsyncExtensions.RunOnFinish<GameObject>(Assets.Prefabs.GroundCircle.GetAsync(), (Action<GameObject>)delegate(GameObject go) { GameObject obj = Object.Instantiate<GameObject>(go); Timer = obj.transform; obj.SetActive(false); TimerAnim.Play(); int stateCount = TimerAnim.GetStateCount(); for (int i = 0; i < stateCount; i++) { TimerAnim.GetStateAtIndex(i).speed = 0f; TimerAnim.GetStateAtIndex(i).time = 0f; } TimerAnim.Stop(); }); } public static async Task MenuGetPosition() { SceneSaverBL.desiredDupePos = null; Page currPage = Menu.CurrentPage; PopUpMenuView popup = Player.UIRig.popUpMenu; UIControllerInput input = popup._lastCursor; popup.Deactivate(); Begin(); while (!SceneSaverBL.desiredDupePos.HasValue) { await UniTask.Yield(); } End(); popup.Activate(((Rig)Player.ControllerRig).m_head, ((Rig)Player.PhysicsRig).m_chest, input, input.isLeft ? Player.LeftController : Player.RightController); await UniTask.Yield(); popup.BypassToPreferences(); PreferencesPanelView preferencesPanelView = popup.preferencesPanelView; int num = ((Il2CppArrayBase<GameObject>)(object)preferencesPanelView.pages).Length - 1; for (int i = 0; i < ((Il2CppArrayBase<GameObject>)(object)preferencesPanelView.pages).Length; i++) { if (Instances<GUIMenu>.Has(((Il2CppArrayBase<GameObject>)(object)preferencesPanelView.pages)[i])) { num = i; break; } } Player.UIRig.popUpMenu.preferencesPanelView.PAGESELECT(num); await UniTask.Yield(); Menu.OpenPage(currPage); await UniTask.Yield(); } public static void End() { if ((Object)(object)Line != (Object)null) { Line.SetActive(false); } if ((Object)(object)Timer != (Object)null) { forceFadeoutTimer = true; } } public static void Fadeout() { if (!((Object)(object)TimerAnim == (Object)null)) { int stateCount = TimerAnim.GetStateCount(); for (int i = 0; i < stateCount; i++) { TimerAnim.GetStateAtIndex(i).speed = -1f; } } } internal static void OnUpdate() { //IL_002e: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Line == (Object)null || !Line.active || (Object)(object)Timer == (Object)null) { return; } Transform val = IndexFinger; Vector3 val2 = val.position - val.parent.parent.position; Vector3 normalized = ((Vector3)(ref val2)).normalized; RaycastHit val3 = default(RaycastHit); float num = (Physics.Raycast(val.position, normalized, ref val3, 50f) ? ((RaycastHit)(ref val3)).distance : 50f); for (int i = 0; i < 10; i++) { if (num > 0.05f) { break; } num = (Physics.Raycast(((RaycastHit)(ref val3)).point + normalized * 0.05f, normalized, ref val3, 50f) ? ((RaycastHit)(ref val3)).distance : 50f); } UpdateLine(val, normalized, num); if (num != 0f && num != 50f) { UpdateTimer(((RaycastHit)(ref val3)).point, ((RaycastHit)(ref val3)).normal, out var completedAt); if (completedAt.HasValue) { SceneSaverBL.desiredDupePos = completedAt.Value; Fadeout(); } } } private static void UpdateLine(Transform startPoint, Vector3 forward, float dist) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) antSize.SetOn(DupeMat, 1f / dist); Line.transform.position = startPoint.position; Line.transform.forward = forward; Line.transform.localScale = new Vector3(1f, 1f, dist); } private static void UpdateTimer(Vector3 hitPoint, Vector3 normal, out Vector3? completedAt) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: 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_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: 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_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: 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_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) completedAt = null; bool flag = false; bool flag2 = Vector3.Dot(normal, Vector3.up) > 0.8f; HandPoseAnimator val = (Prefs.rightHandDupeLine ? Player.RightHand.Animator : Player.LeftHand.Animator); bool flag3 = (int)(Prefs.rightHandDupeLine ? Player.RightController : Player.LeftController).Type == 1; bool flag4 = val._currentMiddle + val._currentRing + val._currentPinky > 2.7f; bool flag5 = val._currentThumb < 0.1f && !forceFadeoutTimer; bool flag6 = flag4 && flag5; if (flag6 && val._currentIndex < 0.15f && flag3) { val._currentIndex = 0f; } int stateCount = TimerAnim.GetStateCount(); for (int i = 0; i < stateCount; i++) { AnimationState stateAtIndex = TimerAnim.GetStateAtIndex(i); float time = stateAtIndex.time; float num = Mathf.Clamp01((flag2 && flag6) ? (time + Time.deltaTime) : (time - Time.deltaTime)); if (!flag4 || time != num) { if (time == 0f) { TimerAnim.Sample(); ((Component)Timer).gameObject.SetActive(true); stateAtIndex.speed = 1f; TimerAnim.Play(); } stateAtIndex.time = num; if (num == 1f && flag4) { flag = true; } if (num == 0f) { ((Component)Timer).gameObject.SetActive(false); TimerAnim.Stop(); return; } } } if (!Prefs.dontAdjustDupePos) { AdjustPosFromWall(ref hitPoint, ref normal); } Vector3 position = Timer.position; if (position == default(Vector3)) { Timer.position = hitPoint; timer.rotation = Quaternion.LookRotation(Vector3.forward, normal); return; } Vector3 val2 = hitPoint + normal * 0.01f; float num2 = (flag2 ? Vector3.Distance(position, val2) : 0f); Vector3 val3 = Vector3.Lerp(position, val2, Time.deltaTime * timerFollowSpeed * num2); if (flag) { completedAt = val3; } Timer.position = val3; if (flag2) { Timer.rotation = Quaternion.LookRotation(Vector3.forward, normal); } _ = val3 != position; } private static void AdjustPosFromWall(ref Vector3 hitPoint, ref Vector3 normal) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0114: 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_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: 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_0155: Unknown result type (might be due to invalid IL or missing references) int num = (Utilities.IsPlatformQuest() ? 4 : 8); RaycastHit val4 = default(RaycastHit); RaycastHit val5 = default(RaycastHit); for (int i = 0; i < num; i++) { Vector3 val = hitPoint + normal * 0.01f; bool flag = false; bool flag2 = false; (float, int) tuple = (1f, 0); for (int j = 0; j < scratchDirections.Length; j++) { Vector3 val2 = directions[(j + i) % directions.Length]; Vector3 val3 = Vector3.Cross(normal, val2); scratchDirections[j] = val3; bool flag3 = Physics.Raycast(val, val3, ref val4, 0.5f); distanceHitPoints[j] = (flag3 ? ((RaycastHit)(ref val4)).distance : 0f); if (flag3) { flag = true; if (tuple.Item1 > ((RaycastHit)(ref val4)).distance) { tuple = (((RaycastHit)(ref val4)).distance, j); } } } if (!flag) { break; } val -= scratchDirections[tuple.Item2] * (0.5f - tuple.Item1); if (Physics.Raycast(val, Vector3.down, ref val5, 0.5f)) { if (Vector3.Distance(hitPoint, ((RaycastHit)(ref val5)).point) < 0.001f) { break; } hitPoint = ((RaycastHit)(ref val5)).point; normal = ((RaycastHit)(ref val5)).normal; } if (flag2) { break; } } } private static Transform GetIndex() { ArtRig artOutput = Player.PhysicsRig.artOutput; return ((Component)(Prefs.rightHandDupeLine ? artOutput._rightAnimatorHand : artOutput._leftAnimatorHand).index3).transform.GetChild(0); } } internal class IMGUIInputField { private const string IMGUI_CONTROL_NAME = "imguiInputField"; private string str; private int duration; private float startTime; private string filter; private UniTaskCompletionSource<string> taskCompletionSource; private GUIStyle? style; public UniTask<string> OnComplete => taskCompletionSource.Task; public bool TimeFinished { get { if (duration != -1) { return Time.realtimeSinceStartup >= startTime + (float)duration; } return false; } } private GUIStyle Style { get { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (style == null || ((Il2CppObjectBase)style).WasCollected) { style = new GUIStyle(GUI.skin.textField); } style.fontSize = (int)Screen.dpi / 4; if (style.fontSize == 0) { style.fontSize = 24; } return style; } } public IMGUIInputField(int duration, string startStr = "", string filter = "") { startTime = Time.realtimeSinceStartup; this.duration = duration; str = startStr; this.filter = filter; taskCompletionSource = new UniTaskCompletionSource<string>(); } public void OnGUI() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) if (TimeFinished) { Finish(); return; } GUI.SetNextControlName("imguiInputField"); bool flag = Event.current.isKey && ((int)Event.current.keyCode == 13 || (int)Event.current.keyCode == 271); if (GUI.GetNameOfFocusedControl() == "imguiInputField" && flag) { Finish(); return; } float num = (float)Screen.width * 0.2f; float num2 = (float)Screen.height * 0.475f; float num3 = (float)Screen.width - 2f * num; float num4 = (float)Screen.height - 2f * num2; Rect val = default(Rect); ((Rect)(ref val))..ctor(num, num2, num3, num4); string text = str; str = GUI.TextField(val, str, 64, Style); if (!string.IsNullOrEmpty(filter) && text != str) { StringBuilder stringBuilder = new StringBuilder(str.Length); for (int i = 0; i < str.Length; i++) { char c = str[i]; if (filter.Contains(c)) { stringBuilder.Append(c); } else if (filter.Contains(char.ToUpper(c))) { stringBuilder.Append(char.ToUpper(c)); } else if (filter.Contains(char.ToLower(c))) { stringBuilder.Append(char.ToLower(c)); } } str = stringBuilder.ToString(); } Rect val2 = new Rect(num, num2 - num4 * 0.5f, num3 * 0.25f, num4 * 0.5f); GUI.SetNextControlName(""); GUI.Box(val2, "Input a string"); if (duration != -1) { float num5 = startTime + (float)duration - Time.realtimeSinceStartup; Rect val3 = val; ((Rect)(ref val3)).y = ((Rect)(ref val3)).y + (((Rect)(ref val3)).height + ((Rect)(ref val3)).height * 0.125f); ((Rect)(ref val3)).height = ((Rect)(ref val3)).height * 0.25f; ((Rect)(ref val3)).width = ((Rect)(ref val3)).width * (num5 / (float)duration); GUI.Box(val3, ""); } } private void Finish() { SceneSaverBL.currentInputter = null; taskCompletionSource.TrySetResult(str); } public static UniTask<string> GetStringAsync(int duration = 30, string startStr = "", string filter = "") { return (SceneSaverBL.currentInputter = new IMGUIInputField(duration, startStr, filter)).OnComplete; } } public static class BuildInfo { public const string Name = "SceneSaverBL"; public const string Author = "extraes"; public const string Company = null; public const string Version = "2.0.0"; public const string DownloadLink = "https://bonelab.thunderstore.io/package/extraes/SceneSaverBL/"; } public class SceneSaverBL : MelonMod { private static PrefEntries prefEntries; internal static bool isFullSave; internal static volatile bool currentlySaving; internal static string username; internal static SceneSaverBL instance; internal static ConcurrentQueue<ISaveFile> needInitialize = new ConcurrentQueue<ISaveFile>(); internal static ConcurrentQueue<Action> runOnMainThread = new ConcurrentQueue<Action>(); internal static Vector3? desiredDupePos = null; private static readonly Stopwatch updateSw = new Stopwatch(); public static int[] supportedSaveVers; internal static IMGUIInputField? currentInputter; internal static string saveDir = Path.Combine(MelonEnvironment.UserDataDirectory, "SceneSaver", "Saves"); internal static string dupesDir = Path.Combine(MelonEnvironment.UserDataDirectory, "SceneSaver", "Dupes"); private static readonly SemaphoreSlim logSemaphore = new SemaphoreSlim(1, 1); public SceneSaverBL() { instance = this; } public override void OnEarlyInitializeMelon() { SaveChecks.EstablishMainThread(); } public override async void OnInitializeMelon() { if (!JevilBuildInfo.RuntimeVersionGreaterThanOrEqual("3.1.0")) { Warn($"SceneSaver v{((MelonBase)this).MelonAssembly.Assembly.GetName().Version} was built for JeviLib v{"3.1.0"}!!! Update your dependencies!!!"); return; } prefEntries = Preferences.Register(typeof(Prefs), (string)null); Page category = prefEntries.BoneMenuPage["About"]; Page repoPage = prefEntries.BoneMenuPage["Repo"]; Prefs.PopulateAboutMenu(category); Utilities.CreateDirectoryRecursive(SaveUtils.PreviewDir); Menu.OnPageOpened += PageSelected; BonemenuStringInput.Init(); SpawnerStates.Init(); FingerOffset.Init(); await Assets.Init(); Hook.OntoMethod<Action>(typeof(PopUpMenuView), "Deactivate", (Action)MenuClosed); Instances<ObjectDestructible>.TryAutoCache(); Instances<Poolee>.TryAutoCache(); Instances<AIBrain>.TryAutoCache(); Instances<GUIMenu>.TryAutoCache(); FetchUsername(); await RepoConsumer.Init(repoPage); await Saves.Init(prefEntries.BoneMenuPage); } private static void LogBoardGunState(string loggedFrom, BoardGenerator bg) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_005f: 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) DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(97, 7); defaultInterpolatedStringHandler.AppendLiteral("Boardgun state as of "); defaultInterpolatedStringHandler.AppendFormatted(loggedFrom); defaultInterpolatedStringHandler.AppendLiteral(":\n\tupDir = "); defaultInterpolatedStringHandler.AppendFormatted<Vector3>(bg.upDir); defaultInterpolatedStringHandler.AppendLiteral("\n\tbuttonDown = "); defaultInterpolatedStringHandler.AppendFormatted(bg.ButtonDown); defaultInterpolatedStringHandler.AppendLiteral("\n\tfirstPoint = "); defaultInterpolatedStringHandler.AppendFormatted<Vector3>(bg.firstPoint); defaultInterpolatedStringHandler.AppendLiteral("\n\tEndPoint = "); defaultInterpolatedStringHandler.AppendFormatted<Vector3>(bg.EndPoint); defaultInterpolatedStringHandler.AppendLiteral("\n\tFirstRb = "); MarrowBody firstRb = bg.FirstRb; defaultInterpolatedStringHandler.AppendFormatted(((firstRb != null) ? Extensions.GetFullPath(((Component)firstRb).transform) : null) ?? "shid"); defaultInterpolatedStringHandler.AppendLiteral("\n\tEndRb = "); MarrowBody endRb = bg.EndRb; defaultInterpolatedStringHandler.AppendFormatted(((endRb != null) ? Extensions.GetFullPath(((Component)endRb).transform) : null) ?? "shid"); Log(defaultInterpolatedStringHandler.ToStringAndClear()); } public override void OnUpdate() { ISaveFile result; while (needInitialize.TryDequeue(out result)) { Log($"Save {result} - Now initializing from main thread Update"); AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow((Func<Task>)result.Initialize), (Action<Exception>)ErrIfNotNull); } updateSw.Restart(); Action result2; while (runOnMainThread.TryDequeue(out result2) && updateSw.ElapsedMilliseconds < ConfigVars.timeSliceMs) { result2(); } CameraFella.UpdatePosition(); } public override void OnLateUpdate() { FingerOffset.OnUpdate(); } public override void OnGUI() { if (currentInputter != null) { currentInputter.OnGUI(); } } private static void FetchUsername() { if (Utilities.IsSteamVersion()) { AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow((Func<Task>)FetchSteamUsernameAsync), (Action<Exception>)ErrIfNotNull); } else { AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow((Func<Task>)FetchOculusUsername), (Action<Exception>)ErrIfNotNull); } } private static async Task FetchSteamUsernameAsync() { while (!JeviLib.DoneMappingNamespacesToAssemblies) { await UniTask.Yield(); } Type typeFromString = Utilities.GetTypeFromString("Steamworks", "SteamClient"); if ((object)typeFromString == null) { Warn("Steamworks not found! Are you using the Oculus/Meta version?"); username = "Unknown"; } else { username = (string)typeFromString.GetProperty("Name").GetValue(null); PostcheckUsername(); } } private static async Task FetchOculusUsername() { while (!Core.IsPlatformInitialized) { await Task.Delay(1000); } await Task.Delay(1000); Request<User> loggedInUser; for (loggedInUser = Users.GetLoggedInUser(); loggedInUser == null; loggedInUser = Users.GetLoggedInUser()) { await Task.Yield(); } loggedInUser.OnComplete(Callback<User>.op_Implicit((Action<Message<User>>)SetOculusUsername)); } private static void SetOculusUsername(Message<User> msg) { if (((Message)msg).IsError) { Error("Error while getting OVR usernameBytes:" + ((Object)((Message)msg).error).ToString()); return; } username = msg.Data.DisplayName; PostcheckUsername(); } private static void PostcheckUsername() { Log("Detected usernameBytes as " + username); if (username.Length > 32) { username = username.Substring(0, 29) + "..."; Log("Username is over 32 char, shortened to: " + username); } } private static void PageSelected(Page category) { if (category == prefEntries.BoneMenuPage) { CameraFella.MenuOpened(); } } private static void MenuClosed() { Saves.MenuClosed(); CameraFella.MenuClosed(); } internal static Poolee? GetPooleeUpwards(Transform? t) { if ((Object)(object)t == (Object)null) { return null; } Poolee val = Poolee.Cache.Get(((Component)t.root).gameObject); if ((Object)(object)val == (Object)null) { return GetPooleeUpwardsRecursive(t); } return val; } private static Poolee? GetPooleeUpwardsRecursive(Transform t) { Poolee val = Poolee.Cache.Get(((Component)t).gameObject); if (Object.op_Implicit((Object)(object)val)) { return val; } Transform parent = t.parent; if ((Object)(object)parent != (Object)null) { return GetPooleeUpwardsRecursive(parent); } return null; } internal static AIBrain? GetBrainImmediateDownward(Transform t) { Transform val = t; AIBrain result = default(AIBrain); if (Instances<AIBrain>.TryGetFromCache(((Component)t).gameObject, ref result)) { return result; } for (int i = 0; i < val.childCount; i++) { t = val.GetChild(i); if (Instances<AIBrain>.TryGetFromCache(((Component)t).gameObject, ref result)) { return result; } } return null; } internal static void Log(string str) { try { logSemaphore.Wait(); ((MelonBase)instance).LoggerInstance.Msg(str); } finally { logSemaphore.Release(); } } internal static void Log(object? obj) { Log(obj?.ToString() ?? "null"); } internal static void Warn(string str) { try { logSemaphore.Wait(); ((MelonBase)instance).LoggerInstance.Warning(str); } finally { logSemaphore.Release(); } } internal static void Warn(object? obj) { Warn(obj?.ToString() ?? "null"); } internal static void WarnVariable(object? obj, [CallerArgumentExpression("obj")] string paramName = "<none>") { Warn(paramName + ": " + obj); } internal static void Error(string str) { try { logSemaphore.Wait(); ((MelonBase)instance).LoggerInstance.Error(str); } finally { logSemaphore.Release(); } } internal static void Error(object? obj) { Error(obj?.ToString() ?? "null"); } internal static void ErrIfNotNull(object? obj) { if (obj != null) { Error(obj); } } } [RegisterTypeInIl2Cpp] internal class OffsetProviderLink : MonoBehaviour { private Transform _firePoint; private Transform FirePoint { get { if ((Object)(object)_firePoint == (Object)null) { _firePoint = ((Component)this).transform.Find("FirePoint"); } return _firePoint; } } public OffsetProviderLink(IntPtr ptr) : base(ptr) { } private void Start() { _ = FirePoint; } public Vector3 AcquirePosition() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) RaycastHit val = default(RaycastHit); if (!Physics.Raycast(FirePoint.position, FirePoint.forward, ref val, 50f)) { return FirePoint.position + FirePoint.forward * 50f; } return ((RaycastHit)(ref val)).point; } public void Clicked() { //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) //IL_001f: Unknown result type (might be due to invalid IL or missing references) Vector3 value = AcquirePosition(); SceneSaverBL.Log($"Clicked at {value}"); } } [Preferences("SceneSaver", true)] internal static class Prefs { [Pref("Always shows where the preview image will be taken from, as opposed to only when SSBL's BoneMenu is open. (By showing an orange smiley face)")] internal static bool showPreviewLocation = false; [Pref("When saving, a 'polaroid' of the preview image is spawned. To disable this, set this to true.")] internal static bool disablePolaroid = false; [Pref] internal static bool filterByLevel = true; [Pref("Freezes objects while other objects are still loading. This may cause saves to load slower than if it was disabled.")] internal static bool freezeWhileLoading = true; [Pref("Performs extra checks while saving, to make sure data will be able to be loaded properly (has fallback behavior)")] internal static bool saveChecks = true; [Pref("Loads WELD constraints that are between the current object and a non-saved object")] internal static bool loadStaticWelds = false; [Pref("If true, will move wire when only Grip+Trigger are held, otherwise will require Grip+Trigger+StickClick")] internal static bool dontUseStickClick = false; [Pref("The URL base to be used when fetching from the online save repository. (if you replace this, dont include a slash at the end.)")] internal static string ssblRepo = "https://ssbl.extraes.xyz"; [Pref("Disables stats. SSBL uses stats to inform the developer (extraes) of: How many times the mod was used on Q2/PC, how many saves were saved/loaded")] internal static bool disableStats = false; [Pref("If true, will use the right hand for the dupe line, otherwise will use the left hand")] internal static bool rightHandDupeLine = true; [Pref("If true, dupes will be allowed to spawn in walls")] internal static bool dontAdjustDupePos = false; [Pref("If true, tells Fusion to sync spawned objects.")] internal static bool fusionSync = true; internal static SelectionZone? activeSelection; [MemberNotNull("activeSelection")] private static void CreateSelectionWire() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("SceneSaver Selection Wire"); Utilities.MoveAndFacePlayer(val); val.transform.localRotation = Quaternion.identity; activeSelection = val.AddComponent<SelectionZone>(); ((Component)activeSelection).gameObject.SetActive(true); } public static void PopulateAboutMenu(Page category) { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_012f: 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) Type[] array = (from t in ((MelonBase)SceneSaverBL.instance).MelonAssembly.Assembly.GetTypes() where t.GetInterfaces().Any((Type t) => t == typeof(ISaveFile)) && t != typeof(FailedSaveFile) select t).ToArray(); int[] array2 = new int[array.Length]; for (int i = 0; i < array.Length; i++) { int.TryParse(new string((array[i].Namespace ?? "").SkipWhile((char c) => !char.IsDigit(c)).ToArray()), out var result); array2[i] = result; } SceneSaverBL.supportedSaveVers = array2; category.CreateFunction("Version 2.0.0 (Release)", Color.white, SaveUtils.NothingAction); category.CreateFunction("Created by extraes", Color.white, SaveUtils.NothingAction); category.CreateFunction($"Creates V{array2.Max()} saves", Color.white, SaveUtils.NothingAction); category.CreateFunction("Reads V" + string.Join(" , V", array2), Color.white, SaveUtils.NothingAction); category.CreateFunction("Donate (opens browser)", new Color(0.9f, 0.75f, 0.75f), (Action)delegate { Application.OpenURL("https://ko-fi.com/extraes"); }); } [Pref("", 0.75f, 1f, 0.75f)] public static void ShowSaves() { Saves.ShowBoneMenu(); } [Pref("Saving")] public static void ToggleSelectionWire() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)activeSelection == (Object)null) { CreateSelectionWire(); } else if (!((Component)activeSelection).gameObject.active) { Utilities.MoveAndFacePlayer(((Component)activeSelection).gameObject); ((Component)activeSelection).transform.rotation = Quaternion.identity; ((Component)activeSelection).gameObject.SetActive(true); } else { ((Component)activeSelection).gameObject.SetActive(false); CameraFella.MenuClosed(); } } [Pref("Saving")] public static void SaveSelection() { if (!((Object)(object)activeSelection == (Object)null) && ((Component)activeSelection).gameObject.active) { SceneSaverBL.isFullSave = true; SceneSaverBL.currentlySaving = true; AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow((Func<Task>)Saves.DoSave), (Action<Exception>)SaveFinished); } } [Pref(/*Could not decode attribute arguments.*/)] public static void ShowTutorial() { ControllerTutorial.Show(); } [Pref("Repo")] public static void ThereWasABugYay() { } [Pref("About")] public static void SceneSaver() { } private static void SaveFinished(Exception? ex) { SceneSaverBL.currentlySaving = false; if (ex == null) { return; } SceneSaverBL.Error($"SAVING FAILED! Cleaning saves directory of unfilled saves.\n\t More details: {ex}"); foreach (string item in Directory.EnumerateFiles(SceneSaverBL.saveDir, "*.ssbl")) { if (new FileInfo(item).Length <= 5) { SceneSaverBL.Warn("Deleting : " + item); File.Delete(item); } } } } internal static class RepoConsumer { private enum InputMethod { BONEMENU, IMGUI, CLIPBOARD } private const string HEXADEC = "0123456789ABCDEF"; private const string RECENTS_MENU_NAME_FORMAT = "Recents as of {0}m ago"; private static readonly string DownloadedSavePath = Path.Combine(SceneSaverBL.saveDir, "Downloaded"); private static Page downloadedPage; private static Page repoPage; private static SceneSaverRepoInfo repoInfo; private static Page workingPage; private static Page genericErrorPage; private static Page recentsPage; private static Page saveMetadataPage; private static Page uploadPage; private static readonly List<ISaveFile> saves = new List<ISaveFile>(); public static async Task Init(Page repoCategory) { Prefs.ssblRepo = Prefs.ssblRepo.TrimEnd('/'); repoPage = repoCategory; workingPage = repoPage.CreatePage("Working...", Color.gray, 0, true); genericErrorPage = repoPage.CreatePage("Error!", Color.Lerp(Color.white, Color.red, 0.5f), 0, true); recentsPage = repoPage.CreatePage("Recents as of {0}m ago", Color.white, 0, true); saveMetadataPage = repoPage.CreatePage("Save metadata placeholder name", Color.white, 0, true); uploadPage = repoPage.CreatePage("Upload a save", Color.white, 0, true); downloadedPage = repoPage.CreatePage("Downloaded", Color.white, 0, true); Utilities.CreateDirectoryRecursive(DownloadedSavePath); repoPage.RemoveAll(); UnityWebRequest repoInfoReq = UnityWebRequest.Get(Prefs.ssblRepo + "/api/repo/info"); Awaiter val = AsyncUtilities.ToUniTask((AsyncOperation)(object)repoInfoReq.SendWebRequest(), (PlayerLoopTiming)8).GetAwaiter(); if (!val.IsCompleted) { await val; object obj = default(object); val = (Awaiter)obj; } val.GetResult(); if (((Il2CppObjectBase)repoInfoReq).WasCollected) { CreateMainErroredMenu("Web req GC'd"); return; } if (((Il2CppObjectBase)repoInfoReq.downloadHandler).WasCollected) { CreateMainErroredMenu("DL handler GC'd"); return; } Result result = repoInfoReq.result; switch (result - 2) { case 0: CreateMainErroredMenu("Net err - " + repoInfoReq.error); return; case 1: CreateMainErroredMenu($"HTTP err ({repoInfoReq.responseCode}) - " + repoInfoReq.error); return; case 2: CreateMainErroredMenu($"Data err (from HTTP{repoInfoReq.responseCode}) - " + repoInfoReq.error); return; } try { repoInfo = JsonSerializer.Deserialize<SceneSaverRepoInfo>(repoInfoReq.downloadHandler.text); } catch (Exception ex) { SceneSaverBL.Log("Raw response from webserver: " + repoInfoReq.downloadHandler.text); SceneSaverBL.Error("Exception while deserializing SSBL Repo info: " + ex); CreateMainErroredMenu("Parse fail: " + ex.Message); return; } PopulateMenu(); foreach (string item2 in Directory.EnumerateFiles(DownloadedSavePath, "*.ssbl")) { ISaveFile item = await Saves.CreateSave(item2); saves.Add(item); } } private static void PopulateMenu() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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_017a: 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_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) int num = Prefs.ssblRepo.IndexOf("://"); string text = ((num == -1 || num >= Prefs.ssblRepo.Length - 3) ? Prefs.ssblRepo : Prefs.ssblRepo.Split(new string[1] { "://" }, StringSplitOptions.None)[1]); Page obj = repoPage.CreatePage(text, Color.white, 0, true); obj.CreateFunction("Running V" + repoInfo.version, Color.white, SaveUtils.NothingAction); obj.CreateFunction("Timezone " + repoInfo.timeZone, Color.white, SaveUtils.NothingAction); obj.CreateFunction(repoInfo.totalDownloads + " total DLs", Color.white, SaveUtils.NothingAction); obj.CreateFunction(repoInfo.totalDownloadsWeek + " DLs this week", Color.white, SaveUtils.NothingAction); repoPage.CreateFunction("Recents", Color.white, (Action)delegate { AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow<int>((Func<int, Task>)ShowRecents, 0), (Action<Exception>)SceneSaverBL.ErrIfNotNull); }); repoPage.CreateFunction("Upload", Color.white, (Action)delegate { AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow((Func<Task>)ShowUploadMenu), (Action<Exception>)SceneSaverBL.ErrIfNotNull); }); Page val = repoPage.CreatePage("DL from tag", Color.Lerp(Color.green, Color.white, 0.5f), 0, true); val.CreateFunction("Input from BoneMenu", Color.white, (Action)delegate { AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow<InputMethod>((Func<InputMethod, Task>)DownloadFromTag, InputMethod.BONEMENU), (Action<Exception>)SceneSaverBL.ErrIfNotNull); }); val.CreateFunction("Input from Clipboard", Color.white, (Action)delegate { AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow<InputMethod>((Func<InputMethod, Task>)DownloadFromTag, InputMethod.CLIPBOARD), (Action<Exception>)SceneSaverBL.ErrIfNotNull); }); if (!Utilities.IsPlatformQuest()) { val.CreateFunction("Input from Spectator Screen", Color.white, (Action)delegate { AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow<InputMethod>((Func<InputMethod, Task>)DownloadFromTag, InputMethod.IMGUI), (Action<Exception>)SceneSaverBL.ErrIfNotNull); }); } repoPage.CreateFunction("Downloaded", Color.white, (Action)ShowDownloaded); } private static void ShowDownloaded() { downloadedPage.RemoveAll(); foreach (ISaveFile safe in saves) { safe.PopulateBoneMenu(downloadedPage); } Menu.OpenPage(downloadedPage); } private static async Task ShowRecents(int pageIdx) { Menu.OpenPage(workingPage); DownloadHandler val = await SendRequestForOrShowError(Prefs.ssblRepo + $"/api/repo/recent?skip={pageIdx * 10}&take={10}"); if (val == null) { return; } SceneSaverEntryCollection sceneSaverEntryCollection; try { sceneSaverEntryCollection = JsonConvert.DeserializeObject<SceneSaverEntryCollection>(val.text); } catch (Exception ex) { SceneSaverBL.Log("Raw response from webserver: " + val.text); SceneSaverBL.Error("Exception while deserializing recent save files: " + ex); SetAndSelectError("Parse fail: " + ex.Message); return; } recentsPage.Name = $"Recents as of {Math.Round(sceneSaverEntryCollection.TimeSinceLastUpdate.TotalMinutes)}m ago"; recentsPage.RemoveAll(); recentsPage.CreateFunction($"Page {pageIdx + 1}", Color.white, SaveUtils.NothingAction); SceneSaverSaveEntry[] array = sceneSaverEntryCollection.Saves; foreach (SceneSaverSaveEntry saveMeta in array) { recentsPage.CreateFunction(saveMeta.name, Color.white, (Action)delegate { SelectSaveMetadata(saveMeta); }); } if (pageIdx > 0) { recentsPage.CreateFunction("< Previous page", Color.white, (Action)delegate { AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow<int>((Func<int, Task>)ShowRecents, pageIdx - 1), (Action<Exception>)SceneSaverBL.ErrIfNotNull); }); } if (sceneSaverEntryCollection.Saves.Count() == 10) { recentsPage.CreateFunction("Next page >", Color.white, (Action)delegate { AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow<int>((Func<int, Task>)ShowRecents, pageIdx + 1), (Action<Exception>)SceneSaverBL.ErrIfNotNull); }); } else if (sceneSaverEntryCollection.Saves.Count() == 0) { recentsPage.CreateFunction("No more pages", Color.white, SaveUtils.NothingAction); } Menu.OpenPage(recentsPage); } private static void SelectSaveMetadata(SceneSaverSaveEntry saveMeta) { //IL_00ae: 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_010a: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) SceneSaverSaveEntry saveMeta2 = saveMeta; if (saveMeta2.name == "error" && string.IsNullOrEmpty(saveMeta2.owner)) { SetAndSelectError("No such save found"); return; } TimeSpan? timeSpan = saveMeta2.TimeUntilExpired(); saveMetadataPage.RemoveAll(); saveMetadataPage.Name = saveMeta2.name; if (!Utilities.IsPlatformQuest()) { saveMetadataPage.CreateFunction("Copy tag to clipboard", Color.white, (Action)delegate { GUIUtility.systemCopyBuffer = saveMeta2.tag; }); } saveMetadataPage.CreateFunction("Tag: " + saveMeta2.tag, Color.white, SaveUtils.NothingAction); saveMetadataPage.CreateFunction("Hash: " + saveMeta2.hash, Color.gray, SaveUtils.NothingAction); saveMetadataPage.CreateFunction("Save file version: " + saveMeta2.version, Color.green, SaveUtils.NothingAction); saveMetadataPage.CreateFunction("Weekly downloads: " + saveMeta2.downloadCountWeek, Color.gray, SaveUtils.NothingAction); saveMetadataPage.CreateFunction("Lifetime downloads: " + saveMeta2.downloadCount, Color.gray, SaveUtils.NothingAction); saveMetadataPage.CreateFunction("Uploaded by ID: " + saveMeta2.owner, Color.gray, SaveUtils.NothingAction); if (timeSpan.HasValue) { saveMetadataPage.CreateFunction($"Expires in {(int)timeSpan.Value.TotalDays}d {timeSpan.Value.Hours}h {timeSpan.Value.Minutes}m", Color.green, SaveUtils.NothingAction); } else { saveMetadataPage.CreateFunction("Doesn't expire", Color.green, SaveUtils.NothingAction); } if (SceneSaverBL.supportedSaveVers.Contains(saveMeta2.version)) { saveMetadataPage.CreateFunction("Download", Color.green, (Action)delegate { AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow<SceneSaverSaveEntry>((Func<SceneSaverSaveEntry, Task>)DownloadSave, saveMeta2), (Action<Exception>)SceneSaverBL.ErrIfNotNull); }); } else { saveMetadataPage.CreateFunction($"Can't read V{saveMeta2.version} saves", Color.red, SaveUtils.NothingAction); } Menu.OpenPage(saveMetadataPage); } private static async Task DownloadSave(SceneSaverSaveEntry metadata) { Menu.OpenPage(workingPage); DownloadHandler val = await SendRequestForOrShowError(Prefs.ssblRepo + "/api/saves/download?tag=" + metadata.tag); if (val != null) { File.WriteAllBytes(Path.Combine(DownloadedSavePath, "DL - " + metadata.name), Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)val.data)); ShowDownloaded(); } } private static void CreateMainErroredMenu(string text, string otherText = "") { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) repoPage.CreateFunction("Repo client error", Color.red, SaveUtils.NothingAction); repoPage.CreateFunction(text, Color.Lerp(Color.white, Color.red, 0.5f), SaveUtils.NothingAction); if (!string.IsNullOrEmpty(otherText)) { repoPage.CreateFunction(otherText, Color.Lerp(Color.white, Color.red, 0.25f), SaveUtils.NothingAction); } repoPage.CreateFunction("Retry", Color.Lerp(Color.white, Color.green, 0.25f), (Action)delegate { AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow<Page>((Func<Page, Task>)Init, repoPage), (Action<Exception>)SceneSaverBL.ErrIfNotNull); }); } private static async Task DownloadFromTag(InputMethod inputMethod) { string text; switch (inputMethod) { default: return; case InputMethod.BONEMENU: text = await BonemenuStringInput.GetStringInput("ABCDEF", "*", string.Empty); break; case InputMethod.IMGUI: Menu.OpenPage(workingPage); text = await IMGUIInputField.GetStringAsync(60, "", "0123456789ABCDEF"); break; case InputMethod.CLIPBOARD: text = GUIUtility.systemCopyBuffer; break; } string text2 = text; for (int i = 0; i < text2.Length; i++) { char c = text2[i]; if (!"0123456789ABCDEF".Contains(char.ToUpper(c))) { SetAndSelectError("Tags don't contain: " + c); return; } } Menu.OpenPage(workingPage); DownloadHandler val = await SendRequestForOrShowError(Prefs.ssblRepo + "/api/saves/info?tag=" + text); if (val != null) { SceneSaverSaveEntry saveMeta; try { saveMeta = JsonSerializer.Deserialize<SceneSaverSaveEntry>(val.text); } catch (Exception ex) { SceneSaverBL.Log("Raw response from webserver: " + val.text); SceneSaverBL.Error("Exception while deserializing recent save files: " + ex); SetAndSelectError("Parse fail: " + ex.Message); return; } SelectSaveMetadata(saveMeta); } } private static async Task ShowUploadMenu() { Menu.OpenPage(workingPage); uploadPage.RemoveAll(); await AsyncUtilities.ForEachTimeSlice<string>(Directory.EnumerateFiles(SceneSaverBL.saveDir, "*.ssbl"), (Action<string>)delegate(string savePath) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) if (!savePath.StartsWith(DownloadedSavePath)) { Page obj = uploadPage.CreatePage(Path.GetFileNameWithoutExtension(savePath), Color.white, 0, true); obj.CreateFunction("Delete", Color.red, (Action)delegate { File.Delete(savePath); }); Action performUpload = delegate { AsyncExtensions.RunOnFinish<Exception>(AsyncUtilities.WrapNoThrow<string>((Func<string, Task>)UploadSave, savePath), (Action<Exception>)SceneSaverBL.ErrIfNotNull); }; obj.CreateFunction("Upload", Color.white, (Action)delegate { Menu.DisplayDialog("Confirmation", "Are you sure you want to upload \"" + Path.GetFileName(savePath) + "\"?", (Texture2D)null, performUpload, (Action)null); }); } }, 1f, (PlayerLoopTiming)8); Menu.OpenPage(uploadPage); } public static async Task UploadSave(string savePath) { if (Utilities.IsPlatformQuest()) { SetAndSelectError("Uploading not possible on Quest. Blame SLZ for IL2CPP!"); return; } Menu.OpenPage(workingPage); string fileName = Path.GetFileName(savePath); byte[] content = File.ReadAllBytes(savePath); string responseTxt = ""; Exception ex2; try { HttpClient httpClient = new HttpClient(); HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Put, Prefs.ssblRepo + "/api/saves/upload?filename=" + fileName); httpRequestMessage.Content = new ByteArrayContent(content); Task<HttpResponseMessage> uploadTask = httpClient.SendAsync(httpRequestMessage); while (!uploadTask.IsCompleted) { await UniTask.Yield(); } uploadTask.Result.EnsureSuccessStatusCode(); Task<string> stringTask = uploadTask.Result.Content.ReadAsStringAsync(); while (!stringTask.IsCompleted) { await UniTask.Yield(); } responseTxt = stringTask.Result; SceneSaverSaveEntry? sceneSaverSaveEntry = JsonSerializer.Deserialize<SceneSaverSaveEntry>(responseTxt); GUIUtility.systemCopyBuffer = sceneSaverSaveEntry.tag; SelectSaveMetadata(sceneSaverSaveEntry); return; } catch (AggregateException ex) { ex2 = ((ex.InnerExceptions.Count != 1) ? ex : ex.InnerExceptions[0]); } catch (Exception ex3) { ex2 = ex3; } if (ex2 is WebException ex4) { SceneSaverBL.Error("Exception uploading file to repo: " + ex4); SetAndSelectError("Upload fail: " + ex4.Message); return; } if (ex2 is DecoderFallbackException ex5) { SceneSaverBL.Error("Exception while decoding binary stream into text: " + ex5); SetAndSelectError("Decode fail: " + ex5.Message); return; } JsonReaderException val = (JsonReaderException)(object)((ex2 is JsonReaderException) ? ex2 : null); if (val != null) { SceneSaverBL.Log("Raw response from webserver: " + responseTxt); SceneSaverBL.Error("Exception while deserializing uploaded save metadata: " + (object)val); SetAndSelectError("Parse fail: " + ((Exception)(object)val).Message); } else { SceneSaverBL.Error("Exception while uploading save file: " + ex2); SetAndSelectError("Generic fail: " + ex2.Message); } } private static async Task<DownloadHandler?> SendRequestForOrShowError(string url, string method = "GET") { UnityWebRequest webReq = UnityWebRequest.Get(url); if (method != "GET") { webReq.method = method; } Awaiter val = AsyncUtilities.ToUniTask((AsyncOperation)(object)webReq.SendWebRequest(), (PlayerLoopTiming)8).GetAwaiter(); if (!val.IsCompleted) { await val; object obj = default(object); val = (Awaiter)obj; } val.GetResult(); if (((Il2CppObjectBase)webReq).WasCollected) { SetAndSelectError("Web req GC'd"); return null; } if (((Il2CppObjectBase)webReq.downloadHandler).WasCollected) { SetAndSelectError("DL handler GC'd"); return null; } Result result = webReq.result; switch (result - 2) { case 0: SetAndSelectError("Net err - " + webReq.error); return null; case 1: SetAndSelectError($"HTTP err ({webReq.responseCode}) - " + webReq.error); return null; case 2: SetAndSelectError($"Data err (from HTTP{webReq.responseCode}) - " + webReq.error); return null; default: return webReq.downloadHandler; } } private static void SetAndSelectError(string text) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) ((Element)(((object)genericErrorPage.Elements.FirstOrDefault()) ?? ((object)genericErrorPage.CreateFunction("", Color.white, SaveUtils.NothingAction)))).ElementName = text; Menu.OpenPage(genericErrorPage); } } internal static class SaveChecks { private const string POOLED_RIG_MANAGER = "SLZ.BONELAB.Core.DefaultPlayerRig"; private const string CONSTRAINT_NAME_START = "jPt"; private const string SAVING_BOUNDS_NAME = "SavingBounds"; private static readonly Dictionary<string, bool> HierarchyMatchCache = new Dictionary<string, bool>(); private static int mainThreadId; public static bool CanBeSerializedDeserialized(string barcode) { return barcode != "SLZ.BONELAB.Core.DefaultPlayerRig"; } public static bool IsTransformIgnored(Transform transformName) { string name = ((Object)transformName).name; if (!name.StartsWith("jPt")) { return name.StartsWith("SavingBounds"); } return true; } public static bool IsHierarchyConsistent(Poolee poolee) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) string iD = ((Scannable)poolee.SpawnableCrate).Barcode.ID; if (HierarchyMatchCache.TryGetValue(iD, out var value)) { return value; } Hash128 val = HierarchyHash(((MarrowAssetT<GameObject>)(object)((GameObjectCrate)poolee.SpawnableCrate).MainGameObject).Asset.transform); Hash128 val2 = HierarchyHash(((Component)poolee).transform); bool flag = ((Hash128)(ref val2)).Equals(val); HierarchyMatchCache[iD] = flag; return flag; } private static Hash128 HierarchyHash(Transform t, Hash128 continueHash = default(Hash128)) { //IL_0041: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) int num = 0; for (int i = 0; i < t.childCount; i++) { Transform child = t.GetChild(i); if (IsTransformIgnored(child)) { num++; } else { continueHash = HierarchyHash(child, continueHash); } } ((Hash128)(ref continueHash)).Append(t.childCount - num); return continueHash; } internal static void ThrowIfDefault(int checkFor, [CallerArgumentExpression("checkFor")] string name = null) { if (checkFor == 0) { LogThrow(new UninitializedSerializedDataException("The int variable " + name + " must be assigned a value instead of its default value.")); } } internal static void ThrowIfDefault(Vector3 checkFor, [CallerArgumentExpression("checkFor")] string name = null) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (checkFor == default(Vector3)) { LogThrow(new UninitializedSerializedDataException("The Vector3 variable " + name + " must be assigned a value instead of its default value.")); } } internal static void ThrowIfDefault<T>(T checkFor, [CallerArgumentExpression("checkFor")] string name = null) where T : IEquatable<T> { ref T reference = ref checkFor; T val = default(T); if (val == null) { val = reference; reference = ref val; } if (reference.Equals(default(T))) { LogThrow(new UninitializedSerializedDataException("The equatable variable " + name + " must be assigned a value instead of its default value.")); } } internal static void ThrowIfDefault(object obj, [CallerArgumentExpression("obj")] string name = null) { if (obj == null) { LogThrow(new UninitializedSerializedDataException("The reference variable " + name + " must be assigned a value instead of null.")); } } internal static void ThrowIfInvalid(Vector3 vec, [CallerArgumentExpression("vec")] string name = null) { //IL_0000: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) bool num = float.IsNaN(vec.x) || float.IsNaN(vec.y) || float.IsNaN(vec.z); bool flag = float.IsInfinity(vec.x) || float.IsInfinity(vec.y) || float.IsInfinity(vec.z); if (num || flag) { LogThrow(new InvalidDataException("Invalid " + name + " Vector3: " + SaveUtils.ToStr(vec))); } } internal static void EstablishMainThread() { if (mainThreadId != 0) { SceneSaverBL.Warn($"Main thread ID is already set to {mainThreadId}! Now setting to {Thread.CurrentThread.ManagedThreadId}"); } mainThreadId = Thread.CurrentThread.ManagedThreadId; } internal static void ThrowIfOffMainThread([CallerMemberName] string caller = null, [CallerLineNumber] int lineNum = 0) { if (Thread.CurrentThread.ManagedThreadId != mainThreadId) { LogThrow(new ThreadStateException($"Execution was knocked off the main thread in {caller}! See line {lineNum}.")); } } internal static void ThrowIfLongerThanByte(string strToCheck, Encoding encoder = null) { if (encoder == null) { encoder = Encoding.UTF8; } int byteCount = encoder.GetByteCount(strToCheck); if (byteCount > 255) { LogThrow(new StringTooLongException($"String is too long to be serialized ({byteCount} is greater than max 255 characters). Report this error to the developer (or maybe shorten barcode)! String: " + strToCheck)); } } internal static void ThrowIfLongerThanByte(Array arr, string arrayPurpose) { int length = arr.Length; if (length > 255) { LogThrow(new ArrayTooLongException($"Array is too long to be serialized ({length} is greater than max length 255). Report this error to the developer! The array used for: " + arrayPurpose)); } } private static void LogThrow(Exception ex) { SceneSaverBL.Error(ex); throw ex; } } internal static class Saves { private static Page savesCategory; private const string DATE_FORMAT = "s"; private static FileSystemWatcher saveWatcher; private static readonly char[] invalidChars = Path.GetInvalidFileNameChars(); private static readonly List<ISaveFile> saves = new List<ISaveFile>(); private static float lastSaveTime; private static Page dupeMenu; private static Vector3 extraDupeOffset; private static Bounds displayBounds; private static Bounds calcBounds; private static Transform boundsVis; private static PositionTween currPosTween; private static bool boxKeepAlive; private static Vector3 GetTotalOffset() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) Vector3 center = ((Bounds)(ref displayBounds)).center; center.y = ((Bounds)(ref displayBounds)).min.y; return (SceneSaverBL.desiredDupePos.HasValue ? (SceneSaverBL.desiredDupePos.Value - center) : Vector3.zero) + extraDupeOffset; } internal static async Task Init(Page parentCategory) { savesCategory = parentCategory.CreatePage("SceneSaver Saves", Color.white, 0, true); dupeMenu = savesCategory.CreatePage("Dupe spawning", Color.green, 0, true); Element val = parentCategory.Elements.First((Element e) => e.ElementName == savesCategory.Name); parentCategory.Remove(val); string directoryName = Path.GetDirectoryName(SceneSaverBL.saveDir); Path.GetDirectoryName(SceneSaverBL.dupesDir); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } if (!Directory.Exists(SceneSaverBL.saveDir)) { Directory.CreateDirectory(SceneSaverBL.saveDir); } saveWatcher = new FileSystemWatcher(SceneSaverBL.saveDir, "*.ssbl"); saveWatcher.Changed += CheckChangedSave; saveWatcher.Created += CheckChangedSave; saveWatcher.Deleted += CheckChangedSave; saveWatcher.EnableRaisingEvents = true; await InitSaves(); } private static async Task InitSa