Decompiled source of SMTSaveAPI v1.1.0

BepInEx/plugins/SMTSaveAPI.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using HutongGames.PlayMaker;
using HutongGames.PlayMaker.Actions;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SMTSaveAPI.API.Events;
using SMTSaveAPI.API.Managers;
using SMTSaveAPI.API.SavedValue;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("SMTSaveAPI")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+b5c598f6979ec7ad18957a88e8b99a44b1ec6591")]
[assembly: AssemblyProduct("SMTSaveAPI")]
[assembly: AssemblyTitle("SMTSaveAPI")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.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 SMTSaveAPI
{
	[BepInPlugin("SMTSaveAPI", "SMTSaveAPI", "1.1.0")]
	public class ModEntry : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		internal static ConfigFile Config;

		private void Awake()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Logger = ((BaseUnityPlugin)this).Logger;
			Config = ((BaseUnityPlugin)this).Config;
			new Harmony("SMTSaveAPI").PatchAll();
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "SMTSaveAPI";

		public const string PLUGIN_NAME = "SMTSaveAPI";

		public const string PLUGIN_VERSION = "1.1.0";
	}
}
namespace SMTSaveAPI.Patches
{
	[HarmonyPatch]
	internal static class SavePatches
	{
		private static readonly SemaphoreSlim SaveLock = new SemaphoreSlim(1, 1);

		private static readonly SynchronizationContext unityContext = SynchronizationContext.Current;

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> OnSaving(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Expected O, but got Unknown
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			return new CodeMatcher(instructions, (ILGenerator)null).Start().MatchForward(false, (CodeMatch[])(object)new CodeMatch[6]
			{
				new CodeMatch((OpCode?)OpCodes.Ldsfld, (object)AccessTools.Field(typeof(GameCanvas), "Instance"), (string)null),
				new CodeMatch((OpCode?)null, (object)null, (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldstr, (object)"SavingContainer", (string)null),
				new CodeMatch((OpCode?)null, (object)null, (string)null),
				new CodeMatch((OpCode?)null, (object)null, (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldc_I4_0, (object)null, (string)null)
			}).RemoveInstructions(10)
				.Insert((CodeInstruction[])(object)new CodeInstruction[2]
				{
					new CodeInstruction(OpCodes.Ldloc_1, (object)null),
					new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(SavePatches), "RunSaveTask", (Type[])null, (Type[])null))
				})
				.Instructions();
		}

		private static void RunSaveTask(NetworkSpawner __instance)
		{
			Task.Run(async delegate
			{
				await SaveLock.WaitAsync();
				try
				{
					CustomSaveManager.SaveValues();
				}
				finally
				{
					SaveLock.Release();
				}
				unityContext.Post(delegate
				{
					((Component)((Component)GameCanvas.Instance).transform.Find("SavingContainer")).gameObject.SetActive(false);
					__instance.isSaving = false;
				}, null);
			});
		}

		[HarmonyPatch(typeof(NetworkSpawner), "OnStartServer")]
		[HarmonyPrefix]
		private static void OnLoading()
		{
			Task.Run((Action)CustomSaveManager.LoadValues);
		}

		[HarmonyPatch(typeof(GameData), "DoDaySaveBackup")]
		[HarmonyPostfix]
		private static void OnSavingBackup()
		{
			string customSaveFilePath = SavePathManager.CustomSaveFilePath;
			if (File.Exists(customSaveFilePath))
			{
				File.Copy(customSaveFilePath, SavePathManager.ToBackupPath(customSaveFilePath, GameData.Instance.NetworkgameDay), overwrite: true);
			}
		}

		[HarmonyPatch(typeof(SetFsmString), "DoSetFsmString")]
		[HarmonyPostfix]
		private static void OnLoadingBackup(SetFsmString __instance)
		{
			if (__instance.variableName.Value == "FilenameToCopy")
			{
				string text = SavePathManager.ToCustomPath(SavePathManager.GetSavePath(__instance.setValue.Value));
				if (File.Exists(text))
				{
					File.Copy(text, SavePathManager.RemoveBackupSuffix(text), overwrite: true);
				}
			}
		}
	}
}
namespace SMTSaveAPI.API.SavedValue
{
	public interface ISavedValue
	{
		object DefaultValue { get; }

		object Value { get; set; }

		Type ValueType { get; }

		bool Persistent { get; set; }
	}
	public class SavedValue<T> : ISavedValue
	{
		public object DefaultValue { get; }

		public T Value { get; set; }

		object ISavedValue.Value
		{
			get
			{
				return Value;
			}
			set
			{
				Value = (T)value;
			}
		}

		public Type ValueType => typeof(T);

		public bool Persistent { get; set; }

		public SavedValue(string key, T defaultValue = default(T), bool persistent = false)
		{
			DefaultValue = defaultValue;
			Value = defaultValue;
			Persistent = persistent;
			if (CustomSaveManager.SavedValues.ContainsKey(key))
			{
				throw new ArgumentException("A saved value with the same key already exists: " + key);
			}
			CustomSaveManager.SavedValues.Add(key, this);
		}
	}
}
namespace SMTSaveAPI.API.Managers
{
	public static class CustomSaveManager
	{
		public static readonly Dictionary<string, ISavedValue> SavedValues = new Dictionary<string, ISavedValue>();

		public static SavedValue<string> BaseSaveHash { get; } = new SavedValue<string>("SMTSaveAPI.BaseSaveHash");


		public static SavedValue<T> GetSavedValue<T>(string key)
		{
			if (!SavedValues.TryGetValue(key, out var value) || !(value is SavedValue<T> result))
			{
				return null;
			}
			return result;
		}

		public static T GetValue<T>(string key)
		{
			if (!SavedValues.TryGetValue(key, out var value) || !(value is SavedValue<T> savedValue))
			{
				return default(T);
			}
			return savedValue.Value;
		}

		internal static void SaveValues()
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			SaveEventHandler.OnSaving();
			Stopwatch stopwatch = Stopwatch.StartNew();
			BaseSaveHash.Value = GetBaseSaveMD5(SavePathManager.BaseSaveFilePath);
			Dictionary<string, JObject> dictionary = (File.Exists(SavePathManager.CustomSaveFilePath) ? JsonConvert.DeserializeObject<Dictionary<string, JObject>>(XORCipher(File.ReadAllText(SavePathManager.CustomSaveFilePath))) : new Dictionary<string, JObject>());
			foreach (KeyValuePair<string, ISavedValue> savedValue in SavedValues)
			{
				if (savedValue.Value.Value == null)
				{
					dictionary.Remove(savedValue.Key);
					continue;
				}
				dictionary[savedValue.Key] = new JObject
				{
					["persistent"] = JToken.op_Implicit(savedValue.Value.Persistent),
					["value"] = JToken.FromObject(savedValue.Value.Value)
				};
			}
			File.WriteAllText(SavePathManager.CustomSaveFilePath, XORCipher(JsonConvert.SerializeObject((object)dictionary)));
			ModEntry.Logger.LogInfo((object)$"Saved custom values in {stopwatch.Elapsed.TotalMilliseconds:0.00}ms");
		}

		internal static void LoadValues()
		{
			Stopwatch stopwatch = Stopwatch.StartNew();
			if (!File.Exists(SavePathManager.CustomSaveFilePath))
			{
				SaveEventHandler.OnLoaded();
				return;
			}
			Dictionary<string, JObject> dictionary = JsonConvert.DeserializeObject<Dictionary<string, JObject>>(XORCipher(File.ReadAllText(SavePathManager.CustomSaveFilePath)));
			if (dictionary.TryGetValue("SMTSaveAPI.BaseSaveHash", out var value))
			{
				JToken obj = value["value"];
				if (!(((obj != null) ? Extensions.Value<string>((IEnumerable<JToken>)obj) : null) != GetBaseSaveMD5(SavePathManager.BaseSaveFilePath)))
				{
					foreach (KeyValuePair<string, JObject> item in dictionary.ToList())
					{
						if (!SavedValues.TryGetValue(item.Key, out var value2))
						{
							if (!((JToken)item.Value).Value<bool>((object)"persistent"))
							{
								dictionary.Remove(item.Key);
							}
						}
						else
						{
							value2.Value = item.Value["value"].ToObject(value2.ValueType);
						}
					}
					File.WriteAllText(SavePathManager.CustomSaveFilePath, XORCipher(JsonConvert.SerializeObject((object)dictionary)));
					ModEntry.Logger.LogInfo((object)$"Loaded custom values in {stopwatch.Elapsed.TotalMilliseconds:0.00}ms");
					SaveEventHandler.OnLoaded();
					return;
				}
			}
			File.Delete(SavePathManager.CustomSaveFilePath);
			SaveEventHandler.OnLoaded();
		}

		private static string XORCipher(string data)
		{
			string text = "Ff2hiu0ofQ456ftoFM";
			char[] array = data.ToCharArray();
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = (char)(data[i] ^ text[i % text.Length]);
			}
			return new string(array);
		}

		private static string GetBaseSaveMD5(string filePath)
		{
			using MD5 mD = MD5.Create();
			return BitConverter.ToString(mD.ComputeHash(ES3.DecryptBytes(File.ReadAllBytes(filePath), "g#asojrtg@omos)^yq"))).Replace("-", "").ToLower();
		}
	}
	public static class SavePathManager
	{
		public static string BaseSaveFileName => FsmVariables.GlobalVariables.GetFsmString("CurrentFilename").Value;

		public static string BaseSaveFilePath => GetSavePath(BaseSaveFileName);

		public static string CustomSaveFilePath => ToCustomPath(BaseSaveFilePath);

		public static string GetSavePath(string fileName)
		{
			return Application.persistentDataPath + "/" + fileName;
		}

		public static string ToCustomPath(string basePath)
		{
			return basePath.Replace("StoreFile", "StoreModdedFile").Replace(".es3", ".smtsave");
		}

		public static string RemoveBackupSuffix(string path)
		{
			return Regex.Replace(path, "Day\\d+", "");
		}

		public static string ToBackupPath(string basePath, int day)
		{
			return ToBackupPath(basePath, day.ToString());
		}

		public static string ToBackupPath(string basePath, string day)
		{
			return basePath.Insert(basePath.Length - Path.GetExtension(basePath).Length, "Day" + day);
		}
	}
}
namespace SMTSaveAPI.API.Events
{
	public class Event
	{
		internal List<Action> Subscribed = new List<Action>();

		public static Event operator +(Event ev, Action onInvoked)
		{
			ev.Subscribe(onInvoked);
			return ev;
		}

		public void Subscribe(Action onInvoked)
		{
			Subscribed.Add(onInvoked);
		}

		internal void Invoke()
		{
			foreach (Action item in Subscribed)
			{
				item();
			}
		}
	}
	public static class SaveEventHandler
	{
		public static Event Saving { get; set; } = new Event();


		public static Event Loaded { get; set; } = new Event();


		internal static void OnSaving()
		{
			Saving.Invoke();
		}

		internal static void OnLoaded()
		{
			Loaded.Invoke();
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}