Decompiled source of BatterySaveFix v1.1.0

BatterySaveFix.dll

Decompiled 6 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("Kai")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("BatterySaveFix")]
[assembly: AssemblyTitle("BatterySaveFix")]
[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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BatterySaveFix
{
	internal static class BatteryRuntimeState
	{
		internal static readonly Dictionary<string, int> LiveBatteryByInstanceName = new Dictionary<string, int>();

		internal static void Clear()
		{
			LiveBatteryByInstanceName.Clear();
		}
	}
	internal static class BatterySaveRuntimeLogic
	{
		private static readonly MethodInfo? ItemBattery_BatteryInit = AccessTools.Method(typeof(ItemBattery), "BatteryInit", (Type[])null, (Type[])null);

		internal static void TryStartBatteryInit(ItemBattery battery)
		{
			try
			{
				if (!((Object)(object)battery == (Object)null) && !(ItemBattery_BatteryInit == null) && ItemBattery_BatteryInit.Invoke(battery, null) is IEnumerator enumerator)
				{
					((MonoBehaviour)battery).StartCoroutine(enumerator);
				}
			}
			catch (Exception arg)
			{
				BatterySaveFix.Logger.LogWarning((object)$"Failed to start BatteryInit: {arg}");
			}
		}

		internal static void RebuildLiveBatteryCache()
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			BatteryRuntimeState.Clear();
			try
			{
				ItemBattery[] array = Object.FindObjectsOfType<ItemBattery>(true);
				int num = 0;
				ItemBattery[] array2 = array;
				foreach (ItemBattery val in array2)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					ItemAttributes component = ((Component)val).GetComponent<ItemAttributes>();
					if (!((Object)(object)component == (Object)null))
					{
						string instanceName = component.instanceName;
						if (!string.IsNullOrWhiteSpace(instanceName))
						{
							int value = Mathf.RoundToInt(val.batteryLife);
							BatteryRuntimeState.LiveBatteryByInstanceName[instanceName] = value;
							num++;
						}
					}
				}
				BatterySaveFix.Logger.LogInfo((object)$"Live battery cache rebuilt. count={num}");
			}
			catch (Exception arg)
			{
				BatterySaveFix.Logger.LogWarning((object)$"Failed to rebuild live battery cache: {arg}");
			}
		}

		internal static int RepairLoadedOrSavingData(StatsManager sm, bool applyRuntimeBattery)
		{
			if ((Object)(object)sm == (Object)null)
			{
				return 0;
			}
			if (sm.item == null || sm.itemStatBattery == null || sm.itemsPurchased == null)
			{
				return 0;
			}
			Dictionary<string, int> item = sm.item;
			Dictionary<string, int> itemStatBattery = sm.itemStatBattery;
			Dictionary<string, int> itemsPurchased = sm.itemsPurchased;
			int num = 0;
			int number;
			foreach (string item2 in item.Keys.ToList())
			{
				if (TryGetSuffixNumber(item2, out number) && !itemStatBattery.ContainsKey(item2))
				{
					int value = 100;
					if (applyRuntimeBattery && BatteryRuntimeState.LiveBatteryByInstanceName.TryGetValue(item2, out var value2))
					{
						value = value2;
					}
					itemStatBattery[item2] = value;
				}
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (KeyValuePair<string, int> item3 in itemsPurchased)
			{
				string key = item3.Key;
				int num2 = Mathf.Max(0, item3.Value);
				if (num2 > 0)
				{
					List<string> instanceKeysTopFirst = GetInstanceKeysTopFirst(item, key);
					for (int i = 0; i < num2 && i < instanceKeysTopFirst.Count; i++)
					{
						hashSet.Add(instanceKeysTopFirst[i]);
					}
				}
			}
			int count = item.Count;
			int count2 = itemStatBattery.Count;
			foreach (string item4 in item.Keys.ToList())
			{
				if (TryGetSuffixNumber(item4, out number) && !hashSet.Contains(item4))
				{
					item.Remove(item4);
					num++;
				}
			}
			foreach (string item5 in itemStatBattery.Keys.ToList())
			{
				if (TryGetSuffixNumber(item5, out number) && !hashSet.Contains(item5))
				{
					itemStatBattery.Remove(item5);
				}
			}
			if (applyRuntimeBattery)
			{
				foreach (string item6 in hashSet)
				{
					if (BatteryRuntimeState.LiveBatteryByInstanceName.TryGetValue(item6, out var value3))
					{
						itemStatBattery[item6] = value3;
					}
					else if (!itemStatBattery.ContainsKey(item6))
					{
						itemStatBattery[item6] = 100;
					}
				}
			}
			BatterySaveFix.Logger.LogInfo((object)$"Prune complete. item: {count} -> {item.Count}, battery: {count2} -> {itemStatBattery.Count}, removed={num}");
			return num;
		}

		private static List<string> GetInstanceKeysTopFirst(Dictionary<string, int> itemDict, string baseName)
		{
			string baseName2 = baseName;
			int number2;
			return itemDict.Keys.Where((string k) => TryGetBaseName(k) == baseName2 && TryGetSuffixNumber(k, out number2)).OrderBy(delegate(string k)
			{
				TryGetSuffixNumber(k, out var number);
				return number;
			}).ToList();
		}

		internal static string TryGetBaseName(string key)
		{
			int num = key.LastIndexOf('/');
			if (num <= 0)
			{
				return key;
			}
			return key.Substring(0, num);
		}

		internal static bool TryGetSuffixNumber(string key, out int number)
		{
			number = 0;
			int num = key.LastIndexOf('/');
			if (num <= 0 || num >= key.Length - 1)
			{
				return false;
			}
			return int.TryParse(key.Substring(num + 1), out number);
		}
	}
	[HarmonyPatch(typeof(ItemBattery), "Start")]
	internal static class ItemBattery_Start_Patch
	{
		private static void Postfix(ItemBattery __instance)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				BatterySaveRuntimeLogic.TryStartBatteryInit(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(StatsManager), "LoadGame")]
	internal static class StatsManager_LoadGame_Patch
	{
		private static void Prefix(string fileName)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				BatterySaveFix.Logger.LogInfo((object)("LoadGame Prefix called. fileName=" + fileName));
			}
		}

		private static void Postfix(StatsManager __instance, string fileName)
		{
			BatterySaveFix.Logger.LogInfo((object)("LoadGame Postfix called. fileName=" + fileName));
			try
			{
				int num = BatterySaveRuntimeLogic.RepairLoadedOrSavingData(__instance, applyRuntimeBattery: false);
				BatterySaveFix.Logger.LogInfo((object)$"LoadGame repaired = {num}");
			}
			catch (Exception arg)
			{
				BatterySaveFix.Logger.LogError((object)$"LoadGame repair failed: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(StatsManager), "SaveGame")]
	internal static class StatsManager_SaveGame_Patch
	{
		private static void Prefix(StatsManager __instance, string fileName)
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			BatterySaveFix.Logger.LogInfo((object)("SaveGame Prefix called. fileName=" + fileName));
			try
			{
				BatterySaveRuntimeLogic.RebuildLiveBatteryCache();
				int num = BatterySaveRuntimeLogic.RepairLoadedOrSavingData(__instance, applyRuntimeBattery: true);
				BatterySaveFix.Logger.LogInfo((object)$"SaveGame repaired = {num}");
			}
			catch (Exception arg)
			{
				BatterySaveFix.Logger.LogError((object)$"SaveGame repair failed: {arg}");
			}
		}
	}
	[BepInPlugin("Kai.BatterySaveFix", "BatterySaveFix", "1.1.0")]
	public class BatterySaveFix : BaseUnityPlugin
	{
		internal static BatterySaveFix Instance { get; private set; }

		internal static ManualLogSource Logger => Instance._logger;

		private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;

		internal Harmony? Harmony { get; set; }

		private void Awake()
		{
			Instance = this;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			Patch();
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!");
		}

		internal void Patch()
		{
			//IL_0019: 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_0020: Expected O, but got Unknown
			//IL_0025: Expected O, but got Unknown
			if (Harmony == null)
			{
				Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
				Harmony val2 = val;
				Harmony = val;
			}
			Harmony.PatchAll();
		}

		internal void Unpatch()
		{
			Harmony? harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}