Decompiled source of Quads No Blasphemy v1.0.0

plugins/NoBlasphemy/NoBlasphemy.dll

Decompiled a day ago
using System;
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 System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CSVFile;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Pigeon.Movement;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially or fully created with the assistance of Generative AI.")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic")]
[assembly: AssemblyCompany("NoBlasphemy")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+f8a12f66cd84cb9bb5142512e181af7111bc681e")]
[assembly: AssemblyProduct("NoBlasphemy")]
[assembly: AssemblyTitle("NoBlasphemy")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace NoBlasphemy
{
	internal static class Filter
	{
		private static readonly string[] Reverent = new string[2] { "exch_faith_5", "vl_building_2" };

		private static readonly HashSet<TextBlock> blocked = new HashSet<TextBlock>();

		private static readonly HashSet<string> mutedNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, float> durations = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static Dictionary<uint, string> mutedIds;

		public static readonly List<(string group, int index, string variant, string id)> Testable = new List<(string, int, string, string)>();

		private static readonly Dictionary<TextBlock, TextBlock> alternatives = new Dictionary<TextBlock, TextBlock>();

		public static bool IsMuted(TextBlock block)
		{
			if (Plugin.MuteVoice.Value && block != null)
			{
				return blocked.Contains(block);
			}
			return false;
		}

		public static bool IsMuted(string eventName)
		{
			if (Plugin.MuteVoice.Value && eventName != null)
			{
				return mutedNames.Contains(eventName);
			}
			return false;
		}

		public static bool IsMuted(uint eventId, out string name)
		{
			name = null;
			if (!Plugin.MuteVoice.Value || mutedNames.Count == 0)
			{
				return false;
			}
			if (mutedIds == null)
			{
				mutedIds = new Dictionary<uint, string>();
				foreach (string mutedName in mutedNames)
				{
					mutedIds[AkUnitySoundEngine.GetIDFromString(mutedName)] = mutedName;
				}
			}
			return mutedIds.TryGetValue(eventId, out name);
		}

		public static float Duration(string name)
		{
			if (name == null || !durations.TryGetValue(name, out var value))
			{
				return 3f;
			}
			return value;
		}

		public static void Apply()
		{
			blocked.Clear();
			mutedNames.Clear();
			durations.Clear();
			mutedIds = null;
			HashSet<string> keep = Ids(Plugin.KeepLines.Value);
			HashSet<string> extra = Ids(Plugin.ExtraLines.Value);
			keep.UnionWith(Reverent);
			Dictionary<string, string> english = ReadEnglish();
			int language = TextBlocks.LanguageIndex;
			bool cleanText = Plugin.CleanSubtitles.Value;
			Dictionary<string, bool> everyLineBlocked = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
			int cleaned = 0;
			List<(string group, int index, string variant, string id)> found = new List<(string, int, string, string)>();
			alternatives.Clear();
			foreach (KeyValuePair<string, TextBlockGroup> @string in TextBlocks.strings)
			{
				TextBlockGroup value = @string.Value;
				HashSet<TextBlock[]> hashSet = new HashSet<TextBlock[]>();
				if (value.variations != null)
				{
					foreach (KeyValuePair<string, TextBlock[]> variation in value.variations)
					{
						hashSet.Add(variation.Value);
						for (int i = 0; i < variation.Value.Length; i++)
						{
							if (variation.Value[i] != null)
							{
								Consider(variation.Value[i], variation.Key, @string.Key, i);
							}
						}
						Alternatives(variation.Value);
					}
				}
				if (value.blocks == null || hashSet.Contains(value.blocks))
				{
					continue;
				}
				for (int j = 0; j < value.blocks.Length; j++)
				{
					if (value.blocks[j] != null)
					{
						Consider(value.blocks[j], null, @string.Key, j);
					}
				}
				Alternatives(value.blocks);
			}
			foreach (KeyValuePair<string, bool> item in everyLineBlocked)
			{
				if (item.Value)
				{
					mutedNames.Add(item.Key);
				}
			}
			Testable.Clear();
			Testable.AddRange(from f in found
				where mutedNames.Contains(f.id)
				orderby f.id
				select f);
			Plugin.Log.LogInfo((object)(string.Format("{0} {1}: {2} lines found, {3} subtitles cleaned, ", "Quad's No Blasphemy", "1.0.0", blocked.Count, cleaned) + $"{(Plugin.SwapLines.Value ? alternatives.Count : 0)} swapped for another line when picked at random, " + (Plugin.MuteVoice.Value ? "otherwise silenced" : "voice left on (MuteVoice = false)")));
			static void Alternatives(TextBlock[] lines)
			{
				List<TextBlock> list = lines.Where((TextBlock b) => b != null && !blocked.Contains(b)).ToList();
				if (list.Count != 0)
				{
					for (int num = 0; num < lines.Length; num++)
					{
						if (lines[num] != null && blocked.Contains(lines[num]))
						{
							alternatives[lines[num]] = list[num % list.Count];
						}
					}
				}
			}
			void Consider(TextBlock block, string variant, string groupKey, int index)
			{
				string uniqueID = block.uniqueID;
				if (!english.TryGetValue(Key(uniqueID, variant), out var value2) && !english.TryGetValue(Key(uniqueID, null), out value2))
				{
					value2 = ((language == 0) ? block.text : null);
				}
				bool flag = keep.Contains(uniqueID);
				bool flag2 = extra.Contains(uniqueID) || (!flag && Words.IsBlasphemous(value2));
				bool num = flag2 || (!flag && language != 0 && Words.IsBlasphemous(block.text, language));
				everyLineBlocked[uniqueID] = (!everyLineBlocked.TryGetValue(uniqueID, out var value3) || value3) && flag2;
				if (flag2)
				{
					blocked.Add(block);
					found.Add((groupKey, index, variant, uniqueID));
					durations[uniqueID] = Mathf.Clamp(1f + (float)(value2 ?? block.text).Length * 0.055f, 2f, 12f);
				}
				if (num)
				{
					if (cleanText)
					{
						string text = block.text;
						block.text = Words.Clean(block.text, language);
						block.variations = null;
						if (block.text != text)
						{
							cleaned++;
						}
					}
					if (Plugin.LogLines.Value)
					{
						Plugin.Log.LogInfo((object)("  " + uniqueID + ((variant != null) ? (" [" + variant + "]") : "") + (flag2 ? "" : " (text only)") + ": " + block.text));
					}
				}
			}
		}

		public static TextBlock Swap(TextBlock block)
		{
			if (block == null || !Plugin.SwapLines.Value || !alternatives.TryGetValue(block, out var value))
			{
				return block;
			}
			if (Plugin.LogLines.Value)
			{
				Plugin.Log.LogInfo((object)("No Blasphemy: said another line instead of " + block.uniqueID));
			}
			return value;
		}

		private static Dictionary<string, string> ReadEnglish()
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			TextAsset val = Resources.Load<TextAsset>("TextBlocks");
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"No Blasphemy: the game's text table wasn't found; only the shown language's text is checked");
				return dictionary;
			}
			try
			{
				CSVReader val2 = CSVReader.FromString(val.text, (CSVSettings)null);
				try
				{
					foreach (string[] item in val2.Lines())
					{
						if (item.Length >= 3 && !string.IsNullOrEmpty(item[0]))
						{
							string text = item[0].Trim();
							string variant = null;
							int num = text.LastIndexOf('[');
							if (text.EndsWith("]") && num > 0)
							{
								variant = text.Substring(num + 1, text.Length - num - 2);
								text = text.Substring(0, num).TrimEnd();
							}
							dictionary[Key(text, variant)] = item[2];
						}
					}
					return dictionary;
				}
				finally
				{
					((IDisposable)val2)?.Dispose();
				}
			}
			finally
			{
				Resources.UnloadAsset((Object)(object)val);
			}
		}

		private static string Key(string id, string variant)
		{
			return id + "\n" + variant;
		}

		private static HashSet<string> Ids(string list)
		{
			return new HashSet<string>(from s in (list ?? "").Split(',')
				select s.Trim() into s
				where s.Length > 0
				select s, StringComparer.OrdinalIgnoreCase);
		}
	}
	[HarmonyPatch(typeof(TextBlocks), "Initialize")]
	internal static class TextLoaded
	{
		private static void Postfix()
		{
			Filter.Apply();
		}
	}
	[HarmonyPatch(typeof(TextBlockGroup), "Get", new Type[] { })]
	internal static class PickAny
	{
		private static void Postfix(ref TextBlock __result)
		{
			__result = Filter.Swap(__result);
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class PickSeeded
	{
		private static void Postfix(ref TextBlock __result)
		{
			__result = Filter.Swap(__result);
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class PickCharacterLine
	{
		private static void Postfix(string parameter, ref TextBlock block, ref string __result)
		{
			TextBlock val = Filter.Swap(block);
			if (val != block)
			{
				block = val;
				__result = val.GetText(parameter, true);
			}
		}
	}
	[HarmonyPatch(typeof(Highlighter), "PlayVoicelineSound", new Type[]
	{
		typeof(TextBlock),
		typeof(GameObject),
		typeof(string),
		typeof(EventCallback),
		typeof(object)
	})]
	internal static class PlayerLine
	{
		private static bool Prefix(TextBlock block, ref bool __result)
		{
			if (!Filter.IsMuted(block))
			{
				return true;
			}
			if (Plugin.LogLines.Value)
			{
				Plugin.Log.LogInfo((object)("No Blasphemy: silenced " + block.uniqueID));
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(AkUnitySoundEngine), "PostEvent", new Type[]
	{
		typeof(uint),
		typeof(GameObject)
	})]
	internal static class EventById
	{
		private static bool Prefix(uint in_eventID, ref uint __result)
		{
			return Sounds.Allow(in_eventID, 0u, null, null, ref __result);
		}
	}
	[HarmonyPatch(typeof(AkUnitySoundEngine), "PostEvent", new Type[]
	{
		typeof(uint),
		typeof(GameObject),
		typeof(uint),
		typeof(EventCallback),
		typeof(object)
	})]
	internal static class EventByIdWithCallback
	{
		private static bool Prefix(uint in_eventID, uint in_uFlags, EventCallback in_pfnCallback, object in_pCookie, ref uint __result)
		{
			return Sounds.Allow(in_eventID, in_uFlags, in_pfnCallback, in_pCookie, ref __result);
		}
	}
	[HarmonyPatch(typeof(AkUnitySoundEngine), "PostEvent", new Type[]
	{
		typeof(string),
		typeof(GameObject)
	})]
	internal static class EventByName
	{
		private static bool Prefix(string in_pszEventName, ref uint __result)
		{
			return Sounds.Allow(in_pszEventName, 0u, null, null, ref __result);
		}
	}
	[HarmonyPatch(typeof(AkUnitySoundEngine), "PostEvent", new Type[]
	{
		typeof(string),
		typeof(GameObject),
		typeof(uint),
		typeof(EventCallback),
		typeof(object)
	})]
	internal static class EventByNameWithCallback
	{
		private static bool Prefix(string in_pszEventName, uint in_uFlags, EventCallback in_pfnCallback, object in_pCookie, ref uint __result)
		{
			return Sounds.Allow(in_pszEventName, in_uFlags, in_pfnCallback, in_pCookie, ref __result);
		}
	}
	internal static class Sounds
	{
		public static bool Allow(uint id, uint flags, EventCallback callback, object cookie, ref uint result)
		{
			if (Filter.IsMuted(id, out var name))
			{
				return Silence(name, flags, callback, cookie, ref result);
			}
			return true;
		}

		public static bool Allow(string name, uint flags, EventCallback callback, object cookie, ref uint result)
		{
			if (Filter.IsMuted(name))
			{
				return Silence(name, flags, callback, cookie, ref result);
			}
			return true;
		}

		private static bool Silence(string name, uint flags, EventCallback callback, object cookie, ref uint result)
		{
			if (Plugin.LogLines.Value)
			{
				Plugin.Log.LogInfo((object)("No Blasphemy: silenced " + name));
			}
			if ((flags & 1) != 0)
			{
				Later.Call(Filter.Duration(name), callback, cookie);
			}
			result = 0u;
			return false;
		}
	}
	[BepInPlugin("quadslab.mycopunk.noblasphemy", "Quad's No Blasphemy", "1.0.0")]
	[BepInProcess("Mycopunk.exe")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "quadslab.mycopunk.noblasphemy";

		public const string Title = "Quad's No Blasphemy";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> MuteVoice;

		internal static ConfigEntry<bool> CleanSubtitles;

		internal static ConfigEntry<bool> SwapLines;

		internal static ConfigEntry<string> KeepLines;

		internal static ConfigEntry<string> ExtraLines;

		internal static ConfigEntry<bool> LogLines;

		internal static ConfigEntry<bool> TestKey;

		private GameObject runner;

		private Harmony harmony;

		private void Awake()
		{
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Expected O, but got Unknown
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			MuteVoice = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "MuteVoice", true, "Silence the spoken lines that use God's name in vain. A recording can't lose just one word, so the whole line is quiet; its subtitle still shows, cleaned.");
			CleanSubtitles = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "CleanSubtitles", true, "Take God's name out of the subtitles, in every language the game has. Takes effect at the next start.");
			SwapLines = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SwapLines", true, "When a character has other lines for the same moment (a call for help), say one of those instead of going quiet.");
			KeepLines = ((BaseUnityPlugin)this).Config.Bind<string>("Lines", "Keep", "", "More line IDs to leave alone, comma-separated (the lines that speak of God sincerely are always kept). Takes effect at the next start.");
			ExtraLines = ((BaseUnityPlugin)this).Config.Bind<string>("Lines", "Extra", "", "More line IDs to clean and silence, comma-separated, for any line the word list misses. Takes effect at the next start.");
			LogLines = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogLines", false, "Write every line changed (at start) and every line silenced (as it plays) to the BepInEx log, with its ID.");
			TestKey = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "TestKey", false, "F8 in a game plays the next line this mod changes, as mission control would say it: you see its cleaned subtitle and hear nothing. (A file BepInEx\\noblasphemy-test.txt does the same, for automated tests.)");
			runner = new GameObject("NoBlasphemy");
			Object.DontDestroyOnLoad((Object)(object)runner);
			((Object)runner).hideFlags = (HideFlags)61;
			runner.AddComponent<Later>();
			harmony = new Harmony("quadslab.mycopunk.noblasphemy");
			harmony.PatchAll(typeof(Plugin).Assembly);
			if (TextBlocks.strings.Count > 0)
			{
				Filter.Apply();
			}
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if ((Object)(object)runner != (Object)null)
			{
				Object.Destroy((Object)(object)runner);
			}
		}
	}
	internal sealed class Later : MonoBehaviour
	{
		private static readonly List<(float at, EventCallback callback, object cookie)> pending = new List<(float, EventCallback, object)>();

		private int nextTest;

		private float nextFileCheck;

		public static void Call(float delay, EventCallback callback, object cookie)
		{
			if (callback != null)
			{
				pending.Add((Time.unscaledTime + delay, callback, cookie));
			}
		}

		private void Update()
		{
			if (Plugin.TestKey.Value && Keyboard.current != null && ((ButtonControl)Keyboard.current.f8Key).wasPressedThisFrame)
			{
				PlayTest();
			}
			if (Plugin.TestKey.Value && Time.unscaledTime >= nextFileCheck)
			{
				nextFileCheck = Time.unscaledTime + 0.5f;
				string path = Path.Combine(Paths.BepInExRootPath, "noblasphemy-test.txt");
				if (File.Exists(path))
				{
					try
					{
						File.Delete(path);
					}
					catch (Exception)
					{
					}
					PlayTest();
				}
			}
			for (int num = pending.Count - 1; num >= 0; num--)
			{
				if (!(Time.unscaledTime < pending[num].at))
				{
					(float at, EventCallback callback, object cookie) tuple = pending[num];
					EventCallback item = tuple.callback;
					object item2 = tuple.cookie;
					pending.RemoveAt(num);
					try
					{
						item.Invoke(item2, (AkCallbackType)1, (AkCallbackInfo)null);
					}
					catch (Exception ex2)
					{
						Plugin.Log.LogWarning((object)("No Blasphemy: a line's finish callback failed: " + ex2.Message));
					}
				}
			}
		}

		private void PlayTest()
		{
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			if (Filter.Testable.Count == 0 || (Object)(object)PlayerLook.Instance == (Object)null || (Object)(object)Highlighter.Instance == (Object)null)
			{
				Plugin.Log.LogInfo((object)"No Blasphemy: the test key works once you're in the game");
				return;
			}
			var (text, num, text2, arg) = Filter.Testable[nextTest++ % Filter.Testable.Count];
			Plugin.Log.LogInfo((object)$"No Blasphemy: test {nextTest}/{Filter.Testable.Count}: {arg}");
			try
			{
				Highlighter.Instance.PlayMissionControlVoiceline(new Voiceline(text, 1000), (string)null, 0, text2, (Predicate)null, (Action)null, 0f, num, (Action<DialogueBox>)null, false, (EventCallback)null, (object)null, (Sprite)null, (GameObject)null);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("No Blasphemy: the test line didn't play: " + ex.Message));
			}
		}
	}
	public static class Words
	{
		private const RegexOptions Opts = RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;

		private static readonly (Regex pattern, MatchEvaluator replace)[][] Rules = new(Regex, MatchEvaluator)[10][]
		{
			new(Regex, MatchEvaluator)[10]
			{
				(new Regex("\\bgod[\\s-]*dam(?:n|m)?(it|ed)?\\b", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant), (Match m) => Cased(m.Value, (m.Groups[1].Value.ToLowerInvariant() == "it") ? "dang it" : "dang")),
				R("(?<=\\bmy\\s+)(god|lord|gawd)\\b", "gosh"),
				R("(?<=\\bthank\\s+)(god|gawd|the\\s+lord)\\b", "goodness"),
				R("\\bfor\\s+god'?s\\s+sake\\b", "for goodness' sake"),
				R("(?<=\\bgood\\s+)lord\\b", "grief"),
				R("(?<=\\b(?:oh|dear|sweet)\\s+)lord\\b", "gosh"),
				R("\\blord\\s+almighty\\b", "goodness gracious"),
				R("\\bomg\\b", "oh my gosh"),
				R("\\bgod\\s+help\\s+us\\b", "heaven help us"),
				R("\\b(god|gawd|jesus|christ|lordy)\\b", "gosh")
			},
			new(Regex, MatchEvaluator)[6]
			{
				R("\\bnom\\s+de\\s+dieu\\b", "nom d'une pipe"),
				R("\\bbonté\\s+divine\\b", "bon sang"),
				R("\\b(?:oh\\s+)?mon\\s+dieu\\b", "oh là là"),
				R("\\bseigneur(?=\\s*[!,.…])", "bon sang"),
				R("\\bdieu(?=\\s*[!…])", "bon sang"),
				R("\\bj[ée]sus\\b", "purée")
			},
			new(Regex, MatchEvaluator)[8]
			{
				R("\\bgottverdammt", "verflixt"),
				R("\\bum\\s+gottes\\s+willen\\b", "um Himmels willen"),
				R("\\bgott\\s+sei\\s+dank\\b", "zum Glück"),
				R("\\bmein\\s+gott\\b", "meine Güte"),
				R("\\boh\\s+gott\\b", "oh Mann"),
				R("\\bgrundgütiger\\b", "meine Güte"),
				R("\\bherrgott\\w*", "Mensch"),
				R("\\b(jessas|jesus)\\b", "Mannomann")
			},
			new(Regex, MatchEvaluator)[7]
			{
				R("\\bdios\\s+m[ií]o\\b", "madre mía"),
				R("\\bgracias\\s+a\\s+dios\\b", "menos mal"),
				R("\\bpor\\s+dios\\b", "por favor"),
				R("\\bque\\s+dios\\s+nos\\s+\\w+", "que haya suerte"),
				R("\\bdios(?=\\s*[,!…])", "madre mía"),
				R("\\bhostias?\\b", "ostras"),
				R("\\bjes[uú]s\\b", "caramba")
			},
			new(Regex, MatchEvaluator)[4]
			{
				R("\\bне\\s+слава\\s+богу\\b", "не так"),
				R("\\bслава\\s+богу\\b", "к счастью"),
				R("\\b(?:о\\s+)?боже(?:\\s+мой)?\\b", "ох"),
				R("\\b(?:о\\s+)?господи\\b", "ох")
			},
			new(Regex, MatchEvaluator)[7]
			{
				R("\\bmeu\\s+deus(?:\\s+do\\s+c[ée]u)?\\b", "minha nossa"),
				R("\\bgraças\\s+a\\s+deus(?=\\s+\\p{L})", "ainda bem que"),
				R("\\bgraças\\s+a\\s+deus\\b", "ainda bem"),
				R("\\bpelo\\s+amor\\s+de\\s+deus\\b", "por favor"),
				R("\\bdeus\\s+do\\s+c[ée]u\\b", "caramba"),
				R("^senhor(?=\\s*[,!])", "caramba"),
				R("\\bjesus\\b", "caramba")
			},
			new(Regex, MatchEvaluator)[4]
			{
				R("愿上帝保佑我们", "但愿我们走运"),
				R("我的上帝", "我的天"),
				R("上帝", "老天"),
				R("耶稣", "天哪")
			},
			new(Regex, MatchEvaluator)[5]
			{
				R("願主保佑我們", "但願我們走運"),
				R("我的上帝", "我的天"),
				R("上帝", "老天"),
				R("主啊", "天啊"),
				R("耶穌", "天哪")
			},
			new(Regex, MatchEvaluator)[3]
			{
				R("オーマイゴッド", "なんてこった"),
				R("ジーザス", "なんてこった"),
				R("神よ", "なんてこった")
			},
			new(Regex, MatchEvaluator)[4]
			{
				R("하느님|하나님", "세상에"),
				R("오\\s*마이\\s*갓", "세상에"),
				R("지저스", "세상에"),
				R("주여", "세상에")
			}
		};

		private static (Regex, MatchEvaluator) R(string pattern, string replacement)
		{
			return (new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant), (Match m) => Cased(m.Value, replacement));
		}

		private static (Regex pattern, MatchEvaluator replace)[] For(int language)
		{
			if (language < 0 || language >= Rules.Length)
			{
				return Rules[0];
			}
			return Rules[language];
		}

		public static bool IsBlasphemous(string text, int language = 0)
		{
			if (!string.IsNullOrEmpty(text))
			{
				return For(language).Any(((Regex pattern, MatchEvaluator replace) r) => r.pattern.IsMatch(text));
			}
			return false;
		}

		public static string Clean(string text, int language = 0)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			(Regex, MatchEvaluator)[] array = For(language);
			for (int i = 0; i < array.Length; i++)
			{
				(Regex, MatchEvaluator) tuple = array[i];
				Regex item = tuple.Item1;
				MatchEvaluator item2 = tuple.Item2;
				text = item.Replace(text, item2);
			}
			return text;
		}

		private static string Cased(string original, string replacement)
		{
			string text = original.Split(' ', '-', '\t', '\n')[^1];
			if (IsShouted(original) || (text.Length > 1 && IsShouted(text)))
			{
				return replacement.ToUpperInvariant();
			}
			if (char.IsUpper(original[0]))
			{
				return char.ToUpperInvariant(replacement[0]) + replacement.Substring(1);
			}
			return replacement;
		}

		private static bool IsShouted(string s)
		{
			bool flag = false;
			foreach (char c in s)
			{
				if (char.IsLower(c))
				{
					return false;
				}
				if (char.IsLetter(c))
				{
					flag = true;
				}
			}
			if (flag)
			{
				return s.Length > 1;
			}
			return false;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}