Decompiled source of BetterSignalCompiler v1.0.2

BepInEx/plugins/BetterSignalCompiler/polymeric.BetterSignalCompiler.dll

Decompiled 10 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;

[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("polymeric")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © polymeric. All rights reserved.")]
[assembly: AssemblyDescription("A mod assembly for TMfDS v1.2.1")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2+8aa99e7c0c6c5bfe38b893c12027481c1f4856dc")]
[assembly: AssemblyProduct("Better Signal Compiler")]
[assembly: AssemblyTitle("Better Signal Compiler")]
[assembly: AssemblyVersion("1.0.2.0")]
[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 polymeric.BetterSignalCompiler
{
	public static class DelegatesAndRefs
	{
		public static readonly FieldRef<CompilerResult, string> CompilerResult__errorMsg = AccessTools.FieldRefAccess<CompilerResult, string>("errorMsg");
	}
	public static class HarmonyPatches
	{
		[HarmonyPatch(typeof(C_Reformatter), "CompileStringToSignal")]
		public static class Patch__C_Reformatter__CompileStringToSignal
		{
			public static void Prefix(ref bool __runOriginal, ref SignalMessage __result, string input, ref CompilerResult cr)
			{
				//IL_000b: 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)
				if (__runOriginal)
				{
					__runOriginal = false;
					__result = MwisCompiler.CompileStringToSignal(input, ref cr);
				}
			}
		}

		public static void PatchInClassShallow(Harmony harmony, Type outerClass)
		{
			foreach (Type item in from type in outerClass.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic)
				where HarmonyMethodExtensions.GetFromType(type).Count > 0
				select type)
			{
				harmony.CreateClassProcessor(item).Patch();
			}
		}
	}
	public enum TokenizeResultKind
	{
		Word,
		Numeral,
		BareId,
		BareIdBig
	}
	public class TokenizeResult
	{
		public readonly TokenizeResultKind kind;

		public readonly int id;

		public readonly int start;

		public readonly int end;

		public TokenizeResult(TokenizeResultKind kind, int id, int start, int end)
		{
			this.kind = kind;
			this.id = id;
			this.start = start;
			this.end = end;
		}

		public static int PartitionPointEndLtEqNeedle(List<TokenizeResult> list, int first, int last, int needle)
		{
			int num = last - first;
			while (num > 0)
			{
				int num2 = num / 2;
				int num3 = first + num2;
				if (list[num3].end <= needle)
				{
					first = num3 + 1;
					num -= num2 + 1;
				}
				else
				{
					num = num2;
				}
			}
			return first;
		}
	}
	public class MwisWeight : IComparable<MwisWeight>
	{
		public readonly uint nonWhitespaceChars;

		public readonly uint tokensPenalty;

		public MwisWeight(uint nonWhitespaceChars, uint tokensPenalty)
		{
			this.nonWhitespaceChars = nonWhitespaceChars;
			this.tokensPenalty = tokensPenalty;
		}

		public int CompareTo(MwisWeight other)
		{
			return (nonWhitespaceChars, other.tokensPenalty).CompareTo((other.nonWhitespaceChars, tokensPenalty));
		}

		public static MwisWeight operator +(MwisWeight x, MwisWeight y)
		{
			return new MwisWeight(x.nonWhitespaceChars + y.nonWhitespaceChars, x.tokensPenalty + y.tokensPenalty);
		}
	}
	public enum CompileErrorTypeInternal
	{
		NaN,
		EntryNotFound,
		EmptyEntry,
		Ambiguous,
		BareIdIntegerTooWide
	}
	public static class CompileErrorTypeInternalExtensions
	{
		public static string GetString(this CompileErrorTypeInternal ceti)
		{
			return ceti switch
			{
				CompileErrorTypeInternal.NaN => "Not a Number", 
				CompileErrorTypeInternal.EntryNotFound => "Entry not found", 
				CompileErrorTypeInternal.EmptyEntry => "Null Input", 
				CompileErrorTypeInternal.Ambiguous => "Ambiguous", 
				CompileErrorTypeInternal.BareIdIntegerTooWide => "ID out of bounds", 
				_ => "Miscellaneous Error", 
			};
		}
	}
	public static class MwisCompiler
	{
		private static readonly Regex BareIdRegex = new Regex("\\G\\|(-?[0-9]+)", RegexOptions.Compiled | RegexOptions.CultureInvariant);

		public static SignalMessage CompileStringToSignal(string input, ref CompilerResult cr)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a27: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a36: Unknown result type (might be due to invalid IL or missing references)
			//IL_0873: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Singleton.logger.LogDebug((object)("CompileStringToSignal input: " + input));
			if (input.Trim() == "")
			{
				cr.compilerResultTag = (CompilerResultTag)1;
				cr.LogError(input, (CompileErrorType)2);
				Plugin.Singleton.logger.LogDebug((object)"CompileStringToSignal cr.LogError: EmptyEntry");
				return new SignalMessage
				{
					signals = Array.Empty<int>()
				};
			}
			Trie<int> trie = new Trie<int>();
			foreach (var (id, word) in UserDictionary.Instance.terms)
			{
				trie.Insert(id, word);
			}
			List<TokenizeResult> list = new List<TokenizeResult>();
			for (int i = 0; i < input.Length; i++)
			{
				foreach (TriePrefix<int> item7 in trie.Prefixes(input, i))
				{
					list.Add(new TokenizeResult(TokenizeResultKind.Word, item7.id, i, i + item7.length));
				}
				if (input[i] >= '0' && input[i] <= '9')
				{
					list.Add(new TokenizeResult(TokenizeResultKind.Numeral, input[i] - 48, i, i + 1));
				}
				if (!Plugin.Singleton.configParserParseBareIds.Value)
				{
					continue;
				}
				Match match = BareIdRegex.Match(input, i);
				if (match.Success)
				{
					if (int.TryParse(match.Groups[1].Value, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var result))
					{
						list.Add(new TokenizeResult(TokenizeResultKind.BareId, result, i, i + match.Length));
					}
					else
					{
						list.Add(new TokenizeResult(TokenizeResultKind.BareIdBig, 0, i, i + match.Length));
					}
				}
			}
			list.Sort((TokenizeResult x, TokenizeResult y) => (x.end, x.start).CompareTo((y.end, y.start)));
			MwisWeight[] array = new MwisWeight[list.Count + 1];
			int[] array2 = new int[list.Count + 1];
			MwisWeight[] array3 = new MwisWeight[list.Count + 1];
			array[0] = new MwisWeight(0u, 0u);
			array2[0] = 0;
			array3[0] = new MwisWeight(0u, 0u);
			for (int num2 = 1; num2 <= list.Count; num2++)
			{
				TokenizeResult tokenizeResult = list[num2 - 1];
				uint num3 = 0u;
				string text2 = input;
				int start = tokenizeResult.start;
				string text3 = text2.Substring(start, tokenizeResult.end - start);
				for (int num4 = 0; num4 < text3.Length; num4++)
				{
					if (!char.IsWhiteSpace(text3[num4]))
					{
						num3++;
					}
				}
				array[num2] = new MwisWeight(num3, 1u);
				array2[num2] = TokenizeResult.PartitionPointEndLtEqNeedle(list, 0, num2 - 1, tokenizeResult.start);
				MwisWeight mwisWeight = array[num2] + array3[array2[num2]];
				MwisWeight mwisWeight2 = array3[num2 - 1];
				array3[num2] = ((mwisWeight.CompareTo(mwisWeight2) > 0) ? mwisWeight : mwisWeight2);
			}
			List<int> list2 = new List<int>();
			List<int> list3 = new List<int>();
			int num5 = list.Count;
			while (num5 > 0)
			{
				int num6 = (array[num5] + array3[array2[num5]]).CompareTo(array3[num5 - 1]);
				if (num6 == 0)
				{
					list3.Add(num5);
				}
				if (num6 >= 0)
				{
					list2.Add(num5);
					num5 = array2[num5];
				}
				else
				{
					num5--;
				}
			}
			list2.Reverse();
			list3.Reverse();
			int[] array4 = new int[list2.Count];
			List<(string, CompileErrorTypeInternal)> list4 = new List<(string, CompileErrorTypeInternal)>();
			SortedDictionary<(int, int), (string, bool)> sortedDictionary = new SortedDictionary<(int, int), (string, bool)>();
			int num7 = 0;
			for (int num8 = 0; num8 < list2.Count; num8++)
			{
				TokenizeResult tokenizeResult2 = list[list2[num8] - 1];
				if (tokenizeResult2.kind != TokenizeResultKind.BareIdBig)
				{
					array4[num8] = tokenizeResult2.id;
					sortedDictionary.Add((tokenizeResult2.start, tokenizeResult2.end), ($"|{tokenizeResult2.id}", true));
				}
				else
				{
					string text4 = input;
					int num4 = tokenizeResult2.start;
					list4.Add((text4.Substring(num4, tokenizeResult2.end - num4), CompileErrorTypeInternal.BareIdIntegerTooWide));
					sortedDictionary.Add((tokenizeResult2.start, tokenizeResult2.end), ("|TOOBIG:(", false));
				}
				CheckUncoveredNonWhitespace(list4, sortedDictionary, input, num7, tokenizeResult2.start);
				num7 = tokenizeResult2.end;
			}
			if (num7 < input.Length)
			{
				CheckUncoveredNonWhitespace(list4, sortedDictionary, input, num7, input.Length);
			}
			Plugin.Singleton.logger.LogDebug((object)("CompileStringToSignal signals: " + string.Join(", ", array4)));
			foreach (int item8 in list3)
			{
				List<TokenizeResult> list5 = new List<TokenizeResult>();
				List<TokenizeResult> list6 = new List<TokenizeResult>();
				int num9 = array2[item8];
				TokenizeResult item = list[item8 - 1];
				list5.Add(item);
				int num10 = item8 - 1;
				while (num9 != num10)
				{
					if (num9 > num10)
					{
						if ((array[num9] + array3[array2[num9]]).CompareTo(array3[num9 - 1]) >= 0)
						{
							TokenizeResult item2 = list[num9 - 1];
							list5.Add(item2);
							num9 = array2[num9];
						}
						else
						{
							num9--;
						}
					}
					else if ((array[num10] + array3[array2[num10]]).CompareTo(array3[num10 - 1]) >= 0)
					{
						TokenizeResult item3 = list[num10 - 1];
						list6.Add(item3);
						num10 = array2[num10];
					}
					else
					{
						num10--;
					}
				}
				list5.Reverse();
				list6.Reverse();
				for (int num11 = 0; num11 < list5.Count; num11++)
				{
					TokenizeResult tokenizeResult3 = list5[num11];
					if (list5.Count == list6.Count)
					{
						sortedDictionary[(tokenizeResult3.start, tokenizeResult3.end)] = ($"|{tokenizeResult3.id}?{list6[num11].id}", false);
					}
					else
					{
						sortedDictionary[(tokenizeResult3.start, tokenizeResult3.end)] = ($"|{tokenizeResult3.id}?", false);
					}
				}
				string text5 = "";
				text5 += string.Join(" ", list5.Select(delegate(TokenizeResult tk)
				{
					string text7 = input;
					int start2 = tk.start;
					return text7.Substring(start2, tk.end - start2);
				}));
				text5 += "  ?  ";
				text5 += string.Join(" ", list6.Select(delegate(TokenizeResult tk)
				{
					string text7 = input;
					int start2 = tk.start;
					return text7.Substring(start2, tk.end - start2);
				}));
				list4.Add((text5, CompileErrorTypeInternal.Ambiguous));
			}
			string text6;
			if (Plugin.Singleton.configErrorLogAbbreviateTrace.Value)
			{
				List<string> list7 = new List<string>();
				bool flag = false;
				foreach (var value2 in sortedDictionary.Values)
				{
					var (item4, _) = value2;
					if (value2.Item2)
					{
						if (!flag)
						{
							list7.Add("|...");
							flag = true;
						}
					}
					else
					{
						list7.Add(item4);
						flag = false;
					}
				}
				text6 = string.Join(" ", list7);
			}
			else
			{
				text6 = string.Join(" ", sortedDictionary.Select((KeyValuePair<(int, int), (string, bool)> pair) => pair.Value.Item1));
			}
			Plugin.Singleton.logger.LogDebug((object)("CompileStringToSignal trace: " + text6));
			if (list4.Count > 0)
			{
				cr.compilerResultTag = (CompilerResultTag)1;
				ref string reference = ref DelegatesAndRefs.CompilerResult__errorMsg.Invoke(cr);
				reference = "- Compilation Failed -" + Environment.NewLine;
				ConfigErrorLogDisplayFormat value = Plugin.Singleton.configErrorLogDisplayFormat.Value;
				if (value == ConfigErrorLogDisplayFormat.TraceAndCount || value == ConfigErrorLogDisplayFormat.TraceAndVerbose)
				{
					reference = reference + text6 + Environment.NewLine + Environment.NewLine;
				}
				if (value == ConfigErrorLogDisplayFormat.TraceAndCount)
				{
					foreach (IGrouping<CompileErrorTypeInternal, (string, CompileErrorTypeInternal)> item9 in from err in list4
						group err by err.Item2 into grp
						orderby grp.Key
						select grp)
					{
						reference = reference + $"{item9.Key.GetString()}: {item9.Count()}" + Environment.NewLine;
					}
				}
				if (value == ConfigErrorLogDisplayFormat.Verbose || value == ConfigErrorLogDisplayFormat.TraceAndVerbose)
				{
					int num12 = 1;
					foreach (var item10 in list4)
					{
						string item5 = item10.Item1;
						CompileErrorTypeInternal item6 = item10.Item2;
						reference = reference + $"{num12} - {item6.GetString()}: " + Environment.NewLine + item5 + Environment.NewLine;
						num12++;
					}
				}
				Plugin.Singleton.logger.LogDebug((object)("CompileStringToSignal errorMsg: " + reference));
			}
			return new SignalMessage
			{
				signals = array4
			};
		}

		private static void CheckUncoveredNonWhitespace(List<(string, CompileErrorTypeInternal)> errors, SortedDictionary<(int, int), (string, bool)> traces, string input, int endPrev, int start)
		{
			int? num = null;
			for (int i = endPrev; i < start; i++)
			{
				if (!char.IsWhiteSpace(input[i]))
				{
					int valueOrDefault = num.GetValueOrDefault();
					if (!num.HasValue)
					{
						valueOrDefault = i;
						num = valueOrDefault;
					}
				}
				else if (num.HasValue)
				{
					int valueOrDefault = num.GetValueOrDefault();
					string item = input.Substring(valueOrDefault, i - valueOrDefault);
					traces.Add((num.GetValueOrDefault(), i), (item, false));
					errors.Add((item, CompileErrorTypeInternal.EntryNotFound));
					num = null;
				}
			}
			if (num.HasValue)
			{
				int valueOrDefault = num.GetValueOrDefault();
				string item2 = input.Substring(valueOrDefault, start - valueOrDefault);
				traces.Add((num.GetValueOrDefault(), start), (item2, false));
				errors.Add((item2, CompileErrorTypeInternal.EntryNotFound));
			}
		}
	}
	public enum ConfigErrorLogDisplayFormat
	{
		TraceAndCount,
		TraceAndVerbose,
		Verbose
	}
	[BepInPlugin("polymeric.BetterSignalCompiler", "Better Signal Compiler", "1.0.2")]
	public class Plugin : BaseUnityPlugin
	{
		internal static Plugin Singleton;

		public ManualLogSource logger;

		public Harmony harmony;

		public ConfigEntry<bool> configParserParseBareIds;

		public ConfigEntry<ConfigErrorLogDisplayFormat> configErrorLogDisplayFormat;

		public ConfigEntry<bool> configErrorLogAbbreviateTrace;

		private Plugin()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			logger = ((BaseUnityPlugin)this).Logger;
			harmony = new Harmony("polymeric.BetterSignalCompiler");
			configParserParseBareIds = ((BaseUnityPlugin)this).Config.Bind<bool>("Parser", "ParseBareIds", true, "Enables parsing expressions of the form '|<integer>' (e.g. '|5', '|-244'), which directly specify frequency offsets.\nfalse: Do not parse '|<integer>' expressions.\ntrue: Parse '|<integer>' expressions.");
			configErrorLogDisplayFormat = ((BaseUnityPlugin)this).Config.Bind<ConfigErrorLogDisplayFormat>("ErrorLog", "DisplayFormat", ConfigErrorLogDisplayFormat.Verbose, "Selects a formatting style for displaying compilation errors.\nTraceAndCount: Shows a compile trace, followed by a count of each error type.\nTraceAndVerbose: Shows a compile trace, followed by every error.\nVerbose: Shows every error, like the base game compiler.");
			configErrorLogAbbreviateTrace = ((BaseUnityPlugin)this).Config.Bind<bool>("ErrorLog", "AbbreviateTrace", false, "Controls whether compile traces print every parsed ID, or whether runs of IDs are collapsed with '|...'.\nfalse: Print every parsed signal ID interspersed with unparsed portions of the input.\ntrue: Collapse parsed signal IDs with '|...' and print unparsed portions of the input.");
		}

		private void Awake()
		{
			Singleton = this;
			Singleton.logger.LogInfo((object)"Hello from polymeric.BetterSignalCompiler v1.0.2!");
			HarmonyPatches.PatchInClassShallow(Singleton.harmony, typeof(HarmonyPatches));
		}
	}
	internal class TrieNode<T>
	{
		public Dictionary<char, TrieNode<T>> children = new Dictionary<char, TrieNode<T>>();

		public List<T> ids = new List<T>();
	}
	public class TriePrefix<T>
	{
		public readonly T id;

		public readonly int length;

		public TriePrefix(T id, int length)
		{
			this.id = id;
			this.length = length;
		}
	}
	public class Trie<T> where T : IComparable<T>
	{
		private readonly TrieNode<T> root = new TrieNode<T>();

		public bool Insert(T id, string word)
		{
			TrieNode<T> trieNode = root;
			foreach (char key in word)
			{
				if (trieNode.children.TryGetValue(key, out TrieNode<T> value))
				{
					trieNode = value;
					continue;
				}
				TrieNode<T> trieNode2 = new TrieNode<T>();
				trieNode.children[key] = trieNode2;
				trieNode = trieNode2;
			}
			int num = trieNode.ids.BinarySearch(id);
			if (num < 0)
			{
				trieNode.ids.Insert(~num, id);
				return true;
			}
			return false;
		}

		public IReadOnlyList<T> Search(string word)
		{
			TrieNode<T> trieNode = root;
			foreach (char key in word)
			{
				TrieNode<T> valueOrDefault = trieNode.children.GetValueOrDefault(key);
				if (valueOrDefault != null)
				{
					trieNode = valueOrDefault;
					continue;
				}
				return Array.Empty<T>();
			}
			return trieNode.ids;
		}

		public bool Delete(T id, string word)
		{
			TrieNode<T> trieNode = root;
			TrieNode<T> trieNode2 = null;
			char key = '\0';
			if (word.Length > 0)
			{
				trieNode2 = root;
				key = word[0];
			}
			foreach (char c in word)
			{
				TrieNode<T> valueOrDefault = trieNode.children.GetValueOrDefault(c);
				if (valueOrDefault != null)
				{
					if (trieNode.ids.Count > 0 || trieNode.children.Count > 1)
					{
						trieNode2 = trieNode;
						key = c;
					}
					trieNode = valueOrDefault;
					continue;
				}
				return false;
			}
			int num = trieNode.ids.BinarySearch(id);
			if (num >= 0)
			{
				if (trieNode2 != null && trieNode.ids.Count == 1 && trieNode.children.Count == 0)
				{
					trieNode2.children.Remove(key);
				}
				else
				{
					trieNode.ids.RemoveAt(num);
				}
				return true;
			}
			return false;
		}

		public List<TriePrefix<T>> Prefixes(string text, int offset)
		{
			List<TriePrefix<T>> list = new List<TriePrefix<T>>();
			TrieNode<T> trieNode = root;
			foreach (T id in trieNode.ids)
			{
				list.Add(new TriePrefix<T>(id, 0));
			}
			for (int i = offset; i < text.Length; i++)
			{
				char key = text[i];
				TrieNode<T> valueOrDefault = trieNode.children.GetValueOrDefault(key);
				if (valueOrDefault == null)
				{
					break;
				}
				trieNode = valueOrDefault;
				foreach (T id2 in trieNode.ids)
				{
					list.Add(new TriePrefix<T>(id2, i - offset + 1));
				}
			}
			return list;
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "polymeric.BetterSignalCompiler";

		public const string PLUGIN_NAME = "Better Signal Compiler";

		public const string PLUGIN_VERSION = "1.0.2";
	}
}