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)
{
}
}
}