Decompiled source of espeakTTS v0.2.3

espeakTTS.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using Unity.VisualScripting;
using UnityEngine;

[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: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("Autodesk.Fbx")]
[assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")]
[assembly: IgnoresAccessChecksTo("FbxBuildTestAssets")]
[assembly: IgnoresAccessChecksTo("Klattersynth")]
[assembly: IgnoresAccessChecksTo("Photon3Unity3D")]
[assembly: IgnoresAccessChecksTo("PhotonChat")]
[assembly: IgnoresAccessChecksTo("PhotonRealtime")]
[assembly: IgnoresAccessChecksTo("PhotonUnityNetworking")]
[assembly: IgnoresAccessChecksTo("PhotonUnityNetworking.Utilities")]
[assembly: IgnoresAccessChecksTo("PhotonVoice.API")]
[assembly: IgnoresAccessChecksTo("PhotonVoice")]
[assembly: IgnoresAccessChecksTo("PhotonVoice.PUN")]
[assembly: IgnoresAccessChecksTo("SingularityGroup.HotReload.Runtime")]
[assembly: IgnoresAccessChecksTo("SingularityGroup.HotReload.Runtime.Public")]
[assembly: IgnoresAccessChecksTo("Sirenix.OdinInspector.Attributes")]
[assembly: IgnoresAccessChecksTo("Sirenix.Serialization.Config")]
[assembly: IgnoresAccessChecksTo("Sirenix.Serialization")]
[assembly: IgnoresAccessChecksTo("Sirenix.Utilities")]
[assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")]
[assembly: IgnoresAccessChecksTo("Unity.Formats.Fbx.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")]
[assembly: IgnoresAccessChecksTo("Unity.Postprocessing.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")]
[assembly: IgnoresAccessChecksTo("Unity.Timeline")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Antlr3.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Core")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Flow")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.State")]
[assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.UI")]
[assembly: IgnoresAccessChecksTo("websocket-sharp")]
[assembly: AssemblyCompany("Lavighju")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("espeakTTS")]
[assembly: AssemblyTitle("espeakTTS")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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 repo_espeak
{
	public class Client
	{
		private enum AudioOutput
		{
			Playback,
			Retrieval,
			Synchronous,
			SynchronousPlayback
		}

		private enum Error
		{
			EE_OK = 0,
			EE_INTERNAL_ERROR = -1,
			EE_BUFFER_FULL = 1,
			EE_NOT_FOUND = 2
		}

		private enum PositionType
		{
			Character = 1,
			Word,
			Sentence
		}

		private enum Parameter
		{
			Rate = 1,
			Volume = 2,
			Pitch = 3,
			Range = 4,
			Punctuation = 5,
			Capitals = 6,
			WordGap = 7,
			Intonation = 9
		}

		private enum ParameterType
		{
			Absolute,
			Relative
		}

		[Flags]
		private enum SpeechFlags
		{
			CharsUtf8 = 1,
			SSML = 0x10
		}

		private static bool Initialized;

		public static int sampleRate;

		public static void Initialize(string path)
		{
			try
			{
				byte[] bytes = Encoding.UTF8.GetBytes(path + "\0");
				IntPtr intPtr = Marshal.AllocHGlobal(bytes.Length);
				Marshal.Copy(bytes, 0, intPtr, bytes.Length);
				int num = espeak_Initialize(AudioOutput.Retrieval, 0, intPtr, 0);
				Marshal.FreeHGlobal(intPtr);
				if (num == -1)
				{
					Debug.LogError((object)$"Could not initialize ESpeak. Maybe there is no espeak data at {path}?");
				}
				else
				{
					sampleRate = num;
				}
			}
			catch (Exception ex)
			{
				Debug.LogError((object)ex);
			}
			espeak_SetSynthCallback(EventHandler.Handle);
			Initialized = true;
		}

		public static int SetRate(int rate)
		{
			if (rate < 80 && rate > 450)
			{
				Debug.LogError((object)"The rate must be between 80 and 450.");
			}
			Error result = espeak_SetParameter(Parameter.Rate, rate, ParameterType.Absolute);
			return CheckResult(result);
		}

		public static int SetWordgap(int wordgap)
		{
			if (wordgap < 0)
			{
				wordgap = 0;
			}
			Error result = espeak_SetParameter(Parameter.WordGap, wordgap, ParameterType.Absolute);
			return CheckResult(result);
		}

		public static bool VoiceFinished()
		{
			bool result = false;
			EventHandler.audio_files_mutex.WaitOne();
			if (EventHandler.audio_files.Count > 0)
			{
				result = true;
			}
			EventHandler.audio_files_mutex.ReleaseMutex();
			return result;
		}

		public static int SetPitch(int pitch)
		{
			if (pitch < 0)
			{
				pitch = 0;
			}
			if (pitch > 100)
			{
				pitch = 100;
			}
			Error result = espeak_SetParameter(Parameter.Pitch, pitch, ParameterType.Absolute);
			return CheckResult(result);
		}

		private static int CheckResult(Error result)
		{
			return (int)result;
		}

		public static int Speak(TTSWord text)
		{
			EventHandler.audio_files_mutex.WaitOne();
			EventHandler.audio_files.Add(text);
			SetVoiceByName(text.parameters.Language + "+" + text.parameters.Variant);
			SetPitch(text.parameters.Pitch);
			SetRate(text.parameters.Speed);
			SetWordgap(text.parameters.Gap / 10);
			Error result = espeak_Synth(text.word, text.word.Length * Marshal.SystemDefaultCharSize, 0u, PositionType.Character, 0u, SpeechFlags.CharsUtf8, (UIntPtr)0u, (IntPtr)0);
			EventHandler.audio_files_mutex.ReleaseMutex();
			return CheckResult(result);
		}

		public static int SetVoiceByName(string name)
		{
			Error result = espeak_SetVoiceByName(name);
			return CheckResult(result);
		}

		[DllImport("libespeak-ng.dll", CallingConvention = CallingConvention.Cdecl)]
		private static extern Error espeak_SetVoiceByName([MarshalAs(UnmanagedType.LPUTF8Str)] string name);

		[DllImport("libespeak-ng.dll", CallingConvention = CallingConvention.Cdecl)]
		private static extern Error espeak_SetParameter(Parameter parameter, int value, ParameterType type);

		[DllImport("libespeak-ng.dll", CallingConvention = CallingConvention.Cdecl)]
		private static extern Error espeak_Synth([MarshalAs(UnmanagedType.LPUTF8Str)] string text, int size, uint startPosition = 0u, PositionType positionType = PositionType.Character, uint endPosition = 0u, SpeechFlags flags = SpeechFlags.CharsUtf8, UIntPtr uniqueIdentifier = default(UIntPtr), IntPtr userData = default(IntPtr));

		[DllImport("libespeak-ng.dll", CallingConvention = CallingConvention.Cdecl)]
		private static extern int espeak_Initialize(AudioOutput output, int bufferLength, IntPtr path, int options);

		[DllImport("libespeak-ng.dll", CallingConvention = CallingConvention.Cdecl)]
		private static extern void espeak_SetSynthCallback(EventHandler.SynthCallback callback);
	}
	internal struct Event
	{
		public enum EventType
		{
			ListTerminated,
			Word,
			Sentence,
			Mark,
			Play,
			End,
			MessageTerminated,
			Phoneme,
			SetSampleRate
		}

		public EventType Type;

		public uint UniqueIdentifier;

		public int TextPosition;

		public int Length;

		public int AudioPosition;

		public int Sample;

		public IntPtr UserData;

		public int Id;
	}
	internal class EventHandler
	{
		public delegate int SynthCallback(IntPtr wavePtr, int bufferLength, IntPtr eventsPtr);

		private static MemoryStream Stream;

		public static Mutex audio_files_mutex = new Mutex();

		public static List<TTSWord> audio_files = new List<TTSWord>();

		public static int Handle(IntPtr wavePtr, int bufferLength, IntPtr eventsPtr)
		{
			if (bufferLength == 0)
			{
				if (Stream != null)
				{
					Stream.Flush();
					audio_files_mutex.WaitOne();
					byte[] array = Stream.ToArray();
					float[] array2 = new float[array.Length / 2];
					for (int i = 0; i < array2.Length; i++)
					{
						if (i < 20 || i > array2.Length - 11)
						{
							array2[i] = 0f;
						}
						else
						{
							array2[i] = (float)BitConverter.ToInt16(array, i * 2) / 32767f;
						}
					}
					if (audio_files.Count > 0 && audio_files[0] != null)
					{
						audio_files[0].audio_buffer = array2;
					}
					audio_files.RemoveAt(0);
					audio_files_mutex.ReleaseMutex();
					Stream.Dispose();
					Stream = null;
				}
				return 0;
			}
			WriteAudioToStream(wavePtr, bufferLength);
			List<Event> list = MarshalEvents(eventsPtr);
			foreach (Event item in list)
			{
			}
			return 0;
		}

		private static List<Event> MarshalEvents(IntPtr eventsPtr)
		{
			List<Event> list = new List<Event>();
			int num = Marshal.SizeOf(typeof(Event));
			int num2 = 0;
			while (true)
			{
				IntPtr ptr = new IntPtr(eventsPtr.ToInt64() + num * num2);
				Event item = (Event)Marshal.PtrToStructure(ptr, typeof(Event));
				if (item.Type == Event.EventType.ListTerminated)
				{
					break;
				}
				list.Add(item);
				num2++;
			}
			return list;
		}

		private static int WriteAudioToStream(IntPtr wavePtr, int bufferLength)
		{
			if (wavePtr == IntPtr.Zero)
			{
				return 0;
			}
			if (Stream == null)
			{
				Stream = new MemoryStream();
				InitializeStream();
			}
			byte[] array = new byte[bufferLength * 2];
			Marshal.Copy(wavePtr, array, 0, array.Length);
			Stream.Write(array, 0, array.Length);
			return 0;
		}

		private static void InitializeStream()
		{
			Encoding aSCII = Encoding.ASCII;
			Stream.Write(aSCII.GetBytes("RIFF"), 0, 4);
			Stream.Write(BitConverter.GetBytes(0), 0, 4);
			Stream.Write(aSCII.GetBytes("WAVEfmt "), 0, 8);
			Stream.Write(BitConverter.GetBytes(16), 0, 4);
			Stream.Write(BitConverter.GetBytes((short)1), 0, 2);
			Stream.Write(BitConverter.GetBytes((short)1), 0, 2);
			Stream.Write(BitConverter.GetBytes(22050), 0, 4);
			Stream.Write(BitConverter.GetBytes(44100), 0, 4);
			Stream.Write(BitConverter.GetBytes((short)2), 0, 2);
			Stream.Write(BitConverter.GetBytes((short)16), 0, 2);
			Stream.Write(aSCII.GetBytes("DATA"), 0, 4);
			Stream.Write(BitConverter.GetBytes(0), 0, 4);
		}

		private static string PrintBytes(byte[] byteArray)
		{
			StringBuilder stringBuilder = new StringBuilder("new byte[] { ");
			for (int i = 0; i < byteArray.Length; i++)
			{
				byte value = byteArray[i];
				stringBuilder.Append(value);
				if (i < byteArray.Length - 1)
				{
					stringBuilder.Append(", ");
				}
			}
			stringBuilder.Append(" }");
			return stringBuilder.ToString();
		}

		private static string ConvertHeadersToString(byte[] buffer)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendFormat("The stream length is {0}.\n", Stream.Length);
			stringBuilder.Append(Encoding.ASCII.GetChars(buffer, 0, 4));
			stringBuilder.Append(BitConverter.ToInt32(buffer, 4));
			stringBuilder.Append(Encoding.ASCII.GetChars(buffer, 8, 8));
			stringBuilder.Append(BitConverter.ToInt32(buffer, 16));
			stringBuilder.Append(BitConverter.ToInt16(buffer, 20));
			stringBuilder.Append(BitConverter.ToInt16(buffer, 22));
			stringBuilder.Append(BitConverter.ToInt32(buffer, 24));
			stringBuilder.Append(BitConverter.ToInt32(buffer, 28));
			stringBuilder.Append(BitConverter.ToInt16(buffer, 32));
			stringBuilder.Append(BitConverter.ToInt16(buffer, 34));
			stringBuilder.Append(Encoding.ASCII.GetChars(buffer, 36, 4));
			stringBuilder.Append(BitConverter.ToInt32(buffer, 40));
			return stringBuilder.ToString();
		}
	}
	public struct eSpeakParameters
	{
		public string Language;

		public string Variant;

		public int Pitch;

		public int Speed;

		public int Gap;

		public eSpeakParameters(string language, string variant, int pitch, int speed, int gap)
		{
			Language = language;
			Variant = variant;
			Pitch = pitch;
			Speed = speed;
			Gap = gap;
		}
	}
	internal class playerEspeak : MonoBehaviour
	{
		public TTSVoice ttsvoice;

		public static eSpeakParameters TTSParameters;

		public long end_timestamp = 0L;

		public List<TTSWord> words = new List<TTSWord>();

		public static void init(string language, string variant, int pitch, int speed, int gap, string espeak_data_path)
		{
			TTSParameters = new eSpeakParameters(language, variant, pitch, speed, gap);
			try
			{
				espeak_data_path += "\\espeak-ng-data";
				Client.Initialize(espeak_data_path);
			}
			catch (Exception ex)
			{
				repo_espeak.Logger.LogError((object)ex);
			}
		}

		public void Update()
		{
			if (words.Count > 0 && words[0] != null)
			{
				bool flag = false;
				long num = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
				if (num <= end_timestamp)
				{
					flag = true;
				}
				if (words[0].audio_buffer != null && !flag)
				{
					repo_espeak.Logger.LogDebug((object)$"{((Object)ttsvoice).GetInstanceID()}: Playing word: {words[0].word}");
					AudioClip val = AudioClip.Create("espeakTTS" + ((Object)ttsvoice).GetInstanceID(), words[0].audio_buffer.Length, 1, Client.sampleRate, false);
					val.SetData(words[0].audio_buffer, 0);
					end_timestamp = num + (long)(val.length * 1000f);
					words[0].TTSInstance.VoiceText(words[0].word, val.length);
					words[0].TTSInstance.audioSource.PlayOneShot(val);
					words.RemoveAt(0);
				}
			}
		}

		public void stopSentence()
		{
			repo_espeak.Logger.LogDebug((object)$"{((Object)ttsvoice).GetInstanceID()}: Stopping all current words...");
			words.RemoveAll((TTSWord word) => word != null);
			words.Clear();
			end_timestamp = 0L;
		}

		public void speakSentence(string text, bool crouch, eSpeakParameters parameters, TTSVoice instance, bool natural)
		{
			instance.StopAndClearVoice();
			stopSentence();
			instance.setVoice(0);
			if (!Object.op_Implicit((Object)(object)instance.activeVoice))
			{
				repo_espeak.Logger.LogError((object)"Active voice is not set.");
			}
			text = instance.TranslateSpecialLetters(text);
			if (natural)
			{
				instance.words = new List<string> { text };
			}
			else
			{
				instance.words = new List<string>(text.Split(' '));
			}
			schedule_play(instance.words, instance, crouch, parameters);
		}

		private void schedule_play(List<string> word_list, TTSVoice TTSInstance, bool whisper = false, eSpeakParameters? parameters = null)
		{
			if (!parameters.HasValue)
			{
				parameters = TTSParameters;
			}
			foreach (string item in word_list)
			{
				TTSWord tTSWord = new TTSWord(item, TTSInstance, parameters.Value, whisper);
				words.Add(tTSWord);
				repo_espeak.Logger.LogDebug((object)$"{((Object)ttsvoice).GetInstanceID()}: Generating audio for word: {tTSWord.word}");
				Client.Speak(tTSWord);
			}
		}
	}
	internal class punEspeak : MonoBehaviour
	{
		public PlayerAvatar playerAvatar;

		public const int mod_version = 1;

		public void sendVersion()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_0019: Expected O, but got Unknown
			Hashtable val = new Hashtable();
			((Dictionary<object, object>)val).Add((object)"espeakVersion", (object)1);
			Hashtable val2 = val;
			playerAvatar.photonView.Owner.SetCustomProperties(val2, (Hashtable)null, (WebFlags)null);
		}

		[PunRPC]
		public void eSpeakChatMessageSendRPC(string _message, bool crouching, string lang, string variant, int speed, int pitch, bool natural, int gap)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			if ((int)GameDirector.instance.currentState != 2)
			{
				return;
			}
			((Dictionary<object, object>)(object)playerAvatar.photonView.Owner.CustomProperties).TryGetValue((object)"espeakVersion", out object value);
			if (!(value is int))
			{
				return;
			}
			int num = (int)value;
			if (num == 1)
			{
				if (variant.ToLower() == "whisper" || variant.ToLower() == "whisperf")
				{
					variant = "";
				}
				if (pitch < 0 || pitch > 99)
				{
					pitch = 25;
				}
				if (speed < 80 || speed > 500)
				{
					speed = 100;
				}
				if (!repo_espeak.natural_voice.Value || gap < 10 || gap > 1000)
				{
					gap = 10;
				}
				variant = (crouching ? "whisper" : variant);
				string[] array = repo_espeak.blacklist.Value.Split(",");
				for (int i = 0; i < array.Length; i++)
				{
					_message = Regex.Replace(_message, Regex.Escape(array[i].Trim()), " ", RegexOptions.IgnoreCase);
				}
				repo_espeak.Logger.LogDebug((object)$"{((Object)playerAvatar).GetInstanceID()}: Received espeak TTS RPC");
				playerEspeak component = ((Component)playerAvatar.voiceChat.ttsVoice).GetComponent<playerEspeak>();
				component.speakSentence(_message, crouching, new eSpeakParameters(lang, variant, pitch, speed, gap), playerAvatar.voiceChat.ttsVoice, natural);
			}
		}
	}
	[BepInPlugin("Lavighju.espeakTTS", "espeakTTS", "0.2.3")]
	public class repo_espeak : BaseUnityPlugin
	{
		private class HarmonyPatches
		{
			[HarmonyPatch(typeof(TTSVoice), "StartSpeakingWithHighlight")]
			[HarmonyPrefix]
			private static bool StartSpeakingWithHighlightPrefix(ref string text, bool crouch, TTSVoice __instance)
			{
				playerEspeak component = ((Component)__instance).GetComponent<playerEspeak>();
				if (Object.op_Implicit((Object)(object)__instance.playerAvatar))
				{
					((Dictionary<object, object>)(object)__instance.playerAvatar.photonView.Owner.CustomProperties).TryGetValue((object)"espeakVersion", out object value);
					if (value is int num)
					{
						if (num == 1)
						{
							return false;
						}
						Logger.LogDebug((object)$"{((Object)__instance.playerAvatar).GetInstanceID()} has an incompatible mod version. You won't be able to hear their custom voice.");
					}
					else
					{
						Logger.LogDebug((object)$"{((Object)__instance.playerAvatar).GetInstanceID()} does not have the mod installed.");
					}
				}
				Logger.LogDebug((object)$"{((Object)__instance).GetInstanceID()}: Received vanilla RPC");
				component.speakSentence(text, crouch, new eSpeakParameters(default_language.Value, default_variant.Value, default_pitch.Value, default_speed.Value, natural_gap.Value), __instance, natural_voice.Value);
				return false;
			}

			public static bool ChatMessageSendPrefix(ref string _message, object[] __args, PlayerAvatar __instance)
			{
				bool flag = false;
				if (__args.Length > 1 && __args[1] is bool flag2)
				{
					flag = flag2;
				}
				string value = language.Value;
				string pattern = "^\\[(.*?)\\]\\s*";
				Match match = Regex.Match(_message, pattern);
				if (match.Success)
				{
					value = match.Groups[1].Value;
					_message = Regex.Replace(_message, pattern, "");
				}
				if (!flag)
				{
					foreach (PlayerVoiceChat voiceChat in RunManager.instance.voiceChats)
					{
						if (!voiceChat.recordingEnabled)
						{
							return true;
						}
					}
				}
				bool flag3 = __instance.isCrouching;
				SemiFunc.Command(_message);
				if (!SemiFunc.IsMultiplayer())
				{
					return true;
				}
				if (__instance.isDisabled)
				{
					flag3 = true;
				}
				Logger.LogDebug((object)("Sending espeak message RPC: " + _message));
				__instance.photonView.RPC("eSpeakChatMessageSendRPC", (RpcTarget)0, new object[8] { _message, flag3, value, variant.Value, speed.Value, pitch.Value, natural_voice.Value, natural_gap.Value });
				return true;
			}

			[HarmonyPatch(typeof(TTSVoice), "Start")]
			[HarmonyPostfix]
			private static void AwakeSpeechPost(TTSVoice __instance)
			{
				if (SemiFunc.IsMultiplayer())
				{
					playerEspeak playerEspeak2 = ComponentHolderProtocol.AddComponent<playerEspeak>((Object)(object)__instance);
					playerEspeak2.ttsvoice = __instance;
					Logger.LogDebug((object)$"Created espeak player for TTSVoice {((Object)__instance).GetInstanceID()}");
				}
			}

			[HarmonyPatch(typeof(PlayerAvatar), "Awake")]
			[HarmonyPostfix]
			private static void AwakePlayerAvatarPost(PlayerAvatar __instance)
			{
				if (SemiFunc.IsMultiplayer())
				{
					punEspeak punEspeak2 = ComponentHolderProtocol.AddComponent<punEspeak>((Object)(object)__instance);
					punEspeak2.playerAvatar = __instance;
					if (__instance.photonView.IsMine)
					{
						punEspeak2.sendVersion();
					}
					Logger.LogDebug((object)$"Created espeak pun for player Avatar {((Object)__instance).GetInstanceID()}");
				}
			}
		}

		public static readonly string[] language_list = new string[128]
		{
			"af", "sq", "am", "ar", "an", "hy", "hyw", "as", "az", "ba",
			"cu", "eu", "be", "bn", "bpy", "bs", "bg", "my", "ca", "chr",
			"yue", "hak", "haw", "cmn", "hr", "cs", "da", "nl", "en-us", "en",
			"en-029", "en-gb-x-gbclan", "en-gb-x-rp", "en-gb-scotland", "en-gb-x-gbcwmd", "eo", "et", "fa", "fa-latn", "fi",
			"fr-be", "fr", "fr-ch", "ga", "gd", "ka", "de", "grc", "el", "kl",
			"gn", "gu", "ht", "he", "hi", "hu", "is", "id", "ia", "io",
			"it", "ja", "kn", "kok", "ko", "ku", "kk", "ky", "la", "lb",
			"ltg", "lv", "lfn", "lt", "jbo", "mi", "mk", "ms", "ml", "mt",
			"mr", "nci", "ne", "nb", "nog", "or", "om", "pap", "py", "pl",
			"pt-br", "qdb", "qu", "quc", "qya", "pt", "pa", "piqd", "ro", "ru",
			"ru-lv", "uk", "sjn", "sr", "tn", "sd", "shn", "si", "sk", "sl",
			"smj", "es", "es-419", "sw", "sv", "ta", "th", "tk", "tt", "te",
			"tr", "ug", "ur", "uz", "vi-vn-x-central", "vi", "vi-vn-x-south", "cy"
		};

		public static readonly string[] variant_list = new string[101]
		{
			"", "semibot", "adam", "Alex", "Alicia", "Andrea", "Andy", "anika", "anikaRobot", "Annie",
			"announcer", "antonio", "AnxiousAndy", "aunty", "belinda", "benjamin", "boris", "caleb", "croak", "david",
			"Demonic", "Denis", "Diogo", "ed", "edward", "edward2", "f1", "f2", "f3", "f4",
			"f5", "fast", "Gene", "Gene2", "grandma", "grandpa", "gustave", "Henrique", "Hugo", "iven",
			"iven2", "iven3", "iven4", "Jacky", "john", "kaukovalta", "klatt", "klatt2", "klatt3", "klatt4",
			"klatt5", "klatt6", "Lee", "linda", "m1", "m2", "m3", "m4", "m5", "m6",
			"m7", "m8", "marcelo", "Marco", "Mario", "max", "Michael", "michel", "miguel", "Mike",
			"Mr serious", "Nguyen", "norbert", "pablo", "paul", "pedro", "quincy", "RicishayMax", "RicishayMax2", "RicishayMax3",
			"rob", "robert", "robosoft", "robosoft2", "robosoft3", "robosoft4", "robosoft5", "robosoft6", "robosoft7", "robosoft8",
			"sandro", "shelby", "steph", "steph2", "steph3", "Storm", "travis", "Tweaky", "UniRobot", "victor",
			"zac"
		};

		public static ConfigEntry<string> language;

		public static ConfigEntry<string> variant;

		public static ConfigEntry<int> pitch;

		public static ConfigEntry<int> speed;

		public static ConfigEntry<string> default_language;

		public static ConfigEntry<string> default_variant;

		public static ConfigEntry<int> default_pitch;

		public static ConfigEntry<int> default_speed;

		public static ConfigEntry<bool> natural_voice;

		public static ConfigEntry<int> natural_gap;

		public static ConfigEntry<string> blacklist;

		internal static repo_espeak Instance { get; private set; } = null;


		public static ManualLogSource Logger => Instance._logger;

		private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;

		internal Harmony? Harmony { get; set; }

		private void Awake()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Expected O, but got Unknown
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Expected O, but got Unknown
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Expected O, but got Unknown
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Expected O, but got Unknown
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Expected O, but got Unknown
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Expected O, but got Unknown
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Expected O, but got Unknown
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Expected O, but got Unknown
			language = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Language", "en", new ConfigDescription("Language set for the TTS. You can find the identifier for your language here: https://github.com/espeak-ng/espeak-ng/blob/59823f30e3edbd01e87002c04a49ebfd63edaaa7/docs/languages.md", (AcceptableValueBase)(object)new AcceptableValueList<string>(language_list), Array.Empty<object>()));
			variant = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Variant", "semibot", new ConfigDescription("Variant of the voice", (AcceptableValueBase)(object)new AcceptableValueList<string>(variant_list), Array.Empty<object>()));
			pitch = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Pitch", 50, new ConfigDescription("Pitch of the voice, 0-99", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 99), Array.Empty<object>()));
			speed = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Speed", 175, new ConfigDescription("Speed of the voice, 80-500", (AcceptableValueBase)(object)new AcceptableValueRange<int>(80, 500), Array.Empty<object>()));
			default_language = ((BaseUnityPlugin)this).Config.Bind<string>("TTS for players without the mod installed", "Language", "en", new ConfigDescription("Language set for the TTS for players without the mod installed", (AcceptableValueBase)(object)new AcceptableValueList<string>(language_list), Array.Empty<object>()));
			default_variant = ((BaseUnityPlugin)this).Config.Bind<string>("TTS for players without the mod installed", "Variant", "semibot", new ConfigDescription("Variant of the voice", (AcceptableValueBase)(object)new AcceptableValueList<string>(variant_list), Array.Empty<object>()));
			default_pitch = ((BaseUnityPlugin)this).Config.Bind<int>("TTS for players without the mod installed", "Pitch", 50, new ConfigDescription("Pitch of the voice, 0-99", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 99), Array.Empty<object>()));
			default_speed = ((BaseUnityPlugin)this).Config.Bind<int>("TTS for players without the mod installed", "Speed", 175, new ConfigDescription("Speed of the voice, 80-500", (AcceptableValueBase)(object)new AcceptableValueRange<int>(80, 500), Array.Empty<object>()));
			natural_voice = ((BaseUnityPlugin)this).Config.Bind<bool>("Full sentences settings", "Enable full sentences", false, new ConfigDescription("Enable or disable full sentences (whole sentences spoken instead of word by word)", (AcceptableValueBase)null, Array.Empty<object>()));
			natural_gap = ((BaseUnityPlugin)this).Config.Bind<int>("Full sentences settings", "Gap", 10, new ConfigDescription("Word gap in sentences, in milliseconds", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 1000), Array.Empty<object>()));
			blacklist = ((BaseUnityPlugin)this).Config.Bind<string>("Words blacklist", "Blacklist", "...", new ConfigDescription("Word list that will be skipped by TTS. Separated by commas (e.g. \"...,test,etc\")", (AcceptableValueBase)null, new object[1] { "HideFromREPOConfig" }));
			Instance = this;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			string text = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			if (Directory.Exists(text + "\\plugins\\espeak-ng-data"))
			{
				text += "\\plugins";
			}
			else if (!Directory.Exists(text + "\\espeak-ng-data"))
			{
				Logger.LogError((object)("Could not find the folder espeak-ng-data. Are you sure it is present in the plugin's folder " + text + " ?"));
				return;
			}
			Logger.LogInfo((object)("Trying to initialize the dictionnary from this location: " + text));
			try
			{
				playerEspeak.init(language.Value, variant.Value, pitch.Value, speed.Value, natural_gap.Value, text);
				Patch();
			}
			catch (Exception ex)
			{
				Logger.LogError((object)"An error occured during initialization:");
				Logger.LogError((object)ex);
			}
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has successfully loaded");
		}

		internal void Patch()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0026: Expected O, but got Unknown
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			if (Harmony == null)
			{
				Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
				Harmony val2 = val;
				Harmony = val;
			}
			Harmony.PatchAll(typeof(HarmonyPatches));
			MethodInfo methodInfo = null;
			methodInfo = typeof(PlayerAvatar).GetMethod("ChatMessageSend", new Type[2]
			{
				typeof(string),
				typeof(bool)
			});
			if (methodInfo == null)
			{
				methodInfo = typeof(PlayerAvatar).GetMethod("ChatMessageSend", new Type[1] { typeof(string) });
			}
			if (methodInfo != null)
			{
				MethodInfo method = typeof(HarmonyPatches).GetMethod("ChatMessageSendPrefix");
				Harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		internal void Unpatch()
		{
			Harmony? harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	public class TTSWord
	{
		public string word { get; }

		public TTSVoice TTSInstance { get; }

		public eSpeakParameters parameters { get; }

		public float[] audio_buffer { get; set; }

		public bool whisper { get; }

		public TTSWord(string w, TTSVoice a, eSpeakParameters p, bool c = false, float[] ab = null)
		{
			word = w;
			TTSInstance = a;
			parameters = p;
			audio_buffer = ab;
			whisper = c;
		}
	}
}