using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using LitJson;
using Microsoft.CodeAnalysis;
using OstraAutoloader.Courtesy;
using OstraAutoloader.Mods;
using OstraAutoloader.Patches;
using Tommy;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyCompany("Ostranauts.Autoloader")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.1.10.0")]
[assembly: AssemblyInformationalVersion("0.1.10+3a76e54111c157c66af6b3a3003f4fa95799b851")]
[assembly: AssemblyProduct("Ostranauts.Autoloader")]
[assembly: AssemblyTitle("Ostranauts.Autoloader")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.10.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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[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;
}
}
}
internal static class LCMPluginInfo
{
public const string PLUGIN_GUID = "Ostranauts.Autoloader";
public const string PLUGIN_NAME = "Ostranauts.Autoloader";
public const string PLUGIN_VERSION = "0.1.10";
}
namespace OstraAutoloader
{
[BepInPlugin("Ostranauts.Autoloader", "Ostranauts.Autoloader", "0.1.10")]
public class AutoloaderPlugin : BaseUnityPlugin
{
internal static AutoloaderPlugin Instance;
internal ManualLogSource Log;
internal ConfigEntry<bool> BackupNeeded;
internal ConfigEntry<bool> UninstallMode;
public AutoloaderPlugin()
{
if (Instance == null)
{
Instance = this;
}
Log = ((BaseUnityPlugin)this).Logger;
BackupNeeded = ((BaseUnityPlugin)this).Config.Bind<bool>("Backup", "BackupNeeded", true, "This is managed automatically by Ostra.Autoloader to keep track of if a backup of the original load_order.json file was made");
UninstallMode = ((BaseUnityPlugin)this).Config.Bind<bool>("Uninstall", "UninstallMode", false, "Set this to true and run the game once. Ostra.Autoloader will undo any changes it made, where possible");
}
private void Awake()
{
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
Log.LogInfo((object)"Plugin Ostranauts.Autoloader version 0.1.10 is loaded!");
Log.LogDebug((object)("Plugins Path: " + Application.dataPath));
Harmony val = new Harmony("Ostranauts.Autoloader");
try
{
val.PatchAll(typeof(DataHandler_Patches));
}
catch (Exception arg)
{
Log.LogFatal((object)$"Failed to run a patch. Autoloader is aborting\n{arg}");
}
}
}
}
namespace OstraAutoloader.Patches
{
[HarmonyPatch(typeof(DataHandler), "Init")]
public static class DataHandler_Patches
{
[HarmonyPrefix]
public static bool Init_Prefix()
{
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Expected O, but got Unknown
string text = string.Empty;
AutoloaderPlugin instance = AutoloaderPlugin.Instance;
if (File.Exists(Application.persistentDataPath + "/settings.json"))
{
Dictionary<string, JsonUserSettings> dictionary = new Dictionary<string, JsonUserSettings>();
DataHandler.JsonToData<JsonUserSettings>(Application.persistentDataPath + "/settings.json", dictionary);
if (dictionary.ContainsKey("UserSettings"))
{
text = dictionary["UserSettings"].strPathMods;
}
}
if (string.IsNullOrEmpty(text))
{
JsonUserSettings val = new JsonUserSettings();
val.Init();
text = val.strPathMods;
}
string directoryName = Path.GetDirectoryName(text);
if (Directory.Exists(text))
{
string text2 = "Error: The load path is a directory when it should be `loading_order.json` please use the file menu under option to select the correct file. Ostra.Autoloader cannot continue, shutting down.\n Path: " + text;
instance.Log.LogError((object)text2);
ConsoleToGUI.instance.Log(text2, string.Empty, (LogType)0);
return true;
}
instance.Log.LogDebug((object)("Using mods path: " + directoryName));
if (instance.UninstallMode.Value)
{
instance.Log.LogMessage((object)"Ostra.Autoloader is uninstalling, goodbye!");
SafeLoadOrderManager.RestoreLoadOrderIfPossible(text);
return true;
}
DirectoryInfo directoryInfo = new DirectoryInfo(Paths.PluginPath);
DirectoryInfo directoryInfo2 = new DirectoryInfo(directoryName);
instance.Log.LogInfo((object)("Searching " + directoryInfo.FullName));
ModListing.FindAllModsInDirectory(directoryInfo);
instance.Log.LogInfo((object)("Searching " + directoryInfo2.FullName));
ModListing.FindAllModsInDirectory(directoryInfo2);
ModListing.CreateLoadingOrder();
instance.Log.LogInfo((object)$"populating load_order.json automatically with {ModListing.sortedMods.Length} autoload mods");
SafeLoadOrderManager.BackupLoadOrderIfRequired(text);
WriteLoadingOrder(text);
return true;
}
internal static void WriteLoadingOrder(string filePath)
{
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Expected O, but got Unknown
//IL_0195: Unknown result type (might be due to invalid IL or missing references)
//IL_019a: Unknown result type (might be due to invalid IL or missing references)
//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
//IL_01ac: Expected O, but got Unknown
AutoloaderPlugin instance = AutoloaderPlugin.Instance;
FileInfo fileInfo = new FileInfo(filePath);
DirectoryInfo directoryInfo = new DirectoryInfo(Path.GetDirectoryName(filePath));
if (!directoryInfo.Exists)
{
instance.Log.LogWarning((object)("\nUnable to write load_order.json\nAutoloader will now shut down, your game will load without mods\n Reason: Mod folder does not exist\n Directory: " + directoryInfo.FullName + "\n"));
ConsoleToGUI.instance.Log("[Ostra.Autoloader] Unable to write mod_info.json, game will load without mods, see LogOutput.txt for more info.", "", (LogType)2);
return;
}
JsonModList val = new JsonModList();
val.strName = "Mod Loading Order";
List<string> list = new List<string>();
list.Add("core");
list.AddRange(ModListing.sortedMods.Select((AutoloadMod x) => x.BaseDir.FullName));
val.aLoadOrder = list.ToArray();
val.aIgnorePatterns = new string[1] { "StreamingAssets/data/names_full" };
JsonModList item = val;
StringBuilder stringBuilder = new StringBuilder("\nAutoloaded the following mods in this order:\n");
stringBuilder.Append(" ");
stringBuilder.AppendLine("core");
AutoloadMod[] sortedMods = ModListing.sortedMods;
foreach (AutoloadMod autoloadMod in sortedMods)
{
stringBuilder.Append(" ");
if (autoloadMod.FailedToLoad)
{
stringBuilder.Append("(FAILED) ");
}
stringBuilder.AppendLine(autoloadMod.Inf.strName);
}
instance.Log.LogInfo((object)stringBuilder);
ConsoleToGUI.instance.LogInfo($"[Ostra.Autoloader]\n{stringBuilder}");
using StreamWriter streamWriter = new StreamWriter(filePath);
JsonWriter val2 = new JsonWriter((TextWriter)streamWriter)
{
PrettyPrint = true,
IndentValue = 2
};
List<JsonModList> list2 = new List<JsonModList>(1) { item };
JsonMapper.ToJson((object)list2, val2);
}
}
}
namespace OstraAutoloader.Mods
{
public class AutoloadMetaInf
{
public readonly string[] Dependencies = new string[0];
public readonly string[] SoftDependencies = new string[0];
public readonly ModLoadingGroup LoadingGroup = ModLoadingGroup.WithVanilla;
public AutoloadMetaInf(string[] Dependencies, string[] SoftDependencies, ModLoadingGroup LoadingGroup)
{
this.Dependencies = Dependencies;
this.SoftDependencies = SoftDependencies;
this.LoadingGroup = LoadingGroup;
}
public static AutoloadMetaInf? FromFile(FileInfo file)
{
if (!file.Exists)
{
return null;
}
using StreamReader streamReader = File.OpenText(file.FullName);
TomlTable val = TOML.Parse((TextReader)streamReader);
AutoloaderPlugin instance = AutoloaderPlugin.Instance;
TomlString asString = ((TomlNode)val)["FileType"].AsString;
if (!((TomlNode)asString).HasValue || !string.Equals(asString.Value, "AUTOLOAD.META", StringComparison.InvariantCultureIgnoreCase))
{
instance.Log.LogWarning((object)$"Skipping a malformed Autoload meta file (no or bad FileType header): {((TomlNode)asString).HasValue}|{asString.Value}");
return null;
}
string value = ((TomlNode)val)["LoadGroup"].AsString.Value;
ModLoadingGroup loadingGroup;
switch (value.ToLowerInvariant())
{
case "withvanilla":
loadingGroup = ModLoadingGroup.WithVanilla;
break;
case "ffucore":
loadingGroup = ModLoadingGroup.FFUCore;
break;
case "afterffu":
loadingGroup = ModLoadingGroup.AfterFFU;
break;
default:
instance.Log.LogWarning((object)("Unable to parse loading group " + value));
return null;
}
TomlNode val2 = ((TomlNode)val)["dependencies"];
if (!val2.IsTable)
{
instance.Log.LogWarning((object)"Dependencies is malformed");
return null;
}
TomlNode val3 = ((TomlNode)val)["softDependencies"];
string[] softDependencies = new string[0];
if (val3.IsTable)
{
softDependencies = val3.Keys.ToArray();
}
return new AutoloadMetaInf(val2.Keys.ToArray(), softDependencies, loadingGroup);
}
}
public class AutoloadMod
{
public enum ResolutionState
{
Pending,
Resolved,
Failed
}
public readonly ModInfo Inf;
public readonly AutoloadMetaInf MetaInf;
public readonly DirectoryInfo BaseDir;
public AutoloadMod[] Dependencies = new AutoloadMod[0];
private ResolutionState _state = ResolutionState.Pending;
private bool _visited = false;
public bool DependenciesResolved => _state == ResolutionState.Resolved;
public bool FailedToLoad => _state == ResolutionState.Failed;
public AutoloadMod(ModInfo info, AutoloadMetaInf meta, DirectoryInfo baseDir)
{
Inf = info;
MetaInf = meta;
BaseDir = baseDir;
}
public bool ResolveDependencies()
{
AutoloaderPlugin plugin = AutoloaderPlugin.Instance;
if (_visited)
{
plugin.Log.LogWarning((object)("Recursive dependency detected in " + Inf.strName));
return false;
}
if (DependenciesResolved)
{
return true;
}
if (FailedToLoad)
{
return false;
}
List<AutoloadMod> temp = new List<AutoloadMod>();
_visited = true;
bool flag = false;
string[] dependencies = MetaInf.Dependencies;
foreach (string name2 in dependencies)
{
if (!TryLoadMod(name2))
{
flag = true;
break;
}
}
if (!flag)
{
string[] softDependencies = MetaInf.SoftDependencies;
foreach (string text in softDependencies)
{
if (!TryLoadMod(text, hardDep: false))
{
plugin.Log.LogDebug((object)("Failed to resolve a soft dependency " + text + " for " + Inf.strName));
}
}
}
Dependencies = temp.ToArray();
_state = ((!flag) ? ResolutionState.Resolved : ResolutionState.Failed);
_visited = false;
return !flag;
bool TryLoadMod(string name, bool hardDep = true)
{
string text2 = (hardDep ? "Dependency" : "Soft Dependency");
if (!ModListing.allModsByIdentifier.TryGetValue(name, out AutoloadMod value))
{
if (hardDep)
{
plugin.Log.LogWarning((object)("Unable to resolve dependency " + name + " for mod " + Inf.strName));
}
return false;
}
if (value.DependenciesResolved || (!value.FailedToLoad && value.ResolveDependencies()))
{
if (MetaInf.LoadingGroup >= value.MetaInf.LoadingGroup)
{
temp.Add(value);
return true;
}
plugin.Log.LogWarning((object)(text2 + " " + name + " is in a later loading group than mod " + Inf.strName));
return false;
}
if (hardDep)
{
plugin.Log.LogWarning((object)("Unable to resolve Dependency " + name + " for mod " + Inf.strName));
}
return false;
}
}
public static AutoloadMod? FromDirectory(DirectoryInfo dir)
{
AutoloaderPlugin instance = AutoloaderPlugin.Instance;
instance.Log.LogDebug((object)("Attempting to create AutoloadMod from " + dir.Name));
if (!dir.Exists)
{
instance.Log.LogWarning((object)("Unable to create an Autoload from " + dir.FullName));
return null;
}
string fileName = Path.Combine(dir.FullName, "mod_info.json");
string fileName2 = Path.Combine(dir.FullName, "Autoload.Meta.toml");
FileInfo fileInfo = new FileInfo(fileName);
FileInfo fileInfo2 = new FileInfo(fileName2);
if (!fileInfo.Exists)
{
instance.Log.LogWarning((object)(dir.FullName + " does not contain a mod_info.json"));
return null;
}
if (!fileInfo2.Exists)
{
instance.Log.LogWarning((object)(dir.FullName + " does not contain an Autoload.Meta.toml"));
return null;
}
try
{
Dictionary<string, JsonModInfo> dictionary = new Dictionary<string, JsonModInfo>();
DataHandler.JsonToData<JsonModInfo>(fileInfo.FullName, dictionary);
if (dictionary.Count >= 1)
{
JsonModInfo val = dictionary.Values.FirstOrDefault();
if (val != null)
{
ModInfo info = new ModInfo(val);
AutoloadMetaInf autoloadMetaInf = AutoloadMetaInf.FromFile(fileInfo2);
if (autoloadMetaInf == null)
{
instance.Log.LogWarning((object)(fileInfo2.FullName + " cannot be deserialized into a mod meta"));
return null;
}
return new AutoloadMod(info, autoloadMetaInf, dir);
}
}
instance.Log.LogWarning((object)(fileInfo.FullName + " cannot be deserialized into a mod info"));
return null;
}
catch (Exception arg)
{
instance.Log.LogWarning((object)$"Failed to create Autoload mod\n{arg}");
return null;
}
}
}
public class ModInfo
{
public readonly string strName;
public readonly string strAuthor;
public readonly string strModURL;
public readonly string strGameVersion;
public readonly string strModVersion;
public readonly string strNotes;
public ModInfo(string strName, string strAuthor, string strModURL, string strGameVersion, string strModVersion, string strNotes)
{
this.strName = strName;
this.strAuthor = strAuthor;
this.strModURL = strModURL;
this.strGameVersion = strGameVersion;
this.strModVersion = strModVersion;
this.strNotes = strNotes;
}
public ModInfo(JsonModInfo info)
: this(info.strName, info.strAuthor, info.strModURL, info.strGameVersion, info.strModVersion, info.strNotes)
{
}
}
public static class ModListing
{
internal static readonly Dictionary<string, AutoloadMod> allModsByIdentifier = new Dictionary<string, AutoloadMod>();
internal static AutoloadMod[] sortedMods = new AutoloadMod[0];
internal static void FindAllModsInDirectory(DirectoryInfo baseDir)
{
AutoloaderPlugin instance = AutoloaderPlugin.Instance;
if (!baseDir.Exists)
{
instance.Log.LogInfo((object)("Skipping a directory in FindAllModsInDirectory(DirectoryInfo baseDir)\n Reason: Directory does not exist\n Directory: " + baseDir.FullName + "\n\nPlease check that your configured mod folder exists, mods from other sources will still load."));
return;
}
DirectoryInfo[] directories = baseDir.GetDirectories();
foreach (DirectoryInfo baseDir2 in directories)
{
FindAllModsInDirectory(baseDir2);
}
FileInfo[] files = baseDir.GetFiles("Autoload.Meta.toml");
foreach (FileInfo fileInfo in files)
{
if (!(fileInfo.Name == "Autoload.Meta.toml"))
{
continue;
}
instance.Log.LogDebug((object)("Found an autoload in " + baseDir.Name));
try
{
AutoloadMod autoloadMod = AutoloadMod.FromDirectory(baseDir);
if (autoloadMod != null)
{
if (allModsByIdentifier.ContainsKey(autoloadMod.Inf.strName))
{
instance.Log.LogWarning((object)("Attempting to load the same mod twice! " + autoloadMod.Inf.strName));
continue;
}
instance.Log.LogInfo((object)("Registered " + autoloadMod.Inf.strName + "@" + autoloadMod.Inf.strModVersion));
allModsByIdentifier.Add(autoloadMod.Inf.strName, autoloadMod);
}
}
catch (Exception arg)
{
instance.Log.LogWarning((object)$"Encountered malformed Autoload:\n{arg}");
}
}
}
internal static void CreateLoadingOrder()
{
List<AutoloadMod> _sortedTemp = new List<AutoloadMod>();
HashSet<AutoloadMod> _added = new HashSet<AutoloadMod>();
AutoloadMod[] items2 = allModsByIdentifier.Values.ToArray();
ResolveLoadingGroup(items2, ModLoadingGroup.WithVanilla);
ResolveLoadingGroup(items2, ModLoadingGroup.FFUCore);
ResolveLoadingGroup(items2, ModLoadingGroup.AfterFFU);
sortedMods = _sortedTemp.ToArray();
void ResolveAndAdd(AutoloadMod item)
{
if (!_added.Contains(item) && item.ResolveDependencies())
{
AutoloadMod[] dependencies = item.Dependencies;
foreach (AutoloadMod autoloadMod in dependencies)
{
if (autoloadMod.ResolveDependencies())
{
ResolveAndAdd(autoloadMod);
}
}
_sortedTemp.Add(item);
_added.Add(item);
}
}
void ResolveLoadingGroup(AutoloadMod[] items, ModLoadingGroup group)
{
foreach (AutoloadMod autoloadMod2 in items)
{
if (autoloadMod2.MetaInf.LoadingGroup == group && autoloadMod2.ResolveDependencies())
{
ResolveAndAdd(autoloadMod2);
}
}
}
}
}
public enum ModLoadingGroup
{
WithVanilla,
FFUCore,
AfterFFU
}
}
namespace OstraAutoloader.Courtesy
{
public static class SafeLoadOrderManager
{
public static void BackupLoadOrderIfRequired(string loadLoc)
{
AutoloaderPlugin instance = AutoloaderPlugin.Instance;
FileInfo fileInfo = new FileInfo(loadLoc);
FileInfo fileInfo2 = new FileInfo(loadLoc + ".old");
if (!instance.BackupNeeded.Value)
{
instance.Log.LogDebug((object)"Skipping backup, we've already made one");
return;
}
instance.BackupNeeded.Value = false;
((BaseUnityPlugin)instance).Config.Save();
if (!fileInfo.Exists)
{
instance.Log.LogMessage((object)"Skipping backup, there is no load_order.json file");
return;
}
if (fileInfo2.Exists)
{
instance.Log.LogMessage((object)"Skipping backup, already exists");
return;
}
try
{
fileInfo.CopyTo(fileInfo2.FullName);
instance.Log.LogMessage((object)"Copied original load_order.json to load_order.json.old");
}
catch (Exception arg)
{
instance.Log.LogError((object)$"Unable to backup load_order.json!\n{arg}");
}
}
public static void RestoreLoadOrderIfPossible(string loadLoc)
{
AutoloaderPlugin instance = AutoloaderPlugin.Instance;
FileInfo fileInfo = new FileInfo(loadLoc);
FileInfo fileInfo2 = new FileInfo(loadLoc + ".old");
if (!fileInfo2.Exists)
{
instance.Log.LogMessage((object)"Not restoring load_order.json backup, no file to restore");
return;
}
if (fileInfo.Exists)
{
instance.Log.LogMessage((object)"Deleting auto-generated load_order.json");
fileInfo.Delete();
}
try
{
fileInfo2.CopyTo(fileInfo.FullName);
instance.Log.LogMessage((object)"Restored backup load_order.json.old to load_order.json");
fileInfo2.Delete();
}
catch (Exception arg)
{
instance.Log.LogError((object)$"Unable to fully restore load_order.json!\n{arg}");
}
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
}