Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ProperSave MultiSave v1.0.0
plugins/ProperSaveMultiSave/ProperSaveMultiSave.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using MonoMod.RuntimeDetour; using Newtonsoft.Json; using On.RoR2; using On.RoR2.UI; using ProperSave; using ProperSave.Data; using ProperSave.SaveData; using ProperSave.Utils; using Rewired; using RiskOfOptions; using RiskOfOptions.OptionConfigs; using RiskOfOptions.Options; using RoR2; using RoR2.ContentManagement; using RoR2.Stats; using RoR2.UI; using TMPro; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; using Zio; using Zio.FileSystems; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("ProperSave")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ProperSaveMultiSave")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+6803bcc513bc35628d9240208ce200d51a53e659")] [assembly: AssemblyProduct("ProperSaveMultiSave")] [assembly: AssemblyTitle("ProperSaveMultiSave")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ProperSaveMultiSave { internal sealed class DetailsPanel { private const float ICON_SIZE = 40f; private const float ARTIFACT_SIZE = 34f; private static readonly Color ITEM_BACKGROUND = new Color(1f, 1f, 1f, 0.06f); private static readonly Color EQUIPMENT_BACKGROUND = new Color(1f, 0.6f, 0.1f, 0.22f); private static readonly Color ARTIFACT_BACKGROUND = new Color(0.6f, 0.4f, 1f, 0.2f); private static readonly Color LINE = new Color(1f, 1f, 1f, 0.08f); private static readonly Dictionary<SceneDef, Texture> previews = new Dictionary<SceneDef, Texture>(); private static readonly Dictionary<SceneDef, AssetOrDirectReference<Texture>> loading = new Dictionary<SceneDef, AssetOrDirectReference<Texture>>(); private readonly TMP_FontAsset font; private readonly RawImage preview; private readonly AspectRatioFitter previewFitter; private readonly ScrollRect scroll; private readonly RectTransform content; private readonly TextMeshProUGUI hint; private SceneDef shownScene; private readonly Dictionary<SaveFileMetadata, GameObject> views = new Dictionary<SaveFileMetadata, GameObject>(); private GameObject shown; public DetailsPanel(RectTransform area, TMP_FontAsset font) { //IL_0030: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: 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_01ef: 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_0219: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Expected O, but got Unknown this.font = font; RectTransform component = new GameObject("Preview", new Type[1] { typeof(RectTransform) }).GetComponent<RectTransform>(); ((Transform)component).SetParent((Transform)(object)area, false); Stretch(component, new Vector2(0.03f, 0.64f), new Vector2(0.97f, 0.98f)); preview = new GameObject("Image", new Type[2] { typeof(RectTransform), typeof(RawImage) }).GetComponent<RawImage>(); ((Transform)((Graphic)preview).rectTransform).SetParent((Transform)(object)component, false); ((Graphic)preview).raycastTarget = false; previewFitter = ((Component)preview).gameObject.AddComponent<AspectRatioFitter>(); previewFitter.aspectMode = (AspectMode)3; RectTransform component2 = new GameObject("Contents", new Type[2] { typeof(RectTransform), typeof(Image) }).GetComponent<RectTransform>(); ((Transform)component2).SetParent((Transform)(object)area, false); Stretch(component2, new Vector2(0f, 0.01f), new Vector2(1f, 0.63f)); ((Graphic)((Component)component2).GetComponent<Image>()).color = Color.clear; RectTransform component3 = new GameObject("Viewport", new Type[2] { typeof(RectTransform), typeof(RectMask2D) }).GetComponent<RectTransform>(); ((Transform)component3).SetParent((Transform)(object)component2, false); Stretch(component3, Vector2.zero, Vector2.one); content = new GameObject("Content", new Type[1] { typeof(RectTransform) }).GetComponent<RectTransform>(); ((Transform)content).SetParent((Transform)(object)component3, false); content.anchorMin = new Vector2(0f, 1f); content.anchorMax = new Vector2(1f, 1f); content.pivot = new Vector2(0.5f, 1f); content.sizeDelta = Vector2.zero; VerticalLayoutGroup obj = ((Component)content).gameObject.AddComponent<VerticalLayoutGroup>(); ((LayoutGroup)obj).padding = new RectOffset(16, 16, 4, 12); ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false; ((Component)content).gameObject.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2; scroll = ((Component)component2).gameObject.AddComponent<ScrollRect>(); scroll.viewport = component3; scroll.content = content; scroll.horizontal = false; scroll.movementType = (MovementType)2; scroll.scrollSensitivity = 40f; hint = Text((Transform)(object)content, 20f); } public void Scroll(float pixels) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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) Rect rect = content.rect; float height = ((Rect)(ref rect)).height; rect = scroll.viewport.rect; float num = height - ((Rect)(ref rect)).height; if (!(num <= 0f)) { scroll.verticalNormalizedPosition = Mathf.Clamp01(scroll.verticalNormalizedPosition - pixels / num); } } public void Clear() { foreach (GameObject value in views.Values) { if (Object.op_Implicit((Object)(object)value)) { Object.Destroy((Object)(object)value); } } views.Clear(); shown = null; } public void Show(SaveFileMetadata save) { if (Object.op_Implicit((Object)(object)shown)) { shown.SetActive(false); } if (((save != null) ? save.Header : null) == null) { ShowPreview(null); shown = null; ((Component)hint).gameObject.SetActive(true); ((TMP_Text)hint).text = ((SaveLibrary.Saves.Count > 0) ? "<color=#a0a0a0>Move the mouse over a save to see what is in it</color>" : ""); return; } ((Component)hint).gameObject.SetActive(false); ShowPreview(SceneCatalog.GetSceneDefFromSceneName(save.Header.SceneName)); if (!views.TryGetValue(save, out shown) || !Object.op_Implicit((Object)(object)shown)) { shown = Build(save); views[save] = shown; } shown.SetActive(true); scroll.verticalNormalizedPosition = 1f; } private void ShowPreview(SceneDef scene) { shownScene = scene; ((Behaviour)preview).enabled = false; if (!Object.op_Implicit((Object)(object)scene)) { return; } if (previews.TryGetValue(scene, out var value)) { SetPreview(value); } else { if (loading.ContainsKey(scene)) { return; } Texture previewTexture = scene.previewTexture; loading[scene] = AssetOrDirectReference<Texture>.GetNewInstance((AssetReferenceT<Texture>)(object)scene.previewTextureReference, true, previewTexture, (Action<Texture>)delegate(Texture loaded) { previews[scene] = loaded; if (Object.op_Implicit((Object)(object)preview) && (Object)(object)shownScene == (Object)(object)scene) { SetPreview(loaded); } }, (Action<Texture>)null); } } private void SetPreview(Texture texture) { if (Object.op_Implicit((Object)(object)texture)) { preview.texture = texture; ((Behaviour)preview).enabled = true; previewFitter.aspectRatio = (float)texture.width / (float)texture.height; } } private GameObject Build(SaveFileMetadata save) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0123: 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_0150: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023b: 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_0278: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Save", new Type[1] { typeof(RectTransform) }); val.transform.SetParent((Transform)(object)content, false); VerticalLayoutGroup obj = val.AddComponent<VerticalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)obj).spacing = 6f; ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false; SaveFileHeader header = save.Header; SaveInfo info = SaveLibrary.GetInfo(save); SaveContents saveContents = SaveContents.Of(save); ((TMP_Text)Text(val.transform, 20f)).text = RunText(save, info, saveContents); if (saveContents != null && saveContents.Artifacts.Count > 0) { Transform grid = Grid(val.transform, 34f); foreach (ArtifactIndex artifact in saveContents.Artifacts) { ArtifactIcon(grid, artifact); } } ReadOnlyCollection<NetworkUser> readOnlyInstancesList = NetworkUser.readOnlyInstancesList; bool flag = readOnlyInstancesList.Count == 1; for (int i = 0; i < header.Users.Length; i++) { HeaderUserData val2 = header.Users[i]; NetworkUserId id = val2.UserId.Load(); Line(val.transform); string text = "<size=115%><b>" + PlayerNames.Of(save, val2, i + 1) + "</b></size> " + Format.SurvivorName(val2.Body); if (!flag && !readOnlyInstancesList.Any(delegate(NetworkUser other) { //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_000a: Unknown result type (might be due to invalid IL or missing references) NetworkUserId id2 = other.id; return ((NetworkUserId)(ref id2)).Equals(id); })) { text += " <color=#ff8a80>not in the lobby</color>"; } ((TMP_Text)Text(val.transform, 20f)).text = text; SaveContents.PlayerContents playerContents = saveContents?.Find(id); SaveInfo.PlayerInfo playerInfo = info?.Find(id); ((TMP_Text)Text(val.transform, 18f)).text = PlayerText(playerContents, playerInfo); if (playerContents == null || (playerContents.Equipment.Count <= 0 && playerContents.Items.Count <= 0)) { continue; } Transform grid2 = Grid(val.transform, 40f); foreach (EquipmentIndex item2 in playerContents.Equipment) { EquipmentIcon(grid2, item2); } foreach (var (item, count) in playerContents.Items) { ItemIcon(grid2, item, count); } } string text2 = NotesText(save, saveContents); if (text2.Length > 0) { Line(val.transform); ((TMP_Text)Text(val.transform, 18f)).text = text2; } return val; } private static string RunText(SaveFileMetadata save, SaveInfo info, SaveContents contents) { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) SaveFileHeader header = save.Header; StringBuilder stringBuilder = new StringBuilder(); string text = info?.Note; stringBuilder.Append("<size=130%><b>" + (text ?? Format.SceneName(header.SceneName)) + "</b></size>\n"); if (text != null) { stringBuilder.Append(Format.SceneName(header.SceneName) + " "); } stringBuilder.Append($"Stage {header.StageClearCount + 1}"); if (contents != null && contents.Loops > 0) { stringBuilder.Append($" Loop {contents.Loops}"); } stringBuilder.Append(" " + Format.RunTime(header.Time)); DifficultyDef difficultyDef = DifficultyCatalog.GetDifficultyDef(header.Difficulty); if (difficultyDef != null) { stringBuilder.Append(" " + Language.GetString(difficultyDef.nameToken)); } stringBuilder.Append('\n'); uint num = ((info != null && info.Level != 0) ? info.Level : (contents?.Level ?? 0)); if (num != 0) { stringBuilder.Append("Level " + Format.Short(num) + " "); } if (info != null && info.MonsterLevel > 0f) { stringBuilder.Append("Monster level " + Format.Short(Math.Floor(info.MonsterLevel)) + " "); } if (contents != null && !string.IsNullOrEmpty(contents.NextScene) && contents.NextScene != header.SceneName) { stringBuilder.Append("Next stage: " + Format.SceneName(contents.NextScene)); } stringBuilder.Append('\n'); stringBuilder.Append("<color=#a0a0a0>Saved " + Format.Date(header.SaveDate) + "</color>"); if (contents != null && contents.Artifacts.Count > 0) { IEnumerable<string> values = contents.Artifacts.Select((ArtifactIndex artifact) => Language.GetString(ArtifactCatalog.GetArtifactDef(artifact).nameToken)); stringBuilder.Append("\n<color=#a0a0a0>Artifacts: " + string.Join(", ", values) + "</color>"); } return stringBuilder.ToString(); } private static string PlayerText(SaveContents.PlayerContents player, SaveInfo.PlayerInfo playerInfo) { if (player == null) { if (playerInfo == null) { return ""; } return $"{playerInfo.Items:N0} items {Format.Short(playerInfo.Gold)} gold"; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(Format.Short(player.ItemCount) + " items " + Format.Short(player.Money) + " gold"); if (player.VoidCoins != 0) { stringBuilder.Append(" " + Format.Short(player.VoidCoins) + " void coins"); } stringBuilder.Append(" " + Format.Short(player.LunarCoins) + " lunar coins\n"); stringBuilder.Append("<color=#a0a0a0>Kills " + Format.Short(player.Kills)); if (player.EliteKills != 0) { stringBuilder.Append(" (" + Format.Short(player.EliteKills) + " elites)"); } stringBuilder.Append(" Damage " + Format.Short(player.DamageDealt) + " Taken " + Format.Short(player.DamageTaken) + "\n"); stringBuilder.Append("Gold collected " + Format.Short(player.GoldCollected) + " Purchases " + Format.Short(player.Purchases) + "</color>"); return stringBuilder.ToString(); } private static string NotesText(SaveFileMetadata save, SaveContents contents) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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) SaveFileHeader header = save.Header; ReadOnlyCollection<NetworkUser> readOnlyInstancesList = NetworkUser.readOnlyInstancesList; StringBuilder stringBuilder = new StringBuilder(); if (readOnlyInstancesList.Count == 1) { HeaderUserData val = header.Users.FirstOrDefault(); if (val != null) { if (header.Users.Length <= 1) { NetworkUserId val2 = val.UserId.Load(); if (((NetworkUserId)(ref val2)).Equals(readOnlyInstancesList[0].id)) { goto IL_0155; } } stringBuilder.Append("<color=#ffd27f>Loading solo: you get " + Format.Possessive(PlayerNames.Of(save, val, 1)) + " items and gold</color>\n"); } } else { List<string> list = (from other in readOnlyInstancesList where !header.Users.Any(delegate(HeaderUserData user) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) NetworkUserId val3 = user.UserId.Load(); return ((NetworkUserId)(ref val3)).Equals(other.id); }) select PlayerNames.Of(other) ?? "A player").ToList(); if (list.Count > 0) { stringBuilder.Append("<color=#ff8a80>" + string.Join(", ", list) + " " + ((list.Count == 1) ? "isn't" : "aren't") + " in the save and start" + ((list.Count == 1) ? "s" : "") + " with nothing</color>\n"); } } goto IL_0155; IL_0155: if (!ProperSaveHooks.FitsLobby(save)) { stringBuilder.Append("<color=#ff8a80>Made in another game mode, it can't be loaded in this lobby</color>\n"); } else if (header.ContentHash != ProperSavePlugin.ContentHash) { stringBuilder.Append("<color=#ffd27f>Made with other mods than you have now, loading it can lead to errors</color>\n"); } if (contents == null) { stringBuilder.Append("<color=#a0a0a0>The items and stats of this save can't be read with the mods you have now</color>\n"); } return stringBuilder.ToString().TrimEnd('\n'); } private void ItemIcon(Transform grid, ItemIndex item, int count) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0008: 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) //IL_0028: 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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_0047: 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) ItemDef itemDef = ItemCatalog.GetItemDef(item); ItemTierDef itemTierDef = ItemTierCatalog.GetItemTierDef(itemDef.tier); Color titleColor = (Object.op_Implicit((Object)(object)itemTierDef) ? Color32.op_Implicit(ColorCatalog.GetColor(itemTierDef.colorIndex)) : Color.white); Icons.SetItem(Icon(grid, ITEM_BACKGROUND, count, itemDef.nameToken, itemDef.descriptionToken, titleColor), item); } private void EquipmentIcon(Transform grid, EquipmentIndex equipment) { //IL_0000: 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) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) EquipmentDef equipmentDef = EquipmentCatalog.GetEquipmentDef(equipment); Icons.SetEquipment(Icon(grid, EQUIPMENT_BACKGROUND, 1, equipmentDef.nameToken, equipmentDef.descriptionToken, Color32.op_Implicit(ColorCatalog.GetColor(equipmentDef.colorIndex))), equipment); } private void ArtifactIcon(Transform grid, ArtifactIndex artifact) { //IL_0000: 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) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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) ArtifactDef artifactDef = ArtifactCatalog.GetArtifactDef(artifact); Icons.SetArtifact(Icon(grid, ARTIFACT_BACKGROUND, 1, artifactDef.nameToken, artifactDef.descriptionToken, Color32.op_Implicit(ColorCatalog.GetColor((ColorIndex)24))), artifact); } private Image Icon(Transform grid, Color background, int count, string titleToken, string bodyToken, Color titleColor) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_003e: 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_008b: 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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Icon", new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(grid, false); ((Graphic)val.GetComponent<Image>()).color = background; Image component = new GameObject("Image", new Type[2] { typeof(RectTransform), typeof(Image) }).GetComponent<Image>(); ((Transform)((Graphic)component).rectTransform).SetParent(val.transform, false); Stretch(((Graphic)component).rectTransform, Vector2.zero, Vector2.one); ((Graphic)component).rectTransform.offsetMin = new Vector2(2f, 2f); ((Graphic)component).rectTransform.offsetMax = new Vector2(-2f, -2f); component.preserveAspect = true; ((Graphic)component).raycastTarget = false; if (count > 1) { TextMeshProUGUI obj = Text(val.transform, 15f); ((TMP_Text)obj).alignment = (TextAlignmentOptions)1028; ((TMP_Text)obj).enableWordWrapping = false; ((TMP_Text)obj).text = "<b>" + ((count < 10000) ? count.ToString() : Format.Short(count)) + "</b>"; Stretch(((TMP_Text)obj).rectTransform, Vector2.zero, Vector2.one); ((TMP_Text)obj).margin = new Vector4(0f, 0f, 2f, 0f); } TooltipProvider obj2 = val.AddComponent<TooltipProvider>(); obj2.titleToken = titleToken; obj2.bodyToken = bodyToken; obj2.titleColor = titleColor; return component; } private static Transform Grid(Transform parent, float size) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) GridLayoutGroup obj = new GameObject("Icons", new Type[1] { typeof(RectTransform) }).AddComponent<GridLayoutGroup>(); ((Component)obj).transform.SetParent(parent, false); obj.cellSize = new Vector2(size, size); obj.spacing = new Vector2(4f, 4f); obj.startCorner = (Corner)0; obj.startAxis = (Axis)0; ((LayoutGroup)obj).childAlignment = (TextAnchor)0; obj.constraint = (Constraint)0; return ((Component)obj).transform; } private static void Line(Transform parent) { //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_0037: 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) GameObject val = new GameObject("Line", new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(parent, false); Image component = val.GetComponent<Image>(); ((Graphic)component).color = LINE; ((Graphic)component).raycastTarget = false; val.AddComponent<LayoutElement>().preferredHeight = 2f; } private TextMeshProUGUI Text(Transform parent, float size) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI val = new GameObject("Text", new Type[1] { typeof(RectTransform) }).AddComponent<TextMeshProUGUI>(); ((Transform)((TMP_Text)val).rectTransform).SetParent(parent, false); if (Object.op_Implicit((Object)(object)font)) { ((TMP_Text)val).font = font; } ((TMP_Text)val).fontSize = size; ((TMP_Text)val).alignment = (TextAlignmentOptions)257; ((TMP_Text)val).richText = true; ((TMP_Text)val).enableWordWrapping = true; ((Graphic)val).raycastTarget = false; return val; } private static void Stretch(RectTransform rect, Vector2 anchorMin, Vector2 anchorMax) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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) rect.anchorMin = anchorMin; rect.anchorMax = anchorMax; rect.offsetMin = Vector2.zero; rect.offsetMax = Vector2.zero; } } internal static class FileDialog { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private class OpenFileName { public int structSize; public IntPtr owner; public IntPtr instance; public string filter; public string customFilter; public int maxCustomFilter; public int filterIndex; public IntPtr file; public int maxFile; public string fileTitle; public int maxFileTitle; public string initialDirectory; public string title; public int flags; public short fileOffset; public short fileExtension; public string defaultExtension; public IntPtr customData; public IntPtr hook; public string templateName; public IntPtr reserved; public int reserved2; public int flagsEx; } private const int MAX_PATH = 4096; private const string FILTER = "ProperSave save (*.bin)\0*.bin\0All files (*.*)\0*.*\0\0"; private const string EXTENSION = "bin"; private const int OFN_OVERWRITEPROMPT = 2; private const int OFN_NOCHANGEDIR = 8; private const int OFN_PATHMUSTEXIST = 2048; private const int OFN_FILEMUSTEXIST = 4096; private const int OFN_EXPLORER = 524288; public static string PickSaveLocation(string fileName, out bool failed) { return Show(save: true, fileName, out failed); } public static string PickFileToOpen(out bool failed) { return Show(save: false, null, out failed); } private static string Show(bool save, string fileName, out bool failed) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) failed = false; IntPtr intPtr = IntPtr.Zero; bool visible = Cursor.visible; CursorLockMode lockState = Cursor.lockState; int num = 0; try { char[] array = (fileName ?? "").ToCharArray(); byte[] array2 = new byte[8192]; Buffer.BlockCopy(array, 0, array2, 0, Math.Min(array.Length, 4095) * 2); intPtr = Marshal.AllocHGlobal(array2.Length); Marshal.Copy(array2, 0, intPtr, array2.Length); OpenFileName dialog = new OpenFileName { structSize = Marshal.SizeOf<OpenFileName>(), owner = GetActiveWindow(), filter = "ProperSave save (*.bin)\0*.bin\0All files (*.*)\0*.*\0\0", file = intPtr, maxFile = 4096, defaultExtension = "bin", title = (save ? "Export save" : "Import save"), flags = (0x80808 | (save ? 2 : 4096)) }; Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; while (ShowCursor(show: true) < 0) { num++; } num++; return (save ? GetSaveFileName(dialog) : GetOpenFileName(dialog)) ? Marshal.PtrToStringUni(intPtr) : null; } catch (Exception ex) { Log.Warning("Could not open the file window: " + ex.Message); failed = true; return null; } finally { if (intPtr != IntPtr.Zero) { Marshal.FreeHGlobal(intPtr); } for (int i = 0; i < num; i++) { ShowCursor(show: false); } Cursor.visible = visible; Cursor.lockState = lockState; } } [DllImport("comdlg32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool GetOpenFileName([In][Out] OpenFileName dialog); [DllImport("comdlg32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool GetSaveFileName([In][Out] OpenFileName dialog); [DllImport("user32.dll")] private static extern IntPtr GetActiveWindow(); [DllImport("user32.dll")] private static extern int ShowCursor(bool show); } internal static class Format { private static readonly string[] SUFFIXES = new string[7] { "", "K", "M", "B", "T", "Qa", "Qi" }; private static CultureInfo dateCulture; private static CultureInfo DateCulture { get { if (dateCulture != null) { return dateCulture; } dateCulture = CultureInfo.CurrentCulture; try { char[] array = new char[85]; int userDefaultLocaleName = GetUserDefaultLocaleName(array, array.Length); if (userDefaultLocaleName > 1) { dateCulture = new CultureInfo(new string(array, 0, userDefaultLocaleName - 1)); } } catch (Exception ex) { Log.Debug("Using the date format of the game: " + ex.Message); } return dateCulture; } } [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] private static extern int GetUserDefaultLocaleName([Out] char[] name, int length); public static string SaveName(SaveFileMetadata save) { string text = SaveLibrary.GetNote(save); if (text == null) { SaveFileHeader header = save.Header; text = SceneName((header != null) ? header.SceneName : null); } return text; } public static string Date(DateTime utc) { return utc.ToLocalTime().ToString("g", DateCulture); } public static string Possessive(string name) { if (!name.EndsWith("s", StringComparison.OrdinalIgnoreCase)) { return name + "'s"; } return name + "'"; } public static string SceneName(string sceneName) { SceneDef val = (string.IsNullOrEmpty(sceneName) ? null : SceneCatalog.GetSceneDefFromSceneName(sceneName)); if (!Object.op_Implicit((Object)(object)val)) { return sceneName; } return Language.GetString(val.nameToken); } public static string SurvivorName(BodyIndex body) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) SurvivorDef val = SurvivorCatalog.FindSurvivorDefFromBody(BodyCatalog.GetBodyPrefab(body)); if (!Object.op_Implicit((Object)(object)val)) { return "?"; } return Language.GetString(val.displayNameToken); } public static string RunTime(int seconds) { TimeSpan timeSpan = TimeSpan.FromSeconds(seconds); if (!(timeSpan.TotalHours >= 1.0)) { return $"{timeSpan.Minutes:00}:{timeSpan.Seconds:00}"; } return $"{(int)timeSpan.TotalHours}:{timeSpan.Minutes:00}:{timeSpan.Seconds:00}"; } public static string Short(double value) { if (double.IsNaN(value) || double.IsInfinity(value)) { return "?"; } if (value < 10000.0) { return value.ToString("N0"); } int num = (int)Math.Floor(Math.Log10(value)); double num2 = Math.Round(value / Math.Pow(10.0, num), 2); if (num2 >= 10.0) { num2 /= 10.0; num++; } int num3 = num / 3; if (num3 >= SUFFIXES.Length) { return num2.ToString("F2", CultureInfo.InvariantCulture) + "e" + num; } int num4 = num % 3; return (num2 * Math.Pow(10.0, num4)).ToString("F" + (2 - num4), CultureInfo.InvariantCulture) + SUFFIXES[num3]; } } internal static class Gamepad { public const int OPEN_ACTION = 19; public const int SUBMIT_ACTION = 14; public const int CANCEL_ACTION = 15; public const int SCROLL_UP_ACTION = 29; public const int SCROLL_DOWN_ACTION = 30; public static string ActionName(int action) { try { object result; if (!ReInput.isReady) { result = null; } else { InputAction action2 = ReInput.mapping.GetAction(action); result = ((action2 != null) ? action2.name : null); } return (string)result; } catch (Exception ex) { Log.Debug($"Could not find the input action {action}: {ex.Message}"); return null; } } public static bool IsUsed(MPEventSystem eventSystem) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 if (Object.op_Implicit((Object)(object)eventSystem)) { return (int)eventSystem.currentInputSource == 1; } return false; } public static UILayerKey AddLayer(GameObject gameObject, int priority) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown UILayer val = ScriptableObject.CreateInstance<UILayer>(); val.priority = priority; UILayerKey obj = gameObject.AddComponent<UILayerKey>(); obj.layer = val; obj.onBeginRepresentTopLayer = new UnityEvent(); obj.onEndRepresentTopLayer = new UnityEvent(); return obj; } public static int TopPriority() { int num = 0; foreach (UILayerKey instances in InstanceTracker.GetInstancesList<UILayerKey>()) { if (Object.op_Implicit((Object)(object)instances) && Object.op_Implicit((Object)(object)instances.layer) && instances.layer.priority > num) { num = instances.layer.priority; } } return num; } public static void Setup(MPButton button, UILayerKey layer) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) button.requiredTopLayer = layer; Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)3; ((Selectable)button).navigation = navigation; } public static void ScrollTo(ScrollRect scroll, RectTransform item) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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_0078: 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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)scroll) && Object.op_Implicit((Object)(object)item)) { Canvas.ForceUpdateCanvases(); Rect rect = scroll.viewport.rect; Bounds val = RectTransformUtility.CalculateRelativeRectTransformBounds((Transform)(object)scroll.viewport, (Transform)(object)item); float num = 0f; if (((Bounds)(ref val)).max.y > ((Rect)(ref rect)).yMax) { num = ((Bounds)(ref val)).max.y - ((Rect)(ref rect)).yMax; } else if (((Bounds)(ref val)).min.y < ((Rect)(ref rect)).yMin) { num = ((Bounds)(ref val)).min.y - ((Rect)(ref rect)).yMin; } if (num != 0f) { RectTransform content = scroll.content; content.anchoredPosition -= new Vector2(0f, num); } } } } internal static class Icons { private static readonly Dictionary<string, Sprite> loaded = new Dictionary<string, Sprite>(); private static readonly Dictionary<string, (AssetOrDirectReference<Sprite> reference, List<Image> waiting)> loading = new Dictionary<string, (AssetOrDirectReference<Sprite>, List<Image>)>(); public static void SetItem(Image image, ItemIndex item) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) ItemDef itemDef = ItemCatalog.GetItemDef(item); if (Object.op_Implicit((Object)(object)itemDef)) { Set(image, itemDef.pickupIconSpriteReference, itemDef.pickupIconSprite); } } public static void SetEquipment(Image image, EquipmentIndex equipment) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) EquipmentDef equipmentDef = EquipmentCatalog.GetEquipmentDef(equipment); if (Object.op_Implicit((Object)(object)equipmentDef)) { Set(image, equipmentDef.pickupIconSpriteReference, equipmentDef.pickupIconSprite); } } public static void SetArtifact(Image image, ArtifactIndex artifact) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) ArtifactDef artifactDef = ArtifactCatalog.GetArtifactDef(artifact); if (Object.op_Implicit((Object)(object)artifactDef)) { Set(image, artifactDef.smallIconSelectedReference, artifactDef.smallIconSelectedSprite); } } private static void Set(Image image, AssetReferenceT<Sprite> reference, Sprite direct) { string key = ((reference != null && ((AssetReference)reference).RuntimeKeyIsValid()) ? ((AssetReference)reference).AssetGUID : null); if (key == null) { Show(image, direct); return; } if (loaded.TryGetValue(key, out var value)) { Show(image, value); return; } ((Behaviour)image).enabled = false; if (loading.TryGetValue(key, out (AssetOrDirectReference<Sprite>, List<Image>) value2)) { value2.Item2.Add(image); return; } List<Image> item = new List<Image> { image }; loading[key] = (null, item); AssetOrDirectReference<Sprite> newInstance = AssetOrDirectReference<Sprite>.GetNewInstance(reference, true, direct, (Action<Sprite>)delegate(Sprite done) { loaded[key] = done; if (loading.TryGetValue(key, out (AssetOrDirectReference<Sprite>, List<Image>) value3)) { foreach (Image item2 in value3.Item2) { Show(item2, done); } value3.Item2.Clear(); } }, (Action<Sprite>)null); if (!loaded.ContainsKey(key)) { loading[key] = (newInstance, item); } else { loading[key] = (newInstance, new List<Image>()); } } private static void Show(Image image, Sprite sprite) { if (Object.op_Implicit((Object)(object)image)) { image.sprite = sprite; ((Behaviour)image).enabled = Object.op_Implicit((Object)(object)sprite); } } } internal static class LobbyButton { private class HostOnly : MonoBehaviour { public HGButton button; public static bool CanLoad { get { LobbyManager lobbyManager = PlatformSystems.lobbyManager; if (lobbyManager != null) { return lobbyManager.isInLobby == lobbyManager.ownsLobby; } return true; } } private void Update() { bool canLoad = CanLoad; if (Object.op_Implicit((Object)(object)button) && ((Selectable)button).interactable != canLoad) { ((Selectable)button).interactable = canLoad; } } } private class GlyphLabel : MonoBehaviour { public TextMeshProUGUI text; public string actionName; private float nextUpdate; private void OnEnable() { nextUpdate = 0f; } private void LateUpdate() { if (!(Time.unscaledTime < nextUpdate) && Object.op_Implicit((Object)(object)text)) { nextUpdate = Time.unscaledTime + 1f; MPEventSystem primaryEventSystem = MPEventSystemManager.primaryEventSystem; if (Gamepad.IsUsed(primaryEventSystem)) { ((TMP_Text)text).text = Glyphs.GetGlyphString(primaryEventSystem, actionName, (AxisRange)0); } } } } private class GamepadHint : MonoBehaviour { public TextMeshProUGUI text; public string actionName; private float nextUpdate; private void OnEnable() { nextUpdate = 0f; } private void Update() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) if (!(Time.unscaledTime < nextUpdate) && Object.op_Implicit((Object)(object)this.text)) { nextUpdate = Time.unscaledTime + 1f; MPEventSystem primaryEventSystem = MPEventSystemManager.primaryEventSystem; string text = (Object.op_Implicit((Object)(object)primaryEventSystem) ? Glyphs.GetGlyphString(primaryEventSystem, actionName, (AxisRange)0) : ""); ((TMP_Text)this.text).text = text + " Saves"; ((Graphic)this.text).color = (Color)(HostOnly.CanLoad ? Color.white : new Color(0.3f, 0.3f, 0.3f)); } } } [CompilerGenerated] private static class <>O { public static hook_Awake <0>__Awake; } private const string PROPER_SAVE_BUTTON = "[ProperSave] Load"; private const string BUTTON_NAME = "[ProperSaveMultiSave] Select"; private const string LABEL = "Saves"; private const float SPACING = 8f; public static bool CanLoad => HostOnly.CanLoad; public static void Init() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown object obj = <>O.<0>__Awake; if (obj == null) { hook_Awake val = Awake; <>O.<0>__Awake = val; obj = (object)val; } CharacterSelectController.Awake += (hook_Awake)obj; } private static void Awake(orig_Awake orig, CharacterSelectController self) { orig.Invoke(self); try { Create(self); } catch (Exception arg) { Log.Error($"Could not add the button for the saves to the lobby: {arg}"); } } private static void Create(CharacterSelectController lobby) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Expected O, but got Unknown //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) Transform load = FindChild(((Component)lobby).transform, "[ProperSave] Load"); if (!Object.op_Implicit((Object)(object)load)) { Log.Warning("ProperSave's Load button isn't there, the saves can't be picked in this lobby"); return; } GameObject val = Object.Instantiate<GameObject>(((Component)load).gameObject, load.parent); ((Object)val).name = "[ProperSaveMultiSave] Select"; UiHelpers.MakePlain(val, "Saves"); HGButton component = val.GetComponent<HGButton>(); ((Button)component).onClick = new ButtonClickedEvent(); ((UnityEvent)((Button)component).onClick).AddListener((UnityAction)delegate { SavePopup.Open(lobby, ((Component)load).gameObject); }); RectTransform val2 = (RectTransform)load; val.GetComponent<RectTransform>().anchoredPosition = val2.anchoredPosition - new Vector2(val2.sizeDelta.x + 8f, 0f); InputSourceFilter[] components = ((Component)lobby).GetComponents<InputSourceFilter>(); foreach (InputSourceFilter val3 in components) { if ((int)val3.requiredInputSource == 0) { Array.Resize(ref val3.objectsToFilter, val3.objectsToFilter.Length + 1); val3.objectsToFilter[val3.objectsToFilter.Length - 1] = val; break; } } val.AddComponent<HostOnly>().button = component; AddGamepadOpener(lobby, val, ((Component)load).gameObject); FixGamepadLoad(lobby); } private static void FixGamepadLoad(CharacterSelectController lobby) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) HGGamepadInputEvent val = ((IEnumerable<HGGamepadInputEvent>)((Component)lobby).GetComponents<HGGamepadInputEvent>()).FirstOrDefault((Func<HGGamepadInputEvent, bool>)((HGGamepadInputEvent input) => ((object)input).GetType().Namespace?.StartsWith("ProperSave") ?? false)); if (!Object.op_Implicit((Object)(object)val) || string.IsNullOrEmpty(val.actionName)) { return; } InputBindingDisplayController[] componentsInChildren = ((Component)lobby).GetComponentsInChildren<InputBindingDisplayController>(true); foreach (InputBindingDisplayController val2 in componentsInChildren) { if (!(val2.actionName != val.actionName)) { val2.axisRange = (AxisRange)0; val2.useExplicitInputSource = false; TextMeshProUGUI component = ((Component)val2).GetComponent<TextMeshProUGUI>(); if (Object.op_Implicit((Object)(object)component)) { GlyphLabel glyphLabel = ((Component)val2).gameObject.AddComponent<GlyphLabel>(); glyphLabel.text = component; glyphLabel.actionName = val.actionName; } } } } private static void AddGamepadOpener(CharacterSelectController lobby, GameObject select, GameObject template) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: 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_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Expected O, but got Unknown //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Expected O, but got Unknown string text = Gamepad.ActionName(19); if (text == null) { Log.Warning("The gamepad button for the saves can't be found, with a gamepad they can only be opened with the mouse"); return; } GameObject val = new GameObject("[ProperSaveMultiSave] Gamepad hint", new Type[1] { typeof(RectTransform) }); val.SetActive(false); RectTransform val2 = (RectTransform)val.transform; RectTransform val3 = (RectTransform)select.transform; ((Transform)val2).SetParent(((Transform)val3).parent, false); val2.anchorMin = val3.anchorMin; val2.anchorMax = val3.anchorMax; val2.pivot = val3.pivot; val2.anchoredPosition = val3.anchoredPosition; val2.sizeDelta = val3.sizeDelta; TextMeshProUGUI val4 = new GameObject("Text", new Type[1] { typeof(RectTransform) }).AddComponent<TextMeshProUGUI>(); ((Transform)((TMP_Text)val4).rectTransform).SetParent((Transform)(object)val2, false); ((TMP_Text)val4).rectTransform.anchorMin = Vector2.zero; ((TMP_Text)val4).rectTransform.anchorMax = Vector2.one; ((TMP_Text)val4).rectTransform.offsetMin = Vector2.zero; ((TMP_Text)val4).rectTransform.offsetMax = Vector2.zero; ((TMP_Text)val4).alignment = (TextAlignmentOptions)514; ((TMP_Text)val4).fontSize = 24f; ((Graphic)val4).raycastTarget = false; TextMeshProUGUI componentInChildren = template.GetComponentInChildren<TextMeshProUGUI>(true); TMP_FontAsset val5 = ((componentInChildren != null) ? ((TMP_Text)componentInChildren).font : null); if (Object.op_Implicit((Object)(object)val5)) { ((TMP_Text)val4).font = val5; } GamepadHint gamepadHint = val.AddComponent<GamepadHint>(); gamepadHint.text = val4; gamepadHint.actionName = text; HGGamepadInputEvent val6 = ((Component)lobby).gameObject.AddComponent<HGGamepadInputEvent>(); val6.actionName = text; UILayerKey component = ((Component)lobby).GetComponent<UILayerKey>(); val6.requiredTopLayer = (Object.op_Implicit((Object)(object)component) ? component : ((Component)lobby).GetComponentInParent<UILayerKey>()); val6.enabledObjectsIfActive = (GameObject[])(object)new GameObject[1] { val }; val6.actionEvent = new UnityEvent(); val6.actionEvent.AddListener((UnityAction)delegate { if (HostOnly.CanLoad) { SavePopup.Open(lobby, template); } }); } private static Transform FindChild(Transform parent, string name) { Transform[] componentsInChildren = ((Component)parent).GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { if (((Object)val).name == name) { return val; } } return null; } } internal static class UiHelpers { public static void MakePlain(GameObject button, string label) { LanguageTextMeshController component = button.GetComponent<LanguageTextMeshController>(); if (Object.op_Implicit((Object)(object)component)) { Object.DestroyImmediate((Object)(object)component); } TooltipProvider component2 = button.GetComponent<TooltipProvider>(); if (Object.op_Implicit((Object)(object)component2)) { Object.DestroyImmediate((Object)(object)component2); } HGButton component3 = button.GetComponent<HGButton>(); if (Object.op_Implicit((Object)(object)component3)) { component3.hoverToken = null; } TextMeshProUGUI componentInChildren = button.GetComponentInChildren<TextMeshProUGUI>(true); if (Object.op_Implicit((Object)(object)componentInChildren)) { ((TMP_Text)componentInChildren).text = label; } } } internal static class Log { private static ManualLogSource _logSource; internal static void Init(ManualLogSource logSource) { _logSource = logSource; } internal static void Debug(object data) { _logSource.LogDebug(data); } internal static void Error(object data) { _logSource.LogError(data); } internal static void Fatal(object data) { _logSource.LogFatal(data); } internal static void Info(object data) { _logSource.LogInfo(data); } internal static void Message(object data) { _logSource.LogMessage(data); } internal static void Warning(object data) { _logSource.LogWarning(data); } } internal static class PlayerNames { private static readonly string[] UNKNOWN = new string[4] { "??", "???", "[unknown]", "[Unknown]" }; private static readonly Dictionary<string, string> resolved = new Dictionary<string, string>(); public static bool IsKnown(string name) { if (!string.IsNullOrWhiteSpace(name)) { return !UNKNOWN.Contains(name.Trim()); } return false; } public static string Of(SaveFileMetadata save, HeaderUserData user, int number) { //IL_000d: 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_0025: 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) NetworkUserId id = user.UserId.Load(); string text = SaveLibrary.GetInfo(save)?.Find(id)?.Name; if (!IsKnown(text)) { text = ((IEnumerable<NetworkUser>)NetworkUser.readOnlyInstancesList).FirstOrDefault((Func<NetworkUser, bool>)delegate(NetworkUser other) { //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_000a: Unknown result type (might be due to invalid IL or missing references) NetworkUserId id2 = other.id; return ((NetworkUserId)(ref id2)).Equals(id); })?.userName; } if (!IsKnown(text)) { text = FromPlatform(id); } if (!IsKnown(text)) { return $"Player {number}"; } if (id.subId <= 0) { return text; } return $"{text} ({id.subId + 1})"; } public static string Of(NetworkUser user) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)user)) { return null; } if (IsKnown(user.userName)) { return user.userName; } string text = FromPlatform(user.id); if (!IsKnown(text)) { return null; } return text; } private static string FromPlatform(NetworkUserId id) { //IL_0000: 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_0021: 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_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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) string text = SaveInfo.IdOf(id); if (resolved.TryGetValue(text, out var value)) { return value; } try { NetworkPlayerName val = default(NetworkPlayerName); if (id.value != 0L) { val.playerId = ((NetworkUserId)(ref id)).steamId; } else { if (string.IsNullOrEmpty(id.strValue) || !PlatformSystems.ShouldUseEpicOnlineSystems) { return null; } val.nameOverride = id.strValue; } value = ((NetworkPlayerName)(ref val)).GetResolvedName(); } catch (Exception ex) { Log.Debug("Could not get the name of " + text + ": " + ex.Message); return null; } if (IsKnown(value)) { resolved[text] = value; } return value; } } internal static class ProperSaveHooks { private const BindingFlags ANY_STATIC = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static Hook getSaveHook; private static Hook saveGameHook; private static Hook gameOverHook; private static Hook tooltipHook; private static bool saving; public static SaveFileMetadata Selected { get; private set; } public static event Action<SaveFileMetadata> onSaving; public static event Action<SaveFileMetadata> onSaved; public static bool Init() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Expected O, but got Unknown //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown MethodInfo method = typeof(SaveFileMetadata).GetMethod("GetCurrentLobbySaveMetadata", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo method2 = typeof(Saving).GetMethod("SaveGame", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo method3 = typeof(Saving).GetMethod("RunOnServerGameOver", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method == null || method2 == null || method3 == null) { return false; } getSaveHook = new Hook((MethodBase)method, (Delegate)new Func<Func<NetworkUser, SaveFileMetadata>, NetworkUser, SaveFileMetadata>(GetCurrentLobbySaveMetadata)); saveGameHook = new Hook((MethodBase)method2, (Delegate)new Action<Action>(SaveGame)); gameOverHook = new Hook((MethodBase)method3, (Delegate)new Action<Action<Run, GameEndingDef>, Run, GameEndingDef>(RunOnServerGameOver)); MethodInfo method4 = typeof(LobbyUI).GetMethod("GetSaveDescription", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method4 != null) { tooltipHook = new Hook((MethodBase)method4, (Delegate)new Func<Func<SaveFileMetadata, string>, SaveFileMetadata, string>(GetSaveDescription)); } else { Log.Warning("ProperSave's Load tooltip can't be found, it shows the names the way ProperSave does"); } Run.onRunStartGlobal += delegate { Selected = null; }; return true; } public static void Select(SaveFileMetadata save) { Selected = save; } public static bool FitsLobby(SaveFileMetadata save) { //IL_001d: 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) if (save != null && save.Header != null && Object.op_Implicit((Object)(object)PreGameController.instance)) { return save.Header.GameMode == PreGameController.instance.gameModeIndex; } return false; } private static string GetSaveDescription(Func<SaveFileMetadata, string> orig, SaveFileMetadata save) { //IL_0039: 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) try { SaveFileHeader header = save.Header; StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < header.Users.Length; i++) { HeaderUserData val = header.Users[i]; stringBuilder.Append(Language.GetStringFormatted(LanguageConsts.PROPER_SAVE_TOOLTIP_LOAD_DESCRIPTION_CHARACTER, new object[2] { PlayerNames.Of(save, val, i + 1), Format.SurvivorName(val.Body) })); } DifficultyDef difficultyDef = DifficultyCatalog.GetDifficultyDef(header.Difficulty); return Language.GetStringFormatted(LanguageConsts.PROPER_SAVE_TOOLTIP_LOAD_DESCRIPTION_BODY, new object[6] { stringBuilder.ToString(), Format.SceneName(header.SceneName), (header.StageClearCount + 1).ToString(), Format.RunTime(header.Time), (difficultyDef != null) ? Language.GetString(difficultyDef.nameToken) : "", (header.ContentHash != ProperSavePlugin.ContentHash) ? Language.GetString(LanguageConsts.PROPER_SAVE_TOOLTIP_LOAD_CONTENT_MISMATCH) : "" }); } catch (Exception ex) { Log.Warning("Could not write the tooltip of the save: " + ex.Message); return orig(save); } } private static SaveFileMetadata GetCurrentLobbySaveMetadata(Func<NetworkUser, SaveFileMetadata> orig, NetworkUser exceptUser) { if (saving) { return ProperSavePlugin.CurrentSave; } if (Selected != null) { if (SaveFileMetadata.SavesMetadata.Contains(Selected) && FitsLobby(Selected)) { return Selected; } Selected = null; } return orig(exceptUser); } private static void SaveGame(Action orig) { SaveFileMetadata currentSave = ProperSavePlugin.CurrentSave; try { ProperSaveHooks.onSaving?.Invoke(currentSave); } catch (Exception data) { Log.Error(data); } saving = true; try { orig(); } finally { saving = false; } SaveFileMetadata currentSave2 = ProperSavePlugin.CurrentSave; if (currentSave2 != null && currentSave2 != currentSave) { if (currentSave2.Header != null) { currentSave2.Header.SaveDate = DateTime.UtcNow; } try { ProperSaveHooks.onSaved?.Invoke(currentSave2); } catch (Exception data2) { Log.Error(data2); } } } private static void RunOnServerGameOver(Action<Run, GameEndingDef> orig, Run run, GameEndingDef ending) { SaveFileMetadata currentSave = ProperSavePlugin.CurrentSave; if (ProperSaveMultiSave.deleteOnRunEnd.Value && !SaveLibrary.IsFavorite(currentSave)) { orig(run, ending); if (currentSave != null) { SaveLibrary.OnRunEndDeleted(currentSave); } } } } [BepInPlugin("360gaggi.ProperSaveMultiSave", "ProperSaveMultiSave", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class ProperSaveMultiSave : BaseUnityPlugin { public const string PLUGIN_GUID = "360gaggi.ProperSaveMultiSave"; public const string PLUGIN_AUTHOR = "360gaggi"; public const string PLUGIN_NAME = "ProperSaveMultiSave"; public const string PLUGIN_VERSION = "1.0.0"; public const string PROPER_SAVE_GUID = "com.KingEnderBrine.ProperSave"; public const int MAX_SAVES = 500; public const int MAX_STAGES_KEPT = 20; public static ConfigEntry<bool> deleteOnRunEnd; public static ConfigEntry<int> maxSaves; public static ConfigEntry<int> stagesKept; public void Awake() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown Log.Init(((BaseUnityPlugin)this).Logger); deleteOnRunEnd = ((BaseUnityPlugin)this).Config.Bind<bool>("Saves", "Delete when the run ends", false, "Deletes the save of a run once it is over, by dying or by winning. That is what ProperSave does on its own, with this off the save stays and the run can be loaded again from its last stage"); maxSaves = ((BaseUnityPlugin)this).Config.Bind<int>("Saves", "Max saves", 50, new ConfigDescription("How many saves are kept at most, the oldest ones are deleted first. The save of the run that is going on and favorites are never deleted. 0 keeps all of them", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 500), Array.Empty<object>())); stagesKept = ((BaseUnityPlugin)this).Config.Bind<int>("Saves", "Stages kept per run", 1, new ConfigDescription("How many stages of a run are kept as saves of their own. 1 only keeps the newest one, like ProperSave. With more you can go back to an earlier stage of the run, the oldest go first. Favorites are never deleted", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>())); if (!ProperSaveHooks.Init()) { PluginInfo value; string text = (Chainloader.PluginInfos.TryGetValue("com.KingEnderBrine.ProperSave", out value) ? value.Metadata.Version.ToString() : "?"); Log.Error("This version of ProperSave (" + text + ") isn't one this mod knows, it stays off"); return; } SaveLibrary.Init(); LobbyButton.Init(); SavePopup.Init(); if (RiskOfOptionsCompat.Enabled) { RiskOfOptionsCompat.Init(((BaseUnityPlugin)this).Info.Location); } } } internal static class RiskOfOptionsCompat { public const string GUID = "com.rune580.riskofoptions"; private const string ICON_FILE = "icon.png"; public static bool Enabled => Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions"); [MethodImpl(MethodImplOptions.NoInlining)] public static void Init(string pluginLocation) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0033: 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_003f: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_004f: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown ModSettingsManager.SetModDescription("Keeps a save of every run instead of one for every group of players, and lets you pick, export, import and delete them in the lobby."); Sprite val = LoadIcon(pluginLocation); if (Object.op_Implicit((Object)(object)val)) { ModSettingsManager.SetModIcon(val); } ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(ProperSaveMultiSave.deleteOnRunEnd)); ConfigEntry<int> maxSaves = ProperSaveMultiSave.maxSaves; IntFieldConfig val2 = new IntFieldConfig(); ((NumericFieldConfig<int>)val2).Min = 0; ((NumericFieldConfig<int>)val2).Max = 500; ModSettingsManager.AddOption((BaseOption)new IntFieldOption(maxSaves, val2)); ModSettingsManager.AddOption((BaseOption)new IntSliderOption(ProperSaveMultiSave.stagesKept, new IntSliderConfig { min = 1, max = 20 })); } private static Sprite LoadIcon(string pluginLocation) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0067: 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) string directoryName = Path.GetDirectoryName(pluginLocation); string path = Path.Combine(directoryName, "icon.png"); if (!File.Exists(path)) { path = Path.Combine(Path.GetDirectoryName(directoryName), "icon.png"); } if (!File.Exists(path)) { return null; } Texture2D val = new Texture2D(2, 2); if (!ImageConversion.LoadImage(val, File.ReadAllBytes(path))) { return null; } return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); } } internal sealed class SaveContents { public sealed class PlayerContents { public NetworkUserId Id; public uint Money; public uint VoidCoins; public uint LunarCoins; public int ItemCount; public readonly List<(ItemIndex item, int count)> Items = new List<(ItemIndex, int)>(); public readonly List<EquipmentIndex> Equipment = new List<EquipmentIndex>(); public ulong Kills; public ulong EliteKills; public ulong DamageDealt; public ulong DamageTaken; public ulong GoldCollected; public ulong Purchases; } public int Loops; public uint Level; public string NextScene; public readonly List<ArtifactIndex> Artifacts = new List<ArtifactIndex>(); public readonly List<PlayerContents> Players = new List<PlayerContents>(); private static readonly Dictionary<string, (DateTime saveDate, SaveContents contents)> cache = new Dictionary<string, (DateTime, SaveContents)>(); public static SaveContents Of(SaveFileMetadata save) { if (save == null || save.FileName == null || save.Header == null) { return null; } DateTime saveDate = save.Header.SaveDate; if (cache.TryGetValue(save.FileName, out (DateTime, SaveContents) value) && value.Item1 == saveDate) { return value.Item2; } SaveContents saveContents = null; try { SaveFile val = save.Body ?? ReadBody(save); if (val != null) { saveContents = From(val); } } catch (Exception ex) { Log.Warning("Could not read what is in save " + save.FileName + ": " + ex.Message); } cache[save.FileName] = (saveDate, saveContents); return saveContents; } public static void Forget(SaveFileMetadata save) { if (save != null && save.FileName != null) { cache.Remove(save.FileName); } } private static SaveFile ReadBody(SaveFileMetadata save) { //IL_001e: 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) if (!save.FilePath.HasValue || !ProperSavePlugin.SavesFileSystem.FileExists(save.FilePath.Value)) { return null; } try { using Stream input = ProperSavePlugin.SavesFileSystem.OpenFile(save.FilePath.Value, FileMode.Open, FileAccess.Read, FileShare.Read); using BinaryReader binaryReader = new BinaryReader(input); SaveFileHeader.Read(binaryReader); return SaveFile.Read(binaryReader); } finally { ObjectBuffer.Clear(); } } private static SaveContents From(SaveFile body) { SaveContents saveContents = new SaveContents(); if (body.RunData != null) { saveContents.Loops = body.RunData.loopClearCount; saveContents.NextScene = body.RunData.nextSceneName; } if (body.TeamData != null) { saveContents.Level = TeamManager.FindLevelForExperience((ulong)Math.Max(0L, body.TeamData.experience)); } if (body.RunArtifactsData?.artifacts != null) { saveContents.Artifacts.AddRange(body.RunArtifactsData.artifacts.Where((ArtifactIndex artifact) => (Object)(object)ArtifactCatalog.GetArtifactDef(artifact) != (Object)null)); } foreach (PlayerData playersDatum in body.PlayersData) { saveContents.Players.Add(FromPlayer(playersDatum)); } return saveContents; } private static PlayerContents FromPlayer(PlayerData data) { //IL_001b: 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_0018: 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_007e: 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_013a: 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) PlayerContents playerContents = new PlayerContents(); UserIDData userId = data.userId; playerContents.Id = (NetworkUserId)((userId != null) ? userId.Load() : default(NetworkUserId)); playerContents.LunarCoins = data.lunarCoins; PlayerContents playerContents2 = playerContents; CharacterMasterData master = data.master; if (master != null) { playerContents2.Money = master.money; playerContents2.VoidCoins = master.voidCoins; InventoryData inventory = master.inventory; if (inventory != null) { foreach (ItemData item in inventory.items) { ItemDef itemDef = ItemCatalog.GetItemDef(item.itemIndex); int num = item.count + item.tempCount; if (Object.op_Implicit((Object)(object)itemDef) && !itemDef.hidden && num > 0) { playerContents2.Items.Add((item.itemIndex, num)); playerContents2.ItemCount += num; } } if (inventory.equipments != null) { EquipmentSlotData[] equipments = inventory.equipments; foreach (EquipmentSlotData val in equipments) { if (val?.sets == null) { continue; } EquipmentStateData[] sets = val.sets; foreach (EquipmentStateData val2 in sets) { if (val2 != null && (Object)(object)EquipmentCatalog.GetEquipmentDef(val2.index) != (Object)null) { playerContents2.Equipment.Add(val2.index); } } } } } } foreach (StatFieldData statsField in data.statsFields) { if (statsField.index >= 0 && statsField.index < StatDef.allStatDefs.Count) { StatDef val3 = StatDef.allStatDefs[statsField.index]; if (val3 == StatDef.totalKills) { playerContents2.Kills = statsField.value; } else if (val3 == StatDef.totalEliteKills) { playerContents2.EliteKills = statsField.value; } else if (val3 == StatDef.totalDamageDealt) { playerContents2.DamageDealt = statsField.value; } else if (val3 == StatDef.totalDamageTaken) { playerContents2.DamageTaken = statsField.value; } else if (val3 == StatDef.goldCollected) { playerContents2.GoldCollected = statsField.value; } else if (val3 == StatDef.totalPurchases) { playerContents2.Purchases = statsField.value; } } } return playerContents2; } public PlayerContents Find(NetworkUserId id) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) return Players.FirstOrDefault((PlayerContents player) => ((NetworkUserId)(ref player.Id)).Equals(id)); } } internal sealed class SaveInfo { public sealed class PlayerInfo { public string Id; public string Name; public int Items; public ulong Gold; } public List<PlayerInfo> Players = new List<PlayerInfo>(); public uint Level; public float MonsterLevel; public string RunId; public bool Favorite; public string Folder; public string Note; public string ToJson() { return JsonConvert.SerializeObject((object)this); } public SaveInfo CopyAs(string runId, bool favorite) { SaveInfo saveInfo = Copy(); saveInfo.RunId = runId; saveInfo.Favorite = favorite; return saveInfo; } public SaveInfo Copy() { return FromJson(ToJson()) ?? new SaveInfo(); } public static string NewRunId() { return Guid.NewGuid().ToString("N"); } public static SaveInfo FromJson(string json) { try { return JsonConvert.DeserializeObject<SaveInfo>(json); } catch (Exception ex) { Log.Warning("Could not read the info of a save: " + ex.Message); return null; } } public static SaveInfo FromCurrentRun(SaveInfo before, string runId) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) SaveInfo saveInfo = new SaveInfo { RunId = runId, Favorite = (before?.Favorite ?? false), Folder = before?.Folder, Note = before?.Note }; if (Object.op_Implicit((Object)(object)TeamManager.instance)) { saveInfo.Level = TeamManager.instance.GetTeamLevel((TeamIndex)1); } if (Object.op_Implicit((Object)(object)Run.instance)) { saveInfo.MonsterLevel = (float.IsNaN(Run.instance.ambientLevel) ? 0f : Math.Min(Run.instance.ambientLevel, float.MaxValue)); } foreach (PlayerCharacterMasterController instance in PlayerCharacterMasterController.instances) { HeaderUserData val = HeaderUserData.Create(instance); if (val != null) { string id = IdOf(val.UserId.Load()); CharacterMaster master = instance.master; saveInfo.Players.Add(new PlayerInfo { Id = id, Name = (PlayerNames.Of(instance.networkUser) ?? before?.Players.FirstOrDefault((PlayerInfo other) => other.Id == id)?.Name), Items = ((Object.op_Implicit((Object)(object)master) && Object.op_Implicit((Object)(object)master.inventory)) ? CountItems(master.inventory) : 0), Gold = (Object.op_Implicit((Object)(object)master) ? master.money : 0u) }); } } return saveInfo; } public PlayerInfo Find(NetworkUserId id) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) string key = IdOf(id); return Players.FirstOrDefault((PlayerInfo player) => player.Id == key); } public static string IdOf(NetworkUserId id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //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) return $"{id.value}|{id.strValue}|{id.subId}"; } private static int CountItems(Inventory inventory) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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) int num = 0; foreach (ItemIndex item in inventory.itemAcquisitionOrder) { num += inventory.GetItemCountPermanent(item); } return num; } } internal static class SaveLibrary { private const string INFO_EXTENSION = ".multisave.json"; private static readonly byte[] EXPORT_MARKER = Encoding.ASCII.GetBytes("MULTISAV"); private static string runId; private static readonly Dictionary<string, (DateTime saveDate, SaveInfo info)> infoCache = new Dictionary<string, (DateTime, SaveInfo)>(); public const int MAX_NOTE = 48; public const int MAX_FOLDER_NAME = 32; private const string FOLDERS_FILE = "ProperSaveMultiSave.folders.json"; private static List<string> madeFolders; public static List<SaveFileMetadata> Saves => SaveFileMetadata.SavesMetadata; private static FileSystem Files => ProperSavePlugin.SavesFileSystem; public static List<string> Folders => (from folder in MadeFolders.Concat(Saves.Select(GetFolder)) where folder != null select folder).Distinct<string>(StringComparer.OrdinalIgnoreCase).OrderBy<string, string>((string folder) => folder, StringComparer.OrdinalIgnoreCase).ToList(); private static List<string> MadeFolders { get { if (madeFolders == null) { madeFolders = ReadFolders(); } return madeFolders; } } public static void Init() { ProperSaveHooks.onSaving += OnSaving; ProperSaveHooks.onSaved += OnSaved; Run.onRunStartGlobal += delegate { runId = null; }; } private static UPath InfoPath(SaveFileMetadata save) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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) return ProperSavePlugin.SavesPath / UPath.op_Implicit(save.FileName + ".multisave.json"); } private static void OnSaving(SaveFileMetadata current) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) if (runId == null) { runId = GetRunId(current); } if (current == null || ProperSaveMultiSave.stagesKept.Value <= 1 || !current.FilePath.HasValue || !Files.FileExists(current.FilePath.Value)) { return; } try { SaveInfo info = GetInfo(current); Add(FileSystemExtensions.ReadAllBytes((IFileSystem)(object)Files, current.FilePath.Value), (info != null) ? info.CopyAs(runId, favorite: false) : new SaveInfo { RunId = runId }); } catch (Exception ex) { Log.Warning("Could not keep the stage before: " + ex.Message); } } private static string GetRunId(SaveFileMetadata current) { string id = GetInfo(current)?.RunId; if (id == null) { return SaveInfo.NewRunId(); } SaveFileHeader header = current.Header; DateTime date = ((header != null) ? header.SaveDate : default(DateTime)); if (!Saves.Any(delegate(SaveFileMetadata other) { if (other != current && GetInfo(other)?.RunId == id) { SaveFileHeader header2 = other.Header; return ((header2 != null) ? header2.SaveDate : default(DateTime)) > date; } return false; })) { return id; } return SaveInfo.NewRunId(); } private static void OnSaved(SaveFileMetadata save) { WriteInfo(save, SaveInfo.FromCurrentRun(GetInfo(save), runId)); DeleteOldStages(save); DeleteOldest(save); } public static SaveInfo GetInfo(SaveFileMetadata save) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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) //IL_006e: Unknown result type (might be due to invalid IL or missing references) if (save == null || save.FileName == null) { return null; } SaveFileHeader header = save.Header; DateTime dateTime = ((header != null) ? header.SaveDate : default(DateTime)); if (infoCache.TryGetValue(save.FileName, out (DateTime, SaveInfo) value) && value.Item1 == dateTime) { return value.Item2; } SaveInfo saveInfo = null; try { UPath val = InfoPath(save); if (Files.FileExists(val)) { saveInfo = SaveInfo.FromJson(FileSystemExtensions.ReadAllText((IFileSystem)(object)Files, val)); } } catch (Exception ex) { Log.Warning("Could not read the info of save " + save.FileName + ": " + ex.Message); } infoCache[save.FileName] = (dateTime, saveInfo); return saveInfo; } private static void WriteInfo(SaveFileMetadata save, SaveInfo info) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) try { FileSystemExtensions.WriteAllText((IFileSystem)(object)Files, InfoPath(save), info.ToJson()); Dictionary<string, (DateTime saveDate, SaveInfo info)> dictionary = infoCache; string fileName = save.FileName; SaveFileHeader header = save.Header; dictionary[fileName] = ((header != null) ? header.SaveDate : default(DateTime), info); } catch (Exception ex) { Log.Warning("Could not write the info of save " + save.FileName + ": " + ex.Message); } } public static bool IsFavorite(SaveFileMetadata save) { if (save != null) { return GetInfo(save)?.Favorite ?? false; } return false; } public static void SetFavorite(SaveFileMetadata save, bool favorite) { ChangeInfo(save, delegate(SaveInfo info) { info.Favorite = favorite; }); } public static string GetNote(SaveFileMetadata save) { if (save == null) { return null; } return GetInfo(save)?.Note; } public static void SetNote(SaveFileMetadata save, string note) { note = note?.Trim(); if (string.IsNullOrEmpty(note)) { note = null; } else if (note.Length > 48) { note = note.Substring(0, 48).Trim(); } ChangeInfo(save, delegate(SaveInfo info) { info.Note = note; }); } public static string GetFolder(SaveFileMetadata save) { if (save == null) { return null; } return GetInfo(save)?.Folder; } public static string CreateFolder(string folder) { folder = CleanFolderName(folder); if (folder == null) { return null; } string text = Folders.FirstOrDefault((string other) => SameFolder(other, folder)); if (text != null) { folder = text; } if (!MadeFolders.Any((string other) => SameFolder(other, folder))) { MadeFolders.Add(folder); WriteFolders(); } return folder; } public static void SetFolder(SaveFileMetadata save, string folder) { folder = CreateFolder(folder); ChangeInfo(save, delegate(SaveInfo info) { info.Folder = folder; }); } public static void RenameFolder(string folder, string newName) { newName = CreateFolder(newName); if (newName == null || folder == newName) { return; } MadeFolders.RemoveAll((string other) => SameFolder(other, folder) && !SameFolder(other, newName)); WriteFolders(); foreach (SaveFileMetadata item in Saves.Where((SaveFileMetadata save) => SameFolder(GetFolder(save), folder)).ToList()) { ChangeInfo(item, delegate(SaveInfo info) { info.Folder = newName; }); } } public static void DeleteFolder(string folder) { MadeFolders.RemoveAll((string other) => SameFolder(other, folder)); WriteFolders(); foreach (SaveFileMetadata item in Saves.Where((SaveFileMetadata save) => SameFolder(GetFolder(save), folder)).ToList()) { ChangeInfo(item, delegate(SaveInfo info) { info.Folder = null; }); } } public static int CountIn(string folder) { return Saves.Count((SaveFileMetadata save) => SameFolder(GetFolder(save), folder)); } private static List<string> ReadFolders() { //IL_0000: 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_000f: Unknown result type (might be due to invalid IL or missing references) //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_0027: Unknown result type (might be due to invalid IL or missing references) try { UPath val = ProperSavePlugin.SavesPath / UPath.op_Implicit("ProperSaveMultiSave.folders.json"); if (Files.FileExists(val)) { return JsonConvert.DeserializeObject<List<string>>(FileSystemExtensions.ReadAllText((IFileSystem)(object)Files, val)) ?? new List<string>(); } } catch (Exception ex) { Log.Warning("Could not read the folders: " + ex.Message); } return new List<string>(); } private static void WriteFolders() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) try { FileSystemExtensions.WriteAllText((IFileSystem)(object)Files, ProperSavePlugin.SavesPath / UPath.op_Implicit("ProperSaveMultiSave.folders.json"), JsonConvert.SerializeObject((object)madeFolders)); } catch (Exception ex) { Log.Warning("Could not write the folders: " + ex.Message); } } public static bool SameFolder(string a, string b) { return string.Equals(a, b, StringComparison.OrdinalIgnoreCase); } public static string CleanFolderName(string folder) { folder = folder?.Trim(); if (string.IsNullOrEmpty(folder)) { return null; } if (folder.Length <= 32) { return folder; } return folder.Substring(0, 32).Trim(); } private static void ChangeInfo(SaveFileMetadata save, Action<SaveInfo> change) { SaveInfo saveInfo = GetInfo(save)?.Copy() ?? new SaveInfo(); change(saveInfo); WriteInfo(save, saveInfo); } public static void Delete(SaveFileMetadata save) { //IL_001e: 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) try { if (save.FilePath.HasValue && Files.FileExists(save.FilePath.Value)) { Files.DeleteFile(save.FilePath.Value); } } catch (Exception ex) { Log.Warning("Could not delete save " + save.FileName + ": " + ex.Message); } Saves.Remove(save); DeleteExtras(save); } public static void DeleteExtras(SaveFileMetadata save) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (ProperSaveHooks.Selected == save) { ProperSaveHooks.Select(null); } if (save.FileName == null) { return; } infoCache.Remove(save.FileName); SaveContents.Forget(save); try { UPath val = InfoPath(save); if (Files.FileExists(val)) { Files.DeleteFile(val); } } catch (Exception ex) { Log.Warning("Could not delete the info of save " + save.FileName + ": " + ex.Message); } } public static void OnRunEndDeleted(SaveFileMetadata save) { DeleteExtras(save); if (runId == null) { return; } foreach (SaveFileMetadata item in Saves.Where((SaveFileMetadata other) => GetInfo(other)?.RunId == runId && !IsFavorite(other)).ToList()) { Delete(item); } } private static void DeleteOldStages(SaveFileMetadata current) { int count = Math.Max(0, ProperSaveMultiSave.stagesKept.Value - 1); foreach (SaveFileMetadata item in Saves.Where((SaveFileMetadata save) => save != current && GetInfo(save)?.RunId == runId && !IsFavorite(save)).OrderByDescending(delegate(SaveFileMetadata save) { SaveFileHeader header = save.Header; return (header == null) ? default(DateTime) : header.SaveDate; }).Skip(count) .ToList()) { Delete(item); } } private static void DeleteOldest(SaveFileMetadata current) { int value = ProperSaveMultiSave.maxSaves.Value; if (value <= 0 || Saves.Count <= value) { return; } foreach (SaveFileMetadata item in Saves.Where((SaveFileMetadata save) => save != current && !IsFavorite(save)).OrderBy(delegate(SaveFileMetadata save) { SaveFileHeader header2 = save.Header; return (header2 == null) ? default(DateTime) : header2.SaveDate; }).Take(Saves.Count - value) .ToList()) { object arg = value; SaveFileHeader header = item.Header; Log.Info($"More than {arg} saves, deleting the oldest one ({((header != null) ? new DateTime?(header.SaveDate) : ((DateTime?)null)):g})"); Delete(item); } } public static void Export(SaveFileMetadata save, string path) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) byte[] array = FileSystemExtensions.ReadAllBytes((IFileSystem)(object)Files, save.FilePath.Value); using FileStream fileStream = File.Create(path); fileStream.Write(array, 0, array.Length); SaveInfo info = GetInfo(save); if (info != null) { byte[] bytes = Encoding.UTF8.GetBytes(info.ToJson()); fileStream.Write(bytes, 0, bytes.Length); fileStream.Write(BitConverter.GetBytes(bytes.Length), 0, 4); fileStream.Write(EXPORT_MARKER, 0, EXPORT_MARKER.Length); } } public static SaveFileMetadata Import(string path) { byte[] bytes = File.ReadAllBytes(path); SaveInfo saveInfo = TakeInfo(ref bytes); using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(bytes))) { SaveFileHeader.Read(binaryReader); } return Add(bytes, saveInfo?.CopyAs(SaveInfo.NewRunId(), favorite: false)); } public static SaveFileMetadata Duplicate(SaveFileMetadata save) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) return Add(FileSystemExtensions.ReadAllBytes((IFileSystem)(object)Files, save.FilePath.Value), GetInfo(save)?.CopyAs(SaveInfo.NewRunId(), favorite: false)); } private static SaveFileMetadata Add(byte[] bytes, SaveInfo info) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) SaveFileMetadata val = new SaveFileMetadata(); do { val.FileName = Guid.NewGuid().ToString(); } while (Files.FileExists(val.FilePath.Value)); FileSystemExtensions.WriteAllBytes((IFileSystem)(object)Files, val.FilePath.Value, bytes); val.ReadHeader(); if (info != null) { WriteInfo(val, info); } Saves.Add(val); Sort(); return val; } private static SaveInfo TakeInfo(ref byte[] bytes) { int num = bytes.Length - EXPORT_MARKER.Length; if (num < 4) { return null; } for (int i = 0; i < EXPORT_MARKER.Length; i++) { if (bytes[num + i] != EXPORT_MARKER[i]) { return null; } } int num2 = BitConverter.ToInt32(bytes, num - 4); int num3 = num - 4 - num2; if (num2 <= 0 || num3 < 0) { return null; } SaveInfo result = SaveInfo.FromJson(Encoding.UTF8.GetString(bytes, num3, num2)); Array.Resize(ref bytes, num3); return result; } private static void Sort() { List<SaveFileMetadata> collection = Saves.OrderByDescending(delegate(SaveFileMetadata save) { SaveFileHeader header = save.Header; return ((header != null) ? header.ContentHash : null) == ProperSavePlugin.ContentHash; }).ThenByDescending(delegate(SaveFileMetadata save) { SaveFileHeader header = save.Header; return (header == null) ? default(DateTime) : header.SaveDate; }).ToList(); Saves.Clear(); Saves.AddRange(collection); } } internal class SavePopup : MonoBehaviour { private enum SortOrder { Newest, Oldest, HighestStage, LongestRun } private class Entry { public SaveFileMetadata save; public GameObject button; public GameObject marker; } private class ListItem : MonoBehaviour { public string groupKey; public SaveFileMetadata save; } private class Group { public string key; public string name; public string folder; public bool favorites; public List<SaveFileMetadata> saves; } private class Hover : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler { public Action onEnter; public Action onExit; public void OnPointerEnter(PointerEventData eventData) { onEnter?.Invoke(); } public void OnPointerExit(PointerEventData eventData) { onExit?.Invoke(); } } private class Drag : MonoBehaviour, IBeginDragHandler, IEventSystemHandler, IDragHandler, IEndDragHandler { public Action<PointerEventData> onBegin; public Action<PointerEventData> onDrag; public Action onEnd; public void OnBeginDrag(PointerEventData eventData) { onBegin?.Invoke(eventData); } public void OnDrag(PointerEventData eventData) { onDrag?.Invoke(eventData); } public void OnEndDrag(PointerEventData eventData) { onEnd?.Invoke(); } } private class Drop : MonoBehaviour, IDropHandler, IEventSystemHandler { public Action onDrop; public void OnDrop(PointerEventData eventData) { onDrop?.Invoke(); } } [CompilerGenerated] private static class <>O { public static hook_CCTogglePause <0>__TogglePause; public static Func<SaveFileMetadata, bool> <1>__IsFavorite; } private static SortOrder sortOrder = SortOrder.Newest; private static readonly HashSet<string> collapsed = new HashSet<string>(); private const string FAVORITES_KEY = "<favorites>"; private const string REST_KEY = "<rest>"; private static readonly Color BACKDROP = new Color(0f, 0f, 0f, 0.8f); private static readonly Color WINDOW = new Color(0.07f, 0.08f, 0.1f, 0.98f); private static readonly Color AREA = new Color(1f, 1f, 1f, 0.04f); private static readonly Color MARKER = new Color(0.95f, 0.78f, 0.3f); internal const string WARNING = "#ff8a80"; internal const string NOTE = "#ffd27f"; internal const string GREY = "#a0a0a0"; private const float ENTRY_HEIGHT = 72f; private const float HEADER_HEIGHT = 44f; private const float BUTTON_HEIGHT = 56f; private const float SPACING = 8f; private const float GAMEPAD_SCROLL_SPEED = 900f; private static SavePopup instance; private GameObject template; private TMP_FontAsset font; private static string star = "*"; private static string open = "-"; private static string closed = "+"; private TextMeshProUGUI sortLabel; private UILayerKey mainLayer; private MPEventSystemLocator eventSystemLocator; private HGButton closeButton; private TextMeshProUGUI gamepadHint; private HGButton moveHereButton; private HGButton takeOutButton; private RectTransform list; private DetailsPanel details; private TextMeshProUGUI status; private HGButton loadButton; private HGButton selectButton; private HGButton favoriteButton; private TextMeshProUGUI favoriteLabel; private HGButton folderButton; private HGButton nameButton; private HGButton duplicateButton; private HGButton exportButton; private HGButton deleteButton; private TextMeshProUGUI deleteLabel; private GameObject folderPanel; private TextMeshProUGUI folderTitle; private RectTransform folderList; private TMP_InputField folderInput; private HGButton newFolderButton; private HGButton renameButton; private HGButton deleteFolderButton; private TextMeshProUGUI deleteFolderLabel; private readonly List<(string folder, GameObject marker)> folderEntries = new List<(string, GameObject)>(); private string pickedFolder; private bool confirmDeleteFolder; private GameObject namePanel; private TextMeshProUGUI nameTitle; private TMP_InputField nameInput; private const float DOUBLE_CLICK_TIME = 0.4f; private SaveFileMetadata lastClicked; private float lastClickTime; private RectTransform ghost; private TextMeshProUGUI ghostText; private SaveFileMetadata dragged; private readonly List<Entry> entries = new List<Entry>(); private readonly List<ListItem> listItems = new List<ListItem>(); private SaveFileMetadata focused; private bool confirmDelete; private static string FallbackFolder => Path.Combine(Application.persistentDataPath, "ProperSaveMultiSave"); private MPEventSystem EventSystem { get { if (!Object.op_Implicit((Object)(object)eventSystemLocator)) { return null; } return eventSystemLocator.eventSystem; } } public static void Open(CharacterSelectController lobby, GameObject template) { //IL_0030: 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) if (!Object.op_Implicit((Object)(object)instance)) { Canvas rootCanvas = ((Component)lobby).GetComponentInParent<Canvas>().rootCanvas; GameObject val = new GameObject("[ProperSaveMultiSave] Saves", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(((Component)rootCanvas).transform, false); instance = val.AddComponent<SavePopup>(); instance.Build(template); } ((Component)instance).transform.SetAsLastSibling(); ((Component)instance).gameObject.SetActive(true); instance.focused = SaveFileMetadata.GetCurrentLobbySaveMetadata((NetworkUser)null); ((TMP_Text)instance.status).text = ""; instance.Refresh(); instance.UpdateGamepadHint(); if (Gamepad.IsUsed(instance.EventSystem)) { instance.SelectInList(null); } } priva