Decompiled source of DSPModSave v1.2.2

DSPModSave.dll

Decompiled a month 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.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using crecheng.DSPModSave.Patches;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("crecheng")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Library that allows to store mod save data separately from vanilla saves")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("DSPModSave")]
[assembly: AssemblyTitle("DSPModSave")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace crecheng.DSPModSave
{
	[BepInPlugin("crecheng.DSPModSave", "DSP Mod Save", "1.2.2")]
	public class DSPModSavePlugin : BaseUnityPlugin
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static Response <>9__26_0;

			public static Response <>9__28_0;

			internal void <SaveData>b__26_0()
			{
			}

			internal void <CallImports>b__28_0()
			{
			}
		}

		public const string MODGUID = "crecheng.DSPModSave";

		public const string MODNAME = "DSP Mod Save";

		public const string MODID = "DSPModSave";

		public const string VERSION = "1.2.2";

		public static ManualLogSource logger;

		internal static Dictionary<string, ModSaveSettings> allModData = new Dictionary<string, ModSaveSettings>();

		private const string saveExt = ".moddsv";

		private const string autoSaveTmp = "_autosave_tmp";

		private const string autoSave0 = "_autosave_0";

		private const string autoSave1 = "_autosave_1";

		private const string autoSave2 = "_autosave_2";

		private const string autoSave3 = "_autosave_3";

		private const string lastExit = "_lastexit_";

		public const int SAVE_FILE_VERSION = 1;

		private static FileStream currentFileStream;

		private static Dictionary<string, ModSaveEntry> saveEntries = new Dictionary<string, ModSaveEntry>();

		private void Start()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			logger = ((BaseUnityPlugin)this).Logger;
			Harmony val = new Harmony("crecheng.DSPModSave");
			val.PatchAll(typeof(GameData_Patch));
			val.PatchAll(typeof(GameSave_Patch));
			Init();
			logger.LogInfo((object)"DSP Mod Save is initialized!");
		}

		private void Init()
		{
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				RegisterModSaveHandler(value.Instance);
			}
		}

		public static void AddModSaveManually(BaseUnityPlugin mod)
		{
			RegisterModSaveHandler(mod);
		}

		public static void RemoveModSaveManually(BaseUnityPlugin mod)
		{
			if (mod is IModCanSave)
			{
				string gUID = mod.Info.Metadata.GUID;
				if (allModData.ContainsKey(gUID))
				{
					allModData.Remove(gUID);
				}
			}
		}

		private static void RegisterModSaveHandler(BaseUnityPlugin mod)
		{
			if (!(mod is IModCanSave mod2))
			{
				return;
			}
			string gUID = mod.Info.Metadata.GUID;
			if (!allModData.ContainsKey(gUID))
			{
				ModSaveSettingsAttribute customAttribute = ((object)mod).GetType().GetCustomAttribute<ModSaveSettingsAttribute>();
				LoadOrder loadOrder = LoadOrder.Postload;
				if (customAttribute != null)
				{
					loadOrder = customAttribute.LoadOrder;
				}
				allModData.Add(gUID, new ModSaveSettings
				{
					mod = mod2,
					loadOrder = loadOrder
				});
			}
		}

		internal static void OnSave(string saveName)
		{
			if ((Object)(object)DSPGame.Game == (Object)null)
			{
				logger.LogError((object)"No game to save");
			}
			else
			{
				if (allModData.Count == 0)
				{
					return;
				}
				saveName = CommonUtils.ValidFileName(saveName);
				string path = GameConfig.gameSaveFolder + saveName + ".moddsv";
				try
				{
					using FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None);
					SaveData(fileStream);
				}
				catch (Exception ex)
				{
					logger.LogError((object)ex.Message);
				}
			}
		}

		internal static void OnAutoSave()
		{
			string text = GameConfig.gameSaveFolder + GameSave.AutoSaveTmp + ".moddsv";
			string text2 = GameConfig.gameSaveFolder + "_autosave_0.moddsv";
			string text3 = GameConfig.gameSaveFolder + "_autosave_1.moddsv";
			string text4 = GameConfig.gameSaveFolder + "_autosave_2.moddsv";
			string text5 = GameConfig.gameSaveFolder + "_autosave_3.moddsv";
			if (File.Exists(text))
			{
				if (File.Exists(text5))
				{
					File.Delete(text5);
				}
				if (File.Exists(text4))
				{
					File.Move(text4, text5);
				}
				if (File.Exists(text3))
				{
					File.Move(text3, text4);
				}
				if (File.Exists(text2))
				{
					File.Move(text2, text3);
				}
				File.Move(text, text2);
			}
		}

		internal static void OnPreLoad(string saveName)
		{
			if (currentFileStream != null)
			{
				currentFileStream.Close();
				currentFileStream = null;
			}
			if ((Object)(object)DSPGame.Game == (Object)null)
			{
				logger.LogError((object)"No game to load");
				return;
			}
			string path = GameConfig.gameSaveFolder + saveName + ".moddsv";
			if (!File.Exists(path))
			{
				logger.LogInfo((object)(saveName + ": Game mod save not exist"));
				{
					foreach (ModSaveSettings value in allModData.Values)
					{
						if (value.loadOrder == LoadOrder.Preload)
						{
							value.mod.IntoOtherSave();
						}
					}
					return;
				}
			}
			try
			{
				logger.LogInfo((object)"Pre Load!");
				currentFileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
				LoadData();
				CallImports(LoadOrder.Preload);
			}
			catch (Exception ex)
			{
				logger.LogError((object)ex.Message);
			}
		}

		internal static void OnPostLoad()
		{
			if (currentFileStream == null)
			{
				foreach (ModSaveSettings value in allModData.Values)
				{
					if (value.loadOrder == LoadOrder.Postload)
					{
						value.mod.IntoOtherSave();
					}
				}
				return;
			}
			logger.LogInfo((object)"Post Load!");
			try
			{
				CallImports(LoadOrder.Postload);
			}
			catch (Exception ex)
			{
				logger.LogError((object)ex.Message);
			}
			currentFileStream.Close();
			currentFileStream = null;
			saveEntries.Clear();
		}

		internal static void OnDeleteSave(string saveName)
		{
			string path = GameConfig.gameSaveFolder + saveName + ".moddsv";
			if (File.Exists(path))
			{
				File.Delete(path);
			}
		}

		internal static void SaveData(FileStream fileStream)
		{
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Expected O, but got Unknown
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Expected O, but got Unknown
			using BinaryWriter binaryWriter = new BinaryWriter(fileStream);
			Dictionary<string, long> dictionary = new Dictionary<string, long>();
			binaryWriter.Write('M');
			binaryWriter.Write('O');
			binaryWriter.Write('D');
			binaryWriter.Write(1);
			binaryWriter.Write(0L);
			binaryWriter.Write(0L);
			binaryWriter.Write(0L);
			binaryWriter.Write(0L);
			binaryWriter.Write(0L);
			binaryWriter.Write(allModData.Count);
			foreach (KeyValuePair<string, ModSaveSettings> allModDatum in allModData)
			{
				binaryWriter.Write(allModDatum.Key);
				dictionary.Add(allModDatum.Key, fileStream.Position);
				binaryWriter.Write(0L);
				binaryWriter.Write(0L);
			}
			foreach (KeyValuePair<string, ModSaveSettings> allModDatum2 in allModData)
			{
				string key = allModDatum2.Key;
				if (!dictionary.ContainsKey(key))
				{
					continue;
				}
				long position = fileStream.Position;
				try
				{
					using MemoryStream memoryStream = new MemoryStream();
					using BinaryWriter w = new BinaryWriter(memoryStream);
					allModDatum2.Value.mod.Export(w);
					byte[] array = memoryStream.ToArray();
					fileStream.Write(array, 0, array.Length);
				}
				catch (Exception ex)
				{
					logger.LogError((object)(allModDatum2.Key + " : mod data export error!"));
					logger.LogError((object)(ex.Message + "\n" + ex.StackTrace));
					string message = "There was an issue saving data of mod " + allModDatum2.Key + ".\nYour save game could be corrupted! Please report this to mod author.\n\nMessage: " + ex.Message + ", Stacktrace:\n" + ex.StackTrace;
					string text = message;
					object obj = <>c.<>9__26_0;
					if (obj == null)
					{
						Response val = delegate
						{
						};
						<>c.<>9__26_0 = val;
						obj = (object)val;
					}
					UIMessageBox.Show("Mod data export error!", text, "Close", "Copy and Close", 3, (Response)obj, (Response)delegate
					{
						GUIUtility.systemCopyBuffer = message;
					});
				}
				long position2 = fileStream.Position;
				fileStream.Seek(dictionary[key], SeekOrigin.Begin);
				binaryWriter.Write(position);
				binaryWriter.Write(position2);
				fileStream.Seek(0L, SeekOrigin.End);
			}
		}

		internal static void LoadData()
		{
			using BinaryReader binaryReader = new BinaryReader(currentFileStream, new UTF8Encoding(), leaveOpen: true);
			saveEntries.Clear();
			if (binaryReader.ReadChar() != 'M' || binaryReader.ReadChar() != 'O' || binaryReader.ReadChar() != 'D')
			{
				logger.LogError((object)"Error loading save file. Save file is corrupted.");
				return;
			}
			int num = binaryReader.ReadInt32();
			binaryReader.ReadInt64();
			binaryReader.ReadInt64();
			binaryReader.ReadInt64();
			binaryReader.ReadInt64();
			binaryReader.ReadInt64();
			int num2 = binaryReader.ReadInt32();
			for (int i = 0; i < num2; i++)
			{
				string text = binaryReader.ReadString();
				long num3 = binaryReader.ReadInt64();
				long end = binaryReader.ReadInt64();
				saveEntries.Add(text, new ModSaveEntry(text, num3, end));
				logger.LogInfo((object)$"File has mod {text} save data starting from {num3}");
			}
		}

		private static void CallImports(LoadOrder currentState)
		{
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Expected O, but got Unknown
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Expected O, but got Unknown
			foreach (KeyValuePair<string, ModSaveSettings> allModDatum in allModData)
			{
				if (allModDatum.Value.loadOrder != currentState)
				{
					continue;
				}
				if (!saveEntries.ContainsKey(allModDatum.Key))
				{
					allModDatum.Value.mod.IntoOtherSave();
					continue;
				}
				ModSaveEntry modSaveEntry = saveEntries[allModDatum.Key];
				currentFileStream.Seek(modSaveEntry.begin, SeekOrigin.Begin);
				byte[] array = new byte[modSaveEntry.end - modSaveEntry.begin];
				currentFileStream.Read(array, 0, array.Length);
				try
				{
					using MemoryStream input = new MemoryStream(array);
					using BinaryReader r = new BinaryReader(input);
					allModDatum.Value.mod.Import(r);
				}
				catch (Exception ex)
				{
					logger.LogError((object)(allModDatum.Key + " :mod data import error!"));
					logger.LogError((object)(ex.Message + "\n" + ex.StackTrace));
					string message = "There was an issue loading data of mod " + allModDatum.Key + ".\nYour save game could be corrupted! Please report this to mod author.\n\nMessage: " + ex.Message + ", Stacktrace:\n" + ex.StackTrace;
					string text = message;
					object obj = <>c.<>9__28_0;
					if (obj == null)
					{
						Response val = delegate
						{
						};
						<>c.<>9__28_0 = val;
						obj = (object)val;
					}
					UIMessageBox.Show("Mod data import error!", text, "Close", "Copy and Close", 3, (Response)obj, (Response)delegate
					{
						GUIUtility.systemCopyBuffer = message;
					});
				}
			}
		}
	}
	public interface IModCanSave
	{
		void Export(BinaryWriter w);

		void Import(BinaryReader r);

		void IntoOtherSave();
	}
	public enum LoadOrder
	{
		Preload,
		Postload
	}
	internal class ModSaveEntry
	{
		public string name;

		public long begin;

		public long end;

		public ModSaveEntry(string name, long begin, long end)
		{
			this.name = name;
			this.begin = begin;
			this.end = end;
		}
	}
	internal class ModSaveSettings
	{
		public IModCanSave mod;

		public LoadOrder loadOrder;
	}
	[AttributeUsage(AttributeTargets.Class)]
	public class ModSaveSettingsAttribute : Attribute
	{
		private LoadOrder _loadOrder;

		public LoadOrder LoadOrder
		{
			get
			{
				return _loadOrder;
			}
			set
			{
				_loadOrder = value;
			}
		}
	}
}
namespace crecheng.DSPModSave.Patches
{
	[HarmonyPatch]
	public class GameData_Patch
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(GameData), "NewGame")]
		private static void EnterGame()
		{
			DSPModSavePlugin.logger.LogInfo((object)"Enter New Game");
			foreach (ModSaveSettings value in DSPModSavePlugin.allModData.Values)
			{
				value.mod.IntoOtherSave();
			}
		}
	}
	[HarmonyPatch]
	public class GameSave_Patch
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(GameSave), "SaveCurrentGame")]
		public static void SaveCurrentGame(bool __result, string saveName)
		{
			if (__result)
			{
				DSPModSavePlugin.OnSave(saveName);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(GameSave), "AutoSave")]
		public static void AutoSave(bool __result)
		{
			if (__result)
			{
				DSPModSavePlugin.OnAutoSave();
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(GameSave), "LoadCurrentGame")]
		public static void PreLoadCurrentGame(bool __result, string saveName)
		{
			DSPModSavePlugin.OnPreLoad(saveName);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(GameSave), "LoadCurrentGame")]
		public static void PostLoadCurrentGame(bool __result)
		{
			if (__result)
			{
				DSPModSavePlugin.OnPostLoad();
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(UIGameSaveEntry), "DeleteSaveFile")]
		public static void PreDeleteSaveFile(UIGameSaveEntry __instance)
		{
			DSPModSavePlugin.OnDeleteSave(__instance.saveName);
		}
	}
}