Decompiled source of GameSaves v0.5.0

plugins/GameSaves.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.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyCompany("GameSaves")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Uses an external file instead of the registry to save your game.")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0")]
[assembly: AssemblyProduct("GameSaves")]
[assembly: AssemblyTitle("GameSaves")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.5.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 GameSaves
{
	public static class Extensions
	{
		public static void WriteString(this Stream stm, string str)
		{
			byte[] bytes = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true).GetBytes(str);
			stm.Write(bytes, 0, bytes.Length);
		}

		public static T GetLast<T>(this List<T> lst)
		{
			return lst[lst.Count - 1];
		}
	}
	[BepInPlugin("GameSaves", "GameSaves", "0.5.0")]
	public class GSPlugin : BaseUnityPlugin
	{
		internal static ManualLogSource LogInstance;

		public const string SAVE_FILE_NAME = "savefile.yaml";

		private void Awake()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			LogInstance = ((BaseUnityPlugin)this).Logger;
			new Harmony("GameSaves").PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin GameSaves is loaded!");
		}
	}
	[HarmonyPatch(typeof(Faction), "PopulateWithPlayerPrefs")]
	internal class LoadPatcher
	{
		private static bool Prefix(string prefsKey, Faction __instance)
		{
			try
			{
				Debug.Log((object)"<color=orange>::: BEGIN FILE LOAD :::</color>");
				Savefile.ResultUnion resultUnion = new Savefile(overwrite: false).ReadFile();
				__instance.ResetDynamicData();
				__instance.currentSortieSeed = resultUnion.table["currentsortieseed"].intResult;
				int intResult = resultUnion.table["points"].intResult;
				__instance.AddPoints(intResult);
				intResult = resultUnion.table["upgradetokens"].intResult;
				__instance.upgradeTokens = ((intResult < 0) ? __instance.upgradeTokens : intResult);
				List<UnitModifier> allAvailableModifiers = CampaignGenerationSettings.Instance.allAvailableModifiers;
				foreach (Savefile.ResultUnion listItem2 in resultUnion.table["unlockedmodifiers"].list)
				{
					UnitModifier val = allAvailableModifiers.Find((UnitModifier m) => ((Object)m).name.ToLower() == listItem2.strResult);
					if ((Object)(object)val != (Object)null)
					{
						__instance.UnlockModifier(val);
					}
					else
					{
						GSPlugin.LogInstance.LogWarning((object)("No modifier found named " + listItem2.strResult));
					}
				}
				if (__instance.unlockedModifiers.Count >= allAvailableModifiers.Count)
				{
					SteamStatsAndAchievements.UnlockAchievement((Achievement)12);
				}
				List<WeaponBlueprint> allAvailableWeapons = CampaignGenerationSettings.Instance.allAvailableWeapons;
				foreach (Savefile.ResultUnion listItem in resultUnion.table["unlockedweapons"].list)
				{
					WeaponBlueprint val2 = allAvailableWeapons.Find((WeaponBlueprint w) => ((Object)w).name.ToLower() == listItem.strResult);
					if ((Object)(object)val2 != (Object)null)
					{
						__instance.UnlockWeapon(val2);
					}
					else
					{
						GSPlugin.LogInstance.LogWarning((object)("No weapon found named " + listItem.strResult));
					}
				}
				LoadUnitConfig(resultUnion, "fighter", __instance);
				LoadUnitConfig(resultUnion, "destroyer", __instance);
				LoadUnitConfig(resultUnion, "frigate", __instance);
				LoadUnitConfig(resultUnion, "battleship", __instance);
				if (resultUnion.table.ContainsKey("campaignflags"))
				{
					__instance.campaignFlags = new List<string>();
					foreach (Savefile.ResultUnion item in resultUnion.table["campaignflags"].list)
					{
						__instance.AddCampaignFlag(item.strResult);
					}
				}
				__instance.currentFleetBlueprintNames = new List<string>();
				foreach (Savefile.ResultUnion item2 in resultUnion.table["currentfleet"].list)
				{
					__instance.currentFleetBlueprintNames.Add(item2.strResult);
				}
				__instance.SetCurrentfleetFromBlueprints();
				Debug.Log((object)"<color=orange>::: END FILE LOAD :::</color>");
			}
			catch (IOException ex)
			{
				GSPlugin.LogInstance.LogWarning((object)("Could not load savefile (using registry): " + ex.Message + " (this can be safely ignored if this is your first time using the mod)"));
				return true;
			}
			catch (Exception ex2)
			{
				GSPlugin.LogInstance.LogError((object)("Could not load savefile (using registry): " + ex2.Message));
				return true;
			}
			return false;
		}

		private static void LoadUnitConfig(Savefile.ResultUnion result, string tag, Faction instance)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			UnitConfig configByID = instance.GetConfigByID(tag);
			if (configByID == null)
			{
				return;
			}
			Dictionary<string, Savefile.ResultUnion> table = result.table["configs"].table[tag].table;
			for (int i = 0; i < table["assignedweapons"].list.Count; i++)
			{
				WeaponAssignment val = new WeaponAssignment();
				val.slot = i;
				string wpnString = table["assignedweapons"].list[i].strResult;
				if (wpnString != "NONE")
				{
					WeaponBlueprint val2 = CampaignGenerationSettings.Instance.allAvailableWeapons.Find((WeaponBlueprint w) => ((Object)w).name.ToLower() == wpnString);
					if ((Object)(object)val2 != (Object)null)
					{
						val.weaponBlueprint = val2;
					}
					else
					{
						GSPlugin.LogInstance.LogWarning((object)("No assignable weapon found named " + wpnString));
					}
				}
				configByID.weaponAssignments[i] = val;
			}
			configByID.unitModifiers = new List<UnitModifier>();
			foreach (Savefile.ResultUnion listItem in table["assignedmodifiers"].list)
			{
				if (listItem.strResult == "NONE")
				{
					continue;
				}
				UnitModifier val3 = CampaignGenerationSettings.Instance.allAvailableModifiers.Find((UnitModifier m) => ((Object)m).name.ToLower() == listItem.strResult);
				if ((Object)(object)val3 != (Object)null)
				{
					if (!configByID.unitModifiers.Contains(val3))
					{
						configByID.unitModifiers.Add(val3);
					}
					else
					{
						GSPlugin.LogInstance.LogWarning((object)("No assignable modifier found named " + listItem.strResult));
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(Faction), "LoadVeteranUnitDataIntoPlayerFleet")]
	internal class LoadVeteransPatcher
	{
		private static bool Prefix(PlayerFleet pf)
		{
			if ((Object)(object)pf == (Object)null)
			{
				return true;
			}
			try
			{
				Savefile.ResultUnion result = new Savefile(overwrite: false).ReadFile();
				List<EmpireFlight> blankEmpireFlights = Globals.Instance.blankEmpireFlights;
				for (int i = 0; i < blankEmpireFlights.Count && i < pf.activeFlights.Count; i++)
				{
					LoadSpecificUnitData(result, ((Object)blankEmpireFlights[i]).name, pf.activeFlights[i]);
				}
			}
			catch (IOException ex)
			{
				GSPlugin.LogInstance.LogWarning((object)("Could not load savefile in veterans step (using registry): " + ex.Message + " (this can be safely ignored if a similar warning appeared before)"));
				return true;
			}
			catch (Exception ex2)
			{
				GSPlugin.LogInstance.LogError((object)("Could not load savefile in veterans step (using registry): " + ex2.Message));
				return true;
			}
			return false;
		}

		private static void LoadSpecificUnitData(Savefile.ResultUnion result, string unitTypeKey, Flight flight)
		{
			if ((Object)(object)flight == (Object)null)
			{
				GSPlugin.LogInstance.LogWarning((object)("No flight was given for " + unitTypeKey + " to load veterancy data into"));
				return;
			}
			List<Savefile.ResultUnion> list = result.table["veterans"].table[unitTypeKey].list;
			SpawnPoint[] componentsInChildren = ((Component)((Component)flight).transform).GetComponentsInChildren<SpawnPoint>();
			for (int i = 0; i < componentsInChildren.Length && i < list.Count; i++)
			{
				Dictionary<string, Savefile.ResultUnion> table = list[i].table;
				SpawnPoint obj = componentsInChildren[i];
				obj.callsign = table["flightname"].strResult;
				obj.experience = table["flightxp"].intResult;
			}
		}
	}
	public class Savefile
	{
		[StructLayout(LayoutKind.Explicit)]
		public class ResultUnion
		{
			[FieldOffset(0)]
			public int intResult;

			[FieldOffset(0)]
			public string strResult;

			[FieldOffset(0)]
			public List<ResultUnion> list;

			[FieldOffset(0)]
			public Dictionary<string, ResultUnion> table;
		}

		private enum BlockType
		{
			Unset,
			Table,
			List
		}

		private FileStream fs;

		private int indentation;

		public Savefile(bool overwrite)
		{
			string path = Application.persistentDataPath + "/savefile.yaml";
			if (File.Exists(path) && overwrite)
			{
				File.Delete(path);
			}
			else if (!File.Exists(path) && !overwrite)
			{
				throw new IOException("Savefile does not exist");
			}
			fs = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite);
		}

		~Savefile()
		{
			fs.Close();
		}

		private string GetIndent()
		{
			return new string(' ', indentation * 2);
		}

		public void WriteEntry(string key, object val)
		{
			fs.WriteString(GetIndent() + key + ": " + val.ToString() + "\n");
		}

		public void BeginBlock(string key)
		{
			fs.WriteString(GetIndent() + key + ":\n");
			indentation++;
		}

		public void BeginListEntryBlock(string key, object val)
		{
			WriteListEntry(key + ": " + val.ToString());
			indentation++;
		}

		public void EndBlock()
		{
			if (indentation == 0)
			{
				throw new Exception("No block existed when EndBlock was called");
			}
			indentation--;
		}

		public void WriteListEntry(object val)
		{
			fs.WriteString(GetIndent() + "- " + val.ToString() + "\n");
		}

		public void WriteStringList(string key, List<string> list)
		{
			BeginBlock(key);
			foreach (string item in list)
			{
				WriteListEntry(item);
			}
			EndBlock();
		}

		private string ReadLine()
		{
			string text = "";
			bool flag = true;
			indentation = 0;
			for (int i = 0; i < int.MaxValue; i++)
			{
				int num = fs.ReadByte();
				if (num == -1)
				{
					break;
				}
				char c = (char)num;
				if (num == -1)
				{
					break;
				}
				if (c == ' ' && flag)
				{
					indentation++;
					continue;
				}
				if (c == '\n')
				{
					break;
				}
				text += c;
				flag = false;
			}
			indentation >>= 1;
			return text;
		}

		public ResultUnion ReadFile()
		{
			ResultUnion result = new ResultUnion();
			result.table = new Dictionary<string, ResultUnion>();
			List<KeyValuePair<string, BlockType>> blocks = new List<KeyValuePair<string, BlockType>>
			{
				new KeyValuePair<string, BlockType>("", BlockType.Table)
			};
			ResultUnion resultUnion = result;
			for (int i = 0; i < int.MaxValue; i++)
			{
				string text = ReadLine();
				if (text.Length == 0)
				{
					break;
				}
				if (blocks.Count > indentation + 1)
				{
					blocks.RemoveRange(indentation + 1, blocks.Count - indentation - 1);
					resultUnion = GetCurrentData();
				}
				KeyValuePair<string, BlockType> last = blocks.GetLast();
				if (text.Contains("- "))
				{
					if (last.Value != 0 && last.Value != BlockType.List)
					{
						throw new Exception($"List in a non-list type is not allowed: {text} (line {i + 1})");
					}
					if (last.Value == BlockType.Unset)
					{
						blocks[blocks.Count - 1] = new KeyValuePair<string, BlockType>(last.Key, BlockType.List);
						last = blocks.GetLast();
						resultUnion.list = new List<ResultUnion>();
					}
					text = text.Remove(0, 2);
				}
				if (text.Contains(":"))
				{
					if (last.Value == BlockType.Unset)
					{
						blocks[blocks.Count - 1] = new KeyValuePair<string, BlockType>(last.Key, BlockType.Table);
						last = blocks.GetLast();
						resultUnion.table = new Dictionary<string, ResultUnion>();
					}
					else if (last.Value == BlockType.List)
					{
						blocks.Add(new KeyValuePair<string, BlockType>("", BlockType.Table));
						last = blocks.GetLast();
						ResultUnion resultUnion2 = new ResultUnion();
						resultUnion2.table = new Dictionary<string, ResultUnion>();
						resultUnion.list.Add(resultUnion2);
						resultUnion = resultUnion2;
					}
					string[] array = text.Split(new char[1] { ':' });
					ResultUnion resultUnion3 = new ResultUnion();
					if (array[1].Length > 0)
					{
						array[1] = StringExt.RemoveChars(array[1].Substring(1), new char[1] { '"' });
						if (int.TryParse(array[1], out var result2))
						{
							resultUnion3.intResult = result2;
						}
						else
						{
							resultUnion3.strResult = array[1];
						}
					}
					resultUnion.table.Add(array[0], resultUnion3);
					if (array[1].Length == 0)
					{
						blocks.Add(new KeyValuePair<string, BlockType>(array[0], BlockType.Unset));
						last = blocks.GetLast();
						resultUnion = resultUnion3;
					}
				}
				else
				{
					if (last.Value != BlockType.List)
					{
						throw new Exception($"A line did not satisfy any condition: {text} (line {i + 1})");
					}
					ResultUnion resultUnion4 = new ResultUnion();
					resultUnion4.strResult = StringExt.RemoveChars(text, new char[1] { '"' });
					resultUnion.list.Add(resultUnion4);
				}
			}
			return result;
			ResultUnion GetCurrentData()
			{
				ResultUnion resultUnion5 = result;
				for (int j = 1; j < blocks.Count; j++)
				{
					if (blocks[j - 1].Value == BlockType.Table)
					{
						resultUnion5 = resultUnion5.table[blocks[j].Key];
					}
					else if (blocks[j - 1].Value == BlockType.List)
					{
						resultUnion5 = resultUnion5.list.GetLast();
					}
				}
				return resultUnion5;
			}
		}
	}
	[HarmonyPatch(typeof(Faction), "WriteToPlayerPrefs")]
	internal class SavePatcher
	{
		private static bool Prefix(string prefsKey, bool saveVeteranUnitData, Faction __instance)
		{
			try
			{
				Savefile savefile = new Savefile(overwrite: true);
				Debug.Log((object)"<color=orange>::: BEGIN FILE SAVE :::</color>");
				Messenger<Faction, Savefile>.Broadcast("OnFactionFileSaving", __instance, savefile);
				savefile.WriteEntry("currentsortieseed", __instance.currentSortieSeed);
				savefile.WriteEntry("points", __instance.points);
				savefile.WriteEntry("upgradetokens", __instance.upgradeTokens);
				savefile.BeginBlock("unlockedmodifiers");
				foreach (UnitModifier unlockedModifier in __instance.unlockedModifiers)
				{
					savefile.WriteListEntry(((Object)unlockedModifier).name.ToLower());
				}
				savefile.EndBlock();
				savefile.BeginBlock("unlockedweapons");
				foreach (WeaponBlueprint unlockedWeapon in __instance.unlockedWeapons)
				{
					savefile.WriteListEntry(((Object)unlockedWeapon).name.ToLower());
				}
				savefile.EndBlock();
				savefile.BeginBlock("configs");
				WriteUnitConfig(savefile, "fighter", __instance);
				WriteUnitConfig(savefile, "destroyer", __instance);
				WriteUnitConfig(savefile, "frigate", __instance);
				WriteUnitConfig(savefile, "battleship", __instance);
				savefile.EndBlock();
				if (saveVeteranUnitData)
				{
					WriteVeterans(savefile, PlayerFleet.Instance);
				}
				if (__instance.campaignFlags.Count > 0)
				{
					savefile.WriteStringList("campaignflags", __instance.campaignFlags);
				}
				savefile.WriteStringList("currentfleet", __instance.currentFleetBlueprintNames);
				Debug.Log((object)"<color=orange>::: END FILE SAVE :::</color>");
			}
			catch (IOException ex)
			{
				GSPlugin.LogInstance.LogError((object)("Saving to registry instead; save file could not be opened: " + ex.Message));
				return true;
			}
			return false;
		}

		private static void WriteUnitConfig(Savefile savefile, string tag, Faction instance)
		{
			UnitConfig configByID = instance.GetConfigByID(tag);
			if (configByID == null)
			{
				return;
			}
			savefile.BeginBlock(tag);
			if (configByID.weaponAssignments.Count > 0)
			{
				savefile.BeginBlock("assignedweapons");
				foreach (WeaponAssignment weaponAssignment in configByID.weaponAssignments)
				{
					WeaponBlueprint val = weaponAssignment.weaponBlueprint ?? null;
					if ((Object)(object)val == (Object)null)
					{
						savefile.WriteListEntry("NONE");
					}
					else
					{
						savefile.WriteListEntry(((Object)val).name.ToLower());
					}
				}
				savefile.EndBlock();
			}
			if (configByID.unitModifiers.Count > 0)
			{
				savefile.BeginBlock("assignedmodifiers");
				foreach (UnitModifier unitModifier in configByID.unitModifiers)
				{
					if ((Object)(object)unitModifier != (Object)null)
					{
						savefile.WriteListEntry(((Object)unitModifier).name.ToLower());
					}
				}
				savefile.EndBlock();
			}
			savefile.EndBlock();
		}

		private static void WriteVeterans(Savefile savefile, PlayerFleet fleet)
		{
			if (!((Object)(object)fleet == (Object)null))
			{
				savefile.BeginBlock("veterans");
				ref List<EmpireFlight> blankEmpireFlights = ref Globals.Instance.blankEmpireFlights;
				for (int i = 0; i < blankEmpireFlights.Count && i < fleet.activeFlights.Count; i++)
				{
					Flight flight = fleet.activeFlights[i];
					WriteFlightMember(savefile, ((Object)blankEmpireFlights[i].unitBlueprint).name, flight);
				}
				savefile.EndBlock();
			}
		}

		private static void WriteFlightMember(Savefile savefile, string unitTypeKey, Flight flight)
		{
			if (((Component)flight).GetComponentsInChildren<SpawnPoint>().Length == 0)
			{
				return;
			}
			savefile.BeginBlock(unitTypeKey);
			SpawnPoint[] componentsInChildren = ((Component)flight).GetComponentsInChildren<SpawnPoint>();
			foreach (SpawnPoint val in componentsInChildren)
			{
				if (!(((UnitContainer)val).GetStrength() <= 0f))
				{
					string callsign = val.GetCallsign();
					int experience = val.experience;
					if ((Object)(object)val.unit != (Object)null)
					{
						callsign = val.unit.callsign;
						experience = val.unit.experience;
					}
					savefile.BeginListEntryBlock("flightname", "\"" + callsign + "\"");
					savefile.WriteEntry("flightxp", experience);
					savefile.EndBlock();
				}
			}
			savefile.EndBlock();
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "GameSaves";

		public const string PLUGIN_NAME = "GameSaves";

		public const string PLUGIN_VERSION = "0.5.0";
	}
}