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