Decompiled source of SaveDataAPI v1.0.1

SaveDataAPI.dll

Decompiled 2 years ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Reptile;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")]
[assembly: AssemblyCompany("SaveDataAPI")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("My first plugin")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("SaveDataAPI")]
[assembly: AssemblyTitle("SaveDataAPI")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace SaveDataAPI
{
	public abstract class ADataSaver
	{
		public CustomSaveData SaveData;

		public abstract void PrepareSave();

		public abstract void OnLoad();

		public abstract void Write(BinaryWriter writer);

		public abstract void Read(BinaryReader reader);
	}
	public class CustomSaveData : ISaveable
	{
		private class OrphanedData
		{
			public string AssemblyName;

			public string TypeFullName;

			public byte[] Data;

			public OrphanedData(string assemblyName, string typeFullName, byte[] data)
			{
				AssemblyName = assemblyName;
				TypeFullName = typeFullName;
				Data = data;
			}
		}

		private List<ADataSaver> _savers = new List<ADataSaver>();

		private const string FileName = "CustomGameProgress{0}.cbrp";

		private const string BackupFileName = "CustomGameProgress{0}_backup.cbrp";

		private ATransaction _lastLoadTransaction;

		private int _slotID;

		private List<OrphanedData> _orphanedSavers = new List<OrphanedData>();

		public bool BusyLoading
		{
			get
			{
				if (_lastLoadTransaction == null)
				{
					return false;
				}
				if (_lastLoadTransaction.IsDone)
				{
					return false;
				}
				return true;
			}
		}

		public int Slot => _slotID;

		public List<ADataSaver> Savers
		{
			get
			{
				lock (_savers)
				{
					return new List<ADataSaver>(_savers);
				}
			}
			set
			{
				lock (_savers)
				{
					_savers = value;
				}
			}
		}

		public CustomSaveData(int slot)
		{
			_slotID = slot;
		}

		public void Write(BinaryWriter writer)
		{
			lock (_savers)
			{
				writer.Write((byte)0);
				writer.Write(_savers.Count + _orphanedSavers.Count);
				foreach (ADataSaver saver in _savers)
				{
					Type type = saver.GetType();
					writer.Write(type.Assembly.GetName().Name);
					writer.Write(type.FullName);
					long position = writer.BaseStream.Position;
					writer.Write(0);
					saver.Write(writer);
					long position2 = writer.BaseStream.Position;
					writer.Seek((int)position, SeekOrigin.Begin);
					writer.Write((int)(position2 - position - 4));
					writer.Seek((int)position2, SeekOrigin.Begin);
				}
				foreach (OrphanedData orphanedSaver in _orphanedSavers)
				{
					writer.Write(orphanedSaver.AssemblyName);
					writer.Write(orphanedSaver.TypeFullName);
					writer.Write(orphanedSaver.Data.Length);
					writer.Write(orphanedSaver.Data);
				}
			}
		}

		public void Read(BinaryReader reader)
		{
			lock (_savers)
			{
				reader.ReadByte();
				int num = reader.ReadInt32();
				for (int i = 0; i < num; i++)
				{
					string text = reader.ReadString();
					string text2 = reader.ReadString();
					int count = reader.ReadInt32();
					long position = reader.BaseStream.Position;
					Assembly assembly = FindAssemblyByName(text);
					Type type = null;
					bool flag = false;
					if (assembly == null)
					{
						flag = true;
					}
					else
					{
						type = assembly.GetType(text2);
						if (type == null)
						{
							flag = true;
						}
						else if (!typeof(ADataSaver).IsAssignableFrom(type))
						{
							flag = true;
						}
					}
					if (flag)
					{
						Plugin.Instance.GetLogger().LogWarning((object)$"Found orphaned data for custom save slot {Slot}: {text2}, {text}");
						if (Plugin.Instance.KeepOrphanedData)
						{
							Plugin.Instance.GetLogger().LogWarning((object)"Keeping it around.");
							byte[] data2 = reader.ReadBytes(count);
							AddOrphanedData(text, text2, data2);
						}
						else
						{
							Plugin.Instance.GetLogger().LogWarning((object)"Disposing of it.");
							reader.ReadBytes(count);
						}
						continue;
					}
					try
					{
						GetSaver(type).Read(reader);
					}
					catch (Exception arg)
					{
						Plugin.Instance.GetLogger().LogError((object)$"Failed to read custom save data for Saver type: {text2}, {text}. {arg}");
						reader.BaseStream.Position = position;
						byte[] data3 = reader.ReadBytes(count);
						if (Plugin.Instance.KeepOrphanedData)
						{
							Plugin.Instance.GetLogger().LogWarning((object)"Keeping it around as orphaned data.");
							AddOrphanedData(text, text2, data3);
						}
					}
				}
			}
			void AddOrphanedData(string assemblyName, string typeName, byte[] data)
			{
				OrphanedData item = new OrphanedData(assemblyName, typeName, data);
				_orphanedSavers.Add(item);
			}
		}

		public void DeleteOrphanedSaver(string assemblyName, string typeFullName)
		{
			foreach (OrphanedData item in new List<OrphanedData>(_orphanedSavers))
			{
				if (item.AssemblyName == assemblyName && item.TypeFullName == typeFullName)
				{
					_orphanedSavers.Remove(item);
					break;
				}
			}
		}

		public T GetDataSaver<T>() where T : ADataSaver
		{
			lock (_savers)
			{
				foreach (ADataSaver saver in _savers)
				{
					if (saver.GetType() == typeof(T))
					{
						return (T)saver;
					}
				}
			}
			return null;
		}

		internal void OnLoad()
		{
			lock (_savers)
			{
				foreach (ADataSaver saver in _savers)
				{
					saver.OnLoad();
				}
			}
		}

		internal LoadTransaction<CustomSaveData> LoadData(string filename)
		{
			lock (_savers)
			{
				PrepareSaversForLoad();
				LoadTransaction<CustomSaveData> val = Core.Instance.Platform.Storage.LoadDataFromStorage<CustomSaveData>(this, filename, "CustomData", (FormatType)1);
				Core.Instance.SaveManager.AddLoadTransactionToTracker((ATransaction)(object)val);
				_lastLoadTransaction = (ATransaction)(object)val;
				return val;
			}
		}

		internal SaveTransaction<CustomSaveData> SaveData(string filename)
		{
			lock (_savers)
			{
				PrepareSaversForSave();
				SaveTransaction<CustomSaveData> val = Core.Instance.Platform.Storage.SaveDataAtStorage<CustomSaveData>(this, filename, "CustomData", (FormatType)1);
				Core.Instance.SaveManager.AddSaveTransactionToTracker((ATransaction)(object)val);
				return val;
			}
		}

		internal static string GetFilename(int slotId)
		{
			return $"CustomGameProgress{slotId}.cbrp";
		}

		internal static string GetBackupFilename(int slotId)
		{
			return $"CustomGameProgress{slotId}_backup.cbrp";
		}

		private void PrepareSaversForLoad()
		{
			_savers.Clear();
			foreach (Type saver in CustomSaveManager.Savers)
			{
				ADataSaver item = CreateSaver(saver);
				_savers.Add(item);
			}
		}

		private void PrepareSaversForSave()
		{
			_savers.Clear();
			foreach (Type saver in CustomSaveManager.Savers)
			{
				ADataSaver aDataSaver = CreateSaver(saver);
				aDataSaver.PrepareSave();
				_savers.Add(aDataSaver);
			}
		}

		private ADataSaver CreateSaver(Type type)
		{
			ADataSaver obj = (ADataSaver)Activator.CreateInstance(type);
			obj.SaveData = this;
			return obj;
		}

		private ADataSaver GetSaver(Type type)
		{
			foreach (ADataSaver saver in _savers)
			{
				if (saver.GetType() == type)
				{
					return saver;
				}
			}
			return null;
		}

		private Assembly FindAssemblyByName(string name)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (assembly.GetName().Name == name)
				{
					return assembly;
				}
			}
			return null;
		}
	}
	public static class CustomSaveManager
	{
		internal static List<Type> Savers = new List<Type>();

		internal static bool StageInitializedCalledForCurrentSaveYet = true;

		private static Dictionary<int, CustomSaveData> SaveDataBySlotID = new Dictionary<int, CustomSaveData>();

		public static CustomSaveData CurrentSaveData
		{
			get
			{
				if (!Core.Instance.SaveManager.HasCurrentSaveSlot)
				{
					return null;
				}
				return GetSaveDataForSlot(Core.Instance.SaveManager.CurrentSaveSlot.saveSlotId);
			}
		}

		public static CustomSaveData GetSaveDataForSlot(int slot)
		{
			if (SaveDataBySlotID.TryGetValue(slot, out var value))
			{
				return value;
			}
			return CreateSaveDataForSlot(slot);
		}

		public static void SetSaveDataForSlot(int slotId, CustomSaveData saveData)
		{
			SaveDataBySlotID[slotId] = saveData;
		}

		public static void DeleteSaveDataForSlot(int slotId)
		{
			DeleteData(CustomSaveData.GetFilename(slotId));
			RemoveSaveDataForSlot(slotId);
		}

		internal static void RegisterSavers()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				Type[] types = assemblies[i].GetTypes();
				foreach (Type type in types)
				{
					if (typeof(ADataSaver).IsAssignableFrom(type) && !type.IsAbstract)
					{
						if (DebugSettings.Verbose)
						{
							Plugin.Instance.GetLogger().LogInfo((object)$"Registered saver {type}");
						}
						Savers.Add(type);
					}
				}
			}
		}

		internal static void Core_OnCoreInitialized()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			((ATransaction)Core.Instance.Platform.Storage.CreateDirectory("CustomData")).OnTransactionDone += new TransactionDoneDelegate(OnCreateDirectoryFinished);
			RegisterSavers();
		}

		private static void OnCreateDirectoryFinished(ATransaction transaction)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			ManualLogSource logger = Plugin.Instance.GetLogger();
			transaction.OnTransactionDone -= new TransactionDoneDelegate(OnCreateDirectoryFinished);
			if ((int)transaction.Status == 7)
			{
				logger.LogWarning((object)"Failed to create CustomData directory, trying again.");
				transaction.OnTransactionDone += new TransactionDoneDelegate(OnCreateDirectoryFinished);
				transaction.ScheduleForReset();
				Core.Instance.Platform.Storage.EnqueueTransaction(transaction);
			}
			else if (transaction.HasError)
			{
				logger.LogError((object)"Failed to create CustomData directory.");
			}
			else if (DebugSettings.Verbose)
			{
				logger.LogInfo((object)"Created CustomData directory.");
			}
		}

		internal static void StageManager_OnStageInitialized()
		{
			if (!StageInitializedCalledForCurrentSaveYet)
			{
				if (DebugSettings.Verbose)
				{
					Plugin.Instance.GetLogger().LogInfo((object)"Calling StageInitialize. New save loaded.");
				}
				StageInitializedCalledForCurrentSaveYet = true;
				CurrentSaveData?.OnLoad();
			}
		}

		internal static DeleteTransaction DeleteData(string filename)
		{
			return Core.Instance.Platform.Storage.DeleteDataFromStorage(filename, "CustomData");
		}

		internal static void RemoveSaveDataForSlot(int slotId)
		{
			if (SaveDataBySlotID.ContainsKey(slotId))
			{
				SaveDataBySlotID.Remove(slotId);
			}
		}

		internal static CustomSaveData CreateSaveDataForSlot(int slot)
		{
			CustomSaveData customSaveData = new CustomSaveData(slot);
			SaveDataBySlotID[slot] = customSaveData;
			return customSaveData;
		}
	}
	internal static class DebugSettings
	{
		public static bool Verbose;
	}
	[BepInPlugin("org.lazyduchess.plugins.brc.savedataapi", "SaveData API", "1.0.1.0")]
	[BepInProcess("Bomb Rush Cyberfunk.exe")]
	internal class Plugin : BaseUnityPlugin
	{
		public static Plugin Instance;

		private const string GUID = "org.lazyduchess.plugins.brc.savedataapi";

		private const string Name = "SaveData API";

		private const string Version = "1.0.1.0";

		private ConfigEntry<bool> _keepOrphanedData;

		internal bool KeepOrphanedData => _keepOrphanedData.Value;

		private void Awake()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			try
			{
				Core.OnCoreInitialized += new OnCoreInitializedHandler(CustomSaveManager.Core_OnCoreInitialized);
				StageManager.OnStageInitialized += new OnStageInitializedDelegate(CustomSaveManager.StageManager_OnStageInitialized);
				_keepOrphanedData = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "KeepOrphanedData", true, "If true, custom save data from mods that were removed, accidentally or otherwise, will be kept around, so progress won't be lost if the mod is reinstalled later.");
				new Harmony("org.lazyduchess.plugins.brc.savedataapi").PatchAll();
				((BaseUnityPlugin)this).Logger.LogInfo((object)"SaveData API 1.0.1.0 was initialized!");
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)string.Format("Failed to initialize {0} {1}: {2}", "SaveData API", "1.0.1.0", arg));
			}
		}

		public ManualLogSource GetLogger()
		{
			return ((BaseUnityPlugin)this).Logger;
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "SaveDataAPI";

		public const string PLUGIN_NAME = "SaveDataAPI";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace SaveDataAPI.Patches
{
	[HarmonyPatch(typeof(SaveSlotHandler))]
	internal class SaveSlotHandlerPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("SaveSaveSlot")]
		private static void SaveSaveSlot_Postfix(int saveSlotId)
		{
			if (saveSlotId > -1)
			{
				if (DebugSettings.Verbose)
				{
					Plugin.Instance.GetLogger().LogInfo((object)$"Saving custom data for Slot {saveSlotId}");
				}
				CustomSaveData saveDataForSlot = CustomSaveManager.GetSaveDataForSlot(saveSlotId);
				string filename = CustomSaveData.GetFilename(saveSlotId);
				saveDataForSlot.SaveData(filename);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("SaveSaveSlotBackup")]
		private static void SaveSaveSlotBackup_Postfix(int slotId)
		{
			if (slotId > -1)
			{
				if (DebugSettings.Verbose)
				{
					Plugin.Instance.GetLogger().LogInfo((object)$"Saving backup custom data for Slot {slotId}");
				}
				CustomSaveData saveDataForSlot = CustomSaveManager.GetSaveDataForSlot(slotId);
				string backupFilename = CustomSaveData.GetBackupFilename(slotId);
				saveDataForSlot.SaveData(backupFilename);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("LoadSaveSlot")]
		private static void LoadSaveSlot_Prefix(string fileName)
		{
			int slotFromFilename = GetSlotFromFilename(fileName);
			if (slotFromFilename > -1)
			{
				if (DebugSettings.Verbose)
				{
					Plugin.Instance.GetLogger().LogInfo((object)$"Loading custom data for Slot {slotFromFilename}");
				}
				CustomSaveManager.GetSaveDataForSlot(slotFromFilename).LoadData(CustomSaveData.GetFilename(slotFromFilename));
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("LoadSaveSlotBackup")]
		private static void LoadSaveSlotBackup_Prefix(SaveSlotData saveSlotData, string saveSlotFileName)
		{
			int slotFromFilename = GetSlotFromFilename(saveSlotFileName);
			if (slotFromFilename > -1)
			{
				if (DebugSettings.Verbose)
				{
					Plugin.Instance.GetLogger().LogInfo((object)$"Loading backup custom data for Slot {slotFromFilename}");
				}
				CustomSaveManager.GetSaveDataForSlot(slotFromFilename).LoadData(CustomSaveData.GetBackupFilename(slotFromFilename));
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("DeleteSaveSlot")]
		private static void DeleteSaveSlot_Prefix(int slotId)
		{
			if (slotId > -1)
			{
				if (DebugSettings.Verbose)
				{
					Plugin.Instance.GetLogger().LogInfo((object)$"Deleting custom data for Slot {slotId}");
				}
				CustomSaveManager.RemoveSaveDataForSlot(slotId);
				CustomSaveManager.DeleteData(CustomSaveData.GetFilename(slotId));
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("DeleteSaveSlotDataBackup")]
		private static void DeleteSaveSlotDataBackup_Prefix(int slotId)
		{
			if (slotId > -1)
			{
				if (DebugSettings.Verbose)
				{
					Plugin.Instance.GetLogger().LogInfo((object)$"Deleting backup custom data for Slot {slotId}");
				}
				CustomSaveManager.DeleteData(CustomSaveData.GetBackupFilename(slotId));
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("AddNewSaveSlot")]
		private static void AddNewSaveSlot_Prefix(int saveSlotId)
		{
			if (saveSlotId > -1)
			{
				if (DebugSettings.Verbose)
				{
					Plugin.Instance.GetLogger().LogInfo((object)$"Creating custom data for Slot {saveSlotId}");
				}
				CustomSaveManager.CreateSaveDataForSlot(saveSlotId);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("SetCurrentSaveSlotDataBySlotId")]
		private static void SetCurrentSaveSlotDataBySlotId_Postfix(int saveSlotId)
		{
			if (saveSlotId > -1)
			{
				CustomSaveManager.StageInitializedCalledForCurrentSaveYet = false;
				if (DebugSettings.Verbose)
				{
					Plugin.Instance.GetLogger().LogInfo((object)$"Current save slot set to {saveSlotId}. Will call initialization on stage load.");
				}
			}
		}

		private static int GetSlotFromFilename(string filename)
		{
			bool flag = false;
			string text = "";
			for (int i = 0; i < filename.Length; i++)
			{
				if (char.IsDigit(filename[i]))
				{
					text += filename[i];
					flag = true;
				}
				else if (flag)
				{
					break;
				}
			}
			if (string.IsNullOrEmpty(text))
			{
				return -1;
			}
			if (DebugSettings.Verbose)
			{
				Plugin.Instance.GetLogger().LogInfo((object)("Slot ID for " + filename + ": " + text));
			}
			return int.Parse(text);
		}
	}
}