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 v3.0.7
plugins/ProperSave/ProperSave.dll
Decompiled 4 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.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.Serialization; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BiggerBazaar; using HG; using HG.Reflection; using IL.RoR2; using Mono.Cecil; using Mono.Cecil.Cil; using MonoMod.Cil; using MonoMod.RuntimeDetour.HookGen; using On.RoR2; using On.RoR2.UI; using PSTinyJson; using ProperSave.Components; using ProperSave.Data; using ProperSave.Old; using ProperSave.Old.Data; using ProperSave.Old.SaveData; using ProperSave.Old.SaveData.Artifacts; using ProperSave.Old.SaveData.Runs; using ProperSave.SaveData; using ProperSave.SaveData.Artifacts; using ProperSave.SaveData.Runs; using ProperSave.TinyJson; using ProperSave.Utils; using R2API; using RoR2; using RoR2.Artifacts; using RoR2.CharacterAI; using RoR2.ContentManagement; using RoR2.ExpansionManagement; using RoR2.Networking; using RoR2.Skills; using RoR2.Stats; using RoR2.UI; using ShareSuite; using TMPro; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.SceneManagement; using UnityEngine.UI; using Zio; using Zio.FileSystems; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: OptIn] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("3.0.7.0")] namespace PSTinyJson { public static class JSONParser { [ThreadStatic] private static Stack<List<string>> splitArrayPool; [ThreadStatic] private static StringBuilder stringBuilder; [ThreadStatic] private static Dictionary<Type, Dictionary<string, FieldInfo>> fieldInfoCache; [ThreadStatic] private static Dictionary<Type, Dictionary<string, PropertyInfo>> propertyInfoCache; public static T FromJson<T>(this string json) { return (T)json.FromJson(typeof(T)); } public static object FromJson(this string json, Type type) { if (propertyInfoCache == null) { propertyInfoCache = new Dictionary<Type, Dictionary<string, PropertyInfo>>(); } if (fieldInfoCache == null) { fieldInfoCache = new Dictionary<Type, Dictionary<string, FieldInfo>>(); } if (stringBuilder == null) { stringBuilder = new StringBuilder(); } if (splitArrayPool == null) { splitArrayPool = new Stack<List<string>>(); } stringBuilder.Length = 0; for (int i = 0; i < json.Length; i++) { char c = json[i]; if (c == '"') { i = AppendUntilStringEnd(appendEscapeCharacter: true, i, json); } else if (!char.IsWhiteSpace(c)) { stringBuilder.Append(c); } } return ParseValue(type, stringBuilder.ToString()); } private static int AppendUntilStringEnd(bool appendEscapeCharacter, int startIdx, string json) { stringBuilder.Append(json[startIdx]); for (int i = startIdx + 1; i < json.Length; i++) { if (json[i] == '\\') { if (appendEscapeCharacter) { stringBuilder.Append(json[i]); } stringBuilder.Append(json[i + 1]); i++; } else { if (json[i] == '"') { stringBuilder.Append(json[i]); return i; } stringBuilder.Append(json[i]); } } return json.Length - 1; } private static List<string> Split(string json) { List<string> list = ((splitArrayPool.Count > 0) ? splitArrayPool.Pop() : new List<string>()); list.Clear(); if (json.Length == 2) { return list; } int num = 0; stringBuilder.Length = 0; for (int i = 1; i < json.Length - 1; i++) { switch (json[i]) { case '[': case '{': num++; break; case ']': case '}': num--; break; case '"': i = AppendUntilStringEnd(appendEscapeCharacter: true, i, json); continue; case ',': case ':': if (num == 0) { list.Add(stringBuilder.ToString()); stringBuilder.Length = 0; continue; } break; } stringBuilder.Append(json[i]); } list.Add(stringBuilder.ToString()); return list; } internal static object ParseValue(Type type, string json) { if (type == typeof(string)) { if (json.Length <= 2) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(json.Length); for (int i = 1; i < json.Length - 1; i++) { if (json[i] == '\\' && i + 1 < json.Length - 1) { int num = "\"\\nrtbf/".IndexOf(json[i + 1]); if (num >= 0) { stringBuilder.Append("\"\\\n\r\t\b\f/"[num]); i++; continue; } if (json[i + 1] == 'u' && i + 5 < json.Length - 1) { uint result = 0u; if (uint.TryParse(json.Substring(i + 2, 4), NumberStyles.AllowHexSpecifier, null, out result)) { stringBuilder.Append((char)result); i += 5; continue; } } } stringBuilder.Append(json[i]); } return stringBuilder.ToString(); } if (type.IsPrimitive) { return Convert.ChangeType(json, type, CultureInfo.InvariantCulture); } if (type == typeof(decimal)) { decimal.TryParse(json, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2); return result2; } if (json == "null") { return null; } if (type.IsEnum) { if (json[0] == '"') { json = json.Substring(1, json.Length - 2); try { return Enum.Parse(type, json, ignoreCase: false); } catch { return 0; } } return Convert.ChangeType(json, Enum.GetUnderlyingType(type)); } if (type.IsArray) { Type elementType = type.GetElementType(); if (json[0] != '[' || json[json.Length - 1] != ']') { return null; } List<string> list = Split(json); Array array = Array.CreateInstance(elementType, list.Count); for (int j = 0; j < list.Count; j++) { array.SetValue(ParseValue(elementType, list[j]), j); } splitArrayPool.Push(list); return array; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) { Type type2 = type.GetGenericArguments()[0]; if (json[0] != '[' || json[json.Length - 1] != ']') { return null; } List<string> list2 = Split(json); IList list3 = (IList)type.GetConstructor(new Type[1] { typeof(int) }).Invoke(new object[1] { list2.Count }); for (int k = 0; k < list2.Count; k++) { list3.Add(ParseValue(type2, list2[k])); } splitArrayPool.Push(list2); return list3; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { Type[] genericArguments = type.GetGenericArguments(); Type type3 = genericArguments[0]; Type type4 = genericArguments[1]; if (type3 != typeof(string)) { return null; } if (json[0] != '{' || json[json.Length - 1] != '}') { return null; } List<string> list4 = Split(json); if (list4.Count % 2 != 0) { return null; } IDictionary dictionary = (IDictionary)type.GetConstructor(new Type[1] { typeof(int) }).Invoke(new object[1] { list4.Count / 2 }); for (int l = 0; l < list4.Count; l += 2) { if (list4[l].Length > 2) { string key = list4[l].Substring(1, list4[l].Length - 2); object value = ParseValue(type4, list4[l + 1]); dictionary.Add(key, value); } } return dictionary; } if (type == typeof(object)) { return ParseAnonymousValue(json); } if (json[0] == '{' && json[json.Length - 1] == '}') { return ParseObject(type, json); } return null; } private static object ParseAnonymousValue(string json) { if (json.Length == 0) { return null; } if (json[0] == '{' && json[json.Length - 1] == '}') { List<string> list = Split(json); if (list.Count % 2 != 0) { return null; } Dictionary<string, object> dictionary = new Dictionary<string, object>(list.Count / 2); for (int i = 0; i < list.Count; i += 2) { dictionary.Add(list[i].Substring(1, list[i].Length - 2), ParseAnonymousValue(list[i + 1])); } return dictionary; } if (json[0] == '[' && json[json.Length - 1] == ']') { List<string> list2 = Split(json); List<object> list3 = new List<object>(list2.Count); for (int j = 0; j < list2.Count; j++) { list3.Add(ParseAnonymousValue(list2[j])); } return list3; } if (json[0] == '"' && json[json.Length - 1] == '"') { return json.Substring(1, json.Length - 2).Replace("\\", string.Empty); } if (char.IsDigit(json[0]) || json[0] == '-') { if (json.Contains(".")) { double.TryParse(json, NumberStyles.Float, CultureInfo.InvariantCulture, out var result); return result; } int.TryParse(json, out var result2); return result2; } if (json == "true") { return true; } if (json == "false") { return false; } return null; } private static Dictionary<string, T> CreateMemberNameDictionary<T>(T[] members) where T : MemberInfo { Dictionary<string, T> dictionary = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase); foreach (T val in members) { if (val.IsDefined(typeof(IgnoreDataMemberAttribute), inherit: true)) { continue; } string name = val.Name; if (val.IsDefined(typeof(DataMemberAttribute), inherit: true)) { DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)Attribute.GetCustomAttribute(val, typeof(DataMemberAttribute), inherit: true); if (!string.IsNullOrEmpty(dataMemberAttribute.Name)) { name = dataMemberAttribute.Name; } } dictionary.Add(name, val); } return dictionary; } private static object ParseObject(Type type, string json) { object uninitializedObject = FormatterServices.GetUninitializedObject(type); List<string> list = Split(json); if (list.Count % 2 != 0) { return uninitializedObject; } if (!fieldInfoCache.TryGetValue(type, out var value)) { value = CreateMemberNameDictionary(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy)); fieldInfoCache.Add(type, value); } if (!propertyInfoCache.TryGetValue(type, out var value2)) { value2 = CreateMemberNameDictionary(type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy)); propertyInfoCache.Add(type, value2); } for (int i = 0; i < list.Count; i += 2) { if (list[i].Length > 2) { string key = list[i].Substring(1, list[i].Length - 2); string json2 = list[i + 1]; PropertyInfo value4; if (value.TryGetValue(key, out var value3)) { DiscoverObjectTypeAttribute customAttribute = value3.GetCustomAttribute<DiscoverObjectTypeAttribute>(); value3.SetValue(uninitializedObject, ParseValue(customAttribute?.GetObjectType(uninitializedObject) ?? value3.FieldType, json2)); } else if (value2.TryGetValue(key, out value4)) { DiscoverObjectTypeAttribute customAttribute2 = value4.GetCustomAttribute<DiscoverObjectTypeAttribute>(); value4.SetValue(uninitializedObject, ParseValue(customAttribute2?.GetObjectType(uninitializedObject) ?? value4.PropertyType, json2), null); } } } return uninitializedObject; } } } namespace ProperSave { public static class Extensions { [StructLayout(LayoutKind.Explicit)] private struct Union { [FieldOffset(0)] public ushort @ushort; [FieldOffset(0)] public uint @uint; [FieldOffset(0)] public ulong @ulong; [FieldOffset(8)] public bool negative; public readonly short @short { get { if (!negative) { return (short)@ushort; } return (short)(-@ushort); } } public readonly int @int { get { if (!negative) { return (int)@uint; } return (int)(0 - @uint); } } public readonly long @long { get { if (!negative) { return (long)@ulong; } return (long)(0L - @ulong); } } public Union(ulong value, bool negative) { @ushort = 0; @uint = 0u; @ulong = value; this.negative = negative; } public static implicit operator Union(short value) { return new Union { @ushort = (ushort)Math.Abs(value), negative = (value < 0) }; } public static implicit operator Union(ushort value) { return new Union { @ushort = value }; } public static implicit operator Union(int value) { return new Union { @uint = (uint)Math.Abs(value), negative = (value < 0) }; } public static implicit operator Union(uint value) { return new Union { @uint = value }; } public static implicit operator Union(long value) { return new Union { @ulong = (ulong)Math.Abs(value), negative = (value < 0) }; } public static implicit operator Union(ulong value) { return new Union { @ulong = value }; } } public static int DifferenceCount<T>(this IEnumerable<T> collection, IEnumerable<T> second) { List<T> list = second.ToList(); int num = 0; foreach (T item in collection) { if (!list.Remove(item)) { num++; } } return num + list.Count; } public static string GetSafe(this string[] list, int index) { if ((uint)index > list.Length) { return null; } return list[index]; } public static int AddOrIndexOf(this List<string> list, string value) { if (value == null) { return -1; } int num = list.IndexOf(value); if (num < 0) { list.Add(value); return list.Count - 1; } return num; } public static T GetSafe<T>(this List<T> list, int index) { if (list == null || list.Count < (uint)index) { return default(T); } return list[index]; } public static void WritePacked(this BinaryWriter writer, short value) { WritePackedSigned(writer, value); } public static void WritePacked(this BinaryWriter writer, ushort value) { WritePacked(writer, (Union)value); } public static void WritePacked(this BinaryWriter writer, int value) { WritePackedSigned(writer, value); } public static void WritePacked(this BinaryWriter writer, uint value) { WritePacked(writer, (Union)value); } public static void WritePacked(this BinaryWriter writer, long value) { WritePackedSigned(writer, value); } public static void WritePacked(this BinaryWriter writer, ulong value) { WritePacked(writer, (Union)value); } private static void WritePackedSigned(BinaryWriter writer, Union union) { ulong num = (ulong)(union.negative ? 0 : 128); ulong num2 = union.@ulong; if (num2 <= 112) { writer.Write((byte)(num2 | num)); } else if (num2 <= 2159) { writer.Write((byte)(((num2 - 112) / 256 + 113) | num)); writer.Write((byte)((num2 - 112) % 256)); } else if (num2 <= 67694) { writer.Write((byte)(0x79 | num)); writer.Write((byte)((num2 - 2160) / 256)); writer.Write((byte)((num2 - 2160) % 256)); } else if (num2 <= 16777215) { writer.Write((byte)(0x7A | num)); writer.Write((byte)(num2 & 0xFF)); writer.Write((byte)((num2 >> 8) & 0xFF)); writer.Write((byte)((num2 >> 16) & 0xFF)); } else if (num2 <= uint.MaxValue) { writer.Write((byte)(0x7B | num)); writer.Write((byte)(num2 & 0xFF)); writer.Write((byte)((num2 >> 8) & 0xFF)); writer.Write((byte)((num2 >> 16) & 0xFF)); writer.Write((byte)((num2 >> 24) & 0xFF)); } else if (num2 <= 1099511627775L) { writer.Write((byte)(0x7C | num)); writer.Write((byte)(num2 & 0xFF)); writer.Write((byte)((num2 >> 8) & 0xFF)); writer.Write((byte)((num2 >> 16) & 0xFF)); writer.Write((byte)((num2 >> 24) & 0xFF)); writer.Write((byte)((num2 >> 32) & 0xFF)); } else if (num2 <= 281474976710655L) { writer.Write((byte)(0x7D | num)); writer.Write((byte)(num2 & 0xFF)); writer.Write((byte)((num2 >> 8) & 0xFF)); writer.Write((byte)((num2 >> 16) & 0xFF)); writer.Write((byte)((num2 >> 24) & 0xFF)); writer.Write((byte)((num2 >> 32) & 0xFF)); writer.Write((byte)((num2 >> 40) & 0xFF)); } else if (num2 <= 72057594037927935L) { writer.Write((byte)(0x7E | num)); writer.Write((byte)(num2 & 0xFF)); writer.Write((byte)((num2 >> 8) & 0xFF)); writer.Write((byte)((num2 >> 16) & 0xFF)); writer.Write((byte)((num2 >> 24) & 0xFF)); writer.Write((byte)((num2 >> 32) & 0xFF)); writer.Write((byte)((num2 >> 40) & 0xFF)); writer.Write((byte)((num2 >> 48) & 0xFF)); } else { writer.Write((byte)(0x7F | num)); writer.Write((byte)(num2 & 0xFF)); writer.Write((byte)((num2 >> 8) & 0xFF)); writer.Write((byte)((num2 >> 16) & 0xFF)); writer.Write((byte)((num2 >> 24) & 0xFF)); writer.Write((byte)((num2 >> 32) & 0xFF)); writer.Write((byte)((num2 >> 40) & 0xFF)); writer.Write((byte)((num2 >> 48) & 0xFF)); writer.Write((byte)((num2 >> 56) & 0xFF)); } } private static void WritePacked(BinaryWriter writer, Union union) { ulong num = union.@ulong; if (num <= 240) { writer.Write((byte)num); } else if (num <= 2287) { writer.Write((byte)((num - 240) / 256 + 241)); writer.Write((byte)((num - 240) % 256)); } else if (num <= 67823) { writer.Write((byte)249); writer.Write((byte)((num - 2288) / 256)); writer.Write((byte)((num - 2288) % 256)); } else if (num <= 16777215) { writer.Write((byte)250); writer.Write((byte)(num & 0xFF)); writer.Write((byte)((num >> 8) & 0xFF)); writer.Write((byte)((num >> 16) & 0xFF)); } else if (num <= uint.MaxValue) { writer.Write((byte)251); writer.Write((byte)(num & 0xFF)); writer.Write((byte)((num >> 8) & 0xFF)); writer.Write((byte)((num >> 16) & 0xFF)); writer.Write((byte)((num >> 24) & 0xFF)); } else if (num <= 1099511627775L) { writer.Write((byte)252); writer.Write((byte)(num & 0xFF)); writer.Write((byte)((num >> 8) & 0xFF)); writer.Write((byte)((num >> 16) & 0xFF)); writer.Write((byte)((num >> 24) & 0xFF)); writer.Write((byte)((num >> 32) & 0xFF)); } else if (num <= 281474976710655L) { writer.Write((byte)253); writer.Write((byte)(num & 0xFF)); writer.Write((byte)((num >> 8) & 0xFF)); writer.Write((byte)((num >> 16) & 0xFF)); writer.Write((byte)((num >> 24) & 0xFF)); writer.Write((byte)((num >> 32) & 0xFF)); writer.Write((byte)((num >> 40) & 0xFF)); } else if (num <= 72057594037927935L) { writer.Write((byte)254); writer.Write((byte)(num & 0xFF)); writer.Write((byte)((num >> 8) & 0xFF)); writer.Write((byte)((num >> 16) & 0xFF)); writer.Write((byte)((num >> 24) & 0xFF)); writer.Write((byte)((num >> 32) & 0xFF)); writer.Write((byte)((num >> 40) & 0xFF)); writer.Write((byte)((num >> 48) & 0xFF)); } else { writer.Write(byte.MaxValue); writer.Write((byte)(num & 0xFF)); writer.Write((byte)((num >> 8) & 0xFF)); writer.Write((byte)((num >> 16) & 0xFF)); writer.Write((byte)((num >> 24) & 0xFF)); writer.Write((byte)((num >> 32) & 0xFF)); writer.Write((byte)((num >> 40) & 0xFF)); writer.Write((byte)((num >> 48) & 0xFF)); writer.Write((byte)((num >> 56) & 0xFF)); } } public static short ReadPackedInt16(this BinaryReader reader) { return ReadPackedSigned(reader).@short; } public static ushort ReadPackedUInt16(this BinaryReader reader) { return ReadPacked(reader).@ushort; } public static int ReadPackedInt32(this BinaryReader reader) { return ReadPackedSigned(reader).@int; } public static uint ReadPackedUInt32(this BinaryReader reader) { return ReadPacked(reader).@uint; } public static long ReadPackedInt64(this BinaryReader reader) { return ReadPackedSigned(reader).@long; } public static ulong ReadPackedUInt64(this BinaryReader reader) { return ReadPacked(reader).@ulong; } private static Union ReadPacked(BinaryReader reader) { byte b = reader.ReadByte(); if (b < 241) { return (ulong)b; } byte b2 = reader.ReadByte(); if (b >= 241 && b <= 248) { return (ulong)(240 + 256 * ((long)b - 241L) + b2); } byte b3 = reader.ReadByte(); if (b == 249) { return (ulong)(2288 + 256L * (long)b2 + b3); } byte b4 = reader.ReadByte(); if (b == 250) { return b2 + ((ulong)b3 << 8) + ((ulong)b4 << 16); } byte b5 = reader.ReadByte(); if (b == 251) { return b2 + ((ulong)b3 << 8) + ((ulong)b4 << 16) + ((ulong)b5 << 24); } byte b6 = reader.ReadByte(); if (b == 252) { return b2 + ((ulong)b3 << 8) + ((ulong)b4 << 16) + ((ulong)b5 << 24) + ((ulong)b6 << 32); } byte b7 = reader.ReadByte(); if (b == 253) { return b2 + ((ulong)b3 << 8) + ((ulong)b4 << 16) + ((ulong)b5 << 24) + ((ulong)b6 << 32) + ((ulong)b7 << 40); } byte b8 = reader.ReadByte(); if (b == 254) { return b2 + ((ulong)b3 << 8) + ((ulong)b4 << 16) + ((ulong)b5 << 24) + ((ulong)b6 << 32) + ((ulong)b7 << 40) + ((ulong)b8 << 48); } byte b9 = reader.ReadByte(); if (b == byte.MaxValue) { return b2 + ((ulong)b3 << 8) + ((ulong)b4 << 16) + ((ulong)b5 << 24) + ((ulong)b6 << 32) + ((ulong)b7 << 40) + ((ulong)b8 << 48) + ((ulong)b9 << 56); } throw new IndexOutOfRangeException("ReadPacked() failure: " + b); } private static Union ReadPackedSigned(BinaryReader reader) { byte b = reader.ReadByte(); bool negative = (b & 0x80) == 0; b &= 0x7F; if (b <= 112) { return new Union(b, negative); } byte b2 = reader.ReadByte(); if (b >= 113 && b <= 120) { return new Union((ulong)(112 + 256 * ((long)b - 113L) + b2), negative); } byte b3 = reader.ReadByte(); if (b == 121) { return new Union((ulong)(2160 + 256L * (long)b2 + b3), negative); } byte b4 = reader.ReadByte(); if (b == 122) { return new Union(b2 + ((ulong)b3 << 8) + ((ulong)b4 << 16), negative); } byte b5 = reader.ReadByte(); if (b == 123) { return new Union(b2 + ((ulong)b3 << 8) + ((ulong)b4 << 16) + ((ulong)b5 << 24), negative); } byte b6 = reader.ReadByte(); if (b == 124) { return new Union(b2 + ((ulong)b3 << 8) + ((ulong)b4 << 16) + ((ulong)b5 << 24) + ((ulong)b6 << 32), negative); } byte b7 = reader.ReadByte(); if (b == 125) { return new Union(b2 + ((ulong)b3 << 8) + ((ulong)b4 << 16) + ((ulong)b5 << 24) + ((ulong)b6 << 32) + ((ulong)b7 << 40), negative); } byte b8 = reader.ReadByte(); if (b == 126) { return new Union(b2 + ((ulong)b3 << 8) + ((ulong)b4 << 16) + ((ulong)b5 << 24) + ((ulong)b6 << 32) + ((ulong)b7 << 40) + ((ulong)b8 << 48), negative); } byte b9 = reader.ReadByte(); if (b == 127) { return new Union(b2 + ((ulong)b3 << 8) + ((ulong)b4 << 16) + ((ulong)b5 << 24) + ((ulong)b6 << 32) + ((ulong)b7 << 40) + ((ulong)b8 << 48) + ((ulong)b9 << 56), negative); } throw new IndexOutOfRangeException("ReadPackedSigned() failure: " + b); } } public static class LanguageConsts { public static readonly string PROPER_SAVE_TITLE_CONTINUE_DESC = "PROPER_SAVE_TITLE_CONTINUE_DESC"; public static readonly string PROPER_SAVE_TITLE_CONTINUE = "PROPER_SAVE_TITLE_CONTINUE"; public static readonly string PROPER_SAVE_TITLE_LOAD = "PROPER_SAVE_TITLE_LOAD"; public static readonly string PROPER_SAVE_CHAT_SAVE = "PROPER_SAVE_CHAT_SAVE"; public static readonly string PROPER_SAVE_QUIT_DIALOG_SAVED = "PROPER_SAVE_QUIT_DIALOG_SAVED"; public static readonly string PROPER_SAVE_QUIT_DIALOG_SAVED_BEFORE = "PROPER_SAVE_QUIT_DIALOG_SAVED_BEFORE"; public static readonly string PROPER_SAVE_QUIT_DIALOG_NOT_SAVED = "PROPER_SAVE_QUIT_DIALOG_NOT_SAVED"; public static readonly string PROPER_SAVE_TOOLTIP_LOAD_TITLE = "PROPER_SAVE_TOOLTIP_LOAD_TITLE"; public static readonly string PROPER_SAVE_TOOLTIP_LOAD_DESCRIPTION_BODY = "PROPER_SAVE_TOOLTIP_LOAD_DESCRIPTION_BODY"; public static readonly string PROPER_SAVE_TOOLTIP_LOAD_DESCRIPTION_CHARACTER = "PROPER_SAVE_TOOLTIP_LOAD_DESCRIPTION_CHARACTER"; public static readonly string PROPER_SAVE_TOOLTIP_LOAD_CONTENT_MISMATCH = "PROPER_SAVE_TOOLTIP_LOAD_CONTENT_MISMATCH"; public static readonly string PROPER_SAVE_CHAT_SAVE_FAILED = "PROPER_SAVE_CHAT_SAVE_FAILED"; } public static class Loading { private static bool isLoading; public static bool IsLoading { get { return isLoading; } internal set { if (isLoading == value) { return; } isLoading = value; SaveFile currentSave = CurrentSave; Delegate[] array = ((!isLoading) ? Loading.OnLoadingEnded?.GetInvocationList() : Loading.OnLoadingStarted?.GetInvocationList()); if (array == null) { return; } Delegate[] array2 = array; foreach (Delegate obj in array2) { try { ((Action<SaveFile>)obj)(currentSave); } catch (Exception ex) { if (ex is MissingMemberException) { ProperSavePlugin.InstanceLogger.LogError((object)("Method " + obj.Method?.DeclaringType.FullName + "." + obj.Method.Name)); } ProperSavePlugin.InstanceLogger.LogError((object)ex); } } } } public static bool FirstRunStage { get; internal set; } public static SaveFile CurrentSave => ProperSavePlugin.CurrentSave?.Body; public static event Action<SaveFile> OnLoadingStarted; public static event Action<SaveFile> OnLoadingEnded; internal static void RegisterHooks() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown Run.Start += new Manipulator(RunStart); TeamManager.Start += new hook_Start(TeamManagerStart); } internal static void UnregisterHooks() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown Run.Start -= new Manipulator(RunStart); TeamManager.Start -= new hook_Start(TeamManagerStart); } private static void TeamManagerStart(orig_Start orig, TeamManager self) { orig.Invoke(self); if (IsLoading) { CurrentSave.LoadTeam(); IsLoading = false; } } private static void RunStart(ILContext il) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); val.EmitDelegate<Func<bool>>((Func<bool>)delegate { FirstRunStage = true; if (IsLoading) { ProperSavePlugin.InstanceLogger.LogInfo((object)("Loading save file " + ProperSavePlugin.CurrentSave.FileName)); CurrentSave.LoadRun(); CurrentSave.LoadArtifacts(); CurrentSave.LoadPlayers(); } else { ProperSavePlugin.CurrentSave = null; } return IsLoading; }); val.Emit(OpCodes.Brfalse, val.Next); val.Emit(OpCodes.Ret); } internal static IEnumerator LoadLobby() { if ((Object)(object)PreGameController.instance == (Object)null) { ProperSavePlugin.InstanceLogger.LogInfo((object)"PreGameController instance not found"); yield break; } NetworkManagerSystem singleton = NetworkManagerSystem.singleton; if (singleton != null && singleton.desiredHost.hostingParameters.listen && !PlatformSystems.lobbyManager.ownsLobby) { ProperSavePlugin.InstanceLogger.LogInfo((object)"You must be a lobby leader to load the game"); yield break; } SaveFileMetadata currentLobbySaveMetadata = SaveFileMetadata.GetCurrentLobbySaveMetadata(); if (currentLobbySaveMetadata == null) { ProperSavePlugin.InstanceLogger.LogInfo((object)"Save file for current users is not found"); yield break; } UPath? filePath = currentLobbySaveMetadata.FilePath; if (!filePath.HasValue) { ProperSavePlugin.InstanceLogger.LogInfo((object)"Metadata doesn't contain file name for the save file"); yield break; } if (!ProperSavePlugin.SavesFileSystem.FileExists(filePath.Value)) { ProperSavePlugin.InstanceLogger.LogInfo((object)$"File \"{filePath}\" is not found"); yield break; } try { currentLobbySaveMetadata.ReadBody(); } catch (Exception ex) { ProperSavePlugin.InstanceLogger.LogWarning((object)"Failed to read save file body"); ProperSavePlugin.InstanceLogger.LogError((object)ex); yield break; } finally { ObjectBuffer.Clear(); } ProperSavePlugin.CurrentSave = currentLobbySaveMetadata; IsLoading = true; if (currentLobbySaveMetadata.Header.ContentHash != ProperSavePlugin.ContentHash) { ProperSavePlugin.InstanceLogger.LogWarning((object)"Loading run but content mismatch detected which may result in errors"); } PreGameController.instance.StartRun(); } [ConCommand(/*Could not decode attribute arguments.*/)] internal static void LoadForce(ConCommandArgs args) { string text = ((ConCommandArgs)(ref args)).TryGetArgString(0); if (string.IsNullOrWhiteSpace(text) || !File.Exists(text)) { Debug.LogError((object)"Incorrect path"); return; } SaveFileMetadata saveFileMetadata = new SaveFileMetadata(); try { saveFileMetadata.ReadForce(text); ProperSavePlugin.CurrentSave = saveFileMetadata; IsLoading = true; } catch (Exception ex) { Debug.LogWarning((object)("Failed to load save file at path \"" + text + "\"")); ProperSavePlugin.InstanceLogger.LogError((object)ex); ResetLoading(); } if (saveFileMetadata.Header.ContentHash != ProperSavePlugin.ContentHash) { ProperSavePlugin.InstanceLogger.LogWarning((object)"Loading run but content mismatch detected which may result in errors"); } if (Object.op_Implicit((Object)(object)PreGameController.instance)) { if (NetworkUser.readOnlyInstancesList.Count > 0) { Debug.LogWarning((object)"Force loading only allowed for 1 player in lobby"); ResetLoading(); } else { PreGameController.instance.StartRun(); } } else { ((MonoBehaviour)ProperSavePlugin.Instance).StartCoroutine(LoadForceCoroutine()); } static void ResetLoading() { ProperSavePlugin.CurrentSave = null; IsLoading = false; } } private static IEnumerator LoadForceCoroutine() { Console.instance.SubmitCmd((NetworkUser)null, "host 0", false); yield return (object)new WaitUntil((Func<bool>)(() => (Object)(object)PreGameController.instance != (Object)null)); PreGameController.instance.StartRun(); } } internal static class LobbyUI { private static GameObject lobbyButton; private static GameObject lobbySubmenuLegend; private static GameObject lobbyGlyphAndDescription; private static TooltipProvider tooltipProvider; private static GamepadTooltipProvider gamepadTooltipProvider; private static SaveFileMetadata lastFileMetadata; private static TooltipContent lastTooltipContent; public static void RegisterHooks() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown CharacterSelectController.Awake += new hook_Awake(CharacterSelectControllerAwake); NetworkUser.onPostNetworkUserStart += new NetworkUserGenericDelegate(NetworkUserOnPostNetworkUserStart); NetworkUser.onNetworkUserLost += new NetworkUserGenericDelegate(NetworkUserOnNetworkUserLost); } public static void UnregisterHooks() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown CharacterSelectController.Awake -= new hook_Awake(CharacterSelectControllerAwake); NetworkUser.onPostNetworkUserStart -= new NetworkUserGenericDelegate(NetworkUserOnPostNetworkUserStart); NetworkUser.onNetworkUserLost -= new NetworkUserGenericDelegate(NetworkUserOnNetworkUserLost); } private static void NetworkUserOnNetworkUserLost(NetworkUser networkUser) { UpdateLobbyControls(networkUser); } private static void NetworkUserOnPostNetworkUserStart(NetworkUser networkUser) { UpdateLobbyControls(); } private static void CharacterSelectControllerAwake(orig_Awake orig, CharacterSelectController self) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Invalid comparison between Unknown and I4 //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Expected O, but got Unknown //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Expected O, but got Unknown //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Expected O, but got Unknown //IL_033b: Unknown result type (might be due to invalid IL or missing references) //IL_0345: Expected O, but got Unknown try { GameObject gameObject = ((Component)((Component)self).transform.GetChild(2).GetChild(4).GetChild(0)).gameObject; lobbyButton = Object.Instantiate<GameObject>(gameObject, gameObject.transform.parent); InputSourceFilter[] components = ((Component)self).GetComponents<InputSourceFilter>(); foreach (InputSourceFilter val in components) { if ((int)val.requiredInputSource == 0) { Array.Resize(ref val.objectsToFilter, val.objectsToFilter.Length + 1); val.objectsToFilter[val.objectsToFilter.Length - 1] = lobbyButton; break; } } ((Object)lobbyButton).name = "[ProperSave] Load"; tooltipProvider = lobbyButton.AddComponent<TooltipProvider>(); RectTransform component = lobbyButton.GetComponent<RectTransform>(); component.anchorMin = new Vector2(1f, 1.5f); component.anchorMax = new Vector2(1f, 1.5f); HGButton component2 = lobbyButton.GetComponent<HGButton>(); component2.hoverToken = LanguageConsts.PROPER_SAVE_TITLE_CONTINUE_DESC; lobbyButton.GetComponent<LanguageTextMeshController>().token = LanguageConsts.PROPER_SAVE_TITLE_LOAD; ((Button)component2).onClick = new ButtonClickedEvent(); ((UnityEvent)((Button)component2).onClick).AddListener(new UnityAction(LoadOnInputEvent)); GameObject gameObject2 = ((Component)((Component)self).transform.GetChild(2).GetChild(4).GetChild(1)).gameObject; lobbySubmenuLegend = Object.Instantiate<GameObject>(gameObject2, gameObject2.transform.parent); components = ((Component)self).GetComponents<InputSourceFilter>(); foreach (InputSourceFilter val2 in components) { if ((int)val2.requiredInputSource == 1) { Array.Resize(ref val2.objectsToFilter, val2.objectsToFilter.Length + 1); val2.objectsToFilter[val2.objectsToFilter.Length - 1] = lobbySubmenuLegend; break; } } ((Object)lobbySubmenuLegend).name = "[ProperSave] SubmenuLegend"; UIJuice component3 = lobbySubmenuLegend.GetComponent<UIJuice>(); OnEnableEvent component4 = lobbySubmenuLegend.GetComponent<OnEnableEvent>(); ((UnityEventBase)component4.action).RemoveAllListeners(); component4.action.AddListener(new UnityAction(component3.TransitionPanFromTop)); component4.action.AddListener(new UnityAction(component3.TransitionAlphaFadeIn)); RectTransform component5 = lobbySubmenuLegend.GetComponent<RectTransform>(); component5.anchorMin = new Vector2(1f, 1f); component5.anchorMax = new Vector2(1f, 2f); lobbyGlyphAndDescription = ((Component)lobbySubmenuLegend.transform.GetChild(0)).gameObject; lobbyGlyphAndDescription.SetActive(true); InputBindingDisplayController component6 = ((Component)lobbyGlyphAndDescription.transform.GetChild(0)).GetComponent<InputBindingDisplayController>(); component6.actionName = "UISubmitTertiary"; Transform child = lobbyGlyphAndDescription.transform.GetChild(1); LanguageTextMeshController component7 = ((Component)lobbyGlyphAndDescription.transform.GetChild(2)).GetComponent<LanguageTextMeshController>(); Object.Destroy((Object)(object)((Component)child).gameObject); component7.token = LanguageConsts.PROPER_SAVE_TITLE_LOAD; for (int j = 1; j < lobbySubmenuLegend.transform.childCount; j++) { Object.Destroy((Object)(object)((Component)lobbySubmenuLegend.transform.GetChild(j)).gameObject); } HoldGamepadInputEvent holdGamepadInputEvent = ((Component)self).gameObject.AddComponent<HoldGamepadInputEvent>(); ((HGGamepadInputEvent)holdGamepadInputEvent).actionName = "UISubmitTertiary"; ((HGGamepadInputEvent)holdGamepadInputEvent).enabledObjectsIfActive = Array.Empty<GameObject>(); ((HGGamepadInputEvent)holdGamepadInputEvent).actionEvent = new UnityEvent(); ((HGGamepadInputEvent)holdGamepadInputEvent).actionEvent.AddListener(new UnityAction(LoadOnInputEvent)); gamepadTooltipProvider = ((Component)component6).gameObject.AddComponent<GamepadTooltipProvider>(); gamepadTooltipProvider.inputEvent = holdGamepadInputEvent; UpdateLobbyControls(); } catch (Exception ex) { ProperSavePlugin.InstanceLogger.LogWarning((object)"Failed while adding lobby buttons"); ProperSavePlugin.InstanceLogger.LogError((object)ex); } orig.Invoke(self); } private static void LoadOnInputEvent() { if ((Object)(object)Run.instance != (Object)null) { ProperSavePlugin.InstanceLogger.LogInfo((object)"Can't load while run is active"); } else if (Loading.IsLoading) { ProperSavePlugin.InstanceLogger.LogInfo((object)"Already loading"); } else { ((MonoBehaviour)ProperSavePlugin.Instance).StartCoroutine(Loading.LoadLobby()); } } private static void UpdateLobbyControls(NetworkUser exceptUser = null) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0092: 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_013d: 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) //IL_0170: Unknown result type (might be due to invalid IL or missing references) SaveFileMetadata currentLobbySaveMetadata = SaveFileMetadata.GetCurrentLobbySaveMetadata(exceptUser); bool flag = PlatformSystems.lobbyManager.isInLobby == PlatformSystems.lobbyManager.ownsLobby && currentLobbySaveMetadata != null && currentLobbySaveMetadata.FilePath.HasValue && ProperSavePlugin.SavesFileSystem.FileExists(currentLobbySaveMetadata.FilePath.Value); if (currentLobbySaveMetadata != lastFileMetadata) { lastFileMetadata = currentLobbySaveMetadata; try { if (currentLobbySaveMetadata != null) { lastTooltipContent = new TooltipContent { titleToken = LanguageConsts.PROPER_SAVE_TOOLTIP_LOAD_TITLE, overrideBodyText = GetSaveDescription(currentLobbySaveMetadata), titleColor = Color.black, disableBodyRichText = false }; } else { lastTooltipContent = default(TooltipContent); } } catch (Exception ex) { ProperSavePlugin.InstanceLogger.LogWarning((object)"Failed to get information about save file"); ProperSavePlugin.InstanceLogger.LogError((object)ex); flag = false; } } try { if (Object.op_Implicit((Object)(object)lobbyButton)) { HGButton component = lobbyButton.GetComponent<HGButton>(); if (Object.op_Implicit((Object)(object)component)) { ((Selectable)component).interactable = flag; } } if (Object.op_Implicit((Object)(object)tooltipProvider)) { tooltipProvider.SetContent(lastTooltipContent); } } catch { } try { if (Object.op_Implicit((Object)(object)lobbyGlyphAndDescription)) { Color color = (Color)(flag ? Color.white : new Color(0.3f, 0.3f, 0.3f)); ((Graphic)((Component)lobbyGlyphAndDescription.transform.GetChild(0)).GetComponent<HGTextMeshProUGUI>()).color = color; ((Graphic)((Component)lobbyGlyphAndDescription.transform.GetChild(1)).GetComponent<HGTextMeshProUGUI>()).color = color; } if (Object.op_Implicit((Object)(object)gamepadTooltipProvider)) { ((TooltipProvider)gamepadTooltipProvider).SetContent(lastTooltipContent); } } catch { } } private static string GetSaveDescription(SaveFileMetadata saveMetadata) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) SaveFileHeader header = saveMetadata.Header; StringBuilder stringBuilder = new StringBuilder(); HeaderUserData[] users = header.Users; foreach (HeaderUserData userData in users) { NetworkUser val = ((IEnumerable<NetworkUser>)NetworkUser.readOnlyInstancesList).FirstOrDefault((Func<NetworkUser, bool>)delegate(NetworkUser user) { //IL_000b: 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_0014: Unknown result type (might be due to invalid IL or missing references) NetworkUserId val3 = userData.UserId.Load(); return ((NetworkUserId)(ref val3)).Equals(user.id); }); SurvivorDef val2 = SurvivorCatalog.FindSurvivorDefFromBody(BodyCatalog.GetBodyPrefab(userData.Body)); stringBuilder.Append(Language.GetStringFormatted(LanguageConsts.PROPER_SAVE_TOOLTIP_LOAD_DESCRIPTION_CHARACTER, new object[2] { val?.userName, ((Object)(object)val2 != (Object)null) ? Language.GetString(val2.displayNameToken) : "" })); } SceneDef sceneDefFromSceneName = SceneCatalog.GetSceneDefFromSceneName(header.SceneName); DifficultyDef difficultyDef = DifficultyCatalog.GetDifficultyDef(header.Difficulty); int time = header.Time; int stageClearCount = header.StageClearCount; return Language.GetStringFormatted(LanguageConsts.PROPER_SAVE_TOOLTIP_LOAD_DESCRIPTION_BODY, new object[6] { stringBuilder.ToString(), Object.op_Implicit((Object)(object)sceneDefFromSceneName) ? Language.GetString(sceneDefFromSceneName.nameToken) : "", (stageClearCount + 1).ToString(), $"{time / 60:00}:{time % 60:00}", (difficultyDef != null) ? Language.GetString(difficultyDef.nameToken) : "", (header.ContentHash != ProperSavePlugin.ContentHash) ? Language.GetString(LanguageConsts.PROPER_SAVE_TOOLTIP_LOAD_CONTENT_MISMATCH) : "" }); } } internal class LostNetworkUser : MonoBehaviour { private static readonly Dictionary<CharacterMaster, LostNetworkUser> lostUsers = new Dictionary<CharacterMaster, LostNetworkUser>(); private CharacterMaster master; public uint lunarCoins; public NetworkUserId userID; public BodyIndex bodyIndexPreference; private void Awake() { master = ((Component)this).GetComponent<CharacterMaster>(); lostUsers[master] = this; } private void OnDestroy() { lostUsers.Remove(master); } public static bool TryGetUser(CharacterMaster master, out LostNetworkUser lostUser) { if (!Object.op_Implicit((Object)(object)master) || !lostUsers.TryGetValue(master, out lostUser)) { lostUser = null; return false; } return true; } public static void Subscribe() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown NetworkUser.onNetworkUserLost += new NetworkUserGenericDelegate(OnNetworkUserLost); } public static void Unsubscribe() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown NetworkUser.onNetworkUserLost -= new NetworkUserGenericDelegate(OnNetworkUserLost); } private static void OnNetworkUserLost(NetworkUser networkUser) { //IL_002b: 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) //IL_0036: 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 (Object.op_Implicit((Object)(object)networkUser.master)) { LostNetworkUser lostNetworkUser = ((Component)networkUser.master).gameObject.AddComponent<LostNetworkUser>(); lostNetworkUser.lunarCoins = networkUser.lunarCoins; lostNetworkUser.userID = networkUser.id; lostNetworkUser.bodyIndexPreference = networkUser.bodyIndexPreference; } } } internal static class ModCompat { public const string BiggerBazaarGUID = "com.MagnusMagnuson.BiggerBazaar"; public const string ShareSuiteGUID = "com.funkfrog_sipondo.sharesuite"; public static bool IsBBLoaded { get; private set; } public static bool IsSSLoaded { get; private set; } public static bool IsR2APIDifficultyLoaded { get; private set; } private static Dictionary<MethodInfo, Action<ILContext>> RegisteredILHooks { get; } = new Dictionary<MethodInfo, Action<ILContext>>(); public static void GatherLoadedPlugins() { IsBBLoaded = Chainloader.PluginInfos.ContainsKey("com.MagnusMagnuson.BiggerBazaar"); IsSSLoaded = Chainloader.PluginInfos.ContainsKey("com.funkfrog_sipondo.sharesuite"); IsR2APIDifficultyLoaded = Chainloader.PluginInfos.ContainsKey("com.bepis.r2api.difficulty"); } public static void RegisterHooks() { if (IsBBLoaded) { try { RegisterBBHooks(); } catch (Exception ex) { ProperSavePlugin.InstanceLogger.LogError((object)"Failed to add support for BiggerBazaar"); ProperSavePlugin.InstanceLogger.LogError((object)ex); } } } public static void UnregisterHooks() { foreach (KeyValuePair<MethodInfo, Action<ILContext>> registeredILHook in RegisteredILHooks) { HookEndpointManager.Unmodify((MethodBase)registeredILHook.Key, (Delegate)registeredILHook.Value); } } [MethodImpl(MethodImplOptions.NoInlining)] private static void RegisterBBHooks() { MethodInfo method = typeof(BiggerBazaar).Assembly.GetType("BiggerBazaar.Bazaar").GetMethod("StartBazaar", BindingFlags.Instance | BindingFlags.Public); Action<ILContext> action = BBHook; HookEndpointManager.Modify((MethodBase)method, (Delegate)action); RegisteredILHooks.Add(method, action); } private static void BBHook(ILContext il) { //IL_001b: 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_0021: 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) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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_0098: Unknown result type (might be due to invalid IL or missing references) Type type = typeof(BiggerBazaar).Assembly.GetType("BiggerBazaar.Bazaar"); ILCursor val = new ILCursor(il); int index = val.Index; val.Index = index + 1; Instruction next = val.Next; val.Emit(OpCodes.Call, (MethodBase)typeof(Loading).GetProperty("FirstRunStage").GetGetMethod()); val.Emit(OpCodes.Brfalse, next); val.Emit(OpCodes.Ldarg_0); val.Emit(OpCodes.Call, (MethodBase)type.GetMethod("ResetBazaarPlayers")); val.Emit(OpCodes.Ldarg_0); val.Emit(OpCodes.Call, (MethodBase)type.GetMethod("CalcDifficultyCoefficient")); } public static void LoadShareSuiteMoney(uint money) { try { if (IsSSLoaded) { ((MonoBehaviour)ProperSavePlugin.Instance).StartCoroutine(LoadShareSuiteMoneyInternal(money)); } } catch (Exception ex) { ProperSavePlugin.InstanceLogger.LogError((object)ex); } } [MethodImpl(MethodImplOptions.NoInlining)] private static IEnumerator LoadShareSuiteMoneyInternal(uint money) { yield return (object)new WaitUntil((Func<bool>)(() => !MoneySharingHooks.MapTransitionActive)); MoneySharingHooks.SharedMoneyValue = (int)money; } public static void ShareSuiteMapTransition() { try { if (IsSSLoaded) { ShareSuiteMapTransitionInternal(); } } catch (Exception ex) { ProperSavePlugin.InstanceLogger.LogError((object)ex); } } [MethodImpl(MethodImplOptions.NoInlining)] private static void ShareSuiteMapTransitionInternal() { MoneySharingHooks.MapTransitionActive = true; } [MethodImpl(MethodImplOptions.NoInlining)] internal static DifficultyIndex FindR2APIDifficultyIndex(string nameToken) { //IL_001f: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) foreach (var (result, val3) in DifficultyAPI.difficultyDefinitions) { if (val3.nameToken == nameToken) { return result; } } return (DifficultyIndex)(-1); } } public enum ObjectType : byte { Sbyte, Byte, Short, UShort, Int, UInt, Long, ULong, Float, Double, Boolean, String, Array, Collection, Class, Object, ValueType, KeyValuePair } [BepInPlugin("com.KingEnderBrine.ProperSave", "Proper Save", "3.0.7")] public class ProperSavePlugin : BaseUnityPlugin { public const string GUID = "com.KingEnderBrine.ProperSave"; public const string Name = "Proper Save"; public const string Version = "3.0.7"; private static readonly char[] invalidSubDirectoryCharacters = new char[3] { '\\', '/', '.' }; internal static ProperSavePlugin Instance { get; private set; } internal static ManualLogSource InstanceLogger { get { ProperSavePlugin instance = Instance; if (instance == null) { return null; } return ((BaseUnityPlugin)instance).Logger; } } internal static FileSystem SavesFileSystem { get; private set; } internal static UPath SavesPath { get; private set; } = UPath.op_Implicit("/ProperSave") / UPath.op_Implicit("Saves"); private static string SavesDirectory { get; set; } internal static SaveFileMetadata CurrentSave { get; set; } internal static string ContentHash { get; private set; } internal static ConfigEntry<bool> UseCloudStorage { get; private set; } internal static ConfigEntry<string> CloudStorageSubDirectory { get; private set; } internal static ConfigEntry<string> UserSavesDirectory { get; private set; } internal static ConfigEntry<bool> Resilient { get; private set; } private void Start() { Instance = this; UseCloudStorage = ((BaseUnityPlugin)this).Config.Bind<bool>("Main", "UseCloudStorage", false, "Store files in Steam/EpicGames cloud. Enabling this feature would not preserve current saves and disabling it wouldn't clear the cloud."); CloudStorageSubDirectory = ((BaseUnityPlugin)this).Config.Bind<string>("Main", "CloudStorageSubDirectory", "", "Sub directory name for cloud storage. Changing it allows to use different save files for different mod profiles."); UserSavesDirectory = ((BaseUnityPlugin)this).Config.Bind<string>("Main", "SavesDirectory", "", "Directory where save files will be stored. \"ProperSave\" directory will be created in the directory you have specified. If the directory doesn't exist the default one will be used."); Resilient = ((BaseUnityPlugin)this).Config.Bind<bool>("Main", "Resilient", true, "Save file type. True - entries from catalogs will be saved by name instead of index, which should have less issue on mod list change, but has bigger save file size. False - the old way, entries from catalogs are saved by index, which works for non-changing mod list, save file size is much lower."); RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, (Action)delegate { InitSaveFileSystem(); ProperSave.Old.SaveFileMetadata.MigrateAll(); SaveFileMetadata.PopulateSavesMetadata(); }); ModCompat.GatherLoadedPlugins(); ModCompat.RegisterHooks(); Saving.RegisterHooks(); Loading.RegisterHooks(); LobbyUI.RegisterHooks(); LostNetworkUser.Subscribe(); Language.collectLanguageRootFolders += CollectLanguageRootFolders; ContentManager.onContentPacksAssigned += ContentManagerOnContentPacksAssigned; } private void InitSaveFileSystem() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown if (UseCloudStorage.Value) { SavesFileSystem = RoR2Application.cloudStorage; if (!string.IsNullOrWhiteSpace(CloudStorageSubDirectory.Value)) { if (CloudStorageSubDirectory.Value.IndexOfAny(invalidSubDirectoryCharacters) != -1) { ((BaseUnityPlugin)this).Logger.LogError((object)"Config entry \"CloudStorageSubDirectory\" contains invalid characters. Falling back to default location."); } else { SavesPath /= UPath.op_Implicit(CloudStorageSubDirectory.Value); } } return; } if (!string.IsNullOrWhiteSpace(UserSavesDirectory.Value)) { if (!Directory.Exists(UserSavesDirectory.Value)) { ((BaseUnityPlugin)this).Logger.LogError((object)"SavesDirectory from the config doesn't exists, using Application.persistentDataPath"); SavesDirectory = Application.persistentDataPath; } else { SavesDirectory = UserSavesDirectory.Value; } } else { SavesDirectory = Application.persistentDataPath; } if (string.IsNullOrWhiteSpace(SavesDirectory)) { ((BaseUnityPlugin)this).Logger.LogError((object)"Application.persistentDataPath is empty. Use SavesDirectory config option to specify a folder."); } PhysicalFileSystem val = new PhysicalFileSystem(); SavesFileSystem = (FileSystem)new SubFileSystem((IFileSystem)(object)val, ((FileSystem)val).ConvertPathFromInternal(SavesDirectory), true); } private void Destroy() { Instance = null; ModCompat.UnregisterHooks(); Saving.UnregisterHooks(); Loading.UnregisterHooks(); LobbyUI.UnregisterHooks(); LostNetworkUser.Unsubscribe(); Language.collectLanguageRootFolders -= CollectLanguageRootFolders; ContentManager.onContentPacksAssigned -= ContentManagerOnContentPacksAssigned; } public void CollectLanguageRootFolders(List<string> folders) { folders.Add(Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "Language")); } private void ContentManagerOnContentPacksAssigned(ReadOnlyArray<ReadOnlyContentPack> contentPacks) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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_00e0: 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) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) MD5 mD = MD5.Create(); StringWriter writer = new StringWriter(); try { Enumerator<ReadOnlyContentPack> enumerator = contentPacks.GetEnumerator(); try { while (enumerator.MoveNext()) { ReadOnlyContentPack current = enumerator.Current; writer.Write(((ReadOnlyContentPack)(ref current)).identifier); writer.Write(';'); WriteCollection<ArtifactDef>(((ReadOnlyContentPack)(ref current)).artifactDefs, "artifactDefs"); WriteCollection<GameObject>(((ReadOnlyContentPack)(ref current)).bodyPrefabs, "bodyPrefabs"); WriteCollection<EquipmentDef>(((ReadOnlyContentPack)(ref current)).equipmentDefs, "equipmentDefs"); WriteCollection<DroneDef>(((ReadOnlyContentPack)(ref current)).droneDefs, "droneDefs"); WriteCollection<ExpansionDef>(((ReadOnlyContentPack)(ref current)).expansionDefs, "expansionDefs"); WriteCollection<GameObject>(((ReadOnlyContentPack)(ref current)).gameModePrefabs, "gameModePrefabs"); WriteCollection<ItemDef>(((ReadOnlyContentPack)(ref current)).itemDefs, "itemDefs"); WriteCollection<ItemTierDef>(((ReadOnlyContentPack)(ref current)).itemTierDefs, "itemTierDefs"); WriteCollection<GameObject>(((ReadOnlyContentPack)(ref current)).masterPrefabs, "masterPrefabs"); WriteCollection<SceneDef>(((ReadOnlyContentPack)(ref current)).sceneDefs, "sceneDefs"); WriteCollection<SkillDef>(((ReadOnlyContentPack)(ref current)).skillDefs, "skillDefs"); WriteCollection<SkillFamily>(((ReadOnlyContentPack)(ref current)).skillFamilies, "skillFamilies"); WriteCollection<SurvivorDef>(((ReadOnlyContentPack)(ref current)).survivorDefs, "survivorDefs"); WriteCollection<UnlockableDef>(((ReadOnlyContentPack)(ref current)).unlockableDefs, "unlockableDefs"); } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } ContentHash = Convert.ToBase64String(mD.ComputeHash(Encoding.UTF8.GetBytes(writer.ToString()))); } finally { if (writer != null) { ((IDisposable)writer).Dispose(); } } void WriteCollection<T>(ReadOnlyNamedAssetCollection<T> collection, string collectionName) { //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) writer.Write(collectionName); int num = 0; AssetEnumerator<T> enumerator2 = collection.GetEnumerator(); try { while (enumerator2.MoveNext()) { T current2 = enumerator2.Current; writer.Write(num); writer.Write('_'); writer.Write(collection.GetAssetName(current2) ?? string.Empty); writer.Write(';'); num++; } } finally { ((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } } } } public class SaveFile { internal static readonly int currentVersion = 2; public ProperSave.SaveData.RunData RunData { get; set; } public ProperSave.SaveData.TeamData TeamData { get; set; } public ProperSave.SaveData.RunArtifactsData RunArtifactsData { get; set; } public ProperSave.SaveData.ArtifactsData ArtifactsData { get; set; } public List<ProperSave.SaveData.PlayerData> PlayersData { get; set; } = new List<ProperSave.SaveData.PlayerData>(); [Obsolete("Use TryGetModdedData<T>() or GetModdedData<T>() instead of directly referencing this field")] public Dictionary<string, ProperSave.Data.ModdedData> ModdedData { get; set; } = new Dictionary<string, ProperSave.Data.ModdedData>(); public Dictionary<string, object> ModdedObjectsData { get; set; } = new Dictionary<string, object>(); public static event Action<Dictionary<string, object>> OnGatherSaveData; internal SaveFile() { } internal void FillFromCurrentRun() { RunData = ProperSave.SaveData.RunData.Create(); TeamData = ProperSave.SaveData.TeamData.Create(); RunArtifactsData = ProperSave.SaveData.RunArtifactsData.Create(); ArtifactsData = ProperSave.SaveData.ArtifactsData.Create(); foreach (PlayerCharacterMasterController instance in PlayerCharacterMasterController.instances) { LostNetworkUser lostUser = null; if (Object.op_Implicit((Object)(object)instance.networkUser) || LostNetworkUser.TryGetUser(instance.master, out lostUser)) { PlayersData.Add(ProperSave.SaveData.PlayerData.Create(instance, lostUser)); } } Dictionary<string, object> dictionary = new Dictionary<string, object>(); Delegate[] array = SaveFile.OnGatherSaveData?.GetInvocationList(); if (array != null) { Delegate[] array2 = array; foreach (Delegate obj in array2) { try { ((Action<Dictionary<string, object>>)obj)(dictionary); } catch (Exception ex) { ProperSavePlugin.InstanceLogger.LogError((object)ex); } } } ModdedObjectsData = dictionary; ModdedData = ModdedObjectsData.ToDictionary((KeyValuePair<string, object> el) => el.Key, (KeyValuePair<string, object> el) => new ProperSave.Data.ModdedData { ObjectType = el.Value?.GetType().AssemblyQualifiedName, Value = el.Value }); } internal void LoadRun() { try { LegacyResourcesAPI.Load<GameObject>("Prefabs/PositionIndicators/TeleporterChargingPositionIndicator", true); } catch { } RunData.LoadData(); } internal void LoadArtifacts() { RunArtifactsData.LoadData(); ArtifactsData.LoadData(); } internal void LoadTeam() { TeamData.LoadData(); } internal void LoadPlayers() { if (NetworkUser.readOnlyInstancesList.Count == 1) { ProperSave.SaveData.PlayerData playerData = PlayersData.FirstOrDefault(); if (playerData != null) { NetworkUser player = NetworkUser.readOnlyInstancesList.FirstOrDefault(); playerData.LoadPlayer(player); } return; } List<ProperSave.SaveData.PlayerData> list = PlayersData.ToList(); foreach (NetworkUser user in NetworkUser.readOnlyInstancesList) { ProperSave.SaveData.PlayerData playerData2 = list.FirstOrDefault(delegate(ProperSave.SaveData.PlayerData el) { //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 val = el.userId.Load(); return ((NetworkUserId)(ref val)).Equals(user.id); }); if (playerData2 != null) { list.Remove(playerData2); playerData2.LoadPlayer(user); } } } public T GetModdedData<T>(string key) { return (T)ModdedObjectsData[key]; } public T TryGetModdedData<T>(string key) { if (ModdedObjectsData.TryGetValue(key, out var value)) { return (T)value; } return default(T); } internal static SaveFile Read(BinaryReader reader) { SaveFile saveFile = new SaveFile(); ReaderContext readerContext = new ReaderContext { Reader = reader }; int num = (readerContext.Version = reader.ReadInt32()); int num3 = num; bool flag = (readerContext.Resilient = reader.ReadBoolean()); long offset = 0L; if (num3 > 1) { offset = reader.ReadInt64(); } long offset2 = reader.ReadInt64(); long position = reader.BaseStream.Position; reader.BaseStream.Seek(offset2, SeekOrigin.Begin); string[] array = (readerContext.SharedStrings = new string[(num3 > 1) ? reader.ReadPackedInt32() : reader.ReadInt32()]); string[] array3 = array; for (int i = 0; i < array3.Length; i++) { array3[i] = reader.ReadString(); } long position2 = reader.BaseStream.Position; if (num3 > 1) { reader.BaseStream.Seek(offset, SeekOrigin.Begin); GenericObjectsHelper.ReadTypesAndObjects(readerContext); } reader.BaseStream.Seek(position, SeekOrigin.Begin); saveFile.ArtifactsData = ProperSave.SaveData.ArtifactsData.Read(readerContext); saveFile.RunData = ProperSave.SaveData.RunData.Read(readerContext); saveFile.RunArtifactsData = ProperSave.SaveData.RunArtifactsData.Read(readerContext); saveFile.TeamData = ProperSave.SaveData.TeamData.Read(readerContext); int num4 = ((num3 > 1) ? reader.ReadPackedInt32() : reader.ReadInt32()); for (int j = 0; j < num4; j++) { saveFile.PlayersData.Add(ProperSave.SaveData.PlayerData.Read(readerContext)); } saveFile.ModdedObjectsData = ReadModdedData(readerContext); saveFile.ModdedData = saveFile.ModdedObjectsData.ToDictionary((KeyValuePair<string, object> el) => el.Key, (KeyValuePair<string, object> el) => new ProperSave.Data.ModdedData { ObjectType = el.Value?.GetType().AssemblyQualifiedName, Value = el.Value }); reader.BaseStream.Seek(position2, SeekOrigin.Begin); return saveFile; } private static Dictionary<string, object> ReadModdedData(ReaderContext context) { BinaryReader reader = context.Reader; if (context.Version > 1) { GenericObjectsHelper.ReadObjectsData(context); return context.Objects[0].obj as Dictionary<string, object>; } return reader.ReadString().FromJson<Dictionary<string, ProperSave.Data.ModdedData>>().ToDictionary((KeyValuePair<string, ProperSave.Data.ModdedData> e) => e.Key, (KeyValuePair<string, ProperSave.Data.ModdedData> e) => e.Value.Value); } internal void Write(BinaryWriter writer, bool resilient) { WriterContext writerContext = new WriterContext { Writer = writer, Resilient = resilient }; writer.Write(currentVersion); writer.Write(resilient); long position = writer.BaseStream.Position; writer.Write(0L); writer.Write(0L); ArtifactsData.Write(writerContext); RunData.Write(writerContext); RunArtifactsData.Write(writerContext); TeamData.Write(writerContext); writer.WritePacked(PlayersData.Count); for (int i = 0; i < PlayersData.Count; i++) { PlayersData[i].Write(writerContext); } GenericObjectsHelper.GetReferenceIndex(ModdedObjectsData, writerContext); GenericObjectsHelper.WriteObjectsData(writerContext); long position2 = writer.BaseStream.Position; GenericObjectsHelper.WriteTypesAndObjects(writerContext); long position3 = writer.BaseStream.Position; writer.WritePacked(writerContext.SharedStrings.Count); for (int j = 0; j < writerContext.SharedStrings.Count; j++) { writer.Write(writerContext.SharedStrings[j]); } writer.BaseStream.Seek(position, SeekOrigin.Begin); writer.Write(position2); writer.Write(position3); writer.BaseStream.Seek(writer.BaseStream.Length, SeekOrigin.Begin); } } public class SaveFileHeader { internal static readonly int currentVersion = 1; public DateTime SaveDate { get; internal set; } public string UserProfileId { get; internal set; } public HeaderUserData[] Users { get; internal set; } public GameModeIndex GameMode { get; internal set; } public string SceneName { get; internal set; } public DifficultyIndex Difficulty { get; internal set; } public int Time { get; internal set; } public int StageClearCount { get; internal set; } public string ContentHash { get; internal set; } public void FillFromCurrentRun() { //IL_0066: 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) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) Users = (from el in PlayerCharacterMasterController.instances.Select(HeaderUserData.Create) where el != null select el).ToArray(); UserProfileId = LocalUserManager.readOnlyLocalUsersList[0].userProfile.fileName; GameMode = Run.instance.gameModeIndex; ContentHash = ProperSavePlugin.ContentHash; Difficulty = Run.instance.selectedDifficulty; Scene activeScene = SceneManager.GetActiveScene(); SceneName = ((Scene)(ref activeScene)).name; StageClearCount = Run.instance.stageClearCount; RunStopwatch runStopwatch = Run.instance.runStopwatch; Time = (runStopwatch.isPaused ? ((int)runStopwatch.offsetFromFixedTime) : ((int)(Run.instance.fixedTime + runStopwatch.offsetFromFixedTime))); } internal static SaveFileHeader Read(BinaryReader reader) { //IL_0100: 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) SaveFileHeader saveFileHeader = new SaveFileHeader(); int version = reader.ReadInt32(); bool resilient = reader.ReadBoolean(); long offset = reader.ReadInt64(); long position = reader.BaseStream.Position; reader.BaseStream.Seek(offset, SeekOrigin.Begin); string[] array = new string[reader.ReadInt32()]; for (int i = 0; i < array.Length; i++) { array[i] = reader.ReadString(); } long position2 = reader.BaseStream.Position; reader.BaseStream.Seek(position, SeekOrigin.Begin); ReaderContext context = new ReaderContext { Reader = reader, Resilient = resilient, SharedStrings = array, Version = version }; saveFileHeader.SaveDate = new DateTime(reader.ReadInt64(), DateTimeKind.Utc); saveFileHeader.UserProfileId = reader.ReadString(); saveFileHeader.Users = new HeaderUserData[reader.ReadInt32()]; for (int j = 0; j < saveFileHeader.Users.Length; j++) { saveFileHeader.Users[j] = HeaderUserData.Read(context); } saveFileHeader.GameMode = SharedIndexHelpers.ResolveGameMode(reader.ReadInt32(), context); saveFileHeader.SceneName = reader.ReadString(); saveFileHeader.Difficulty = SharedIndexHelpers.ResolveDifficulty(reader.ReadInt32(), context); saveFileHeader.Time = reader.ReadInt32(); saveFileHeader.StageClearCount = reader.ReadInt32(); saveFileHeader.ContentHash = reader.ReadString(); reader.BaseStream.Seek(position2, SeekOrigin.Begin); return saveFileHeader; } internal void Write(BinaryWriter writer, bool resilient) { //IL_008f: 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) WriterContext writerContext = new WriterContext { Writer = writer, Resilient = resilient }; writer.Write(currentVersion); writer.Write(resilient); long position = writer.BaseStream.Position; writer.Write(0L); writer.Write(DateTime.UtcNow.Ticks); writer.Write(UserProfileId); writer.Write(Users.Length); for (int i = 0; i < Users.Length; i++) { Users[i].Write(writerContext); } writer.Write(SharedIndexHelpers.FromGameMode(GameMode, writerContext)); writer.Write(SceneName); writer.Write(SharedIndexHelpers.FromDifficulty(Difficulty, writerContext)); writer.Write(Time); writer.Write(StageClearCount); writer.Write(ContentHash); long position2 = writer.BaseStream.Position; writer.Write(writerContext.SharedStrings.Count); for (int j = 0; j < writerContext.SharedStrings.Count; j++) { writer.Write(writerContext.SharedStrings[j]); } writer.BaseStream.Seek(position, SeekOrigin.Begin); writer.Write(position2); writer.BaseStream.Seek(writer.BaseStream.Length, SeekOrigin.Begin); } } public class SaveFileMetadata { public string FileName { get; internal set; } public bool ForceLoad { get; internal set; } public SaveFileHeader Header { get; internal set; } public long BodyOffset { get; internal set; } public SaveFile Body { get; internal set; } public UPath? FilePath => string.IsNullOrEmpty(FileName) ? UPath.op_Implicit((string)null) : (ProperSavePlugin.SavesPath / UPath.op_Implicit(FileName + ".bin")); internal static List<SaveFileMetadata> SavesMetadata { get; } = new List<SaveFileMetadata>(); internal void FillMetadataForCurrentLobby() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) ForceLoad = false; Header = new SaveFileHeader(); Body = new SaveFile(); BodyOffset = 0L; Header.FillFromCurrentRun(); Body.FillFromCurrentRun(); if (FileName == null) { do { FileName = Guid.NewGuid().ToString(); } while (ProperSavePlugin.SavesFileSystem.FileExists(FilePath.Value)); } } internal static SaveFileMetadata GetCurrentLobbySaveMetadata(NetworkUser exceptUser = null) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Invalid comparison between Unknown and I4 //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Invalid comparison between Unknown and I4 try { List<NetworkUserId> users = NetworkUser.readOnlyInstancesList.Select((NetworkUser el) => el.id).ToList(); if ((Object)(object)exceptUser != (Object)null) { users.Remove(exceptUser.id); } if (users.Count == 0) { return null; } GameModeIndex gameMode = (GameModeIndex)((!Object.op_Implicit((Object)(object)PreGameController.instance)) ? ((!Object.op_Implicit((Object)(object)Run.instance)) ? (-1) : ((int)Run.instance.gameModeIndex)) : ((int)PreGameController.instance.gameModeIndex)); if ((int)gameMode == -1) { return null; } if (users.Count == 1) { string profile = Path.GetFileNameWithoutExtension(LocalUserManager.readOnlyLocalUsersList[0].userProfile.fileName); return SavesMetadata.FirstOrDefault((SaveFileMetadata el) => el.Header.UserProfileId == profile && el.Header.Users.Length == 1 && el.Header.GameMode == gameMode); } return SavesMetadata.FirstOrDefault((SaveFileMetadata el) => el.Header.Users.Length == users.Count && el.Header.GameMode == gameMode && users.DifferenceCount(el.Header.Users.Select((HeaderUserData e) => (e?.UserId?.Load()).GetValueOrDefault())) == 0); } catch (Exception ex) { ProperSavePlugin.InstanceLogger.LogWarning((object)"Couldn't get save metadata for current lobby"); ProperSavePlugin.InstanceLogger.LogError((object)ex.ToString()); return null; } } internal static void PopulateSavesMetadata() { //IL_0005: 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) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (!ProperSavePlugin.SavesFileSystem.DirectoryExists(ProperSavePlugin.SavesPath)) { ProperSavePlugin.SavesFileSystem.CreateDirectory(ProperSavePlugin.SavesPath); return; } SavesMetadata.Clear(); List<SaveFileMetadata> list = new List<SaveFileMetadata>(); foreach (UPath item in FileSystemExtensions.EnumerateFiles((IFileSystem)(object)ProperSavePlugin.SavesFileSystem, ProperSavePlugin.SavesPath, "*.bin")) { try { SaveFileMetadata saveFileMetadata = new SaveFileMetadata(); saveFileMetadata.FileName = UPathExtensions.GetNameWithoutExtension(item); saveFileMetadata.ReadHeader(); list.Add(saveFileMetadata); } catch (Exception ex) { ProperSavePlugin.InstanceLogger.LogWarning((object)("Failed to load save file \"" + UPathExtensions.GetName(item) + "\"")); ProperSavePlugin.InstanceLogger.LogError((object)ex); } } SavesMetadata.AddRange(from el in list orderby el.Header.ContentHash == ProperSavePlugin.ContentHash descending, el.Header.SaveDate descending select el); } internal static void Replace(SaveFileMetadata metadata, SaveFileMetadata oldMetadata) { if (oldMetadata != null) { SavesMetadata.Remove(oldMetadata); } SavesMetadata.Insert(0, metadata); } internal void Write(bool resilient) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter writer = new BinaryWriter(memoryStream); Header.Write(writer, resilient); Body.Write(writer, resilient); using Stream destination = ProperSavePlugin.SavesFileSystem.OpenFile(FilePath.Value, FileMode.Create, FileAccess.Write, FileShare.None); memoryStream.Seek(0L, SeekOrigin.Begin); memoryStream.CopyTo(destination); } internal void ReadBody() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (Body != null) { return; } using Stream stream = ProperSavePlugin.SavesFileSystem.OpenFile(FilePath.Value, FileMode.Open, FileAccess.Read, FileShare.None); stream.Seek(BodyOffset, SeekOrigin.Begin); using BinaryReader reader = new BinaryReader(stream); Body = SaveFile.Read(reader); } internal void ReadHeader() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (Header != null) { return; } using Stream stream = ProperSavePlugin.SavesFileSystem.OpenFile(FilePath.Value, FileMode.Open, FileAccess.Read, FileShare.None); using BinaryReader reader = new BinaryReader(stream); Header = SaveFileHeader.Read(reader); BodyOffset = stream.Position; ForceLoad = false; } internal void ReadForce(string path) { using FileStream fileStream = File.Open(path, FileMode.Open, FileAccess.Read); using BinaryReader reader = new BinaryReader(fileStream); Header = SaveFileHeader.Read(reader); BodyOffset = fileStream.Position; Body = SaveFile.Read(reader); ForceLoad = true; } } internal static class Saving { internal static ProperSave.SaveData.RunRngData PreStageRng { get; private set; } internal static ProperSave.Data.RngData PreStageInfiniteTowerSafeWardRng { get; private set; } internal static string PreStageSceneName { get; private set; } internal static void RegisterHooks() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown Run.BeginStage += new hook_BeginStage(StageOnStageStartGlobal); Run.onServerGameOver += RunOnServerGameOver; Run.AdvanceStage += new hook_AdvanceStage(RunAdvanceStage); QuitConfirmationHelper.IssueQuitCommand_Action += new Manipulator(IssueQuitCommandIL); } internal static void UnregisterHooks() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown Run.BeginStage -= new hook_BeginStage(StageOnStageStartGlobal); Run.onServerGameOver -= RunOnServerGameOver; Run.AdvanceStage -= new hook_AdvanceStage(RunAdvanceStage); QuitConfirmationHelper.IssueQuitCommand_Action -= new Manipulator(IssueQuitCommandIL); } private static void RunAdvanceStage(orig_AdvanceStage orig, Run self, SceneDef sceneDef) { PreStageSceneName = SceneCatalog.GetSceneDefForCurrentScene().cachedName; PreStageRng = ProperSave.SaveData.RunRngData.Create(Run.instance); InfiniteTowerRun val = (InfiniteTowerRun)(object)((self is InfiniteTowerRun) ? self : null); if (val != null) { PreStageInfiniteTowerSafeWardRng = ProperSave.Data.RngData.Create(val.safeWardRng); } orig.Invoke(self, sceneDef); } private static void RunOnServerGameOver(Run run, GameEndingDef ending) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) try { SaveFileMetadata currentSave = ProperSavePlugin.CurrentSave; if (currentSave != null && currentSave.FilePath.HasValue) { if (ProperSavePlugin.SavesFileSystem.FileExists(currentSave.FilePath.Value)) { ProperSavePlugin.SavesFileSystem.DeleteFile(currentSave.FilePath.Value); } ProperSavePlugin.CurrentSave = null; SaveFileMetadata.SavesMetadata.Remove(currentSave); } } catch (Exception ex) { ProperSavePlugin.InstanceLogger.LogWarning((object)"Failed to delete save file"); ProperSavePlugin.InstanceLogger.LogError((object)ex); } } private static void StageOnStageStartGlobal(orig_BeginStage orig, Run self) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 try { if (!NetworkServer.active) { return; } if (Loading.FirstRunStage) { Loading.FirstRunStage = false; return; } SceneDef sceneDefForCurrentScene = SceneCatalog.GetSceneDefForCurrentScene(); if ((int)sceneDefForCurrentScene.sceneType != 0 && (int)sceneDefForCurrentScene.sceneType != 3) { SaveFileMetadata currentSave = ProperSavePlugin.CurrentSave; if (currentSave == null || !currentSave.ForceLoad) { SaveGame(); } } } catch (Exception ex) { ProperSavePlugin.InstanceLogger.LogError((object)ex); } finally { orig.Invoke(self); } } private static void SaveGame() { //IL_0073: 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_00a1: Expected O, but got Unknown //IL_0042: 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_0070: Expected O, but got Unknown SaveFileMetadata currentLobbySaveMetadata = SaveFileMetadata.GetCurrentLobbySaveMetadata(); SaveFileMetadata saveFileMetadata = new SaveFileMetadata { FileName = currentLobbySaveMetadata?.FileName }; saveFileMetadata.FillMetadataForCurrentLobby(); try { saveFileMetadata.Write(ProperSavePlugin.Resilient.Value); ProperSavePlugin.CurrentSave = saveFileMetadata; SaveFileMetadata.Replace(saveFileMetadata, currentLobbySaveMetadata); Chat.SendBroadcastChat((ChatMessageBase)new SimpleChatMessage { baseToken = string.Format(Language.GetString(LanguageConsts.PROPER_SAVE_CHAT_SAVE), Language.GetString(SceneCatalog.currentSceneDef.nameToken)) }); } catch (Exception ex) { Chat.SendBroadcastChat((ChatMessageBase)new SimpleChatMessage { baseToken = string.Format(Language.GetString(LanguageConsts.PROPER_SAVE_CHAT_SAVE_FAILED), Language.GetString(SceneCatalog.currentSceneDef.nameToken)) }); ProperSavePlugin.InstanceLogger.LogWarning((object)"Failed to save the game"); ProperSavePlugin.InstanceLogger.LogError((object)ex); } finally { ObjectBuffer.Clear(); } } private static void IssueQuitCommandIL(ILContext il) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); MethodReference val2 = default(MethodReference); string text = default(string); val.GotoNext(new Func<Instruction, bool>[4] { (Instruction x) => ILPatternMatchingExt.MatchLdarg(x, 0), (Instruction x) => ILPatternMatchingExt.MatchLdftn(x, ref val2), (Instruction x) => ILPatternMatchingExt.MatchNewobj(x, ref val2), (Instruction x) => ILPatternMatchingExt.MatchLdstr(x, ref text) }); val.Emit(OpCodes.Dup); val.EmitDelegate<Action<SimpleDialogBox>>((Action<SimpleDialogBox>)AddQuitText); } private static void AddQuitText(SimpleDialogBox simpleDialogBox) { if (NetworkServer.active || NetworkUser.readOnlyInstancesList.Count == NetworkUser.readOnlyLocalPlayersList.Count) { if (ProperSavePlugin.CurrentSave == null) { TextMeshProUGUI descriptionLabel = simpleDialogBox.descriptionLabel; ((TMP_Text)descriptionLabel).text = ((TMP_Text)descriptionLabel).text + Language.GetString(LanguageConsts.PROPER_SAVE_QUIT_DIALOG_NOT_SAVED); return; } if (ProperSavePlugin.CurrentSave.Header.StageClearCount == Run.instance.stageClearCount) { TextMeshProUGUI descriptionLabel2 = simpleDialogBox.descriptionLabel; ((TMP_Text)descriptionLabel2).text = ((TMP_Text)descriptionLabel2).text + Language.GetString(LanguageConsts.PROPER_SAVE_QUIT_DIALOG_SAVED); return; } TextMeshProUGUI descriptionLabel3 = simpleDialogBox.descriptionLabel; string text = ((TMP_Text)descriptionLabel3).text; string pROPER_SAVE_QUIT_DIALOG_SAVED_BEFORE = LanguageConsts.PROPER_SAVE_QUIT_DIALOG_SAVED_BEFORE; object[] array = new string[1] { (Run.instance.stageClearCount - ProperSavePlugin.CurrentSave.Header.StageClearCount).ToString() }; ((TMP_Text)descriptionLabel3).text = text + Language.GetStringFormatted(pROPER_SAVE_QUIT_DIALOG_SAVED_BEFORE, array); } } } } namespace ProperSave.Utils { public static class CatalogHelpers { private static Dictionary<string, DroneIndex> DroneNameToIndex { get; set; } public static DifficultyIndex FindDifficultyIndex(string nameToken) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) try { if (ModCompat.IsR2APIDifficultyLoaded) { return ModCompat.FindR2APIDifficultyIndex(nameToken); } } catch (Exception ex) { ProperSavePlugin.InstanceLogger.LogError((object)ex); } for (int i = 0; i < DifficultyCatalog.difficultyDefs.Length; i++) { if (DifficultyCatalog.difficultyDefs[i].nameToken == nameToken) { return (DifficultyIndex)i; } } return (DifficultyIndex)(-1); } public static DroneIndex FindDroneIndex(string name) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (DroneNameToIndex == null) { DroneNameToIndex = DroneCatalog.droneDefs.ToDictionary((DroneDef d) => ((Object)d).name, (DroneDef d) => d.droneIndex); } if (DroneNameToIndex.TryGetValue(name, out var value)) { return value; } return (DroneIndex)(-1); } } internal static class GenericObjectsHelper { public static void ReadTypesAndObjects(ReaderContext context) { BinaryReader reader = context.Reader; TypeData[] array = (context.Types = new TypeData[reader.ReadPackedInt32()]); TypeData[] array3 = array; for (int i = 0; i < array3.Length; i++) { array3[i] = new TypeData { TypeIndex = i }; } for (int j = 0; j < array3.Length; j++) { array3[j].Read(context); } foreach (TypeData typeData in array3) { if (typeData.IsPrimitive) { typeData.Type = GetPrimitiveType(typeData.ObjectType); continue; } string text = typeData.Name.Build(); typeData.Type = Type.GetType(text, throwOnError: false); if ((object)typeData.Type == null) { ProperSavePlugin.InstanceLogger.LogWarning((object)("Type " + text + " was not found")); } else if (typeData.ObjectType == ObjectType.ValueType || typeData.ObjectType == ObjectType.Object || typeData.ObjectType == ObjectType.Class) { for (int l = 0; l < typeData.Fields.Count; l++) { FieldData fieldData = typeData.Fields[l]; fieldData.Field = typeData.Type.GetField(fieldData.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy); if (fieldData.Field == null) { fieldData.Property = typeData.Type.GetProperty(fieldData.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy); if (fieldData.Property != null && !fieldData.Property.CanRead && !fieldData.Property.CanWrite) { fieldData.Property = null; } } } } else if (typeData.ObjectType == ObjectType.Collection) { typeData.AddCollectionElementMethod = typeData.Type.GetInterfaces().FirstOrDefault((Type type3) => type3.IsGenericType && type3.GetGenericTypeDefinition() == typeof(ICollection<>))?.GetMethod("Add"); } else if (typeData.ObjectType == ObjectType.KeyValuePair) { typeData.Constructor = typeData.Type.GetConstructors()[0]; } } (object, TypeData)[] array4 = (context.Objects = new(object, TypeData)[reader.ReadPackedInt32()]); (object, TypeData)[] array6 = array4; for (int num = 0; num < array6.Length; num++) { TypeData typeData2 = array3[reader.ReadPackedInt32()]; Type type = typeData2.Type; if (typeData2.ObjectType == ObjectType.Array) { int[] array7 = new int[typeData2.ArrayRank]; for (int num2 = 0; num2 < typeData2.ArrayRank; num2++) { array7[num2] = reader.ReadPackedInt32(); } Type type2 = typeData2.CollectionArgumentType.Type; if (type != null && type2 != null) { array6[num] = (Array.CreateInstance(type2, array7), typeData2); } else { array6[num] = (null, typeData2); } } else if (type != null) { if (typeData2.IsCollection) { try { array6[num] = (Activator.CreateInstance(type), typeData2); } catch (Exception ex) { ProperSavePlugin.InstanceLogger.LogWarning((object)ex); array6[num] = (null, typeData2); } } else { array6[num] = (RuntimeHelpers.GetUninitializedObject(type), typeData2); } } else { array6[num] = (null, typeData2); } } } internal static void ReadObjectsData(ReaderContext context) { for (int i = 0; i < context.Objects.Length; i++) { var (obj, type) = context.Objects[i]; ReadObject(obj, type, context); } } private static void ReadObject(object obj, TypeData type, ReaderContext context) { if (type.IsCollection) { ReadCollection(obj, type, context); return; } foreach (FieldData field in type.Fields) { field.SetValue(obj, ReadValue(field.Type.ObjectType, context)); } } private static object ReadValue(ObjectType objectType, ReaderContext context) { BinaryReader reader = context.Reader; if (objectType == ObjectType.Object) { objectType = (ObjectType)reader.ReadByte(); } switch (objectType) { case ObjectType.Sbyte: return reader.ReadSByte(); case ObjectType.Byte: return reader.ReadByte(); case ObjectType.Short: return reader.ReadPackedInt16(); case ObjectType.UShort: return reader.ReadPackedUInt16(); case ObjectType.Int: return reader.ReadPackedInt32(); case ObjectType.UInt: return reader.ReadPackedUInt32(); case ObjectType.Long: return reader.ReadPackedInt64(); case ObjectType.ULong: return reader.ReadPackedUInt64(); case ObjectType.Float: return reader.ReadSingle(); case ObjectType.Double: return reader.ReadDouble(); case ObjectType.Boolean: return reader.ReadBoolean(); case ObjectType.String: return context.SharedStrings.GetSafe(reader.ReadPackedInt32()); case ObjectType.KeyValuePair: { int num3 = reader.ReadPackedInt32(); TypeData typeData2 = context.Types[num3]; object[] array = ObjectBuffer.Buffers[2]; for (int i = 0; i < typeData2.Fields.Count; i++) { FieldData fieldData = typeData2.Fields[i]; array[i] = ReadValue(fieldData.Type.ObjectType, context); } return typeData2.CreateObject(array); } case ObjectType.ValueType: { int num2 = reader.ReadPackedInt32(); TypeData typeData = context.Types[num2]; object obj = (((object)typeData.Type == null) ? null : RuntimeHelpers.GetUninitializedObject(typeData.Type)); { foreach (FieldData field in typeData.Fields) { field.SetValue(obj, ReadValue(field.Type.ObjectType, context)); } return obj; } } case ObjectType.Array: case ObjectType.Collection: case ObjectType.Class: case ObjectType.Object: { int num = reader.ReadPackedInt32(); if (num == -1) { return null; } return context.Objects[num].obj; } default: throw new NotSupportedException(); } } private static void ReadCollection(object obj, TypeData type, ReaderContext context) { BinaryReader reader = context.Reader; switch (type.ObjectType) { case ObjectType.Array: { Array array = obj as Array; int num2 = reader.ReadPackedInt32(); TypeData collectionArgumentType2 = type.CollectionArgumentType; if (array == null) { for (int j = 0; j < num2; j++) { ReadValue(collectionArgumentType2.ObjectType, context); } break; } int arrayRank = type.ArrayRank; int[] array2 = new int[arrayRank]; int[] indices = new int[arrayRank]; for (int k = 0; k < arrayRank; k++) { array2[k] = array.GetLength(k); } if (array2.All((int l) => l > 0)) { do { array.SetValue(ReadValue(collectionArgumentType2.ObjectType, context), indices); } while (IncrementIndices(indices, array2)); } break; } case ObjectType.Collection: { TypeData collectionArgumentType = type.CollectionArgumentType; int num = reader.ReadPackedInt32(); for (int i = 0; i < num; i++) { object value = ReadValue(collectionArgumentType.ObjectType, context); type.AddCollectionElement(obj, value); } break; } } } private static bool IncrementIndices(int[] indices, int[] lengths) { int num = lengths.Length - 1; while (num >= 0) { if (++indices[num] == lengths[num]) { indices[num] = 0; num--; continue; } return true; } return false; } private static Type GetPrimitiveType(ObjectType objectType) { return objectType switch { ObjectType.Sbyte => typeof(sbyte), ObjectType.Byte => typeof(byte), ObjectType.Short => typeof(short), ObjectType.UShort => typeof(ushort), ObjectType.Int => typeof(int), ObjectType.UInt => typeof(uint), ObjectType.Long => typeof(long), ObjectType.ULong => typeof(ulong), ObjectType.Float => typeof(float), ObjectType.Double => typeof(double), ObjectType.Boolean => typeof(bool), ObjectType.String => typeof(string), _ => throw new NotSupportedException(), }; } internal static void WriteObjectsData(WriterContext context) { while (context.ObjectsQueue.Count > 0) { WriteObject(context.ObjectsQueue.Dequeue(), context); } } internal static void WriteTypesAndObjects(WriterContext context) { BinaryWriter writer = context.Writer; writer.WritePacked(context.Types.Count); foreach (KeyValuePair<Type, TypeData> item2 in context.Types.OrderBy((KeyValuePair<Type, TypeData> kvp) => kvp.Value.TypeIndex)) { item2.Deconstruct(out var _, out var value); value.Write(context); } writer.WritePacked(context.Objects.Count); foreach (KeyValuePair<object, (int, TypeData)> item3 in context.Objects.OrderBy((KeyValuePair<object, (int index, TypeData typeData)> kvp) => kvp.Value.index)) { item3.Deconstruct(out var key2, out var value2); (int, TypeData) tuple = value2; object obj = key2; TypeData item = tuple.Item2; writer.WritePacked(item.TypeIndex); if (item.ObjectType == ObjectType.Array) { for (int num = 0; num < item.ArrayRank; num++) { writer.WritePacked(((Array)obj).GetLength(num)); } } } } private static TypeData GetTypeData(Type type, WriterContext context) { if (!context.Types.TryGetValue(type, out var value)) { Dictionary<Type, TypeData> types = context.Types; TypeData obj = new TypeData { Type = type, Name = TypeName.FromType(type), TypeIndex = context.Types.Count }; value = obj; types[type] = obj; if (type == typeof(sbyte)) { value.ObjectType = ObjectType.Sbyte; } else if (type == typeof(byte)) { value.ObjectType = ObjectType.Byte; } else if (type == typeof(short)) { value.ObjectType = ObjectType.Short; } else if (type == typeof(ushort))