Decompiled source of Posters v0.1.0

plugins/tech.rethunk.mortar.Posters.dll

Decompiled 11 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
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.Logging;
using ConfigSync;
using Microsoft.CodeAnalysis;
using MonoMod.RuntimeDetour;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Rethunk Tech")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Rethunk Tech")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+939405354fd52367dd2c5b7d612b2ae5b146d83f")]
[assembly: AssemblyProduct("tech.rethunk.mortar.Posters")]
[assembly: AssemblyTitle("Posters")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/Rethunk-Tech/mortar-lc-posters")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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]
	[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 Posters
{
	[BepInPlugin("tech.rethunk.mortar.Posters", "Posters", "0.1.0")]
	[BepInProcess("Lethal Company.exe")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public enum Change
		{
			EachLanding,
			Never
		}

		private sealed class Loader : MonoBehaviour
		{
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static Action<IReadOnlyCollection<string>> <0>__Reload;

			public static Action<Action<StartOfRound>, StartOfRound> <1>__ShipLoaded;

			public static Action<Action<StartOfRound, ulong, int, ulong[], int, int, int, int, int, int, int, bool>, StartOfRound, ulong, int, ulong[], int, int, int, int, int, int, int, bool> <2>__PlayerConnected;

			public static Action<Action<RoundManager, int, int, int, int, int[]>, RoundManager, int, int, int, int, int[]> <3>__LevelGenerated;

			public static Action<Action<GrabbableObject, int>, GrabbableObject, int> <4>__ValueSet;

			public static Action<Action<RoundManager, NetworkObjectReference[], int[]>, RoundManager, NetworkObjectReference[], int[]> <5>__ScrapSynced;

			public static Action<Action<GameNetworkManager>, GameNetworkManager> <6>__ItemsSaved;

			public static Action<Action<StartOfRound>, StartOfRound> <7>__ItemsLoaded;

			public static Func<string, IEnumerable<string>> <8>__GetFiles;

			public static Func<string, bool> <9>__IsImage;

			public static Func<string, bool> <10>__Exists;

			public static HandleNamedMessageDelegate <11>__Received;
		}

		private static readonly List<Hook> hooks = new List<Hook>();

		private static readonly Dictionary<string, Texture2D> textures = new Dictionary<string, Texture2D>();

		private static readonly Dictionary<Material, string> wanted = new Dictionary<Material, string>();

		private static Loader runner = null;

		private const int MaxSide = 1024;

		private const string Message = "tech.rethunk.mortar.Posters";

		private const string SaveKey = "tech.rethunk.mortar.Posters.paintings";

		private static readonly Dictionary<ulong, string> saved = new Dictionary<ulong, string>();

		private static readonly ConditionalWeakTable<GrabbableObject, string> shown = new ConditionalWeakTable<GrabbableObject, string>();

		private static string[] posters = Array.Empty<string>();

		private static string[] tips = Array.Empty<string>();

		private static string[] paintings = Array.Empty<string>();

		private static SyncedEntry<Change> change = null;

		private static SyncedEntry<bool> mixGame = null;

		private static int lastSeed;

		private static GameObject? artPlane;

		private static Texture? gamePoster;

		private static Texture? gameTips;

		internal static ManualLogSource Log { get; private set; } = null;

		private void Awake()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Expected O, but got Unknown
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Expected O, but got Unknown
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Expected O, but got Unknown
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Expected O, but got Unknown
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Expected O, but got Unknown
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02db: Expected O, but got Unknown
			//IL_0311: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			GameObject val = new GameObject("Posters loader");
			Object.DontDestroyOnLoad((Object)(object)val);
			((Object)val).hideFlags = (HideFlags)61;
			runner = val.AddComponent<Loader>();
			Watch.Start(val, Reload, Paths.PluginPath);
			Find();
			Log.LogInfo((object)string.Format("{0} {1} loaded {2} posters, {3} tips, {4} paintings", "Posters", "0.1.0", posters.Length, tips.Length, paintings.Length));
			SyncedConfig val2 = SyncedConfig.For((BaseUnityPlugin)(object)this);
			change = val2.Share<Change>(((BaseUnityPlugin)this).Config.Bind<Change>("Ship", "Change", Change.EachLanding, "When the ship's posters and tips change: each landing, or never (the same pictures every game)."), (Sync)1);
			mixGame = val2.Share<bool>(((BaseUnityPlugin)this).Config.Bind<bool>("Ship", "Include game posters", false, "Keep the game's own poster and tips pictures in the mix."), (Sync)1);
			change.Changed += delegate
			{
				Ship(lastSeed);
			};
			mixGame.Changed += delegate
			{
				Ship(lastSeed);
			};
			hooks.Add(new Hook((MethodBase)typeof(StartOfRound).GetMethod("Start", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), (Delegate)new Action<Action<StartOfRound>, StartOfRound>(ShipLoaded)));
			hooks.Add(new Hook((MethodBase)typeof(StartOfRound).GetMethod("OnPlayerConnectedClientRpc", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), (Delegate)new Action<Action<StartOfRound, ulong, int, ulong[], int, int, int, int, int, int, int, bool>, StartOfRound, ulong, int, ulong[], int, int, int, int, int, int, int, bool>(PlayerConnected)));
			hooks.Add(new Hook((MethodBase)typeof(RoundManager).GetMethod("GenerateNewLevelClientRpc"), (Delegate)new Action<Action<RoundManager, int, int, int, int, int[]>, RoundManager, int, int, int, int, int[]>(LevelGenerated)));
			hooks.Add(new Hook((MethodBase)typeof(GrabbableObject).GetMethod("SetScrapValue"), (Delegate)new Action<Action<GrabbableObject, int>, GrabbableObject, int>(ValueSet)));
			hooks.Add(new Hook((MethodBase)typeof(RoundManager).GetMethod("SyncScrapValuesClientRpc"), (Delegate)new Action<Action<RoundManager, NetworkObjectReference[], int[]>, RoundManager, NetworkObjectReference[], int[]>(ScrapSynced)));
			hooks.Add(new Hook((MethodBase)typeof(GameNetworkManager).GetMethod("SaveItemsInShip", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), (Delegate)new Action<Action<GameNetworkManager>, GameNetworkManager>(ItemsSaved)));
			hooks.Add(new Hook((MethodBase)typeof(StartOfRound).GetMethod("LoadShipGrabbableItems", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), (Delegate)new Action<Action<StartOfRound>, StartOfRound>(ItemsLoaded)));
		}

		private static void Reload(IReadOnlyCollection<string> changed)
		{
			if (!changed.Any((string f) => IsImage(f) || Path.GetExtension(f) == ""))
			{
				return;
			}
			Find();
			foreach (string item2 in changed)
			{
				if (textures.Remove(item2, out Texture2D value))
				{
					Object.Destroy((Object)(object)value);
				}
			}
			Log.LogInfo((object)$"Found {posters.Length} posters, {tips.Length} tips, {paintings.Length} paintings");
			Ship(lastSeed);
			GrabbableObject[] array = Object.FindObjectsByType<GrabbableObject>((FindObjectsSortMode)0);
			foreach (GrabbableObject item in array)
			{
				Paint(item);
			}
		}

		private static bool IsImage(string file)
		{
			if (!file.EndsWith(".png", StringComparison.OrdinalIgnoreCase) && !file.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase))
			{
				return file.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static void Find()
		{
			posters = Images("posters", "LethalPosters", "posters");
			tips = Images("tips", "LethalPosters", "tips");
			paintings = Images("paintings", "LethalPaintings", "paintings");
		}

		private static string[] Images(string kind, string oldPack, string oldKind)
		{
			return Folders("mortar", kind).Concat(Folders(oldPack, oldKind)).SelectMany(Directory.GetFiles).Where(IsImage)
				.Distinct()
				.OrderBy<string, string>((string f) => Path.GetFileName(f), StringComparer.Ordinal)
				.ToArray();
		}

		private static IEnumerable<string> Folders(string parent, string kind)
		{
			return (from dir in Directory.GetDirectories(Paths.PluginPath, parent, SearchOption.AllDirectories)
				select Path.Combine(dir, kind)).Where(Directory.Exists);
		}

		private static void ShipLoaded(Action<StartOfRound> orig, StartOfRound self)
		{
			saved.Clear();
			wanted.Clear();
			orig(self);
			Listen();
			Ship(self.randomMapSeed);
		}

		private unsafe static void Listen()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null)
			{
				return;
			}
			CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager;
			object obj = <>O.<11>__Received;
			if (obj == null)
			{
				HandleNamedMessageDelegate val = Received;
				<>O.<11>__Received = val;
				obj = (object)val;
			}
			customMessagingManager.RegisterNamedMessageHandler("tech.rethunk.mortar.Posters", (HandleNamedMessageDelegate)obj);
			if (singleton.IsServer)
			{
				return;
			}
			FastBufferWriter val2 = default(FastBufferWriter);
			((FastBufferWriter)(ref val2))..ctor(1, (Allocator)2, -1);
			try
			{
				singleton.CustomMessagingManager.SendNamedMessage("tech.rethunk.mortar.Posters", 0uL, val2, (NetworkDelivery)3);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private unsafe static void Received(ulong sender, FastBufferReader reader)
		{
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			if (singleton.IsServer)
			{
				FastBufferWriter val = default(FastBufferWriter);
				((FastBufferWriter)(ref val))..ctor(256, (Allocator)2, 1048576);
				try
				{
					int count = saved.Count;
					((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref count, default(ForPrimitives));
					foreach (KeyValuePair<ulong, string> item2 in saved)
					{
						ulong key = item2.Key;
						((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref key, default(ForPrimitives));
						((FastBufferWriter)(ref val)).WriteValueSafe(item2.Value, false);
					}
					singleton.CustomMessagingManager.SendNamedMessage("tech.rethunk.mortar.Posters", sender, val, (NetworkDelivery)4);
					return;
				}
				finally
				{
					((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
				}
			}
			int num = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
			ulong key2 = default(ulong);
			string value = default(string);
			for (int i = 0; i < num; i++)
			{
				((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref key2, default(ForPrimitives));
				((FastBufferReader)(ref reader)).ReadValueSafe(ref value, false);
				saved[key2] = value;
			}
			GrabbableObject[] array = Object.FindObjectsByType<GrabbableObject>((FindObjectsSortMode)0);
			foreach (GrabbableObject item in array)
			{
				Paint(item);
			}
		}

		private static string Where(Vector3 position)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			return $"{position.x:F2},{position.y:F2},{position.z:F2}";
		}

		private static void ItemsSaved(Action<GameNetworkManager> orig, GameNetworkManager self)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			orig(self);
			if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.isChallengeFile)
			{
				return;
			}
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			GrabbableObject[] array = Object.FindObjectsByType<GrabbableObject>((FindObjectsSortMode)0);
			foreach (GrabbableObject val in array)
			{
				if (shown.TryGetValue(val, out string value))
				{
					dictionary[Where(((Component)val).transform.position)] = value;
				}
			}
			ES3.Save<Dictionary<string, string>>("tech.rethunk.mortar.Posters.paintings", dictionary, self.currentSaveFileName);
		}

		private static void ItemsLoaded(Action<StartOfRound> orig, StartOfRound self)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			orig(self);
			string currentSaveFileName = GameNetworkManager.Instance.currentSaveFileName;
			if (!ES3.KeyExists("tech.rethunk.mortar.Posters.paintings", currentSaveFileName))
			{
				return;
			}
			Dictionary<string, string> dictionary = ES3.Load<Dictionary<string, string>>("tech.rethunk.mortar.Posters.paintings", currentSaveFileName);
			GrabbableObject[] array = Object.FindObjectsByType<GrabbableObject>((FindObjectsSortMode)0);
			foreach (GrabbableObject val in array)
			{
				if (IsPainting(val) && dictionary.TryGetValue(Where(((Component)val).transform.position), out var value))
				{
					saved[((NetworkBehaviour)val).NetworkObjectId] = value;
					Paint(val);
				}
			}
		}

		private static void PlayerConnected(Action<StartOfRound, ulong, int, ulong[], int, int, int, int, int, int, int, bool> orig, StartOfRound self, ulong clientId, int connectedPlayers, ulong[] connectedPlayerIdsOrdered, int assignedPlayerObjectId, int serverMoneyAmount, int levelID, int profitQuota, int timeUntilDeadline, int quotaFulfilled, int randomSeed, bool isChallenge)
		{
			orig(self, clientId, connectedPlayers, connectedPlayerIdsOrdered, assignedPlayerObjectId, serverMoneyAmount, levelID, profitQuota, timeUntilDeadline, quotaFulfilled, randomSeed, isChallenge);
			Ship(self.randomMapSeed);
		}

		private static void LevelGenerated(Action<RoundManager, int, int, int, int, int[]> orig, RoundManager self, int randomSeed, int levelID, int moldIterations, int moldStartPosition, int[] syncDestroyedMold)
		{
			orig(self, randomSeed, levelID, moldIterations, moldStartPosition, syncDestroyedMold);
			Ship(randomSeed);
		}

		private static void Ship(int seed)
		{
			lastSeed = seed;
			GameObject val = GameObject.Find("HangarShip/Plane.001");
			if (!((Object)(object)val == (Object)null))
			{
				Material[] materials = ((Renderer)val.GetComponent<MeshRenderer>()).materials;
				if ((Object)(object)val != (Object)(object)artPlane)
				{
					artPlane = val;
					gamePoster = materials[0].mainTexture;
					gameTips = materials[1].mainTexture;
				}
				if (change.Value == Change.Never)
				{
					seed = 0;
				}
				Show(materials[0], posters, seed, mixGame.Value ? gamePoster : null);
				Show(materials[1], tips, seed, mixGame.Value ? gameTips : null);
			}
		}

		private static void ValueSet(Action<GrabbableObject, int> orig, GrabbableObject self, int value)
		{
			orig(self, value);
			Paint(self);
		}

		private static void ScrapSynced(Action<RoundManager, NetworkObjectReference[], int[]> orig, RoundManager self, NetworkObjectReference[] spawnedScrap, int[] allScrapValue)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			orig(self, spawnedScrap, allScrapValue);
			NetworkObject val2 = default(NetworkObject);
			GrabbableObject item = default(GrabbableObject);
			for (int i = 0; i < spawnedScrap.Length; i++)
			{
				NetworkObjectReference val = spawnedScrap[i];
				if (((NetworkObjectReference)(ref val)).TryGet(ref val2, (NetworkManager)null) && ((Component)val2).TryGetComponent<GrabbableObject>(ref item))
				{
					Paint(item);
				}
			}
		}

		private static bool IsPainting(GrabbableObject item)
		{
			if ((Object)(object)item.itemProperties != (Object)null)
			{
				return item.itemProperties.itemName == "Painting";
			}
			return false;
		}

		private static void Paint(GrabbableObject item)
		{
			MeshRenderer val = default(MeshRenderer);
			if (!IsPainting(item) || !((Component)item).TryGetComponent<MeshRenderer>(ref val))
			{
				return;
			}
			string text = null;
			if (((NetworkBehaviour)item).IsSpawned && saved.TryGetValue(((NetworkBehaviour)item).NetworkObjectId, out string name))
			{
				text = Array.Find(paintings, (string f) => Path.GetFileName(f) == name);
			}
			text = ((text != null) ? ShowFile(((Renderer)val).material, text) : Show(((Renderer)val).material, paintings, item.scrapValue));
			shown.Remove(item);
			if (text != null)
			{
				shown.Add(item, Path.GetFileName(text));
			}
		}

		private static string? Show(Material material, string[] files, int seed, Texture? game = null)
		{
			int num = files.Length + (((Object)(object)game != (Object)null) ? 1 : 0);
			if (num == 0)
			{
				return null;
			}
			int num2 = (seed % num + num) % num;
			if (num2 == files.Length)
			{
				material.mainTexture = game;
				return null;
			}
			return ShowFile(material, files[num2]);
		}

		private static string ShowFile(Material material, string file)
		{
			wanted[material] = file;
			if (textures.TryGetValue(file, out Texture2D value))
			{
				material.mainTexture = (Texture)(object)value;
			}
			else
			{
				((MonoBehaviour)runner).StartCoroutine(Load(file, material));
			}
			return file;
		}

		private static IEnumerator Load(string file, Material material)
		{
			UnityWebRequest request = UnityWebRequestTexture.GetTexture(FileUrl(file), true);
			try
			{
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					Log.LogError((object)(file + ": " + request.error));
					yield break;
				}
				if (!textures.TryGetValue(file, out Texture2D value))
				{
					value = Fit(DownloadHandlerTexture.GetContent(request));
					textures[file] = value;
				}
				if ((Object)(object)material != (Object)null && wanted.TryGetValue(material, out string value2) && value2 == file)
				{
					material.mainTexture = (Texture)(object)value;
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		private static Texture2D Fit(Texture2D full)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			int num = Math.Max(((Texture)full).width, ((Texture)full).height);
			if (num <= 1024)
			{
				return full;
			}
			int num2 = ((Texture)full).width * 1024 / num;
			int num3 = ((Texture)full).height * 1024 / num;
			RenderTexture temporary = RenderTexture.GetTemporary(num2, num3);
			Graphics.Blit((Texture)(object)full, temporary);
			RenderTexture active = RenderTexture.active;
			RenderTexture.active = temporary;
			Texture2D val = new Texture2D(num2, num3, (TextureFormat)4, true);
			val.ReadPixels(new Rect(0f, 0f, (float)num2, (float)num3), 0, 0);
			val.Apply(true, true);
			RenderTexture.active = active;
			RenderTexture.ReleaseTemporary(temporary);
			Object.Destroy((Object)(object)full);
			return val;
		}

		private static string FileUrl(string file)
		{
			return "file:///" + file.Replace('\\', '/').Replace("%", "%25").Replace("#", "%23");
		}
	}
	internal sealed class Watch : MonoBehaviour
	{
		private static readonly TimeSpan quiet = TimeSpan.FromSeconds(1.0);

		private readonly List<FileSystemWatcher> watchers = new List<FileSystemWatcher>();

		private readonly object gate = new object();

		private readonly HashSet<string> changed = new HashSet<string>();

		private DateTime last;

		private Action<IReadOnlyCollection<string>> onChange = delegate
		{
		};

		public static void Start(GameObject host, Action<IReadOnlyCollection<string>> onChange, params string[] dirs)
		{
			Watch watch = host.AddComponent<Watch>();
			watch.onChange = onChange;
			foreach (string item in dirs.Where(Directory.Exists))
			{
				try
				{
					FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(item)
					{
						IncludeSubdirectories = true
					};
					fileSystemWatcher.Created += delegate(object _, FileSystemEventArgs e)
					{
						watch.Note(e.FullPath);
					};
					fileSystemWatcher.Changed += delegate(object _, FileSystemEventArgs e)
					{
						watch.Note(e.FullPath);
					};
					fileSystemWatcher.Deleted += delegate(object _, FileSystemEventArgs e)
					{
						watch.Note(e.FullPath);
					};
					fileSystemWatcher.Renamed += delegate(object _, RenamedEventArgs e)
					{
						watch.Note(e.OldFullPath);
						watch.Note(e.FullPath);
					};
					fileSystemWatcher.EnableRaisingEvents = true;
					watch.watchers.Add(fileSystemWatcher);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Can't watch " + item + " for new files, so they need a restart: " + ex.Message));
				}
			}
		}

		private void Note(string path)
		{
			lock (gate)
			{
				changed.Add(path);
				last = DateTime.UtcNow;
			}
		}

		private void Update()
		{
			string[] obj;
			lock (gate)
			{
				if (changed.Count == 0 || DateTime.UtcNow - last < quiet)
				{
					return;
				}
				obj = changed.ToArray();
				changed.Clear();
			}
			try
			{
				onChange((IReadOnlyCollection<string>)(object)obj);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Couldn't pick up changed files: {arg}");
			}
		}

		private void OnDestroy()
		{
			foreach (FileSystemWatcher watcher in watchers)
			{
				watcher.Dispose();
			}
		}
	}
	internal static class MyPluginInfo
	{
		public const string Id = "tech.rethunk.mortar.Posters";

		public const string Name = "Posters";

		public const string Version = "0.1.0";
	}
}