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