Decompiled source of TrollReplacer v2.1.2

TrollReplacer.dll

Decompiled 3 weeks 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.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("TrollReplacer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("2.1.2.0")]
[assembly: AssemblyInformationalVersion("2.1.2")]
[assembly: AssemblyProduct("TrollReplacer")]
[assembly: AssemblyTitle("TrollReplacer")]
[assembly: AssemblyVersion("2.1.2.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 TrollReplacer
{
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class AudioSource_SetClip_Patch
	{
		private static void Prefix(AudioSource __instance, ref AudioClip __0)
		{
			try
			{
				AudioClip replacement = Plugin.GetReplacement(__0, ((Component)__instance).gameObject);
				if ((Object)(object)replacement != (Object)null)
				{
					__0 = replacement;
				}
			}
			catch (Exception e)
			{
				Plugin.LogErrorOnce(e);
			}
		}
	}
	[HarmonyPatch(typeof(AudioSource), "PlayOneShot", new Type[]
	{
		typeof(AudioClip),
		typeof(float)
	})]
	internal static class AudioSource_PlayOneShot2_Patch
	{
		private static void Prefix(AudioSource __instance, ref AudioClip __0)
		{
			try
			{
				AudioClip replacement = Plugin.GetReplacement(__0, ((Component)__instance).gameObject);
				if ((Object)(object)replacement != (Object)null)
				{
					__0 = replacement;
				}
			}
			catch (Exception e)
			{
				Plugin.LogErrorOnce(e);
			}
		}
	}
	[HarmonyPatch(typeof(AudioSource), "PlayOneShot", new Type[] { typeof(AudioClip) })]
	internal static class AudioSource_PlayOneShot1_Patch
	{
		private static void Prefix(AudioSource __instance, ref AudioClip __0)
		{
			try
			{
				AudioClip replacement = Plugin.GetReplacement(__0, ((Component)__instance).gameObject);
				if ((Object)(object)replacement != (Object)null)
				{
					__0 = replacement;
				}
			}
			catch (Exception e)
			{
				Plugin.LogErrorOnce(e);
			}
		}
	}
	[HarmonyPatch(typeof(AudioSource), "Play", new Type[] { })]
	internal static class AudioSource_Play_Patch
	{
		private static void Prefix(AudioSource __instance)
		{
			try
			{
				AudioClip clip = __instance.clip;
				if (!((Object)(object)clip == (Object)null))
				{
					AudioClip replacement = Plugin.GetReplacement(clip, ((Component)__instance).gameObject);
					if ((Object)(object)replacement != (Object)null)
					{
						__instance.clip = replacement;
					}
				}
			}
			catch (Exception e)
			{
				Plugin.LogErrorOnce(e);
			}
		}
	}
	[HarmonyPatch(typeof(ZSFX), "Awake")]
	internal static class ZSFX_Awake_Patch
	{
		private static void Prefix(ZSFX __instance)
		{
			Plugin.ReplaceInZsfx(__instance);
		}

		private static void Postfix(ZSFX __instance)
		{
			Plugin.ReplaceInZsfx(__instance);
		}
	}
	[HarmonyPatch]
	internal static class ZSFX_Play_Patch
	{
		private static MethodBase Find()
		{
			try
			{
				return AccessTools.Method(typeof(ZSFX), "Play", new Type[0], (Type[])null);
			}
			catch
			{
				return null;
			}
		}

		private static bool Prepare()
		{
			return Find() != null;
		}

		private static MethodBase TargetMethod()
		{
			return Find();
		}

		private static void Prefix(ZSFX __instance)
		{
			Plugin.ReplaceInZsfx(__instance);
		}
	}
	[HarmonyPatch]
	internal static class Character_Init_Patch
	{
		private static List<MethodBase> Find()
		{
			List<MethodBase> list = new List<MethodBase>();
			string[] array = new string[2] { "Awake", "Start" };
			foreach (string text in array)
			{
				MethodBase methodBase = null;
				try
				{
					methodBase = AccessTools.DeclaredMethod(typeof(Character), text, (Type[])null, (Type[])null);
				}
				catch
				{
				}
				if (methodBase != null)
				{
					list.Add(methodBase);
				}
			}
			return list;
		}

		private static bool Prepare()
		{
			return Find().Any();
		}

		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Find();
		}

		private static void Postfix(Character __instance)
		{
			TrollScanner.OnCharacterAwake(__instance);
		}
	}
	[BepInPlugin("com.niclas.trollreplacer.v2", "Troll Replacer", "2.1.2")]
	public class Plugin : BaseUnityPlugin
	{
		private sealed class KeywordSet
		{
			private string _raw;

			private string[] _parts = new string[0];

			public string[] Get(string raw)
			{
				raw = raw ?? "";
				if (raw != _raw)
				{
					_raw = raw;
					List<string> list = new List<string>();
					string[] array = raw.Split(new char[1] { ',' });
					for (int i = 0; i < array.Length; i++)
					{
						string text = array[i].Trim().ToLowerInvariant();
						if (text.Length > 0)
						{
							list.Add(text);
						}
					}
					_parts = list.ToArray();
				}
				return _parts;
			}
		}

		public const string PluginGuid = "com.niclas.trollreplacer.v2";

		public const string PluginName = "Troll Replacer";

		public const string PluginVersion = "2.1.2";

		internal static ManualLogSource Log;

		internal static ConfigEntry<string> MatchKeywords;

		internal static ConfigEntry<string> ExcludeKeywords;

		internal static ConfigEntry<string> ReplacementFileName;

		internal static ConfigEntry<float> VolumeBoost;

		private static ConfigEntry<int> _configVersion;

		private const int CurrentConfigVersion = 2;

		internal static ConfigEntry<bool> ReplaceEffectPrefabs;

		internal static ConfigEntry<bool> LogUniqueSounds;

		internal static ConfigEntry<bool> LogAllClipNames;

		private static string _pluginDir = ".";

		private static ConfigFile _configFile;

		private static WavLoader.WavData _data;

		private static AudioClip _clip;

		private static int _createFailures;

		private static int _errors;

		private static int _matchLogs;

		private static int _totalReplaced;

		private const int MaxUniqueLogs = 400;

		private static readonly HashSet<string> _seen = new HashSet<string>();

		private static readonly KeywordSet _match = new KeywordSet();

		private static readonly KeywordSet _exclude = new KeywordSet();

		private static readonly Type[] PatchTypes = new Type[7]
		{
			typeof(AudioSource_SetClip_Patch),
			typeof(AudioSource_PlayOneShot2_Patch),
			typeof(AudioSource_PlayOneShot1_Patch),
			typeof(AudioSource_Play_Patch),
			typeof(ZSFX_Awake_Patch),
			typeof(ZSFX_Play_Patch),
			typeof(Character_Init_Patch)
		};

		private static readonly Type[] SetDataParamTypes = new Type[2]
		{
			typeof(float[]),
			typeof(int)
		};

		private static MethodInfo _setDataMethod;

		internal static AudioClip Replacement
		{
			get
			{
				if ((Object)(object)_clip != (Object)null)
				{
					return _clip;
				}
				if (_data == null || _createFailures >= 3)
				{
					return null;
				}
				try
				{
					AudioClip obj = AudioClip.Create("custom_replacement_clip", _data.Samples.Length, 1, _data.SampleRate, false);
					if ((Object)(object)obj == (Object)null)
					{
						throw new Exception("AudioClip.Create lieferte null");
					}
					if (!CallSetData(obj, _data.Samples))
					{
						throw new Exception("SetData lieferte false");
					}
					((Object)obj).hideFlags = (HideFlags)61;
					_clip = obj;
				}
				catch (Exception ex)
				{
					_createFailures++;
					Log.LogError((object)("[TrollReplacer] Ersatz-Clip konnte nicht erzeugt werden (Versuch " + _createFailures + "): " + ex.Message));
					return null;
				}
				return _clip;
			}
		}

		private void Awake()
		{
			try
			{
				Log = ((BaseUnityPlugin)this).Logger;
				_configFile = ((BaseUnityPlugin)this).Config;
				_pluginDir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? ".";
				MatchKeywords = ((BaseUnityPlugin)this).Config.Bind<string>("General", "MatchKeywords", "troll_attacking", "Kommagetrennte Suchbegriffe (Gross-/Kleinschreibung egal). Ersetzt wird, wenn der Clip-Name ODER der Name des abspielenden Objekts einen davon enthaelt. Standard: NUR das Angriffsbruellen (sfx_troll_attacking) - Treffer/Idle/Alarm/Tod bleiben unangetastet.");
				ExcludeKeywords = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ExcludeKeywords", "step,walk,foot,run,jump,swim", "Kommagetrennte Begriffe, die NICHT ersetzt werden (z.B. Schrittgeraeusche). Leer = alles ersetzen.");
				ReplacementFileName = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ReplacementFileName", "TROLL.wav", "OPTIONAL: Eigene Audiodatei neben der DLL, die die eingebettete ersetzt. Jede WAV geht (8/16/24/32-bit, Float, Stereo). Ist die Datei nicht da, wird die eingebettete benutzt.");
				ReplaceEffectPrefabs = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced", "ReplaceTrollEffectPrefabs", true, "Wenn true: Die Sound-Effekte, die ein Troll ueber seine eigenen Effektlisten (Alarm/Idle/Tod/Treffer) abspielt, werden direkt umgestellt - unabhaengig von den Clip-Namen.");
				VolumeBoost = ((BaseUnityPlugin)this).Config.Bind<float>("General", "VolumeBoost", 3f, "Verstaerkungsfaktor fuer die Lautstaerke (3.0 = 300% / dreifache Lautstaerke). Wird direkt auf die Samples angewendet, bevor der Clip erzeugt wird. Bei hohen Werten kann es zu Verzerrungen (Clipping) kommen, wenn das Original schon laut war - das ist bei so einem Boost gewollt/normal.");
				LogUniqueSounds = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogUniqueSounds", true, "Schreibt jeden verschiedenen Sound (max. 400) einmalig ins Log. Hilft, echte Namen zu finden.");
				LogAllClipNames = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogAllClipNames", false, "Schreibt JEDEN Abspielvorgang ins Log (sehr viel Text!).");
				MigrateOldConfig();
				Log.LogInfo((object)("[TrollReplacer] v2.1.2 startet. DLL-Ordner: " + _pluginDir));
				Log.LogInfo((object)("[TrollReplacer] Suchbegriffe='" + MatchKeywords.Value + "' Ausschluss='" + ExcludeKeywords.Value + "' EffektPrefabs=" + ReplaceEffectPrefabs.Value + " VolumeBoost=x" + VolumeBoost.Value));
				ApplyPatches();
				((MonoBehaviour)this).StartCoroutine(LoadRoutine());
			}
			catch (Exception ex)
			{
				if (Log != null)
				{
					Log.LogError((object)("[TrollReplacer] Fehler beim Start: " + ex));
				}
			}
		}

		private static void MigrateOldConfig()
		{
			_configVersion = _configFile.Bind<int>("Internal", "ConfigVersion", 0, "Interner Zaehler fuer automatische Config-Migrationen. Nicht manuell aendern.");
			if (_configVersion.Value < 2)
			{
				if (_configVersion.Value < 2 && MatchKeywords.Value == "troll")
				{
					Log.LogInfo((object)"[TrollReplacer] Config-Migration: MatchKeywords war noch der alte Standardwert 'troll' -> auf neuen Standard 'troll_attacking' angehoben. (Eigene Anpassungen waeren hiervon nicht betroffen gewesen.)");
					MatchKeywords.Value = "troll_attacking";
				}
				_configVersion.Value = 2;
			}
		}

		private static void ApplyPatches()
		{
			int num = 0;
			StringBuilder stringBuilder = new StringBuilder();
			Type[] patchTypes = PatchTypes;
			foreach (Type type in patchTypes)
			{
				try
				{
					Harmony.CreateAndPatchAll(type, "com.niclas.trollreplacer.v2");
					num++;
				}
				catch (Exception ex)
				{
					stringBuilder.Append(type.Name).Append(" (").Append(ex.GetType().Name)
						.Append(": ")
						.Append(ex.Message)
						.Append(")  ");
				}
			}
			Log.LogInfo((object)("[TrollReplacer] Patches angewendet: " + num + " von " + PatchTypes.Length));
			if (stringBuilder.Length > 0)
			{
				Log.LogWarning((object)("[TrollReplacer] Folgende Patches sind ausgefallen (der Rest laeuft weiter): " + stringBuilder));
			}
		}

		private IEnumerator LoadRoutine()
		{
			yield return null;
			yield return null;
			try
			{
				LoadData();
				AudioClip replacement = Replacement;
				if ((Object)(object)replacement != (Object)null)
				{
					Log.LogInfo((object)("[TrollReplacer] BEREIT - Ersatz-Sound aktiv (" + replacement.length.ToString("0.00") + " s)."));
				}
				else
				{
					Log.LogError((object)"[TrollReplacer] NICHT BEREIT - kein Ersatz-Sound verfuegbar, siehe Meldungen oben.");
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("[TrollReplacer] Fehler beim Laden: " + ex));
			}
		}

		private static void LoadData()
		{
			WavLoader.WavData wavData = null;
			string text = null;
			string value = ReplacementFileName.Value;
			if (!string.IsNullOrEmpty(value))
			{
				string text2 = Path.Combine(_pluginDir, value);
				if (File.Exists(text2))
				{
					try
					{
						wavData = WavLoader.Parse(File.ReadAllBytes(text2));
						text = "externe Datei " + text2;
					}
					catch (Exception ex)
					{
						Log.LogWarning((object)("[TrollReplacer] Externe Datei '" + text2 + "' unbrauchbar (" + ex.Message + "). Nutze stattdessen die eingebettete."));
					}
				}
			}
			if (wavData == null)
			{
				try
				{
					byte[] array = ReadEmbedded();
					if (array == null)
					{
						Log.LogError((object)"[TrollReplacer] Eingebettete Audiodatei nicht im DLL gefunden.");
					}
					else
					{
						wavData = WavLoader.Parse(array);
						text = "eingebettete Datei";
					}
				}
				catch (Exception ex2)
				{
					Log.LogError((object)("[TrollReplacer] Eingebettete Audiodatei unbrauchbar: " + ex2.Message));
				}
			}
			if (wavData != null)
			{
				ApplyVolumeBoost(wavData);
				_data = wavData;
				Log.LogInfo((object)("[TrollReplacer] Audio geladen aus " + text + ": " + wavData.Samples.Length + " Samples, " + wavData.SampleRate + " Hz, Original " + wavData.SourceChannels + " Kanal/" + wavData.SourceBits + "-bit, Spitzenpegel nach Boost " + wavData.Peak.ToString("0.00")));
				if (wavData.Peak < 0.001f)
				{
					Log.LogWarning((object)"[TrollReplacer] ACHTUNG: Die Audiodatei ist praktisch stumm!");
				}
			}
		}

		private static void ApplyVolumeBoost(WavLoader.WavData data)
		{
			float value = VolumeBoost.Value;
			if (value == 1f)
			{
				return;
			}
			float[] samples = data.Samples;
			int num = 0;
			float num2 = 0f;
			for (int i = 0; i < samples.Length; i++)
			{
				float num3 = samples[i] * value;
				if (num3 > 1f)
				{
					num3 = 1f;
					num++;
				}
				else if (num3 < -1f)
				{
					num3 = -1f;
					num++;
				}
				samples[i] = num3;
				float num4 = ((num3 < 0f) ? (0f - num3) : num3);
				if (num4 > num2)
				{
					num2 = num4;
				}
			}
			data.Peak = num2;
			if (num > 0)
			{
				Log.LogWarning((object)("[TrollReplacer] Lautstaerke-Boost x" + value.ToString("0.0") + " angewendet - " + num + " von " + samples.Length + " Samples wurden dabei gekappt (haerterer/verzerrter Klang, aber lauter)."));
			}
			else
			{
				Log.LogInfo((object)("[TrollReplacer] Lautstaerke-Boost x" + value.ToString("0.0") + " angewendet, keine Kappung noetig."));
			}
		}

		private static byte[] ReadEmbedded()
		{
			Assembly assembly = typeof(Plugin).Assembly;
			Stream manifestResourceStream = assembly.GetManifestResourceStream("TROLL.wav");
			if (manifestResourceStream == null)
			{
				string[] manifestResourceNames = assembly.GetManifestResourceNames();
				foreach (string text in manifestResourceNames)
				{
					if (text.EndsWith(".wav", StringComparison.OrdinalIgnoreCase))
					{
						manifestResourceStream = assembly.GetManifestResourceStream(text);
						break;
					}
				}
			}
			if (manifestResourceStream == null)
			{
				return null;
			}
			using (manifestResourceStream)
			{
				using MemoryStream memoryStream = new MemoryStream();
				manifestResourceStream.CopyTo(memoryStream);
				return memoryStream.ToArray();
			}
		}

		private static bool CallSetData(AudioClip clip, float[] samples)
		{
			if (_setDataMethod == null)
			{
				_setDataMethod = typeof(AudioClip).GetMethod("SetData", SetDataParamTypes);
				if (_setDataMethod == null)
				{
					throw new Exception("AudioClip.SetData(float[], int) wurde nicht gefunden.");
				}
			}
			return (bool)_setDataMethod.Invoke(clip, new object[2] { samples, 0 });
		}

		internal static bool AnyContains(string haystackLower, string[] keywords)
		{
			if (string.IsNullOrEmpty(haystackLower))
			{
				return false;
			}
			for (int i = 0; i < keywords.Length; i++)
			{
				if (haystackLower.Contains(keywords[i]))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool MatchesKeywords(string lower)
		{
			return AnyContains(lower, _match.Get(MatchKeywords.Value));
		}

		internal static bool IsExcluded(string lower)
		{
			return AnyContains(lower, _exclude.Get(ExcludeKeywords.Value));
		}

		private static string ChainLower(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder(96);
			Transform val = go.transform;
			int num = 0;
			while ((Object)(object)val != (Object)null && num < 4)
			{
				stringBuilder.Append(((Object)val).name).Append('/');
				val = val.parent;
				num++;
			}
			Transform root = go.transform.root;
			if ((Object)(object)root != (Object)null)
			{
				stringBuilder.Append(((Object)root).name);
			}
			return stringBuilder.ToString().ToLowerInvariant();
		}

		internal static AudioClip GetReplacement(AudioClip clip, GameObject owner)
		{
			try
			{
				if ((Object)(object)clip == (Object)null)
				{
					return null;
				}
				AudioClip clip2 = _clip;
				if ((Object)(object)clip2 != (Object)null && clip == clip2)
				{
					return null;
				}
				string text = ((Object)clip).name ?? "";
				string text2 = (((Object)(object)owner != (Object)null) ? ((Object)owner).name : "");
				string text3 = ChainLower(owner);
				if (LogAllClipNames.Value)
				{
					Log.LogInfo((object)("[TrollReplacer] Abspielen: clip='" + text + "' objekt='" + text2 + "'"));
				}
				LogUnique(text, text2, text3);
				string lower = text.ToLowerInvariant();
				string lower2 = text2.ToLowerInvariant();
				if (IsExcluded(lower) || IsExcluded(lower2))
				{
					return null;
				}
				bool flag = MatchesKeywords(lower);
				bool flag2 = !flag && MatchesKeywords(text3);
				if (!flag && !flag2)
				{
					return null;
				}
				clip2 = Replacement;
				if ((Object)(object)clip2 == (Object)null)
				{
					return null;
				}
				_totalReplaced++;
				if (_matchLogs < 40)
				{
					_matchLogs++;
					Log.LogInfo((object)("[TrollReplacer] TREFFER #" + _totalReplaced + ": clip='" + text + "' objekt='" + text2 + "' (erkannt ueber " + (flag ? "Clip-Name" : "Objekt-Name") + ") -> ersetzt"));
				}
				return clip2;
			}
			catch (Exception e)
			{
				LogErrorOnce(e);
				return null;
			}
		}

		internal static void ReplaceInZsfx(ZSFX z)
		{
			try
			{
				if ((Object)(object)z == (Object)null || z.m_audioClips == null)
				{
					return;
				}
				GameObject gameObject = ((Component)z).gameObject;
				for (int i = 0; i < z.m_audioClips.Length; i++)
				{
					AudioClip replacement = GetReplacement(z.m_audioClips[i], gameObject);
					if ((Object)(object)replacement != (Object)null)
					{
						z.m_audioClips[i] = replacement;
					}
				}
			}
			catch (Exception e)
			{
				LogErrorOnce(e);
			}
		}

		private static void LogUnique(string clip, string own, string chain)
		{
			if (!LogUniqueSounds.Value)
			{
				return;
			}
			if (_seen.Count >= 400)
			{
				if (_seen.Count == 400)
				{
					_seen.Add("#limit");
					Log.LogInfo((object)("[TrollReplacer] (Limit von " + 400 + " verschiedenen Sounds im Log erreicht.)"));
				}
			}
			else if (_seen.Add(clip + "|" + own))
			{
				Log.LogInfo((object)("[TrollReplacer] Sound gesehen: clip='" + clip + "' objekt='" + own + "' pfad='" + chain + "'"));
			}
		}

		internal static void LogErrorOnce(Exception e)
		{
			if (_errors < 5)
			{
				_errors++;
				if (Log != null)
				{
					Log.LogError((object)("[TrollReplacer] Interner Fehler (Spiel laeuft weiter): " + e));
				}
			}
		}
	}
	internal static class TrollScanner
	{
		private static readonly HashSet<string> _done = new HashSet<string>();

		private static readonly HashSet<int> _patchedPrefabs = new HashSet<int>();

		private static readonly string[] VoiceHints = new string[14]
		{
			"alert", "idle", "death", "dead", "hit", "crit", "backstab", "pain", "taunt", "roar",
			"voice", "sound", "growl", "scream"
		};

		private static readonly string[] SkipHints = new string[12]
		{
			"foot", "step", "walk", "run", "jump", "land", "swim", "water", "slide", "fall",
			"burn", "fire"
		};

		internal static void OnCharacterAwake(Character c)
		{
			try
			{
				if ((Object)(object)c == (Object)null)
				{
					return;
				}
				string text = ((Object)((Component)c).gameObject).name.Replace("(Clone)", "").Trim();
				if (!Plugin.MatchesKeywords(text.ToLowerInvariant()) || _done.Contains(text))
				{
					return;
				}
				bool flag = (Object)(object)Plugin.Replacement != (Object)null;
				Plugin.Log.LogInfo((object)("[TrollReplacer] Passendes Wesen gefunden: '" + text + "' - untersuche seine Sound-Effekte:"));
				int num = 0;
				Component[] components = ((Component)c).gameObject.GetComponents<Component>();
				foreach (Component val in components)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					Type type = ((object)val).GetType();
					while (type != null && type != typeof(MonoBehaviour) && type != typeof(object))
					{
						FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						foreach (FieldInfo fieldInfo in fields)
						{
							if (!(fieldInfo.FieldType.Name != "EffectList"))
							{
								object value = fieldInfo.GetValue(val);
								if (value != null)
								{
									num += HandleList(type.Name, fieldInfo.Name, value, flag);
								}
							}
						}
						type = type.BaseType;
					}
				}
				if (flag)
				{
					_done.Add(text);
				}
				Plugin.Log.LogInfo((object)("[TrollReplacer] Untersuchung von '" + text + "' fertig. Direkt umgestellte Prefabs: " + num + (flag ? "" : " (Ersatz-Clip war noch nicht geladen - wird beim naechsten Spawn wiederholt)")));
			}
			catch (Exception e)
			{
				Plugin.LogErrorOnce(e);
			}
		}

		private static int HandleList(string compName, string fieldName, object list, bool haveClip)
		{
			string haystackLower = fieldName.ToLowerInvariant();
			bool flag = Plugin.AnyContains(haystackLower, VoiceHints) && !Plugin.AnyContains(haystackLower, SkipHints);
			FieldInfo field = list.GetType().GetField("m_effectPrefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			Array array = ((field != null) ? (field.GetValue(list) as Array) : null);
			if (array == null || array.Length == 0)
			{
				return 0;
			}
			int num = 0;
			foreach (object item in array)
			{
				if (item == null)
				{
					continue;
				}
				FieldInfo field2 = item.GetType().GetField("m_prefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				GameObject val = (GameObject)((field2 != null) ? /*isinst with value type is only supported in some contexts*/: null);
				if (!((Object)(object)val == (Object)null))
				{
					string name = ((Object)val).name;
					string lower = name.ToLowerInvariant();
					bool num2 = val.GetComponentsInChildren<ZSFX>(true).Length != 0 || val.GetComponentsInChildren<AudioSource>(true).Length != 0;
					bool flag2 = false;
					string text;
					if (!num2)
					{
						text = "kein Audio";
					}
					else if (!flag)
					{
						text = "keine Stimm-Liste, bleibt";
					}
					else if (Plugin.IsExcluded(lower))
					{
						text = "ausgeschlossen (Schritt/Lauf), bleibt";
					}
					else if (Plugin.MatchesKeywords(lower))
					{
						text = "Name passt -> ersetzen";
						flag2 = true;
					}
					else
					{
						text = "Suchbegriff passt nicht, bleibt unangetastet";
					}
					Plugin.Log.LogInfo((object)("[TrollReplacer]    " + compName + "." + fieldName + ": '" + name + "' -> " + text));
					if (flag2 && haveClip && Plugin.ReplaceEffectPrefabs.Value && PatchPrefab(val))
					{
						num++;
					}
				}
			}
			return num;
		}

		private static bool PatchPrefab(GameObject pf)
		{
			int instanceID = ((Object)pf).GetInstanceID();
			if (_patchedPrefabs.Contains(instanceID))
			{
				return false;
			}
			AudioClip replacement = Plugin.Replacement;
			if ((Object)(object)replacement == (Object)null)
			{
				return false;
			}
			int num = 0;
			ZSFX[] componentsInChildren = pf.GetComponentsInChildren<ZSFX>(true);
			foreach (ZSFX val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && val.m_audioClips != null)
				{
					for (int j = 0; j < val.m_audioClips.Length; j++)
					{
						val.m_audioClips[j] = replacement;
						num++;
					}
				}
			}
			AudioSource[] componentsInChildren2 = pf.GetComponentsInChildren<AudioSource>(true);
			foreach (AudioSource val2 in componentsInChildren2)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					AudioClip clip = val2.clip;
					if ((Object)(object)clip != (Object)null && clip != replacement)
					{
						val2.clip = replacement;
						num++;
					}
				}
			}
			_patchedPrefabs.Add(instanceID);
			Plugin.Log.LogInfo((object)("[TrollReplacer]       -> Prefab '" + ((Object)pf).name + "' direkt umgestellt (" + num + " Clips)."));
			return true;
		}
	}
	internal static class WavLoader
	{
		internal sealed class WavData
		{
			public float[] Samples;

			public int SampleRate;

			public int SourceChannels;

			public int SourceBits;

			public float Peak;
		}

		private static string Tag(byte[] b, int pos)
		{
			return new string(new char[4]
			{
				(char)b[pos],
				(char)b[pos + 1],
				(char)b[pos + 2],
				(char)b[pos + 3]
			});
		}

		public static WavData Parse(byte[] b)
		{
			if (b == null || b.Length < 44)
			{
				throw new Exception("Datei ist zu klein, um eine WAV zu sein.");
			}
			if (Tag(b, 0) != "RIFF" || Tag(b, 8) != "WAVE")
			{
				throw new Exception("Kein gueltiges RIFF/WAVE-Format (ist es evtl. eine umbenannte MP3/OGG?).");
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			bool flag = false;
			int num5 = -1;
			long num6 = 0L;
			long num7 = 12L;
			while (num7 + 8 <= b.Length)
			{
				string text = Tag(b, (int)num7);
				long num8 = BitConverter.ToUInt32(b, (int)num7 + 4);
				long num9 = num7 + 8;
				if (text == "fmt " && !flag)
				{
					if (num8 < 16 || num9 + 16 > b.Length)
					{
						throw new Exception("fmt-Chunk ist beschaedigt.");
					}
					num = BitConverter.ToUInt16(b, (int)num9);
					num2 = BitConverter.ToUInt16(b, (int)num9 + 2);
					num3 = BitConverter.ToInt32(b, (int)num9 + 4);
					num4 = BitConverter.ToUInt16(b, (int)num9 + 14);
					if (num == 65534 && num8 >= 26 && num9 + 26 <= b.Length)
					{
						num = BitConverter.ToUInt16(b, (int)num9 + 24);
					}
					flag = true;
				}
				else if (text == "data" && num5 < 0)
				{
					num5 = (int)num9;
					num6 = Math.Min(num8, b.Length - num9);
				}
				long num10 = num9 + num8 + (num8 & 1);
				if (num10 <= num7 || num10 > b.Length)
				{
					break;
				}
				num7 = num10;
			}
			if (!flag)
			{
				throw new Exception("Kein fmt-Chunk gefunden.");
			}
			if (num5 < 0 || num6 <= 0)
			{
				throw new Exception("Kein Audio-Datenblock (data-Chunk) gefunden.");
			}
			if (num2 < 1 || num2 > 32)
			{
				throw new Exception("Ungueltige Kanalzahl: " + num2);
			}
			if (num3 < 1000 || num3 > 384000)
			{
				throw new Exception("Ungueltige Samplerate: " + num3);
			}
			bool flag2 = num == 3;
			bool num11 = num == 1;
			if (!num11 && !flag2)
			{
				throw new Exception("Nicht unterstuetztes WAV-Format (Tag " + num + "). Bitte als PCM speichern.");
			}
			if (num11 && num4 != 8 && num4 != 16 && num4 != 24 && num4 != 32)
			{
				throw new Exception("Nicht unterstuetzte Bittiefe: " + num4);
			}
			if (flag2 && num4 != 32 && num4 != 64)
			{
				throw new Exception("Nicht unterstuetzte Float-Bittiefe: " + num4);
			}
			int num12 = num4 / 8;
			int num13 = num12 * num2;
			long num14 = num6 / num13;
			if (num14 < 1)
			{
				throw new Exception("Audio enthaelt keine Samples.");
			}
			if (num14 > 50000000)
			{
				throw new Exception("Audio ist unrealistisch lang.");
			}
			float[] array = new float[num14];
			float num15 = 0f;
			long num16 = num5;
			for (long num17 = 0L; num17 < num14; num17++)
			{
				double num18 = 0.0;
				for (int i = 0; i < num2; i++)
				{
					int num19 = (int)num16;
					float num20;
					if (flag2)
					{
						num20 = ((num4 == 32) ? BitConverter.ToSingle(b, num19) : ((float)BitConverter.ToDouble(b, num19)));
						if (float.IsNaN(num20) || float.IsInfinity(num20))
						{
							num20 = 0f;
						}
					}
					else
					{
						switch (num4)
						{
						case 8:
							num20 = (float)(b[num19] - 128) / 128f;
							break;
						case 16:
							num20 = (float)BitConverter.ToInt16(b, num19) / 32768f;
							break;
						case 24:
						{
							int num21 = b[num19] | (b[num19 + 1] << 8) | (b[num19 + 2] << 16);
							if ((num21 & 0x800000) != 0)
							{
								num21 |= -16777216;
							}
							num20 = (float)num21 / 8388608f;
							break;
						}
						default:
							num20 = (float)BitConverter.ToInt32(b, num19) / 2.1474836E+09f;
							break;
						}
					}
					num18 += (double)num20;
					num16 += num12;
				}
				float num22 = (float)(num18 / (double)num2);
				if (num22 > 1f)
				{
					num22 = 1f;
				}
				else if (num22 < -1f)
				{
					num22 = -1f;
				}
				array[num17] = num22;
				float num23 = ((num22 < 0f) ? (0f - num22) : num22);
				if (num23 > num15)
				{
					num15 = num23;
				}
			}
			return new WavData
			{
				Samples = array,
				SampleRate = num3,
				SourceChannels = num2,
				SourceBits = num4,
				Peak = num15
			};
		}
	}
}