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.4
plugins/ProperSave/ProperSave.dll
Decompiled 3 months 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.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.4.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; } } public static class JSONWriter { public static string ToJson(this object item) { StringBuilder stringBuilder = new StringBuilder(); AppendValue(stringBuilder, item); return stringBuilder.ToString(); } private static void AppendValue(StringBuilder stringBuilder, object item) { if (item == null) { stringBuilder.Append("null"); return; } Type type = item.GetType(); if (type == typeof(string)) { stringBuilder.Append('"'); string text = (string)item; for (int i = 0; i < text.Length; i++) { if (text[i] < ' ' || text[i] == '"' || text[i] == '\\') { stringBuilder.Append('\\'); int num = "\"\\\n\r\t\b\f".IndexOf(text[i]); if (num >= 0) { stringBuilder.Append("\"\\nrtbf"[num]); } else { stringBuilder.AppendFormat("u{0:X4}", (uint)text[i]); } } else { stringBuilder.Append(text[i]); } } stringBuilder.Append('"'); return; } if (type == typeof(byte) || type == typeof(int) || type == typeof(long) || type == typeof(uint) || type == typeof(ulong)) { stringBuilder.Append(item.ToString()); return; } if (type == typeof(float)) { stringBuilder.Append(((float)item).ToString(CultureInfo.InvariantCulture)); return; } if (type == typeof(double)) { stringBuilder.Append(((double)item).ToString(CultureInfo.InvariantCulture)); return; } if (type == typeof(bool)) { stringBuilder.Append(((bool)item) ? "true" : "false"); return; } if (type.IsEnum) { stringBuilder.Append('"'); stringBuilder.Append(item.ToString()); stringBuilder.Append('"'); return; } if (item is IList) { stringBuilder.Append('['); bool flag = true; IList list = item as IList; for (int j = 0; j < list.Count; j++) { if (flag) { flag = false; } else { stringBuilder.Append(','); } AppendValue(stringBuilder, list[j]); } stringBuilder.Append(']'); return; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { if (type.GetGenericArguments()[0] != typeof(string)) { stringBuilder.Append("{}"); return; } stringBuilder.Append('{'); IDictionary dictionary = item as IDictionary; bool flag2 = true; foreach (object key in dictionary.Keys) { if (flag2) { flag2 = false; } else { stringBuilder.Append(','); } stringBuilder.Append('"'); stringBuilder.Append((string)key); stringBuilder.Append("\":"); AppendValue(stringBuilder, dictionary[key]); } stringBuilder.Append('}'); return; } stringBuilder.Append('{'); bool flag3 = true; FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy); for (int k = 0; k < fields.Length; k++) { if (fields[k].IsDefined(typeof(IgnoreDataMemberAttribute), inherit: true)) { continue; } object value = fields[k].GetValue(item); if (value != null) { if (flag3) { flag3 = false; } else { stringBuilder.Append(','); } stringBuilder.Append('"'); stringBuilder.Append(GetMemberName(fields[k])); stringBuilder.Append("\":"); AppendValue(stringBuilder, value); } } PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy); for (int l = 0; l < properties.Length; l++) { if (!properties[l].CanRead || properties[l].IsDefined(typeof(IgnoreDataMemberAttribute), inherit: true)) { continue; } object value2 = properties[l].GetValue(item, null); if (value2 != null) { if (flag3) { flag3 = false; } else { stringBuilder.Append(','); } stringBuilder.Append('"'); stringBuilder.Append(GetMemberName(properties[l])); stringBuilder.Append("\":"); AppendValue(stringBuilder, value2); } } stringBuilder.Append('}'); } private static string GetMemberName(MemberInfo member) { if (member.IsDefined(typeof(DataMemberAttribute), inherit: true)) { DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), inherit: true); if (!string.IsNullOrEmpty(dataMemberAttribute.Name)) { return dataMemberAttribute.Name; } } return member.Name; } } } namespace ProperSave { public static class Extensions { 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 int AddOrIndexOf<T>(this List<T> list, T value) { 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 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 { [CompilerGenerated] private sealed class <LoadForceCoroutine>d__22 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <LoadForceCoroutine>d__22(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; Console.instance.SubmitCmd((NetworkUser)null, "host 0", false); <>2__current = (object)new WaitUntil((Func<bool>)(() => (Object)(object)PreGameController.instance != (Object)null)); <>1__state = 1; return true; case 1: <>1__state = -1; PreGameController.instance.StartRun(); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <LoadLobby>d__20 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <LoadLobby>d__20(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if (<>1__state != 0) { return false; } <>1__state = -1; if ((Object)(object)PreGameController.instance == (Object)null) { ProperSavePlugin.InstanceLogger.LogInfo((object)"PreGameController instance not found"); return false; } 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"); return false; } SaveFileMetadata currentLobbySaveMetadata = SaveFileMetadata.GetCurrentLobbySaveMetadata(); if (currentLobbySaveMetadata == null) { ProperSavePlugin.InstanceLogger.LogInfo((object)"Save file for current users is not found"); return false; } UPath? filePath = currentLobbySaveMetadata.FilePath; if (!filePath.HasValue) { ProperSavePlugin.InstanceLogger.LogInfo((object)"Metadata doesn't contain file name for the save file"); return false; } if (!ProperSavePlugin.SavesFileSystem.FileExists(filePath.Value)) { ProperSavePlugin.InstanceLogger.LogInfo((object)$"File \"{filePath}\" is not found"); return false; } currentLobbySaveMetadata.ReadBody(); 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(); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static bool isLoading; public static bool IsLoading { get { return isLoading; } internal set { if (isLoading != value) { isLoading = value; if (isLoading) { Loading.OnLoadingStarted?.Invoke(CurrentSave); } else { Loading.OnLoadingEnded?.Invoke(CurrentSave); } } } } 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); } [IteratorStateMachine(typeof(<LoadLobby>d__20))] internal static IEnumerator LoadLobby() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <LoadLobby>d__20(0); } [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; } } [IteratorStateMachine(typeof(<LoadForceCoroutine>d__22))] private static IEnumerator LoadForceCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <LoadForceCoroutine>d__22(0); } } 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) { TooltipContent val = default(TooltipContent); val.titleToken = LanguageConsts.PROPER_SAVE_TOOLTIP_LOAD_TITLE; val.overrideBodyText = GetSaveDescription(currentLobbySaveMetadata); val.titleColor = Color.black; val.disableBodyRichText = false; lastTooltipContent = val; } 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 { [CompilerGenerated] private sealed class <LoadShareSuiteMoneyInternal>d__23 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public uint money; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <LoadShareSuiteMoneyInternal>d__23(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitUntil((Func<bool>)(() => !MoneySharingHooks.MapTransitionActive)); <>1__state = 1; return true; case 1: <>1__state = -1; MoneySharingHooks.SharedMoneyValue = (int)money; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } 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)] [IteratorStateMachine(typeof(<LoadShareSuiteMoneyInternal>d__23))] private static IEnumerator LoadShareSuiteMoneyInternal(uint money) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <LoadShareSuiteMoneyInternal>d__23(0) { money = 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); } } [BepInPlugin("com.KingEnderBrine.ProperSave", "Proper Save", "3.0.4")] public class ProperSavePlugin : BaseUnityPlugin { public const string GUID = "com.KingEnderBrine.ProperSave"; public const string Name = "Proper Save"; public const string Version = "3.0.4"; 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).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).Dispose(); } } } } public class SaveFile { internal static readonly int currentVersion = 1; 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>(); public Dictionary<string, ProperSave.Data.ModdedData> ModdedData { get; set; } = new Dictionary<string, ProperSave.Data.ModdedData>(); 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 @delegate in array2) { try { ((Action<Dictionary<string, object>>)@delegate)(dictionary); } catch (Exception ex) { ProperSavePlugin.InstanceLogger.LogError((object)ex); } } } ModdedData = dictionary.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)ModdedData[key].Value; } internal static SaveFile Read(BinaryReader reader) { SaveFile saveFile = new SaveFile(); 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 }; saveFile.ArtifactsData = ProperSave.SaveData.ArtifactsData.Read(context); saveFile.RunData = ProperSave.SaveData.RunData.Read(context); saveFile.RunArtifactsData = ProperSave.SaveData.RunArtifactsData.Read(context); saveFile.TeamData = ProperSave.SaveData.TeamData.Read(context); int num = reader.ReadInt32(); for (int j = 0; j < num; j++) { saveFile.PlayersData.Add(ProperSave.SaveData.PlayerData.Read(context)); } saveFile.ModdedData = ReadModdedData(context); reader.BaseStream.Seek(position2, SeekOrigin.Begin); return saveFile; } private static Dictionary<string, ProperSave.Data.ModdedData> ReadModdedData(ReaderContext context) { return context.Reader.ReadString().FromJson<Dictionary<string, ProperSave.Data.ModdedData>>(); } internal void Write(BinaryWriter writer, bool resilient) { WriterContext writerContext = new WriterContext { Writer = writer, SharedStrings = new List<string>(), Resilient = resilient }; writer.Write(currentVersion); writer.Write(resilient); long position = writer.BaseStream.Position; writer.Write(0L); ArtifactsData.Write(writerContext); RunData.Write(writerContext); RunArtifactsData.Write(writerContext); TeamData.Write(writerContext); writer.Write(PlayersData.Count); for (int i = 0; i < PlayersData.Count; i++) { PlayersData[i].Write(writerContext); } WriteModdedData(writerContext); 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); } private void WriteModdedData(WriterContext context) { context.Writer.Write(ModdedData.ToJson()); } } 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_009a: 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) WriterContext writerContext = new WriterContext { Writer = writer, SharedStrings = new List<string>(), 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 { [CompilerGenerated] private sealed class <>c__DisplayClass26_0 { public GameModeIndex gameMode; public List<NetworkUserId> users; internal bool <GetCurrentLobbySaveMetadata>b__1(SaveFileMetadata el) { //IL_0020: 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) if (el.Header.Users.Length != users.Count || el.Header.GameMode != gameMode) { return false; } return users.DifferenceCount(el.Header.Users.Select((HeaderUserData e) => (e?.UserId?.Load()).GetValueOrDefault())) == 0; } } 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_00a0: Expected O, but got I4 //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_009b->IL009b: Incompatible stack types: O vs I4 //IL_0091->IL009b: Incompatible stack types: O vs I4 //IL_0085->IL009b: Incompatible stack types: I4 vs O //IL_0085->IL009b: Incompatible stack types: O vs I4 try { <>c__DisplayClass26_0 CS$<>8__locals0 = new <>c__DisplayClass26_0(); CS$<>8__locals0.users = NetworkUser.readOnlyInstancesList.Select((NetworkUser el) => el.id).ToList(); if ((Object)(object)exceptUser != (Object)null) { CS$<>8__locals0.users.Remove(exceptUser.id); } if (CS$<>8__locals0.users.Count == 0) { return null; } object obj = CS$<>8__locals0; int num; if (Object.op_Implicit((Object)(object)PreGameController.instance)) { obj = PreGameController.instance.gameModeIndex; num = (int)obj; } else if (Object.op_Implicit((Object)(object)Run.instance)) { obj = Run.instance.gameModeIndex; num = (int)obj; } else { num = -1; obj = num; num = (int)obj; } ((<>c__DisplayClass26_0)num).gameMode = (GameModeIndex)obj; if ((int)CS$<>8__locals0.gameMode == -1) { return null; } if (CS$<>8__locals0.users.Count == 1) { <>c__DisplayClass26_0 <>c__DisplayClass26_ = CS$<>8__locals0; 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 == <>c__DisplayClass26_.gameMode); } return SavesMetadata.FirstOrDefault((SaveFileMetadata el) => el.Header.Users.Length == CS$<>8__locals0.users.Count && el.Header.GameMode == CS$<>8__locals0.gameMode && CS$<>8__locals0.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); } } 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 val3 = default(MethodReference); 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 val3), (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); } } public class ReaderContext { public BinaryReader Reader { get; set; } public string[] SharedStrings { get; set; } public bool Resilient { get; set; } public int Version { get; set; } } public static class SharedIndexHelpers { public static int FromDifficulty(DifficultyIndex difficultyIndex, WriterContext context) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected I4, but got Unknown //IL_0024: Unknown result type (might be due to invalid IL or missing references) if ((int)difficultyIndex == -1) { return -1; } if (!context.Resilient) { return (int)difficultyIndex; } DifficultyDef difficultyDef = DifficultyCatalog.GetDifficultyDef(difficultyIndex); if (difficultyDef == null) { ProperSavePlugin.InstanceLogger.LogWarning((object)$"Couldn't find difficulty with index \"{difficultyIndex}\" in DifficultyCatalog"); return -1; } return context.SharedStrings.AddOrIndexOf(difficultyDef.nameToken); } public static DifficultyIndex ResolveDifficulty(int sharedIndex, ReaderContext context) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (sharedIndex == -1) { return (DifficultyIndex)(-1); } if (!context.Resilient) { return (DifficultyIndex)sharedIndex; } string text = context.SharedStrings[sharedIndex]; DifficultyIndex val = CatalogHelpers.FindDifficultyIndex(text); if ((int)val == -1) { ProperSavePlugin.InstanceLogger.LogWarning((object)("Couldn't find difficulty \"" + text + "\" in DifficultyCatalog")); return (DifficultyIndex)(-1); } return val; } public static int FromRule(int ruleIndex, WriterContext context) { if (!context.Resilient || ruleIndex == -1) { return ruleIndex; } RuleDef ruleDef = RuleCatalog.GetRuleDef(ruleIndex); if (ruleDef == null) { ProperSavePlugin.InstanceLogger.LogWarning((object)$"Couldn't find rule with index \"{ruleIndex}\" in RuleCatalog"); return -1; } return context.SharedStrings.AddOrIndexOf(ruleDef.globalName); } public static int ResolveRule(int sharedIndex, ReaderContext context) { if (sharedIndex == -1) { return -1; } if (!context.Resilient) { return sharedIndex; } string text = context.SharedStrings[sharedIndex]; RuleDef val = RuleCatalog.FindRuleDef(text); if (val == null) { ProperSavePlugin.InstanceLogger.LogWarning((object)("Couldn't find rule \"" + text + "\" in RuleCatalog")); return -1; } return val.globalIndex; } public static int FromArtifact(ArtifactIndex artifactIndex, WriterContext context) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected I4, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) if ((int)artifactIndex == -1) { return -1; } if (!context.Resilient) { return (int)artifactIndex; } ArtifactDef artifactDef = ArtifactCatalog.GetArtifactDef(artifactIndex); if ((Object)(object)artifactDef == (Object)null) { ProperSavePlugin.InstanceLogger.LogWarning((object)$"Couldn't find artifact with index \"{artifactIndex}\" in ArtifactCatalog"); return -1; } return context.SharedStrings.AddOrIndexOf(artifactDef.cachedName); } public static ArtifactIndex ResolveArtifact(int sharedIndex, ReaderContext context) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (sharedIndex == -1) { return (ArtifactIndex)(-1); } if (!context.Resilient) { return (ArtifactIndex)sharedIndex; } string text = context.SharedStrings[sharedIndex]; ArtifactDef val = ArtifactCatalog.FindArtifactDef(text); if ((Object)(object)val == (Object)null) { ProperSavePlugin.InstanceLogger.LogWarning((object)("Couldn't find artifact \"" + text + "\" in ArtifactCatalog")); return (ArtifactIndex)(-1); } return val.artifactIndex; } public static int FromBody(BodyIndex bodyIndex, WriterContext context) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected I4, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) if ((int)bodyIndex == -1) { return -1; } if (!context.Resilient) { return (int)bodyIndex; } string bodyName = BodyCatalog.GetBodyName(bodyIndex); if (string.IsNullOrEmpty(bodyName)) { ProperSavePlugin.InstanceLogger.LogWarning((object)$"Couldn't find body with index \"{bodyIndex}\" in BodyCatalog"); return -1; } return context.SharedStrings.AddOrIndexOf(bodyName); } public static BodyIndex ResolveBody(int sharedIndex, ReaderContext context) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (sharedIndex == -1) { return (BodyIndex)(-1); } if (!context.Resilient) { return (BodyIndex)sharedIndex; } string text = context.SharedStrings[sharedIndex]; BodyIndex val = BodyCatalog.FindBodyIndex(text); if ((int)val == -1) { ProperSavePlugin.InstanceLogger.LogWarning((object)("Couldn't find body \"" + text + "\" in BodyCatalog")); return (BodyIndex)(-1); } return val; } public static int FromStatField(int statIndex, WriterContext context) { if (!context.Resilient || statIndex == -1) { return statIndex; } StatDef safe = StatDef.allStatDefs.GetSafe(statIndex); if (safe == null) { ProperSavePlugin.InstanceLogger.LogWarning((object)$"Couldn't find stat with index \"{statIndex}\" in StatDef"); return -1; } return context.SharedStrings.AddOrIndexOf(safe.name); } public static int ResolveStatField(int sharedIndex, ReaderContext context) { if (sharedIndex == -1) { return -1; } if (!context.Resilient) { return sharedIndex; } string text = context.SharedStrings[sharedIndex]; StatDef val = StatDef.Find(text); if (val == null) { ProperSavePlugin.InstanceLogger.LogWarning((object)("Couldn't find stat \"" + text + "\" in StatDef")); return -1; } return val.index; } public static int FromUnlockable(UnlockableIndex unlockableIndex, WriterContext context) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected I4, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) if ((int)unlockableIndex == -1) { return -1; } if (!context.Resilient) { return (int)unlockableIndex; } UnlockableDef unlockableDef = UnlockableCatalog.GetUnlockableDef(unlockableIndex); if ((Object)(object)unlockableDef == (Object)null) { ProperSavePlugin.InstanceLogger.LogWarning((object)$"Couldn't find unlockable with index \"{unlockableIndex}\" in UnlockableCatalog"); return -1; } return context.SharedStrings.AddOrIndexOf(unlockableDef.cachedName); } public static UnlockableIndex ResolveUnlockable(int sharedIndex, ReaderContext context) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (sharedIndex == -1) { return (UnlockableIndex)(-1); } if (!context.Resilient) { return (UnlockableIndex)sharedIndex; } string text = context.SharedStrings[sharedIndex]; UnlockableDef unlockableDef = UnlockableCatalog.GetUnlockableDef(text); if ((Object)(object)unlockableDef == (Object)null) { ProperSavePlugin.InstanceLogger.LogWarning((object)("Couldn't find unlockable \"" + text + "\" in UnlockableCatalog")); return (UnlockableIndex)(-1); } return unlockableDef.index; } public static int FromMaster(MasterIndex masterIndex, WriterContext context) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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) if (masterIndex == MasterIndex.none) { return -1; } if (!context.Resilient) { return masterIndex.i; } string masterName = MasterCatalog.GetMasterName(masterIndex); if (string.IsNullOrEmpty(masterName)) { ProperSavePlugin.InstanceLogger.LogWarning((object)$"Couldn't find master with index \"{masterIndex}\" in MasterCatalog"); return -1; } return context.SharedStrings.AddOrIndexOf(masterName); } public static MasterIndex ResolveMaster(int sharedIndex, ReaderContext context) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (sharedIndex == -1) { return MasterIndex.none; } if (!context.Resilient) { return new MasterIndex(sharedIndex); } string text = context.SharedStrings[sharedIndex]; MasterIndex val = MasterCatalog.FindMasterIndex(text); if (val == MasterIndex.none) { ProperSavePlugin.InstanceLogger.LogWarning((object)("Couldn't find master \"" + text + "\" in MasterCatalog")); return MasterIndex.none; } return val; } public static int FromItem(ItemIndex itemIndex, WriterContext context) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected I4, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) if ((int)itemIndex == -1) { return -1; } if (!context.Resilient) { return (int)itemIndex; } ItemDef itemDef = ItemCatalog.GetItemDef(itemIndex); if ((Object)(object)itemDef == (Object)null) { ProperSavePlugin.InstanceLogger.LogWarning((object)$"Couldn't find item with index \"{itemIndex}\" in ItemCatalog"); return -1; } return context.SharedStrings.AddOrIndexOf(((Object)itemDef).name); } public static ItemIndex ResolveItem(int sharedIndex, ReaderContext context) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (sharedIndex == -1) { return (ItemIndex)(-1); } if (!context.Resilient) { return (ItemIndex)sharedIndex; } string text = context.SharedStrings[sharedIndex]; ItemIndex val = ItemCatalog.FindItemIndex(text); if ((int)val == -1) { ProperSavePlugin.InstanceLogger.LogWarning((object)("Couldn't find item \"" + text