Decompiled source of TheVault v2.0.4
TheVault.dll
Decompiled 2 hours 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.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using SunhavenMods.Shared; using TheVault.Patches; using TheVault.UI; using TheVault.Vault; using UnityEngine; using UnityEngine.Networking; using UnityEngine.SceneManagement; using Wish; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("TheVault")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+c7976283ee1309608b00428a169c130e784344a0")] [assembly: AssemblyProduct("TheVault")] [assembly: AssemblyTitle("TheVault")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SunhavenMods.Shared { public static class VersionChecker { public class VersionCheckResult { public bool Success { get; set; } public bool UpdateAvailable { get; set; } public string CurrentVersion { get; set; } public string LatestVersion { get; set; } public string ModName { get; set; } public string NexusUrl { get; set; } public string Changelog { get; set; } public string ErrorMessage { get; set; } } private class VersionCheckRunner : MonoBehaviour { public void StartCheck(string pluginGuid, string currentVersion, Action<VersionCheckResult> onComplete) { ((MonoBehaviour)this).StartCoroutine(CheckVersionCoroutine(pluginGuid, currentVersion, onComplete)); } private IEnumerator CheckVersionCoroutine(string pluginGuid, string currentVersion, Action<VersionCheckResult> onComplete) { VersionCheckResult result = new VersionCheckResult { CurrentVersion = currentVersion }; UnityWebRequest www = UnityWebRequest.Get("https://azraelgodking.github.io/SunhavenMod/versions.json"); try { www.timeout = 10; yield return www.SendWebRequest(); if ((int)www.result == 2 || (int)www.result == 3) { result.Success = false; result.ErrorMessage = "Network error: " + www.error; LogWarning(result.ErrorMessage); onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); yield break; } try { string text = www.downloadHandler.text; string pattern = "\"" + Regex.Escape(pluginGuid) + "\"\\s*:\\s*\\{([^}]+)\\}"; Match match = Regex.Match(text, pattern, RegexOptions.Singleline); if (!match.Success) { result.Success = false; result.ErrorMessage = "Mod '" + pluginGuid + "' not found in versions.json"; LogWarning(result.ErrorMessage); onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); yield break; } string value = match.Groups[1].Value; result.LatestVersion = ExtractJsonString(value, "version"); result.ModName = ExtractJsonString(value, "name"); result.NexusUrl = ExtractJsonString(value, "nexus"); result.Changelog = ExtractJsonString(value, "changelog"); if (string.IsNullOrEmpty(result.LatestVersion)) { result.Success = false; result.ErrorMessage = "Could not parse version from response"; LogWarning(result.ErrorMessage); onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); yield break; } result.Success = true; result.UpdateAvailable = CompareVersions(currentVersion, result.LatestVersion) < 0; if (result.UpdateAvailable) { Log("Update available for " + result.ModName + ": " + currentVersion + " -> " + result.LatestVersion); } else { Log(result.ModName + " is up to date (v" + currentVersion + ")"); } } catch (Exception ex) { result.Success = false; result.ErrorMessage = "Parse error: " + ex.Message; LogError(result.ErrorMessage); } } finally { ((IDisposable)www)?.Dispose(); } onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); } private string ExtractJsonString(string json, string key) { string pattern = "\"" + key + "\"\\s*:\\s*(?:\"([^\"]*)\"|null)"; Match match = Regex.Match(json, pattern); if (!match.Success) { return null; } return match.Groups[1].Value; } } private const string VersionsUrl = "https://azraelgodking.github.io/SunhavenMod/versions.json"; private static ManualLogSource _logger; public static void CheckForUpdate(string pluginGuid, string currentVersion, ManualLogSource logger = null, Action<VersionCheckResult> onComplete = null) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) _logger = logger; VersionCheckRunner versionCheckRunner = new GameObject("VersionChecker").AddComponent<VersionCheckRunner>(); Object.DontDestroyOnLoad((Object)(object)((Component)versionCheckRunner).gameObject); versionCheckRunner.StartCheck(pluginGuid, currentVersion, onComplete); } public static int CompareVersions(string v1, string v2) { if (string.IsNullOrEmpty(v1) || string.IsNullOrEmpty(v2)) { return 0; } v1 = v1.TrimStart('v', 'V'); v2 = v2.TrimStart('v', 'V'); string[] array = v1.Split(new char[1] { '.' }); string[] array2 = v2.Split(new char[1] { '.' }); int num = Math.Max(array.Length, array2.Length); for (int i = 0; i < num; i++) { int result; int num2 = ((i < array.Length && int.TryParse(array[i], out result)) ? result : 0); int result2; int num3 = ((i < array2.Length && int.TryParse(array2[i], out result2)) ? result2 : 0); if (num2 < num3) { return -1; } if (num2 > num3) { return 1; } } return 0; } internal static void Log(string message) { ManualLogSource logger = _logger; if (logger != null) { logger.LogInfo((object)("[VersionChecker] " + message)); } } internal static void LogWarning(string message) { ManualLogSource logger = _logger; if (logger != null) { logger.LogWarning((object)("[VersionChecker] " + message)); } } internal static void LogError(string message) { ManualLogSource logger = _logger; if (logger != null) { logger.LogError((object)("[VersionChecker] " + message)); } } } public static class VersionCheckerExtensions { public static void NotifyUpdateAvailable(this VersionChecker.VersionCheckResult result, ManualLogSource logger = null) { if (!result.UpdateAvailable) { return; } string text = result.ModName + " update available: v" + result.LatestVersion; try { Type type = ReflectionHelper.FindWishType("NotificationStack"); if (type != null) { Type type2 = ReflectionHelper.FindType("SingletonBehaviour`1", "Wish"); if (type2 != null) { object obj = type2.MakeGenericType(type).GetProperty("Instance")?.GetValue(null); if (obj != null) { MethodInfo method = type.GetMethod("SendNotification", new Type[5] { typeof(string), typeof(int), typeof(int), typeof(bool), typeof(bool) }); if (method != null) { method.Invoke(obj, new object[5] { text, 0, 1, false, true }); return; } } } } } catch (Exception ex) { if (logger != null) { logger.LogWarning((object)("Failed to send native notification: " + ex.Message)); } } if (logger != null) { logger.LogWarning((object)("[UPDATE AVAILABLE] " + text)); } if (!string.IsNullOrEmpty(result.NexusUrl) && logger != null) { logger.LogWarning((object)("Download at: " + result.NexusUrl)); } } } public static class ReflectionHelper { public static readonly BindingFlags AllBindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; public static Type FindType(string typeName, params string[] namespaces) { Type type = AccessTools.TypeByName(typeName); if (type != null) { return type; } for (int i = 0; i < namespaces.Length; i++) { type = AccessTools.TypeByName(namespaces[i] + "." + typeName); if (type != null) { return type; } } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == typeName || t.FullName == typeName); if (type != null) { return type; } } catch (ReflectionTypeLoadException) { } } return null; } public static Type FindWishType(string typeName) { return FindType(typeName, "Wish"); } public static object GetStaticValue(Type type, string memberName) { if (type == null) { return null; } PropertyInfo property = type.GetProperty(memberName, AllBindingFlags); if (property != null && property.GetMethod != null) { return property.GetValue(null); } FieldInfo field = type.GetField(memberName, AllBindingFlags); if (field != null) { return field.GetValue(null); } return null; } public static object GetSingletonInstance(Type type) { if (type == null) { return null; } string[] array = new string[5] { "Instance", "instance", "_instance", "Singleton", "singleton" }; foreach (string memberName in array) { object staticValue = GetStaticValue(type, memberName); if (staticValue != null) { return staticValue; } } return null; } public static object GetInstanceValue(object instance, string memberName) { if (instance == null) { return null; } Type type = instance.GetType(); while (type != null) { PropertyInfo property = type.GetProperty(memberName, AllBindingFlags); if (property != null && property.GetMethod != null) { return property.GetValue(instance); } FieldInfo field = type.GetField(memberName, AllBindingFlags); if (field != null) { return field.GetValue(instance); } type = type.BaseType; } return null; } public static bool SetInstanceValue(object instance, string memberName, object value) { if (instance == null) { return false; } Type type = instance.GetType(); while (type != null) { PropertyInfo property = type.GetProperty(memberName, AllBindingFlags); if (property != null && property.SetMethod != null) { property.SetValue(instance, value); return true; } FieldInfo field = type.GetField(memberName, AllBindingFlags); if (field != null) { field.SetValue(instance, value); return true; } type = type.BaseType; } return false; } public static object InvokeMethod(object instance, string methodName, params object[] args) { if (instance == null) { return null; } Type type = instance.GetType(); Type[] array = args?.Select((object a) => a?.GetType() ?? typeof(object)).ToArray() ?? Type.EmptyTypes; MethodInfo methodInfo = AccessTools.Method(type, methodName, array, (Type[])null); if (methodInfo == null) { methodInfo = type.GetMethod(methodName, AllBindingFlags); } if (methodInfo == null) { return null; } return methodInfo.Invoke(instance, args); } public static object InvokeStaticMethod(Type type, string methodName, params object[] args) { if (type == null) { return null; } Type[] array = args?.Select((object a) => a?.GetType() ?? typeof(object)).ToArray() ?? Type.EmptyTypes; MethodInfo methodInfo = AccessTools.Method(type, methodName, array, (Type[])null); if (methodInfo == null) { methodInfo = type.GetMethod(methodName, AllBindingFlags); } if (methodInfo == null) { return null; } return methodInfo.Invoke(null, args); } public static FieldInfo[] GetAllFields(Type type) { if (type == null) { return Array.Empty<FieldInfo>(); } FieldInfo[] fields = type.GetFields(AllBindingFlags); IEnumerable<FieldInfo> second; if (!(type.BaseType != null) || !(type.BaseType != typeof(object))) { second = Enumerable.Empty<FieldInfo>(); } else { IEnumerable<FieldInfo> allFields = GetAllFields(type.BaseType); second = allFields; } return fields.Concat(second).Distinct().ToArray(); } public static PropertyInfo[] GetAllProperties(Type type) { if (type == null) { return Array.Empty<PropertyInfo>(); } PropertyInfo[] properties = type.GetProperties(AllBindingFlags); IEnumerable<PropertyInfo> second; if (!(type.BaseType != null) || !(type.BaseType != typeof(object))) { second = Enumerable.Empty<PropertyInfo>(); } else { IEnumerable<PropertyInfo> allProperties = GetAllProperties(type.BaseType); second = allProperties; } return (from p in properties.Concat(second) group p by p.Name into g select g.First()).ToArray(); } public static T TryGetValue<T>(object instance, string memberName, T defaultValue = default(T)) { try { object instanceValue = GetInstanceValue(instance, memberName); if (instanceValue is T result) { return result; } if (instanceValue != null && typeof(T).IsAssignableFrom(instanceValue.GetType())) { return (T)instanceValue; } return defaultValue; } catch { return defaultValue; } } } } namespace TheVault { public static class ItemIds { public const int SpringToken = 18020; public const int SummerToken = 18021; public const int WinterToken = 18022; public const int FallToken = 18023; public const int CopperKey = 1251; public const int IronKey = 1252; public const int AdamantKey = 1253; public const int MithrilKey = 1254; public const int SuniteKey = 1255; public const int GloriteKey = 1256; public const int KingsLostMineKey = 1257; public const int CommunityToken = 18013; public const int Doubloon = 60014; public const int BlackBottleCap = 60013; public const int RedCarnivalTicket = 18012; public const int CandyCornPieces = 18016; public const int ManaShard = 18015; } [BepInPlugin("com.azraelgodking.thevault", "The Vault", "2.0.3")] public class Plugin : BaseUnityPlugin { private static VaultManager _staticVaultManager; private static VaultSaveSystem _staticSaveSystem; private static VaultUI _staticVaultUI; private static VaultHUD _staticVaultHUD; internal static KeyCode StaticToggleKey = (KeyCode)118; internal static bool StaticRequireCtrl = true; internal static KeyCode StaticAltToggleKey = (KeyCode)289; internal static KeyCode StaticHUDToggleKey = (KeyCode)288; private Harmony _harmony; private VaultManager _vaultManager; private VaultSaveSystem _saveSystem; private VaultUI _vaultUI; private VaultHUD _vaultHUD; private ConfigEntry<KeyCode> _toggleKey; private ConfigEntry<bool> _requireCtrlModifier; private ConfigEntry<KeyCode> _altToggleKey; private ConfigEntry<bool> _enableHUD; private ConfigEntry<string> _hudPosition; private ConfigEntry<KeyCode> _hudToggleKey; private ConfigEntry<bool> _enableAutoSave; private ConfigEntry<float> _autoSaveInterval; private ConfigEntry<bool> _checkForUpdates; private string _lastKnownScene = ""; private bool _wasInMenuScene = true; private float _sceneCheckTimer; private const float SCENE_CHECK_INTERVAL = 0.5f; private float _heartbeatTimer; private const float HEARTBEAT_INTERVAL = 30f; private int _heartbeatCount; private static GameObject _persistentRunner; private static PersistentUpdateRunner _updateRunner; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } public static ConfigFile ConfigFile { get; private set; } private void Awake() { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Expected O, but got Unknown //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; ConfigFile = ((BaseUnityPlugin)this).Config; Log.LogInfo((object)"Loading The Vault v2.0.3"); CreatePersistentRunner(); try { InitializeConfig(); _vaultManager = new VaultManager(); _saveSystem = new VaultSaveSystem(_vaultManager); _staticVaultManager = _vaultManager; _staticSaveSystem = _saveSystem; GameObject val = new GameObject("TheVault_UI"); Object.DontDestroyOnLoad((Object)(object)val); _vaultUI = val.AddComponent<VaultUI>(); _vaultUI.Initialize(_vaultManager); _vaultUI.SetToggleKey(_toggleKey.Value, _requireCtrlModifier.Value); _vaultUI.SetAltToggleKey(_altToggleKey.Value); _staticVaultUI = _vaultUI; StaticToggleKey = _toggleKey.Value; StaticRequireCtrl = _requireCtrlModifier.Value; StaticAltToggleKey = _altToggleKey.Value; StaticHUDToggleKey = _hudToggleKey.Value; _vaultHUD = val.AddComponent<VaultHUD>(); _vaultHUD.Initialize(_vaultManager); _vaultHUD.SetEnabled(_enableHUD.Value); _vaultHUD.SetPosition(ParseHUDPosition(_hudPosition.Value)); _staticVaultHUD = _vaultHUD; IconCache.Initialize(); RegisterItemMappings(); _harmony = new Harmony("com.azraelgodking.thevault"); ApplyPatches(); PatchGameSave(); SceneManager.sceneLoaded += OnSceneLoaded; Log.LogInfo((object)"Subscribed to SceneManager.sceneLoaded for vault loading"); if (_checkForUpdates.Value) { VersionChecker.CheckForUpdate("com.azraelgodking.thevault", "2.0.3", Log, delegate(VersionChecker.VersionCheckResult result) { result.NotifyUpdateAvailable(Log); }); } Log.LogInfo((object)"The Vault loaded successfully!"); Log.LogInfo((object)string.Format("Press {0}{1} or {2} to open the vault", _requireCtrlModifier.Value ? "Ctrl+" : "", _toggleKey.Value, _altToggleKey.Value)); } catch (Exception arg) { Log.LogError((object)string.Format("Failed to load {0}: {1}", "The Vault", arg)); } } private void CreatePersistentRunner() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown if ((Object)(object)_persistentRunner != (Object)null) { Log.LogInfo((object)"PersistentRunner already exists"); return; } _persistentRunner = new GameObject("TheVault_PersistentRunner"); Object.DontDestroyOnLoad((Object)(object)_persistentRunner); ((Object)_persistentRunner).hideFlags = (HideFlags)61; _updateRunner = _persistentRunner.AddComponent<PersistentUpdateRunner>(); Log.LogInfo((object)"Created hidden PersistentRunner that survives game cleanup"); } public static void EnsureUIComponentsExist() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_persistentRunner == (Object)null || (Object)(object)_updateRunner == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"[EnsureUI] Recreating PersistentRunner..."); } _persistentRunner = new GameObject("TheVault_PersistentRunner"); Object.DontDestroyOnLoad((Object)(object)_persistentRunner); ((Object)_persistentRunner).hideFlags = (HideFlags)61; _updateRunner = _persistentRunner.AddComponent<PersistentUpdateRunner>(); ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)"[EnsureUI] PersistentRunner recreated"); } } if ((Object)(object)_staticVaultUI == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogInfo((object)"[EnsureUI] Recreating VaultUI..."); } GameObject val = new GameObject("TheVault_UI"); Object.DontDestroyOnLoad((Object)val); _staticVaultUI = val.AddComponent<VaultUI>(); _staticVaultUI.Initialize(_staticVaultManager); _staticVaultUI.SetToggleKey(StaticToggleKey, StaticRequireCtrl); _staticVaultUI.SetAltToggleKey(StaticAltToggleKey); _staticVaultHUD = val.AddComponent<VaultHUD>(); _staticVaultHUD.Initialize(_staticVaultManager); ManualLogSource log4 = Log; if (log4 != null) { log4.LogInfo((object)"[EnsureUI] VaultUI and VaultHUD recreated"); } } } catch (Exception ex) { ManualLogSource log5 = Log; if (log5 != null) { log5.LogError((object)("[EnsureUI] Error recreating UI: " + ex.Message)); } } } private void InitializeConfig() { _toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("UI", "ToggleKey", (KeyCode)118, "Key to toggle the vault UI"); _requireCtrlModifier = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "RequireCtrlModifier", true, "Require Ctrl key to be held when pressing toggle key"); _altToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("UI", "AltToggleKey", (KeyCode)289, "Alternative key to toggle vault UI (no modifier required). Useful for Steam Deck."); _enableHUD = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "EnableHUD", true, "Show a persistent HUD bar displaying vault currency totals"); _hudPosition = ((BaseUnityPlugin)this).Config.Bind<string>("HUD", "Position", "TopLeft", "HUD position: TopLeft, TopCenter, TopRight, BottomLeft, BottomCenter, BottomRight"); _hudToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("HUD", "ToggleKey", (KeyCode)288, "Key to toggle the HUD display on/off"); _enableAutoSave = ((BaseUnityPlugin)this).Config.Bind<bool>("Saving", "EnableAutoSave", true, "Automatically save vault data periodically"); _autoSaveInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Saving", "AutoSaveInterval", 300f, "Auto-save interval in seconds (default: 5 minutes)"); _checkForUpdates = ((BaseUnityPlugin)this).Config.Bind<bool>("Updates", "CheckForUpdates", true, "Check for mod updates on startup"); } private void RegisterItemMappings() { ItemPatches.AutoDepositEnabled = true; ItemPatches.RegisterItemCurrencyMapping(18020, "seasonal_Spring", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(18021, "seasonal_Summer", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(18022, "seasonal_Winter", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(18023, "seasonal_Fall", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(1251, "key_copper", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(1252, "key_iron", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(1253, "key_adamant", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(1254, "key_mithril", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(1255, "key_sunite", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(1256, "key_glorite", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(1257, "key_kingslostmine", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(18013, "special_communitytoken", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(60014, "special_doubloon", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(60013, "special_blackbottlecap", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(18012, "special_redcarnivalticket", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(18016, "special_candycornpieces", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(18015, "special_manashard", autoDeposit: true); Log.LogInfo((object)"Registered item-to-currency mappings with auto-deposit enabled"); } private void ApplyPatches() { try { Type typeFromHandle = typeof(Player); PatchMethod(typeFromHandle, "InitializeAsOwner", typeof(PlayerPatches), "OnPlayerInitialized"); Type type = AccessTools.TypeByName("Wish.ShopMenu"); if (type != null) { PatchMethodPrefix(type, "BuyItem", typeof(ShopPatches), "OnBeforeBuyItem"); } else { Log.LogWarning((object)"Could not find ShopMenu type - shop vault integration unavailable"); } Type type2 = AccessTools.TypeByName("Wish.SaveLoadManager"); if (type2 != null) { PatchMethod(type2, "Save", typeof(SaveLoadPatches), "OnGameSaved"); PatchMethod(type2, "Load", typeof(SaveLoadPatches), "OnGameLoaded"); } else { Log.LogWarning((object)"Could not find SaveLoadManager - using fallback save triggers"); } Type type3 = AccessTools.TypeByName("Wish.MainMenuController"); if (type3 != null) { PatchMethod(type3, "ReturnToMainMenu", typeof(SaveLoadPatches), "OnReturnToMenu"); } Type type4 = AccessTools.TypeByName("Wish.TitleScreen"); if (type4 != null) { PatchMethod(type4, "Start", typeof(SaveLoadPatches), "OnReturnToMenu"); } Type type5 = AccessTools.TypeByName("Wish.GameManager"); if (type5 != null) { PatchMethod(type5, "ReturnToMainMenu", typeof(SaveLoadPatches), "OnReturnToMenu"); PatchMethod(type5, "QuitToMainMenu", typeof(SaveLoadPatches), "OnReturnToMenu"); } PatchItemPickup(typeFromHandle); IEnumerable<MethodBase> patchedMethods = _harmony.GetPatchedMethods(); int num = 0; foreach (MethodBase item in patchedMethods) { Log.LogInfo((object)("Patched: " + item.DeclaringType?.Name + "." + item.Name)); num++; } Log.LogInfo((object)$"Total methods patched: {num}"); } catch (Exception arg) { Log.LogError((object)$"Harmony patching failed: {arg}"); } } private void PatchMethod(Type targetType, string methodName, Type patchType, string patchMethodName, Type[] parameters = null) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown try { MethodInfo methodInfo = ((parameters == null) ? AccessTools.Method(targetType, methodName, (Type[])null, (Type[])null) : AccessTools.Method(targetType, methodName, parameters, (Type[])null)); if (methodInfo == null) { Log.LogWarning((object)("Could not find method " + targetType.Name + "." + methodName)); return; } MethodInfo methodInfo2 = AccessTools.Method(patchType, patchMethodName, (Type[])null, (Type[])null); if (methodInfo2 == null) { Log.LogWarning((object)("Could not find patch method " + patchType.Name + "." + patchMethodName)); return; } _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + targetType.Name + "." + methodName)); } catch (Exception ex) { Log.LogError((object)("Failed to patch " + targetType.Name + "." + methodName + ": " + ex.Message)); } } private void PatchMethodPrefix(Type targetType, string methodName, Type patchType, string patchMethodName, Type[] parameters = null) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown try { MethodInfo methodInfo = ((parameters == null) ? AccessTools.Method(targetType, methodName, (Type[])null, (Type[])null) : AccessTools.Method(targetType, methodName, parameters, (Type[])null)); if (methodInfo == null) { Log.LogWarning((object)("Could not find method " + targetType.Name + "." + methodName)); return; } MethodInfo methodInfo2 = AccessTools.Method(patchType, patchMethodName, (Type[])null, (Type[])null); if (methodInfo2 == null) { Log.LogWarning((object)("Could not find patch method " + patchType.Name + "." + patchMethodName)); return; } _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + targetType.Name + "." + methodName + " (prefix)")); } catch (Exception ex) { Log.LogError((object)("Failed to patch " + targetType.Name + "." + methodName + ": " + ex.Message)); } } private void PatchGameSave() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("Wish.GameSave"); if (type == null) { Log.LogWarning((object)"Could not find Wish.GameSave type"); return; } MethodInfo methodInfo = AccessTools.Method(type, "Load", (Type[])null, (Type[])null); if (methodInfo != null) { MethodInfo methodInfo2 = AccessTools.Method(typeof(GameSavePatches), "OnGameSaveLoad", (Type[])null, (Type[])null); if (methodInfo2 != null) { _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched GameSave.Load"); } } MethodInfo methodInfo3 = AccessTools.Method(type, "LoadCharacter", (Type[])null, (Type[])null); if (methodInfo3 != null) { MethodInfo methodInfo4 = AccessTools.Method(typeof(GameSavePatches), "OnLoadCharacter", (Type[])null, (Type[])null); if (methodInfo4 != null) { _harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(methodInfo4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched GameSave.LoadCharacter"); } } MethodInfo methodInfo5 = AccessTools.Method(type, "SetCurrentCharacter", (Type[])null, (Type[])null); if (methodInfo5 != null) { MethodInfo methodInfo6 = AccessTools.Method(typeof(GameSavePatches), "OnSetCurrentCharacter", (Type[])null, (Type[])null); if (methodInfo6 != null) { _harmony.Patch((MethodBase)methodInfo5, (HarmonyMethod)null, new HarmonyMethod(methodInfo6), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched GameSave.SetCurrentCharacter"); } } } catch (Exception ex) { Log.LogError((object)("Error patching GameSave: " + ex.Message)); } } private void PatchItemPickup(Type playerType) { //IL_0398: Unknown result type (might be due to invalid IL or missing references) //IL_03ac: Unknown result type (might be due to invalid IL or missing references) //IL_03b9: Expected O, but got Unknown //IL_092f: Unknown result type (might be due to invalid IL or missing references) //IL_0c1a: Unknown result type (might be due to invalid IL or missing references) //IL_0c27: Expected O, but got Unknown //IL_098d: Unknown result type (might be due to invalid IL or missing references) //IL_099a: Expected O, but got Unknown //IL_0943: Unknown result type (might be due to invalid IL or missing references) //IL_0c9f: Unknown result type (might be due to invalid IL or missing references) //IL_0cac: Expected O, but got Unknown //IL_0950: Expected O, but got Unknown //IL_0d42: Unknown result type (might be due to invalid IL or missing references) //IL_0d4f: Expected O, but got Unknown //IL_0acb: Unknown result type (might be due to invalid IL or missing references) //IL_0e16: Unknown result type (might be due to invalid IL or missing references) //IL_0e1d: Unknown result type (might be due to invalid IL or missing references) //IL_0e2a: Expected O, but got Unknown //IL_0e2a: Expected O, but got Unknown //IL_0b47: Unknown result type (might be due to invalid IL or missing references) //IL_0b54: Expected O, but got Unknown //IL_0adf: Unknown result type (might be due to invalid IL or missing references) //IL_0aec: Expected O, but got Unknown Log.LogInfo((object)"Searching for item pickup methods on Player..."); MethodInfo[] methods = playerType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { string text = methodInfo.Name.ToLowerInvariant(); if (text.Contains("pickup") || text.Contains("additem") || text.Contains("collect") || text.Contains("gain")) { ParameterInfo[] parameters = methodInfo.GetParameters(); string text2 = string.Join(", ", parameters.Select((ParameterInfo p) => p.ParameterType.Name + " " + p.Name)); Log.LogInfo((object)(" Found: " + methodInfo.Name + "(" + text2 + ") in " + methodInfo.DeclaringType.Name)); } } string[] array = new string[12] { "Wish.ItemPickup", "Wish.DroppedItem", "Wish.Collectible", "Wish.GroundItem", "Wish.ItemEntity", "Wish.PickupItem", "Wish.WorldItem", "Wish.ItemDrop", "ItemPickup", "DroppedItem", "Collectible", "GroundItem" }; for (int i = 0; i < array.Length; i++) { Type type = AccessTools.TypeByName(array[i]); if (!(type != null)) { continue; } Log.LogInfo((object)("Found potential pickup class: " + type.FullName)); methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { string text3 = methodInfo2.Name.ToLowerInvariant(); if (text3.Contains("pickup") || text3.Contains("collect") || text3.Contains("interact") || text3.Contains("onpick") || text3.Contains("trigger")) { ParameterInfo[] parameters2 = methodInfo2.GetParameters(); string text4 = string.Join(", ", parameters2.Select((ParameterInfo p) => p.ParameterType.Name + " " + p.Name)); Log.LogInfo((object)(" " + type.Name + "." + methodInfo2.Name + "(" + text4 + ")")); } } } MethodInfo methodInfo3 = AccessTools.Method(playerType, "Pickup", (Type[])null, (Type[])null); if (methodInfo3 != null) { Log.LogInfo((object)("Found Pickup method: " + methodInfo3.DeclaringType.FullName + "." + methodInfo3.Name)); ParameterInfo[] parameters3 = methodInfo3.GetParameters(); string text5 = string.Join(", ", parameters3.Select((ParameterInfo p) => p.ParameterType.Name + " " + p.Name)); Log.LogInfo((object)(" Parameters: (" + text5 + ")")); MethodInfo methodInfo4 = AccessTools.Method(typeof(ItemPatches), "OnPlayerPickupPrefix", (Type[])null, (Type[])null); MethodInfo methodInfo5 = AccessTools.Method(typeof(ItemPatches), "OnPlayerPickup", (Type[])null, (Type[])null); if (methodInfo4 != null) { object obj = _harmony; object obj2 = methodInfo3; ? val = new HarmonyMethod(methodInfo4); if (methodInfo5 != null) { obj = (object)new HarmonyMethod(methodInfo5); obj2 = (object)val; val = obj2; } else { obj = null; obj2 = (object)val; val = obj2; } ((Harmony)obj).Patch((MethodBase)val, (HarmonyMethod)obj2, (HarmonyMethod)obj, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + playerType.Name + ".Pickup with PREFIX for auto-deposit")); } } else { Log.LogWarning((object)"Could not find Pickup method on Player"); } Type type2 = AccessTools.TypeByName("Wish.Inventory"); if (type2 == null) { type2 = AccessTools.TypeByName("Wish.PlayerInventory"); } if (type2 != null) { Log.LogInfo((object)("Searching Inventory class: " + type2.FullName)); MethodInfo[] methods2 = type2.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); Log.LogInfo((object)"=== Inventory methods for checking item amounts ==="); methods = methods2; foreach (MethodInfo methodInfo6 in methods) { string text6 = methodInfo6.Name.ToLowerInvariant(); if (text6.Contains("get") || text6.Contains("has") || text6.Contains("count") || text6.Contains("amount") || text6.Contains("total") || text6.Contains("contain")) { ParameterInfo[] parameters4 = methodInfo6.GetParameters(); string text7 = string.Join(", ", parameters4.Select((ParameterInfo p) => p.ParameterType.Name + " " + p.Name)); Log.LogInfo((object)(" " + methodInfo6.Name + "(" + text7 + ") -> " + methodInfo6.ReturnType.Name)); } } Log.LogInfo((object)"=== Inventory AddItem methods ==="); methods = methods2; foreach (MethodInfo methodInfo7 in methods) { string text8 = methodInfo7.Name.ToLowerInvariant(); if (text8.Contains("add") && text8.Contains("item")) { ParameterInfo[] parameters5 = methodInfo7.GetParameters(); string text9 = string.Join(", ", parameters5.Select((ParameterInfo p) => p.ParameterType.Name + " " + p.Name)); Log.LogInfo((object)(" " + methodInfo7.Name + "(" + text9 + ") in " + methodInfo7.DeclaringType.Name)); } } Log.LogInfo((object)"=== Inventory RemoveItem methods ==="); methods = methods2; foreach (MethodInfo methodInfo8 in methods) { if (methodInfo8.Name.ToLowerInvariant().Contains("remove")) { ParameterInfo[] parameters6 = methodInfo8.GetParameters(); string text10 = string.Join(", ", parameters6.Select((ParameterInfo p) => p.ParameterType.Name + " " + p.Name)); Log.LogInfo((object)(" " + methodInfo8.Name + "(" + text10 + ") -> " + methodInfo8.ReturnType.Name)); } } Log.LogInfo((object)"=== Searching for Shop/Store/Purchase types ==="); Assembly assembly = type2.Assembly; Type[] types = assembly.GetTypes(); foreach (Type type3 in types) { string text11 = type3.Name.ToLowerInvariant(); if (text11.Contains("shop") || text11.Contains("store") || text11.Contains("purchase") || text11.Contains("buy") || text11.Contains("vendor") || text11.Contains("merchant")) { Log.LogInfo((object)(" Found type: " + type3.FullName)); } } Log.LogInfo((object)"=== Searching for Door/Chest/Lock types ==="); types = assembly.GetTypes(); foreach (Type type4 in types) { string text12 = type4.Name.ToLowerInvariant(); if (text12.Contains("door") || text12.Contains("chest") || text12.Contains("lock") || text12.Contains("treasure") || text12.Contains("gate")) { Log.LogInfo((object)(" Found type: " + type4.FullName)); } } Type type5 = AccessTools.TypeByName("Wish.Item"); if (type5 != null) { Log.LogInfo((object)("Found Wish.Item type: " + type5.FullName)); MethodInfo methodInfo9 = AccessTools.Method(type2, "AddItem", new Type[6] { type5, typeof(int), typeof(int), typeof(bool), typeof(bool), typeof(bool) }, (Type[])null); if (methodInfo9 != null) { MethodInfo methodInfo10 = AccessTools.Method(typeof(ItemPatches), "OnInventoryAddItemObjectPrefix", (Type[])null, (Type[])null); MethodInfo methodInfo11 = AccessTools.Method(typeof(ItemPatches), "OnInventoryAddItemObjectPostfix", (Type[])null, (Type[])null); if (methodInfo10 != null) { object obj3 = _harmony; object obj4 = methodInfo9; ? val2 = new HarmonyMethod(methodInfo10); if (methodInfo11 != null) { obj3 = (object)new HarmonyMethod(methodInfo11); obj4 = (object)val2; val2 = obj4; } else { obj3 = null; obj4 = (object)val2; val2 = obj4; } ((Harmony)obj3).Patch((MethodBase)val2, (HarmonyMethod)obj4, (HarmonyMethod)obj3, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + type2.Name + ".AddItem(Item,int,int,bool,bool,bool) with PREFIX+POSTFIX for auto-deposit")); } else if (methodInfo11 != null) { _harmony.Patch((MethodBase)methodInfo9, (HarmonyMethod)null, new HarmonyMethod(methodInfo11), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + type2.Name + ".AddItem(Item,int,int,bool,bool,bool) with POSTFIX only for auto-deposit")); } } else { Log.LogWarning((object)"Could not find AddItem(Item,int,int,bool,bool,bool) method"); methods = methods2; foreach (MethodInfo methodInfo12 in methods) { if (!(methodInfo12.Name == "AddItem")) { continue; } ParameterInfo[] parameters7 = methodInfo12.GetParameters(); if (parameters7.Length == 0 || !(parameters7[0].ParameterType == type5)) { continue; } string text13 = string.Join(", ", parameters7.Select((ParameterInfo p) => p.ParameterType.Name + " " + p.Name)); Log.LogInfo((object)("Found alternative AddItem: " + methodInfo12.Name + "(" + text13 + ")")); MethodInfo methodInfo13 = AccessTools.Method(typeof(ItemPatches), "OnInventoryAddItemObjectPrefix", (Type[])null, (Type[])null); MethodInfo methodInfo14 = AccessTools.Method(typeof(ItemPatches), "OnInventoryAddItemObjectPostfix", (Type[])null, (Type[])null); if (methodInfo13 != null) { object obj5 = _harmony; object obj6 = methodInfo12; ? val3 = new HarmonyMethod(methodInfo13); if (methodInfo14 != null) { obj5 = (object)new HarmonyMethod(methodInfo14); obj6 = (object)val3; val3 = obj6; } else { obj5 = null; obj6 = (object)val3; val3 = obj6; } ((Harmony)obj5).Patch((MethodBase)val3, (HarmonyMethod)obj6, (HarmonyMethod)obj5, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + type2.Name + "." + methodInfo12.Name + " with PREFIX+POSTFIX for auto-deposit")); break; } if (methodInfo14 != null) { _harmony.Patch((MethodBase)methodInfo12, (HarmonyMethod)null, new HarmonyMethod(methodInfo14), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + type2.Name + "." + methodInfo12.Name + " with POSTFIX for auto-deposit")); break; } } } } else { Log.LogWarning((object)"Could not find Wish.Item type"); } MethodInfo methodInfo15 = AccessTools.Method(type2, "AddItem", new Type[2] { typeof(int), typeof(int) }, (Type[])null); if (methodInfo15 != null) { MethodInfo methodInfo16 = AccessTools.Method(typeof(ItemPatches), "OnInventoryAddItem", (Type[])null, (Type[])null); if (methodInfo16 != null) { _harmony.Patch((MethodBase)methodInfo15, (HarmonyMethod)null, new HarmonyMethod(methodInfo16), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + type2.Name + ".AddItem(int,int) for auto-deposit")); } } MethodInfo methodInfo17 = AccessTools.Method(type2, "GetAmount", new Type[1] { typeof(int) }, (Type[])null); if (methodInfo17 != null) { MethodInfo methodInfo18 = AccessTools.Method(typeof(ItemPatches), "OnInventoryGetAmount", (Type[])null, (Type[])null); if (methodInfo18 != null) { _harmony.Patch((MethodBase)methodInfo17, (HarmonyMethod)null, new HarmonyMethod(methodInfo18), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + type2.Name + ".GetAmount for vault integration")); } } else { Log.LogWarning((object)"Could not find Inventory.GetAmount method"); } MethodInfo methodInfo19 = AccessTools.Method(type2, "HasEnough", new Type[2] { typeof(int), typeof(int) }, (Type[])null); if (methodInfo19 != null) { MethodInfo methodInfo20 = AccessTools.Method(typeof(ItemPatches), "OnInventoryHasEnough", (Type[])null, (Type[])null); if (methodInfo20 != null) { _harmony.Patch((MethodBase)methodInfo19, (HarmonyMethod)null, new HarmonyMethod(methodInfo20), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + type2.Name + ".HasEnough for vault integration")); } } else { Log.LogWarning((object)"Could not find Inventory.HasEnough method"); } MethodInfo methodInfo21 = AccessTools.Method(type2, "RemoveItem", new Type[3] { typeof(int), typeof(int), typeof(int) }, (Type[])null); if (methodInfo21 != null) { MethodInfo methodInfo22 = AccessTools.Method(typeof(ItemPatches), "OnInventoryRemoveItemPrefix", (Type[])null, (Type[])null); MethodInfo methodInfo23 = AccessTools.Method(typeof(ItemPatches), "OnInventoryRemoveItemPostfix", (Type[])null, (Type[])null); if (methodInfo22 != null && methodInfo23 != null) { _harmony.Patch((MethodBase)methodInfo21, new HarmonyMethod(methodInfo22), new HarmonyMethod(methodInfo23), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + type2.Name + ".RemoveItem for vault integration")); } } else { Log.LogWarning((object)"Could not find Inventory.RemoveItem method"); } } else { Log.LogWarning((object)"Could not find Inventory type"); } } private void Update() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (_enableAutoSave.Value) { _saveSystem?.CheckAutoSave(); } if (Input.GetKeyDown(_hudToggleKey.Value)) { _vaultHUD?.Toggle(); } _sceneCheckTimer += Time.deltaTime; if (_sceneCheckTimer >= 0.5f) { _sceneCheckTimer = 0f; CheckForMenuSceneChange(); } _heartbeatTimer += Time.deltaTime; if (_heartbeatTimer >= 30f) { _heartbeatTimer = 0f; _heartbeatCount++; Log.LogInfo((object)string.Format("[Heartbeat #{0}] Plugin alive. Scene: {1}, VaultLoaded: {2}, Character: {3}", _heartbeatCount, _lastKnownScene, PlayerPatches.IsVaultLoaded, PlayerPatches.LoadedCharacterName ?? "none")); } } private void CheckForMenuSceneChange() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) try { Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (name != _lastKnownScene) { Log.LogInfo((object)("[ScenePoll] Scene changed: '" + _lastKnownScene + "' -> '" + name + "'")); _lastKnownScene = name; string text = name.ToLowerInvariant(); bool flag = text.Contains("menu") || text.Contains("title"); if (flag && !_wasInMenuScene) { Log.LogInfo((object)("[ScenePoll] Menu scene detected via polling: " + name)); PlayerPatches.SaveAndReset(); } _wasInMenuScene = flag; } } catch (Exception ex) { Log.LogError((object)("Error in CheckForMenuSceneChange: " + ex.Message)); } } private static VaultHUD.HUDPosition ParseHUDPosition(string position) { return position?.ToLower() switch { "topleft" => VaultHUD.HUDPosition.TopLeft, "topcenter" => VaultHUD.HUDPosition.TopCenter, "topright" => VaultHUD.HUDPosition.TopRight, "bottomleft" => VaultHUD.HUDPosition.BottomLeft, "bottomcenter" => VaultHUD.HUDPosition.BottomCenter, "bottomright" => VaultHUD.HUDPosition.BottomRight, _ => VaultHUD.HUDPosition.TopLeft, }; } private void OnApplicationQuit() { Log.LogInfo((object)"Application quitting - saving vault data"); _saveSystem?.ForceSave(); } private void OnDisable() { Log.LogWarning((object)"[CRITICAL] Plugin OnDisable called! Plugin is being disabled."); Log.LogWarning((object)("[CRITICAL] Last known scene: " + _lastKnownScene)); Log.LogWarning((object)("[CRITICAL] Stack trace: " + Environment.StackTrace)); } private void OnDestroy() { Log.LogWarning((object)"[CRITICAL] Plugin OnDestroy called! Plugin is being destroyed."); Log.LogWarning((object)("[CRITICAL] Last known scene: " + _lastKnownScene)); Log.LogWarning((object)("[CRITICAL] Stack trace: " + Environment.StackTrace)); SceneManager.sceneLoaded -= OnSceneLoaded; _saveSystem?.ForceSave(); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) try { Log.LogInfo((object)$"[SceneChange] Scene loaded: '{((Scene)(ref scene)).name}' (mode: {mode})"); string text = ((Scene)(ref scene)).name.ToLowerInvariant(); if (text.Contains("menu") || text.Contains("title")) { Log.LogInfo((object)("Menu scene detected: " + ((Scene)(ref scene)).name)); PlayerPatches.SaveAndReset(); } } catch (Exception ex) { Log.LogError((object)("Error in OnSceneLoaded: " + ex.Message)); } } public static VaultManager GetVaultManager() { return _staticVaultManager; } public static VaultSaveSystem GetSaveSystem() { return _staticSaveSystem; } public static VaultUI GetVaultUI() { return _staticVaultUI; } public static void OpenVault() { _staticVaultUI?.Show(); } public static void CloseVault() { _staticVaultUI?.Hide(); } public static void LoadVaultForPlayer(string playerName) { _staticSaveSystem?.Load(playerName); } public static void SaveVault() { _staticSaveSystem?.ForceSave(); } public static VaultHUD GetVaultHUD() { return _staticVaultHUD; } public static void ToggleHUD() { _staticVaultHUD?.Toggle(); } } public static class PluginInfo { public const string PLUGIN_GUID = "com.azraelgodking.thevault"; public const string PLUGIN_NAME = "The Vault"; public const string PLUGIN_VERSION = "2.0.3"; } public class PersistentUpdateRunner : MonoBehaviour { private string _lastKnownScene = ""; private bool _wasInMenuScene = true; private float _sceneCheckTimer; private float _heartbeatTimer; private int _heartbeatCount; private const float SCENE_CHECK_INTERVAL = 0.5f; private const float HEARTBEAT_INTERVAL = 30f; private void Awake() { ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[PersistentRunner] Created hidden persistent runner"); } } private void Update() { _sceneCheckTimer += Time.deltaTime; if (_sceneCheckTimer >= 0.5f) { _sceneCheckTimer = 0f; CheckForMenuSceneChange(); } _heartbeatTimer += Time.deltaTime; if (_heartbeatTimer >= 30f) { _heartbeatTimer = 0f; _heartbeatCount++; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)string.Format("[PersistentRunner Heartbeat #{0}] Scene: {1}, VaultLoaded: {2}, Character: {3}", _heartbeatCount, _lastKnownScene, PlayerPatches.IsVaultLoaded, PlayerPatches.LoadedCharacterName ?? "none")); } } CheckHotkeys(); } private void CheckHotkeys() { //IL_0048: 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_0061: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) try { VaultUI vaultUI = Plugin.GetVaultUI(); if (!((Object)(object)vaultUI == (Object)null)) { if ((!Plugin.StaticRequireCtrl || Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) && Input.GetKeyDown(Plugin.StaticToggleKey)) { vaultUI.Toggle(); } if ((int)Plugin.StaticAltToggleKey != 0 && Input.GetKeyDown(Plugin.StaticAltToggleKey)) { vaultUI.Toggle(); } if (Input.GetKeyDown(Plugin.StaticHUDToggleKey)) { Plugin.GetVaultHUD()?.Toggle(); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("[PersistentRunner] Hotkey error: " + ex.Message)); } } } private void CheckForMenuSceneChange() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) try { Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (!(name != _lastKnownScene)) { return; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[PersistentRunner] Scene changed: '" + _lastKnownScene + "' -> '" + name + "'")); } _lastKnownScene = name; string text = name.ToLowerInvariant(); bool flag = text.Contains("menu") || text.Contains("title"); if (flag && !_wasInMenuScene) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[PersistentRunner] Menu scene detected: " + name)); } PlayerPatches.SaveAndReset(); } _wasInMenuScene = flag; } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("[PersistentRunner] Error: " + ex.Message)); } } } private void OnDestroy() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[PersistentRunner] OnDestroy called - this should NOT happen!"); } } } public static class SecretGifts { private static readonly HashSet<string> _eligibleSteamIdHashes = new HashSet<string> { "Tr4eyf86yKRu6fhNQjQOrO/ZnwasQaY/fgKV0PcnoGA=", "zAKJKUjJVGiQRm+4gCGcMZqq1sF8vQZT7bhd+r0Q0kw=" }; private const int TREASURE_CHEST_ITEM_ID = 3789; private const int GIFT_QUANTITY = 10; private const string ENCRYPTION_SALT = "SecretG1fts_S@lt2026_Secure"; private const int KEY_SIZE = 256; private const int ITERATIONS = 10000; private static readonly byte[] _iv = new byte[16] { 83, 101, 99, 114, 101, 116, 71, 105, 102, 116, 115, 73, 86, 48, 48, 49 }; private const string FILE_HEADER = "SCRTGIFT"; private static string _currentSteamId; private static bool _giftCheckPerformed; private static bool _dataLoaded; private static HashSet<string> _giftRecipients = new HashSet<string>(); public static void CheckAndGiveFirstOpenGift(string characterName) { if (_giftCheckPerformed) { return; } _giftCheckPerformed = true; try { _currentSteamId = TryGetSteamId(); if (string.IsNullOrEmpty(_currentSteamId)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[SecretGifts] Could not retrieve Steam ID"); } return; } if (!_dataLoaded) { LoadGiftRecipients(); _dataLoaded = true; } string text = ComputeHash(_currentSteamId); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[SecretGifts] Steam hash: " + text)); } if (!_eligibleSteamIdHashes.Contains(text)) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"[SecretGifts] User not eligible for secret gift"); } return; } if (HasReceivedGift(characterName)) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("[SecretGifts] " + characterName + " has already received the secret gift")); } return; } ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)("[SecretGifts] Giving secret gift to " + characterName + "!")); } if (SpawnItems(3789, 10)) { MarkGiftReceived(characterName); ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)$"[SecretGifts] Successfully gave {10} treasure chests to {characterName}!"); } } } catch (Exception ex) { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogError((object)("[SecretGifts] Error: " + ex.Message)); } } } public static void ResetGiftCheck() { _giftCheckPerformed = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[SecretGifts] Reset for character switch. {_giftRecipients.Count} gift records in memory."); } } private static bool SpawnItems(int itemId, int amount) { try { Player instance = Player.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.Inventory == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[SecretGifts] Player or inventory not available"); } return false; } Inventory inventory = instance.Inventory; MethodInfo methodInfo = AccessTools.Method(((object)inventory).GetType(), "AddItem", new Type[3] { typeof(int), typeof(int), typeof(bool) }, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(inventory, new object[3] { itemId, amount, true }); return true; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[SecretGifts] Could not find AddItem method"); } } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("[SecretGifts] Spawn error: " + ex.Message)); } } return false; } private static string GetSaveDirectory() { return Path.Combine(Paths.ConfigPath, "TheVault", "Saves"); } private static string GetGiftTrackingFilePath() { return Path.Combine(GetSaveDirectory(), "SecretGifts.dat"); } private static string GetBackupFilePath() { return Path.Combine(GetSaveDirectory(), "SecretGifts.dat.backup"); } private static void LoadGiftRecipients() { try { string giftTrackingFilePath = GetGiftTrackingFilePath(); string backupFilePath = GetBackupFilePath(); if (File.Exists(giftTrackingFilePath) && TryLoadFromFile(giftTrackingFilePath)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[SecretGifts] Loaded {_giftRecipients.Count} gift records"); } return; } if (File.Exists(backupFilePath)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[SecretGifts] Main file failed, trying backup..."); } if (TryLoadFromFile(backupFilePath)) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[SecretGifts] Restored {_giftRecipients.Count} gift records from backup"); } SaveGiftRecipients(); return; } } _giftRecipients = new HashSet<string>(); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)"[SecretGifts] No existing gift records found, starting fresh"); } } catch (Exception ex) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogWarning((object)("[SecretGifts] Error loading gift records: " + ex.Message)); } _giftRecipients = new HashSet<string>(); } } private static bool TryLoadFromFile(string filePath) { try { string text = Decrypt(File.ReadAllBytes(filePath)); if (string.IsNullOrEmpty(text)) { return false; } _giftRecipients = new HashSet<string>(); string[] array = text.Split(new char[2] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { if (!string.IsNullOrWhiteSpace(text2)) { _giftRecipients.Add(text2.Trim()); } } return true; } catch { return false; } } private static void SaveGiftRecipients() { try { string giftTrackingFilePath = GetGiftTrackingFilePath(); string backupFilePath = GetBackupFilePath(); string saveDirectory = GetSaveDirectory(); if (!Directory.Exists(saveDirectory)) { Directory.CreateDirectory(saveDirectory); } byte[] bytes = Encrypt(string.Join("\n", _giftRecipients)); string text = giftTrackingFilePath + ".tmp"; File.WriteAllBytes(text, bytes); if (File.Exists(giftTrackingFilePath)) { if (File.Exists(backupFilePath)) { File.Delete(backupFilePath); } File.Move(giftTrackingFilePath, backupFilePath); } File.Move(text, giftTrackingFilePath); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[SecretGifts] Saved {_giftRecipients.Count} gift records (encrypted)"); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[SecretGifts] Error saving gift records: " + ex.Message)); } } } private static bool HasReceivedGift(string characterName) { string item = _currentSteamId + ":" + characterName; return _giftRecipients.Contains(item); } private static void MarkGiftReceived(string characterName) { string item = _currentSteamId + ":" + characterName; _giftRecipients.Add(item); SaveGiftRecipients(); } private static byte[] GenerateKey() { string text = ((!string.IsNullOrEmpty(_currentSteamId)) ? ("Steam_" + _currentSteamId) : "DefaultKey"); using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes("SecretG1fts_S@lt2026_Secure_" + text + "_SecretGiftsKey", Encoding.UTF8.GetBytes("SecretG1fts_S@lt2026_Secure"), 10000); return rfc2898DeriveBytes.GetBytes(32); } private static byte[] Encrypt(string plainText) { byte[] key = GenerateKey(); using Aes aes = Aes.Create(); aes.Key = key; aes.IV = _iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using ICryptoTransform transform = aes.CreateEncryptor(); using MemoryStream memoryStream = new MemoryStream(); byte[] bytes = Encoding.UTF8.GetBytes("SCRTGIFT"); memoryStream.Write(bytes, 0, bytes.Length); using (CryptoStream stream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write)) { using StreamWriter streamWriter = new StreamWriter(stream, Encoding.UTF8); streamWriter.Write(plainText); } return memoryStream.ToArray(); } private static string Decrypt(byte[] cipherData) { try { if (cipherData.Length < "SCRTGIFT".Length) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[SecretGifts] File too small, may be corrupted"); } return null; } if (Encoding.UTF8.GetString(cipherData, 0, "SCRTGIFT".Length) != "SCRTGIFT") { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[SecretGifts] Detected legacy unencrypted file, will re-encrypt on save"); } return Encoding.UTF8.GetString(cipherData); } byte[] key = GenerateKey(); using Aes aes = Aes.Create(); aes.Key = key; aes.IV = _iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using ICryptoTransform transform = aes.CreateDecryptor(); using MemoryStream stream = new MemoryStream(cipherData, "SCRTGIFT".Length, cipherData.Length - "SCRTGIFT".Length); using CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Read); using StreamReader streamReader = new StreamReader(stream2, Encoding.UTF8); return streamReader.ReadToEnd(); } catch (CryptographicException ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("[SecretGifts] Decryption failed: " + ex.Message)); } return null; } catch (Exception ex2) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogError((object)("[SecretGifts] Error decrypting: " + ex2.Message)); } return null; } } private static string TryGetSteamId() { try { Assembly assembly = null; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { string name = assembly2.GetName().Name; if (name.Contains("rlabrecque") || name == "Steamworks.NET") { assembly = assembly2; break; } } if (assembly == null) { assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly3 in assemblies) { try { if (assembly3.GetType("Steamworks.SteamUser") != null) { assembly = assembly3; break; } } catch { } } } if (assembly == null) { return null; } Type type = assembly.GetType("Steamworks.SteamUser"); if (type == null) { return null; } MethodInfo method = type.GetMethod("GetSteamID", BindingFlags.Static | BindingFlags.Public); if (method == null) { return null; } object obj2 = method.Invoke(null, null); if (obj2 == null) { return null; } string text = obj2.ToString(); if (string.IsNullOrEmpty(text) || text == "0" || text.Length < 10) { return null; } return text; } catch { return null; } } private static string ComputeHash(string input) { using SHA256 sHA = SHA256.Create(); return Convert.ToBase64String(sHA.ComputeHash(Encoding.UTF8.GetBytes(input))); } } } namespace TheVault.Vault { [Serializable] public class VaultData { public int Version { get; set; } = 1; public Dictionary<SeasonalTokenType, int> SeasonalTokens { get; set; } public Dictionary<string, int> CommunityTokens { get; set; } public Dictionary<string, int> Keys { get; set; } public Dictionary<string, int> CustomCurrencies { get; set; } public Dictionary<string, int> Tickets { get; set; } public Dictionary<string, int> Orbs { get; set; } public DateTime LastSaved { get; set; } public string PlayerName { get; set; } public VaultData() { SeasonalTokens = new Dictionary<SeasonalTokenType, int>(); CommunityTokens = new Dictionary<string, int>(); Keys = new Dictionary<string, int>(); CustomCurrencies = new Dictionary<string, int>(); Tickets = new Dictionary<string, int>(); Orbs = new Dictionary<string, int>(); LastSaved = DateTime.Now; PlayerName = ""; foreach (SeasonalTokenType value in Enum.GetValues(typeof(SeasonalTokenType))) { SeasonalTokens[value] = 0; } } public VaultData Clone() { return new VaultData { Version = Version, LastSaved = LastSaved, PlayerName = PlayerName, SeasonalTokens = new Dictionary<SeasonalTokenType, int>(SeasonalTokens), CommunityTokens = new Dictionary<string, int>(CommunityTokens), Keys = new Dictionary<string, int>(Keys), CustomCurrencies = new Dictionary<string, int>(CustomCurrencies), Tickets = new Dictionary<string, int>(Tickets), Orbs = new Dictionary<string, int>(Orbs) }; } } public enum SeasonalTokenType { Spring, Summer, Fall, Winter, Anniversary, Special } public class CurrencyDefinition { public string Id { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public CurrencyCategory Category { get; set; } public string IconPath { get; set; } public int GameItemId { get; set; } public CurrencyDefinition(string id, string displayName, CurrencyCategory category, int gameItemId = -1) { Id = id; DisplayName = displayName; Category = category; GameItemId = gameItemId; Description = ""; IconPath = ""; } } public enum CurrencyCategory { SeasonalToken, CommunityToken, Key, Special, Orb, Custom } public class VaultManager { private VaultData _vaultData; private Dictionary<string, CurrencyDefinition> _currencyDefinitions; private bool _isDirty; public bool IsDirty => _isDirty; public event Action<string, int, int> OnCurrencyChanged; public event Action OnVaultLoaded; public VaultManager() { _vaultData = new VaultData(); _currencyDefinitions = new Dictionary<string, CurrencyDefinition>(); _isDirty = false; InitializeCurrencyDefinitions(); } private void InitializeCurrencyDefinitions() { RegisterCurrency(new CurrencyDefinition("token_spring", "Spring Token", CurrencyCategory.SeasonalToken, 18020)); RegisterCurrency(new CurrencyDefinition("token_summer", "Summer Token", CurrencyCategory.SeasonalToken, 18021)); RegisterCurrency(new CurrencyDefinition("token_fall", "Fall Token", CurrencyCategory.SeasonalToken, 18023)); RegisterCurrency(new CurrencyDefinition("token_winter", "Winter Token", CurrencyCategory.SeasonalToken, 18022)); RegisterCurrency(new CurrencyDefinition("key_copper", "Copper Key", CurrencyCategory.Key, 1251)); RegisterCurrency(new CurrencyDefinition("key_iron", "Iron Key", CurrencyCategory.Key, 1252)); RegisterCurrency(new CurrencyDefinition("key_adamant", "Adamant Key", CurrencyCategory.Key, 1253)); RegisterCurrency(new CurrencyDefinition("key_mithril", "Mithril Key", CurrencyCategory.Key, 1254)); RegisterCurrency(new CurrencyDefinition("key_sunite", "Sunite Key", CurrencyCategory.Key, 1255)); RegisterCurrency(new CurrencyDefinition("key_glorite", "Glorite Key", CurrencyCategory.Key, 1256)); RegisterCurrency(new CurrencyDefinition("key_kingslostmine", "King's Lost Mine Key", CurrencyCategory.Key, 1257)); RegisterCurrency(new CurrencyDefinition("special_communitytoken", "Community Token", CurrencyCategory.Special, 18013)); RegisterCurrency(new CurrencyDefinition("special_doubloon", "Doubloon", CurrencyCategory.Special, 60014)); RegisterCurrency(new CurrencyDefinition("special_blackbottlecap", "Black Bottle Cap", CurrencyCategory.Special, 60013)); RegisterCurrency(new CurrencyDefinition("special_redcarnivalticket", "Red Carnival Ticket", CurrencyCategory.Special, 18012)); RegisterCurrency(new CurrencyDefinition("special_candycornpieces", "Candy Corn Pieces", CurrencyCategory.Special, 18016)); RegisterCurrency(new CurrencyDefinition("special_manashard", "Mana Shard", CurrencyCategory.Special, 18015)); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Initialized {_currencyDefinitions.Count} currency definitions"); } } public void RegisterCurrency(CurrencyDefinition definition) { _currencyDefinitions[definition.Id] = definition; } public CurrencyDefinition GetCurrencyDefinition(string currencyId) { if (!_currencyDefinitions.TryGetValue(currencyId, out var value)) { return null; } return value; } public CurrencyDefinition GetCurrencyByGameItemId(int gameItemId) { foreach (CurrencyDefinition value in _currencyDefinitions.Values) { if (value.GameItemId == gameItemId) { return value; } } return null; } public IEnumerable<CurrencyDefinition> GetAllCurrencies() { return _currencyDefinitions.Values; } public IEnumerable<CurrencyDefinition> GetCurrenciesByCategory(CurrencyCategory category) { foreach (KeyValuePair<string, CurrencyDefinition> currencyDefinition in _currencyDefinitions) { if (currencyDefinition.Value.Category == category) { yield return currencyDefinition.Value; } } } public void LoadVaultData(VaultData data) { _vaultData = data ?? new VaultData(); _isDirty = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Vault data loaded for player: " + _vaultData.PlayerName)); } this.OnVaultLoaded?.Invoke(); } public VaultData GetVaultData() { _vaultData.LastSaved = DateTime.Now; return _vaultData; } public void MarkClean() { _isDirty = false; } public void SetPlayerName(string playerName) { _vaultData.PlayerName = playerName; _isDirty = true; } public int GetSeasonalTokens(SeasonalTokenType type) { if (!_vaultData.SeasonalTokens.TryGetValue(type, out var value)) { return 0; } return value; } public bool AddSeasonalTokens(SeasonalTokenType type, int amount) { if (amount < 0) { return false; } int seasonalTokens = GetSeasonalTokens(type); _vaultData.SeasonalTokens[type] = seasonalTokens + amount; _isDirty = true; this.OnCurrencyChanged?.Invoke($"seasonal_{type}", seasonalTokens, seasonalTokens + amount); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Added {amount} {type} tokens. New total: {_vaultData.SeasonalTokens[type]}"); } return true; } public bool RemoveSeasonalTokens(SeasonalTokenType type, int amount) { if (amount < 0) { return false; } int seasonalTokens = GetSeasonalTokens(type); if (seasonalTokens < amount) { return false; } int arg = seasonalTokens; _vaultData.SeasonalTokens[type] = seasonalTokens - amount; _isDirty = true; this.OnCurrencyChanged?.Invoke($"seasonal_{type}", arg, seasonalTokens - amount); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Removed {amount} {type} tokens. New total: {_vaultData.SeasonalTokens[type]}"); } return true; } public bool HasSeasonalTokens(SeasonalTokenType type, int amount) { return GetSeasonalTokens(type) >= amount; } public int GetCommunityTokens(string tokenId) { if (!_vaultData.CommunityTokens.TryGetValue(tokenId, out var value)) { return 0; } return value; } public bool AddCommunityTokens(string tokenId, int amount) { if (amount < 0 || string.IsNullOrEmpty(tokenId)) { return false; } int communityTokens = GetCommunityTokens(tokenId); _vaultData.CommunityTokens[tokenId] = communityTokens + amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("community_" + tokenId, communityTokens, communityTokens + amount); return true; } public bool RemoveCommunityTokens(string tokenId, int amount) { if (amount < 0 || string.IsNullOrEmpty(tokenId)) { return false; } int communityTokens = GetCommunityTokens(tokenId); if (communityTokens < amount) { return false; } int arg = communityTokens; _vaultData.CommunityTokens[tokenId] = communityTokens - amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("community_" + tokenId, arg, communityTokens - amount); return true; } public bool HasCommunityTokens(string tokenId, int amount) { return GetCommunityTokens(tokenId) >= amount; } public int GetKeys(string keyId) { if (!_vaultData.Keys.TryGetValue(keyId, out var value)) { return 0; } return value; } public bool AddKeys(string keyId, int amount) { if (amount < 0 || string.IsNullOrEmpty(keyId)) { return false; } int keys = GetKeys(keyId); _vaultData.Keys[keyId] = keys + amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("key_" + keyId, keys, keys + amount); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Added {amount} {keyId} keys. New total: {_vaultData.Keys[keyId]}"); } return true; } public bool RemoveKeys(string keyId, int amount) { if (amount < 0 || string.IsNullOrEmpty(keyId)) { return false; } int keys = GetKeys(keyId); if (keys < amount) { return false; } int arg = keys; _vaultData.Keys[keyId] = keys - amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("key_" + keyId, arg, keys - amount); return true; } public bool HasKeys(string keyId, int amount) { return GetKeys(keyId) >= amount; } public int GetSpecial(string specialId) { if (!_vaultData.Tickets.TryGetValue(specialId, out var value)) { return 0; } return value; } public bool AddSpecial(string specialId, int amount) { if (amount < 0 || string.IsNullOrEmpty(specialId)) { return false; } int special = GetSpecial(specialId); _vaultData.Tickets[specialId] = special + amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("special_" + specialId, special, special + amount); return true; } public bool RemoveSpecial(string specialId, int amount) { if (amount < 0 || string.IsNullOrEmpty(specialId)) { return false; } int special = GetSpecial(specialId); if (special < amount) { return false; } int arg = special; _vaultData.Tickets[specialId] = special - amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("special_" + specialId, arg, special - amount); return true; } public bool HasSpecial(string specialId, int amount) { return GetSpecial(specialId) >= amount; } public int GetTickets(string ticketId) { return GetSpecial(ticketId); } public bool AddTickets(string ticketId, int amount) { return AddSpecial(ticketId, amount); } public bool RemoveTickets(string ticketId, int amount) { return RemoveSpecial(ticketId, amount); } public bool HasTickets(string ticketId, int amount) { return HasSpecial(ticketId, amount); } public int GetOrbs(string orbId) { if (!_vaultData.Orbs.TryGetValue(orbId, out var value)) { return 0; } return value; } public bool AddOrbs(string orbId, int amount) { if (amount < 0 || string.IsNullOrEmpty(orbId)) { return false; } int orbs = GetOrbs(orbId); _vaultData.Orbs[orbId] = orbs + amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("orb_" + orbId, orbs, orbs + amount); return true; } public bool RemoveOrbs(string orbId, int amount) { if (amount < 0 || string.IsNullOrEmpty(orbId)) { return false; } int orbs = GetOrbs(orbId); if (orbs < amount) { return false; } int arg = orbs; _vaultData.Orbs[orbId] = orbs - amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("orb_" + orbId, arg, orbs - amount); return true; } public bool HasOrbs(string orbId, int amount) { return GetOrbs(orbId) >= amount; } public int GetCustomCurrency(string currencyId) { if (!_vaultData.CustomCurrencies.TryGetValue(currencyId, out var value)) { return 0; } return value; } public bool AddCustomCurrency(string currencyId, int amount) { if (amount < 0 || string.IsNullOrEmpty(currencyId)) { return false; } int customCurrency = GetCustomCurrency(currencyId); _vaultData.CustomCurrencies[currencyId] = customCurrency + amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("custom_" + currencyId, customCurrency, customCurrency + amount); return true; } public bool RemoveCustomCurrency(string currencyId, int amount) { if (amount < 0 || string.IsNullOrEmpty(currencyId)) { return false; } int customCurrency = GetCustomCurrency(currencyId); if (customCurrency < amount) { return false; } int arg = customCurrency; _vaultData.CustomCurrencies[currencyId] = customCurrency - amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("custom_" + currencyId, arg, customCurrency - amount); return true; } public bool HasCustomCurrency(string currencyId, int amount) { return GetCustomCurrency(currencyId) >= amount; } public int GetCurrency(string fullCurrencyId) { if (string.IsNullOrEmpty(fullCurrencyId)) { return 0; } if (fullCurrencyId.StartsWith("seasonal_")) { if (Enum.TryParse<SeasonalTokenType>(fullCurrencyId.Substring("seasonal_".Length), out var result)) { return GetSeasonalTokens(result); } } else { if (fullCurrencyId.StartsWith("community_")) { return GetCommunityTokens(fullCurrencyId.Substring("community_".Length)); } if (fullCurrencyId.StartsWith("key_")) { return GetKeys(fullCurrencyId.Substring("key_".Length)); } if (fullCurrencyId.StartsWith("special_")) { return GetSpecial(fullCurrencyId.Substring("special_".Length)); } if (fullCurrencyId.StartsWith("orb_")) { return GetOrbs(fullCurrencyId.Substring("orb_".Length)); } if (fullCurrencyId.StartsWith("custom_")) { return GetCustomCurrency(fullCurrencyId.Substring("custom_".Length)); } } return 0; } public bool HasCurrency(string fullCurrencyId, int amount) { return GetCurrency(fullCurrencyId) >= amount; } public Dictionary<string, int> GetAllNonZeroCurrencies() { Dictionary<string, int> dictionary = new Dictionary<string, int>(); foreach (KeyValuePair<SeasonalTokenType, int> seasonalToken in _vaultData.SeasonalTokens) { if (seasonalToken.Value > 0) { dictionary[$"seasonal_{seasonalToken.Key}"] = seasonalToken.Value; } } foreach (KeyValuePair<string, int> communityToken in _vaultData.CommunityTokens) { if (communityToken.Value > 0) { dictionary["community_" + communityToken.Key] = communityToken.Value; } } foreach (KeyValuePair<string, int> key in _vaultData.Keys) { if (key.Value > 0) { dictionary["key_" + key.Key] = key.Value; } } foreach (KeyValuePair<string, int> ticket in _vaultData.Tickets) { if (ticket.Value > 0) { dictionary["special_" + ticket.Key] = ticket.Value; } } foreach (KeyValuePair<string, int> orb in _vaultData.Orbs) { if (orb.Value > 0) { dictionary["orb_" + orb.Key] = orb.Value; } } foreach (KeyValuePair<string, int> customCurrency in _vaultData.CustomCurrencies) { if (customCurrency.Value > 0) { dictionary["custom_" + customCurrency.Key] = customCurrency.Value; } } return dictionary; } } public class VaultSaveSystem { private readonly string _saveDirectory; private readonly VaultManager _vaultManager; private string _currentSaveFile; private static string _cachedSteamId = null; private static bool _steamIdChecked = false; private const string ENCRYPTION_SALT = "TheV4ultS@lt2026Secure"; private const int KEY_SIZE = 256; private const int ITERATIONS = 10000; private static readonly byte[] _iv = new byte[16] { 67, 117, 114, 114, 101, 110, 99, 121, 83, 112, 101, 108, 108, 73, 86, 49 }; private const float AUTO_SAVE_INTERVAL = 300f; private float _lastAutoSave; private bool _needsReEncryption; public VaultSaveSystem(VaultManager vaultManager) { _vaultManager = vaultManager; _saveDirectory = Path.Combine(Paths.ConfigPath, "TheVault", "Saves"); _lastAutoSave = Time.time; if (!Directory.Exists(_saveDirectory)) { Directory.CreateDirectory(_saveDirectory); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Created save directory: " + _saveDirectory)); } } MigrateOldSaves(); } private void MigrateOldSaves() { try { string path = Path.Combine(Paths.ConfigPath, "CurrencySpell", "Saves"); if (!Directory.Exists(path)) { return; } string[] files = Directory.GetFiles(path, "*.vault"); if (files.Length == 0) { return; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Found {files.Length} save files in old CurrencySpell folder, migrating..."); } string[] array = files; foreach (string text in array) { string fileName = Path.GetFileName(text); string text2 = Path.Combine(_saveDirectory, fileName); if (!File.Exists(text2)) { File.Copy(text, text2); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Migrated save: " + fileName)); } } } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"Save migration complete. You can delete the old CurrencySpell folder if desired."); } } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)("Failed to migrate old saves: " + ex.Message)); } } } private string GetSaveFilePath(string playerName) { string text = SanitizeFileName(playerName); if (string.IsNullOrEmpty(text)) { text = "default"; } return Path.Combine(_saveDirectory, text + ".vault"); } private string SanitizeFileName(string name) { if (string.IsNullOrEmpty(name)) { return ""; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { name = name.Replace(oldChar, '_'); } return name.Trim(); } public bool Load(string playerName) { try { _currentSaveFile = GetSaveFilePath(playerName); if (!File.Exists(_currentSaveFile)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("No existing save file for player '" + playerName + "', creating new vault")); } VaultData data = new VaultData { PlayerName = playerName }; _vaultManager.LoadVaultData(data); return true; } byte[] array = File.ReadAllBytes(_currentSaveFile); string text = Decrypt(array, playerName); if (string.IsNullOrEmpty(text)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Current decryption failed, attempting legacy migration for '" + playerName + "'...")); } text = TryLegacyDecryption(array, playerName); if (!string.IsNullOrEmpty(text)) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"Legacy decryption successful - will re-encrypt with new method on save"); } _needsReEncryption = true; } } if (string.IsNullOrEmpty(text)) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)("Failed to decrypt vault data for '" + playerName + "' with all methods, creating new vault")); } VaultData data2 = new VaultData { PlayerName = playerName }; _vaultManager.LoadVaultData(data2); return true; } VaultDataWrapper vaultDataWrapper = JsonUtility.FromJson<VaultDataWrapper>(text); VaultData data3; if (vaultDataWrapper != null) { data3 = vaultDataWrapper.ToVaultData(); } else { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogWarning((object)"Failed to deserialize vault data, creating new vault"); } data3 = new VaultData { PlayerName = playerName }; } data3 = MigrateData(data3); _vaultManager.LoadVaultData(data3); ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)("Loaded vault data for player '" + playerName + "'")); } if (_needsReEncryption) { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogInfo((object)"Re-encrypting vault with new method..."); } Save(); _needsReEncryption = false; ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogInfo((object)"Vault successfully migrated to new encryption!"); } } return true; } catch (Exception ex) { ManualLogSource log9 = Plugin.Log; if (log9 != null) { log9.LogError((object)("Failed to load vault data: " + ex.Message)); } _vaultManager.LoadVaultData(new VaultData { PlayerName = playerName }); return false; } } private string TryLegacyDecryption(byte[] encryptedData, string playerName) { string[] array = new string[3] { "TheV4ultS@lt2026Secure_" + playerName + "_TheVaultPortable", "TheV4ultS@lt2026Secure_Player_" + playerName + "_TheVaultPortable", "TheV4ultS@lt2026Secure_" + playerName + "_" + GetMachineId() }; foreach (string combined in array) { try { byte[] key = GenerateLegacyKey(combined); string text = DecryptWithKey(encryptedData, key); if (!string.IsNullOrEmpty(text) && text.Contains("PlayerName")) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Successfully decrypted with legacy method"); } return text; } } catch { } } return null; } private string GetMachineId() { try { return SystemInfo.deviceUniqueIdentifier; } catch { return "unknown"; } } private byte[] GenerateLegacyKey(string combined) { using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(combined, Encoding.UTF8.GetBytes("TheV4ultS@lt2026Secure"), 10000); return rfc2898DeriveBytes.GetBytes(32); } private string DecryptWithKey(byte[] encryptedData, byte[] key) { try { using Aes aes = Aes.Create(); aes.Key = key; aes.IV = _iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using ICryptoTransform cryptoTransform = aes.CreateDecryptor(); byte[] bytes = cryptoTransform.TransformFinalBlock(encryptedData, 0, encryptedData.Length); return Encoding.UTF8.GetString(bytes); } catch { return null; } } public bool Save() { if (string.IsNullOrEmpty(_currentSaveFile)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"No save file set, cannot save"); } return false; } try { VaultData vaultData = _vaultManager.GetVaultData(); string plainText = JsonUtility.ToJson((object)VaultDataWrapper.FromVaultData(vaultData), true); byte[] bytes = Encrypt(plainText, vaultData.PlayerName); string text = _currentSaveFile + ".tmp"; File.WriteAllBytes(text, bytes); if (File.Exists(_currentSaveFile)) { string text2 = _currentSaveFile + ".backup"; if (File.Exists(text2)) { File.Delete(text2); } File.Move(_currentSaveFile, text2); } File.Move(text, _currentSaveFile); _vaultManager.MarkClean(); _lastAutoSave = Time.time; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Saved vault data to " + _currentSaveFile)); } return true; } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("Failed to save vault data: " + ex.Message)); } return false; } } public void ForceSave() { if (_vaultManager.IsDirty) { Save(); } } public void CheckAutoSave() { if (_vaultManager.IsDirty && Time.time - _lastAutoSave >= 300f) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Auto-saving vault data..."); } Save(); } } private VaultData MigrateData(VaultData data) { if (data.Version < 1) { data.Version = 1; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Migrated vault data to version 1"); } } return data; } public bool DeleteSave(string playerName) { try { string saveFilePath = GetSaveFilePath(playerName); if (File.Exists(saveFilePath)) { File.Delete(saveFilePath); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Deleted vault save for player '" + playerName + "'")); } } string path = saveFilePath + ".backup"; if (File.Exists(path)) { File.Delete(path); } return true; } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Failed to delete vault save: " + ex.Message)); } return false; } } public string[] GetAllSavedPlayers() { try { string[] files = Directory.GetFiles(_saveDirectory, "*.vault"); string[] array = new string[files.Length]; for (int i = 0; i < files.Length; i++) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(files[i]); array[i] = fileNameWithoutExtension; } return array; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Failed to get saved players: " + ex.Message)); } return Array.Empty<string>(); } } public bool ExportVault(string exportPath) { try { string contents = JsonUtility.ToJson((object)VaultDataWrapper.FromVaultData(_vaultManager.GetVaultData()), true); File.WriteAllText(exportPath, contents); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Exported vault to " + exportPath)); } return true; } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Failed to export vault: " + ex.Message)); } return false; } } public bool ImportVault(string importPath) { try { if (!File.Exists(importPath)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Import file not found: " + importPath)); } return false; } VaultDataWrapper vaultDataWrapper = JsonUtility.FromJson<VaultDataWrapper>(File.ReadAllText(importPath)); if (vaultDataWrapper == null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)"Failed to parse import file"); } return false; } VaultData data = vaultDataWrapper.ToVaultData(); data = MigrateData(data); _vaultManager.LoadVaultData(data); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Imported vault from " + importPath)); } return true; } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogError((object)("Failed to import vault: " + ex.Message)); } return false; } } private static string TryGetSteamId() { if (_steamIdChecked) { return _cachedSteamId; } _steamIdChecked = true; _cachedSteamId = null; try { Assembly assembly = null; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { string name = assembly2.GetName().Name; if (name.Contains("rlabrecque") || name == "Steamworks.NET") { assembly = assembly2; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[VaultSave] Found Steamworks assembly: " + name)); } break; } } if (assembly == null) { assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly3 in assemblies) { try { if (assembly3.GetType("Steamworks.SteamUser") != null) { assembly = assembly3; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[VaultSave] Found SteamUser in assembly: " + assembly3.GetName().Name)); } break; } } catch { } } } if (assembly == null) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"[VaultSave] Steam assembly not found - using player name for encryption"); } return null; } Type type = assembly.GetType("Steamworks.SteamUser"); if (type == null) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)"[VaultSave] SteamUser type not found in assembly"); } return null; } MethodInfo method = type.GetMethod("GetSteamID", BindingFlags.Static | BindingFlags.Public); if (method == null) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)"[VaultSave] GetSteamID method not found"); } return null; } object obj2 = method.Invoke(null, null); if (obj2 == null) { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)"[VaultSave] GetSteamID returned null"); } return null; } string text = obj2.ToString(); if (string.IsNullOrEmpty(text) || text == "0" || text.Length < 10) { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogInfo((object)("[VaultSave] Invalid Steam ID: " + text)); } return null; } _cachedSteamId = text; ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogInfo((object)$"[VaultSave] Successfully retrieved Steam ID for encryption (length: {text.Length})"); } return _cachedSteamId; } catch (Exception ex) { ManualLogSource log9 = Plugin.Log; if (log9 != null) { log9.LogWarning((object)("[VaultSave] Failed to get Steam ID: " + ex.Message)); } return null; } } private byte[] GenerateKey(string playerName) { string text = TryGetSteamId(); string text2; if (!string.IsNullOrEmpty(text)) { text2 = "Steam_" + text; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Using Steam ID for vault encryption (cross-device compatible)"); } } else { text2 = "Player_" + playerName; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"Using player name for vault encryption (non-Steam mode)"); } } using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes("TheV4ultS@lt2026Secure_" + text2 + "_TheVaultPortable", Encoding.UTF8.GetBytes("TheV4ultS@lt2026Secure"), 10000); return rfc2898DeriveBytes.GetBytes(32); } private byte[] Encrypt(string plainText, string playerName) { byte[] key = GenerateKey(playerName); using Aes aes = Aes.Create(); aes.Key = key; aes.IV = _iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using ICryptoTransform transform = aes.CreateEncryptor(); using MemoryStream memoryStream = new MemoryStream(); byte[] bytes = Encoding.UTF8.GetBytes("CSVAULT2"); memoryStream.Write(bytes, 0, bytes.Length); using (CryptoStream stream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write)) { using StreamWriter streamWriter = new StreamWriter(stream, Encoding.UTF8); streamWriter.Write(plainText); } return memoryStream.ToArray(); } private string Decrypt(byte[] cipherData, string playerName) { try { if (cipherData.Length < 8) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Vault file too small, may be corrupted"); } return null; } if (Encoding.UTF8.GetString(cipherData, 0, 8) != "CSVAULT2") { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"Detected legacy unencrypted vault file, will re-encrypt on save"); } return Encoding.UTF8.GetString(cipherData); } byte[] key = GenerateKey(playerName); using Aes aes = Aes.Create(); aes.Key = key; aes.IV = _iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using ICryptoTransform transform = aes.CreateDecryptor(); using MemoryStream stream = new MemoryStream(cipherData, 8, cipherData.Length - 8); using CryptoStream stream2 = new CryptoStream(stream, trans