Decompiled source of PDXModsBridge v0.0.1

PDXModsBridge.dll

Decompiled 9 months ago
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using Colossal.Logging;
using Game;
using Game.Modding;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Mono.Collections.Generic;
using MonoMod.Utils;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PDXModsBridge")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+6db79e57f3b1cc3a953d26b72f879adbda474500")]
[assembly: AssemblyProduct("PDXModsBridge")]
[assembly: AssemblyTitle("PDXModsBridge")]
[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 PDXModsBridge
{
	internal static class ModLoader
	{
		private static char _S = Path.DirectorySeparatorChar;

		private static string GAME_PATH = Path.GetDirectoryName(Application.dataPath);

		private static string BEPINEX_PATH = Path.Combine(GAME_PATH, $"BepInEx{_S}plugins");

		private static string THUNDERSTORE_PATH = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), $"AppData{_S}Roaming{_S}Thunderstore Mod Manager{_S}DataFolder{_S}CitiesSkylines2{_S}profiles");

		private static string RMODMAN_PATH = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), $"AppData{_S}Roaming{_S}r2modmanPlus-local{_S}CitiesSkylines2{_S}profiles");

		private static ILog _logger = LogManager.GetLogger("Cities2Modding");

		private static Dictionary<string, List<IMod>> _loadedMods = new Dictionary<string, List<IMod>>();

		public static void ScanDirectory()
		{
			try
			{
				if (Directory.Exists(BEPINEX_PATH))
				{
					_logger.Info((object)"[PDXModsBridge] Scanning BepInEx folder...");
					ProcessSource(BEPINEX_PATH);
				}
				string activeThunderstoreProfile = GetActiveThunderstoreProfile();
				if (!string.IsNullOrEmpty(activeThunderstoreProfile))
				{
					_logger.Info((object)("[PDXModsBridge] Scanning Thunderstore folder '" + activeThunderstoreProfile + "'..."));
					ProcessSource(activeThunderstoreProfile);
				}
				string activeRModManProfile = GetActiveRModManProfile();
				if (!string.IsNullOrEmpty(activeRModManProfile))
				{
					_logger.Info((object)("[PDXModsBridge] Scanning rModMan folder '" + activeRModManProfile + "'..."));
					ProcessSource(activeRModManProfile);
				}
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private static void ProcessSource(string sourceDirectory)
		{
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Expected O, but got Unknown
			string[] files = Directory.GetFiles(sourceDirectory, "*.dll", SearchOption.AllDirectories);
			string[] array = files;
			foreach (string text in array)
			{
				try
				{
					Dictionary<string, List<IMod>> loadedMods = _loadedMods;
					if (loadedMods != null && loadedMods.ContainsKey(text))
					{
						continue;
					}
					AssemblyDefinition val = AssemblyDefinition.ReadAssembly(text, TypeLoader.ReaderParameters);
					List<IMod> list = new List<IMod>();
					List<Type> list2 = (from t in ((IEnumerable<TypeDefinition>)val.MainModule.Types).Where(delegate(TypeDefinition t)
						{
							int result;
							if (t != null && t.HasInterfaces)
							{
								Collection<InterfaceImplementation> interfaces = t.Interfaces;
								result = ((interfaces != null && ((IEnumerable<InterfaceImplementation>)interfaces).Count(delegate(InterfaceImplementation i)
								{
									TypeReference interfaceType = i.InterfaceType;
									return ((interfaceType != null) ? ((MemberReference)interfaceType).FullName : null) == typeof(IMod).FullName;
								}) > 0) ? 1 : 0);
							}
							else
							{
								result = 0;
							}
							return (byte)result != 0;
						})
						select ReflectionHelper.ResolveReflection((TypeReference)(object)t)).Distinct().ToList();
					if (list2 != null && list2.Count > 0)
					{
						foreach (Type item in list2)
						{
							if (item == null)
							{
								continue;
							}
							DescriptionAttribute customAttribute = item.GetCustomAttribute<DescriptionAttribute>();
							if (customAttribute != null && !string.IsNullOrEmpty(customAttribute.Description) && !(customAttribute.Description.ToLowerInvariant() != "bridge"))
							{
								IMod val2 = (IMod)Activator.CreateInstance(item);
								if (val2 != null)
								{
									list.Add(val2);
									_logger.Info((object)("[PDXModsBridge] Initialised IMod from third-party mod sources: '" + item.FullName + "'."));
									Debug.Log((object)("[PDXModsBridge] Initialised IMod from third-party mod sources: '" + item.FullName + "'."));
								}
							}
						}
						if (list.Count == 0)
						{
							_logger.Debug((object)("[PDXModsBridge] No mods to load in '" + text + "'."));
						}
					}
					_loadedMods[text] = list;
					val.Dispose();
				}
				catch (BadImageFormatException ex)
				{
					_logger.Debug((object)("Invalid .NET assembly, skipping " + text + ": " + ex.Message));
				}
				catch (Exception ex2)
				{
					_logger.Error((object)ex2.ToString());
				}
			}
		}

		private static string GetActiveProfile(string path)
		{
			if (!Directory.Exists(path))
			{
				return null;
			}
			DateTime dateTime = DateTime.MinValue;
			string result = string.Empty;
			string[] directories = Directory.GetDirectories(path);
			foreach (string path2 in directories)
			{
				string text = Path.Combine(path2, $"BepInEx{_S}plugins");
				if (Directory.Exists(text))
				{
					DateTime mostRecentModifiedDate = GetMostRecentModifiedDate(text);
					if (mostRecentModifiedDate > dateTime)
					{
						dateTime = mostRecentModifiedDate;
						result = text;
					}
				}
			}
			return result;
		}

		public static DateTime GetMostRecentModifiedDate(string directory)
		{
			return (from file in Directory.GetFiles(directory, "*", SearchOption.AllDirectories)
				select new FileInfo(file).LastWriteTime into date
				orderby date descending
				select date).FirstOrDefault();
		}

		private static string GetActiveThunderstoreProfile()
		{
			return GetActiveProfile(THUNDERSTORE_PATH);
		}

		private static string GetActiveRModManProfile()
		{
			if (Directory.Exists(RMODMAN_PATH))
			{
				return GetActiveProfile(RMODMAN_PATH);
			}
			string environmentVariable = Environment.GetEnvironmentVariable("DOORSTOP_INVOKE_DLL_PATH");
			string directoryName = Path.GetDirectoryName(environmentVariable);
			string fullPath = Path.GetFullPath(Path.Combine(directoryName, "..", "..", ".."));
			return GetActiveProfile(fullPath);
		}

		private static void HandleException(Exception ex)
		{
			if (ex is IOException || ex is UnauthorizedAccessException || ex is SecurityException || ex is InvalidDataException || ex is FileNotFoundException)
			{
				_logger.Error(ex);
				return;
			}
			throw ex;
		}

		public static void Load(UpdateSystem updateSystem)
		{
			if (_loadedMods == null || _loadedMods.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<string, List<IMod>> loadedMod in _loadedMods)
			{
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(loadedMod.Key);
				List<IMod> value = loadedMod.Value;
				if (!value.Any())
				{
					continue;
				}
				foreach (IMod item in value)
				{
					item.OnLoad(updateSystem);
					_logger.Info((object)("[PDXModsBridge] On load '" + fileNameWithoutExtension + "' ."));
				}
			}
		}

		public static void Unload()
		{
			if (_loadedMods == null || _loadedMods.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<string, List<IMod>> loadedMod in _loadedMods)
			{
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(loadedMod.Key);
				List<IMod> value = loadedMod.Value;
				if (!value.Any())
				{
					continue;
				}
				foreach (IMod item in value)
				{
					item.OnDispose();
					_logger.Info((object)("[PDXModsBridge] Unloaded mod '" + fileNameWithoutExtension + "'."));
				}
			}
		}
	}
	[BepInPlugin("PDXModsBridge", "PDXModsBridge", "1.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		private static Harmony _harmony;

		private void Awake()
		{
			_harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "PDXModsBridge_Cities2Harmony");
			MethodBase[] array = _harmony.GetPatchedMethods().ToArray();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"=================================================================");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"PDXModsBridge by Cities2Modding community.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"=================================================================");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Reddit link: https://www.reddit.com/r/cities2modding/");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Discord link: https://discord.gg/KGRNBbm5Fh");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Our mods are officially distributed via Thunderstore.io and https://github.com/Cities2Modding");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Example mod repository and modding info: https://github.com/optimus-code/Cities2Modding");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Thanks to 89pleasure, Rebecca, optimus-code and the Cites2Modding community!");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"=================================================================");
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Plugin PDXModsBridge is loaded! Patched methods: " + array.Length));
			MethodBase[] array2 = array;
			foreach (MethodBase methodBase in array2)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Patched method: " + methodBase.Module.Name + ":" + methodBase.Name));
			}
			ModLoader.ScanDirectory();
		}

		private void OnDestroy()
		{
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "PDXModsBridge";

		public const string PLUGIN_NAME = "PDXModsBridge";

		public const string PLUGIN_VERSION = "1.1.0";
	}
}
namespace PDXModsBridge.Patches
{
	[HarmonyPatch(typeof(ModManager), "InitializeMods")]
	internal static class InitializeMods_Patch
	{
		private static void Postfix(UpdateSystem updateSystem)
		{
			ModLoader.Load(updateSystem);
		}
	}
}