Decompiled source of ConfigurableMoonPreview v1.0.0

BepInEx/plugins/ConfigurableMoonPreview/ConfigurableMoonPreview.dll

Decompiled 13 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Video;

[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("ConfigurableMoonPreview")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ConfigurableMoonPreview")]
[assembly: AssemblyTitle("ConfigurableMoonPreview")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 ConfigurableMoonPreview
{
	internal sealed class Manifest
	{
		internal const int SupportedFormatVersion = 1;

		[JsonProperty("formatVersion")]
		public int FormatVersion = 1;

		[JsonProperty("packName")]
		public string PackName;

		[JsonProperty("author")]
		public string Author;

		[JsonProperty("entries")]
		public List<ManifestEntry> Entries;
	}
	internal sealed class ManifestEntry
	{
		[JsonProperty("scene")]
		public string Scene;

		[JsonProperty("planet")]
		public string Planet;

		[JsonProperty("description")]
		public string Description;

		[JsonProperty("video")]
		public string Video;

		[JsonProperty("image")]
		public string Image;

		internal bool HasSelector => !string.IsNullOrEmpty(Scene) || !string.IsNullOrEmpty(Planet);

		internal bool HasReel => !string.IsNullOrEmpty(Video);

		internal bool HasPayload => Description != null || HasReel;

		internal string DescribeSelector => (!string.IsNullOrEmpty(Scene)) ? ("scene '" + Scene + "'") : ((!string.IsNullOrEmpty(Planet)) ? ("planet '" + Planet + "'") : "<no selector>");
	}
	internal static class ManifestLoader
	{
		private const string Suffix = "*.moonpreview.json";

		private const string BundledManifest = "vanilla.moonpreview.json";

		private static int bundledEntries;

		internal static ReelStore LoadAll(string pluginDirectory)
		{
			bundledEntries = 0;
			ReelStore reelStore = new ReelStore();
			string skip = LoadBundled(reelStore, pluginDirectory);
			(int, int) tuple = LoadFrom(reelStore, Paths.PluginPath, "mod", skip);
			(int, int) tuple2 = LoadFrom(reelStore, Paths.ConfigPath, "user override", null);
			Plugin.Log.LogInfo((object)(string.Format("{0} moon entr{1} loaded: ", reelStore.Count, (reelStore.Count == 1) ? "y" : "ies") + $"{bundledEntries} bundled, {tuple.Item2} from {tuple.Item1} mod manifest(s), " + $"{tuple2.Item2} from {tuple2.Item1} user override(s)."));
			return reelStore;
		}

		private static string LoadBundled(ReelStore store, string pluginDirectory)
		{
			if (string.IsNullOrEmpty(pluginDirectory))
			{
				return null;
			}
			string text = Path.Combine(pluginDirectory, "vanilla.moonpreview.json");
			if (!File.Exists(text))
			{
				Plugin.Log.LogWarning((object)("'vanilla.moonpreview.json' is missing from " + pluginDirectory + ". The vanilla moons keep the base game's descriptions. Reinstall if that was not deliberate."));
				return text;
			}
			bundledEntries = Math.Max(Load(store, text, pluginDirectory), 0);
			return text;
		}

		private static (int manifests, int entries) LoadFrom(ReelStore store, string root, string label, string skip)
		{
			if (string.IsNullOrEmpty(root) || !Directory.Exists(root))
			{
				return (manifests: 0, entries: 0);
			}
			string[] files;
			try
			{
				files = Directory.GetFiles(root, "*.moonpreview.json", SearchOption.AllDirectories);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Scanning '{root}' threw. {arg}");
				return (manifests: 0, entries: 0);
			}
			if (files.Length == 0)
			{
				return (manifests: 0, entries: 0);
			}
			Array.Sort(files, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
			int num = 0;
			int num2 = 0;
			string[] array = files;
			foreach (string text in array)
			{
				if (skip == null || !string.Equals(text, skip, StringComparison.OrdinalIgnoreCase))
				{
					int num3 = Load(store, text, root);
					if (num3 >= 0)
					{
						num++;
						num2 += num3;
					}
				}
			}
			return (manifests: num, entries: num2);
		}

		private static int Load(ReelStore store, string path, string root)
		{
			string text = Shorten(path, root);
			Manifest manifest;
			try
			{
				manifest = JsonConvert.DeserializeObject<Manifest>(File.ReadAllText(path));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("'" + text + "' is not valid JSON and was skipped. " + ex.Message));
				return -1;
			}
			if (manifest == null)
			{
				Plugin.Log.LogWarning((object)("'" + text + "' is empty and was skipped."));
				return -1;
			}
			if (manifest.FormatVersion > 1)
			{
				Plugin.Log.LogWarning((object)($"'{text}' declares formatVersion {manifest.FormatVersion}; this build understands " + $"up to {1}. Update Configurable Moon Preview. Skipped rather " + "than half-applied."));
				return -1;
			}
			if (manifest.Entries == null || manifest.Entries.Count == 0)
			{
				Plugin.Log.LogWarning((object)("'" + text + "' has no entries."));
				return -1;
			}
			string dir = Path.GetDirectoryName(path) ?? string.Empty;
			string text2 = ((!string.IsNullOrEmpty(manifest.PackName)) ? manifest.PackName : text);
			if (!string.IsNullOrEmpty(manifest.Author))
			{
				text2 = text2 + " by " + manifest.Author;
			}
			int num = 0;
			for (int i = 0; i < manifest.Entries.Count; i++)
			{
				if (AddEntry(store, manifest.Entries[i], i, dir, text2, text))
				{
					num++;
				}
			}
			return num;
		}

		private static bool AddEntry(ReelStore store, ManifestEntry entry, int index, string dir, string source, string name)
		{
			string text = $"'{name}' entry {index}";
			if (entry == null)
			{
				Plugin.Log.LogWarning((object)(text + " is null; skipped."));
				return false;
			}
			if (!entry.HasSelector)
			{
				Plugin.Log.LogWarning((object)(text + " names no moon. Give it a \"scene\" or a \"planet\". Skipped — an entry matching everything would repaint every moon in the game."));
				return false;
			}
			if (!string.IsNullOrEmpty(entry.Image))
			{
				Plugin.Log.LogWarning((object)(text + " sets \"image\", which is not supported — reels are video files. Convert it to an mp4 and use \"video\" instead."));
			}
			if (!entry.HasPayload)
			{
				Plugin.Log.LogWarning((object)(text + " (" + entry.DescribeSelector + ") asks for nothing: no \"description\" and no \"video\". Skipped."));
				return false;
			}
			MoonReel moonReel = new MoonReel
			{
				Description = entry.Description
			};
			if (entry.Description != null)
			{
				moonReel.DescriptionSource = source;
			}
			if (!string.IsNullOrEmpty(entry.Video))
			{
				moonReel.VideoPath = Resolve(dir, entry.Video);
				moonReel.ReelSource = source;
				if (!File.Exists(moonReel.VideoPath))
				{
					Plugin.Log.LogWarning((object)(text + " points at '" + entry.Video + "', which does not exist (" + moonReel.VideoPath + "). The moon keeps its own reel."));
					moonReel.VideoPath = null;
					moonReel.ReelSource = null;
					if (moonReel.Description == null)
					{
						return false;
					}
				}
			}
			store.Add(entry.Scene, entry.Planet, moonReel);
			return true;
		}

		private static string Resolve(string dir, string relative)
		{
			return Path.IsPathRooted(relative) ? relative : Path.Combine(dir, relative);
		}

		private static string Shorten(string path, string root)
		{
			if (!string.IsNullOrEmpty(root) && path.StartsWith(root, StringComparison.OrdinalIgnoreCase))
			{
				return path.Substring(root.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
			}
			return Path.GetFileName(path);
		}
	}
	internal static class Monitor
	{
		private static VideoAudioOutputMode? originalAudioMode;

		internal static void ApplyDescriptions()
		{
			SelectableLevel[] array = StartOfRound.Instance?.levels;
			if (array == null)
			{
				return;
			}
			int num = 0;
			SelectableLevel[] array2 = array;
			foreach (SelectableLevel level in array2)
			{
				if (ApplyDescription(level))
				{
					num++;
				}
			}
			Plugin.Log.LogInfo((object)$"Applied {num} description(s) across {array.Length} moon(s).");
		}

		internal static bool ApplyDescription(SelectableLevel level)
		{
			if ((Object)(object)level == (Object)null)
			{
				return false;
			}
			MoonReel moonReel = Plugin.Store.Lookup(level.sceneName, level.PlanetName);
			if (moonReel?.Description == null)
			{
				return false;
			}
			level.LevelDescription = moonReel.Description;
			return true;
		}

		internal static void Refresh(StartOfRound start)
		{
			if ((Object)(object)start == (Object)null)
			{
				return;
			}
			VideoPlayer screenLevelVideoReel = start.screenLevelVideoReel;
			SelectableLevel currentLevel = start.currentLevel;
			if ((Object)(object)screenLevelVideoReel == (Object)null || (Object)(object)currentLevel == (Object)null)
			{
				return;
			}
			try
			{
				MoonReel moonReel = Plugin.Store.Lookup(currentLevel.sceneName, currentLevel.PlanetName);
				if (start.isChallengeFile || moonReel?.VideoPath == null)
				{
					RestoreClipMode(start, screenLevelVideoReel, currentLevel);
				}
				else
				{
					Play(screenLevelVideoReel, moonReel.VideoPath);
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Updating the monitor for {currentLevel.PlanetName} threw. {arg}");
			}
		}

		private static void Play(VideoPlayer player, string path)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			if ((int)player.source != 1 || !string.Equals(player.url, path, StringComparison.Ordinal))
			{
				player.Stop();
				if (!originalAudioMode.HasValue)
				{
					originalAudioMode = player.audioOutputMode;
				}
				player.audioOutputMode = (VideoAudioOutputMode)0;
				player.source = (VideoSource)1;
				player.clip = null;
				player.url = path;
				player.isLooping = true;
			}
			((Behaviour)player).enabled = true;
			((Component)player).gameObject.SetActive(true);
			player.Play();
		}

		private static void RestoreClipMode(StartOfRound start, VideoPlayer player, SelectableLevel level)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if ((int)player.source == 1)
			{
				player.Stop();
				player.source = (VideoSource)0;
				player.url = string.Empty;
				player.clip = level.videoReel;
				if (originalAudioMode.HasValue)
				{
					player.audioOutputMode = originalAudioMode.Value;
				}
				bool flag = (((Behaviour)player).enabled = (Object)(object)level.videoReel != (Object)null && !start.isChallengeFile);
				((Component)player).gameObject.SetActive(flag);
				if (flag)
				{
					player.Play();
				}
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	internal static class Patches
	{
		[HarmonyPostfix]
		[HarmonyPatch("Start")]
		[HarmonyPriority(200)]
		private static void Start_Postfix()
		{
			try
			{
				Monitor.ApplyDescriptions();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Applying descriptions at StartOfRound.Start threw. {arg}");
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("SetMapScreenInfoToCurrentLevel")]
		private static void SetMapScreenInfoToCurrentLevel_Prefix(StartOfRound __instance)
		{
			try
			{
				Monitor.ApplyDescription(__instance.currentLevel);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Applying a description threw; the screen keeps vanilla text. {arg}");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("SetMapScreenInfoToCurrentLevel")]
		private static void SetMapScreenInfoToCurrentLevel_Postfix(StartOfRound __instance)
		{
			Monitor.Refresh(__instance);
		}

		[HarmonyPostfix]
		[HarmonyPatch("SwitchMapMonitorPurpose")]
		private static void SwitchMapMonitorPurpose_Postfix(StartOfRound __instance, bool displayInfo)
		{
			if (displayInfo)
			{
				Monitor.Refresh(__instance);
			}
		}
	}
	[BepInPlugin("com.luth.configurablemoonpreview", "ConfigurableMoonPreview", "1.0.0")]
	[BepInProcess("Lethal Company.exe")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.luth.configurablemoonpreview";

		public const string Name = "ConfigurableMoonPreview";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log { get; private set; }

		internal static ReelStore Store { get; private set; } = new ReelStore();

		private void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			string pluginDirectory = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? string.Empty;
			Store = ManifestLoader.LoadAll(pluginDirectory);
			try
			{
				new Harmony("com.luth.configurablemoonpreview").PatchAll(typeof(Plugin).Assembly);
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Harmony patching failed; Configurable Moon Preview is inert this session. {arg}");
				return;
			}
			Log.LogInfo((object)"ConfigurableMoonPreview 1.0.0 loaded.");
		}
	}
	internal sealed class MoonReel
	{
		internal string Description;

		internal string VideoPath;

		internal string DescriptionSource;

		internal string ReelSource;

		internal bool HasReel => VideoPath != null;

		internal MoonReel Clone()
		{
			return new MoonReel
			{
				Description = Description,
				VideoPath = VideoPath,
				DescriptionSource = DescriptionSource,
				ReelSource = ReelSource
			};
		}

		internal void Overlay(MoonReel other)
		{
			if (other != null)
			{
				if (other.Description != null)
				{
					Description = other.Description;
					DescriptionSource = other.DescriptionSource;
				}
				if (other.HasReel)
				{
					VideoPath = other.VideoPath;
					ReelSource = other.ReelSource;
				}
			}
		}
	}
	internal sealed class ReelStore
	{
		private readonly Dictionary<string, MoonReel> byScene = new Dictionary<string, MoonReel>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, MoonReel> byPlanet = new Dictionary<string, MoonReel>(StringComparer.OrdinalIgnoreCase);

		internal int Count => byScene.Count + byPlanet.Count;

		internal void Add(string scene, string planet, MoonReel reel)
		{
			if (reel != null)
			{
				if (!string.IsNullOrEmpty(scene))
				{
					Merge(byScene, scene, reel);
				}
				if (!string.IsNullOrEmpty(planet))
				{
					Merge(byPlanet, Numberless(planet), reel);
				}
			}
		}

		private static void Merge(Dictionary<string, MoonReel> target, string key, MoonReel reel)
		{
			if (target.TryGetValue(key, out var value))
			{
				value.Overlay(reel);
			}
			else
			{
				target[key] = reel.Clone();
			}
		}

		internal MoonReel Lookup(string sceneName, string planetName)
		{
			MoonReel moonReel = null;
			if (!string.IsNullOrEmpty(planetName) && byPlanet.TryGetValue(Numberless(planetName), out var value))
			{
				moonReel = value.Clone();
			}
			if (!string.IsNullOrEmpty(sceneName) && byScene.TryGetValue(sceneName, out var value2))
			{
				if (moonReel == null)
				{
					moonReel = value2.Clone();
				}
				else
				{
					moonReel.Overlay(value2);
				}
			}
			return moonReel;
		}

		internal static string Numberless(string planetName)
		{
			if (string.IsNullOrEmpty(planetName))
			{
				return string.Empty;
			}
			int i;
			for (i = 0; i < planetName.Length && char.IsDigit(planetName[i]); i++)
			{
			}
			for (; i < planetName.Length && planetName[i] == ' '; i++)
			{
			}
			return (i >= planetName.Length) ? planetName.Trim() : planetName.Substring(i).Trim();
		}
	}
}