Decompiled source of AEIOUCompany v1.3.6

aeioucompany/AEIOUCompany.dll

Decompiled 3 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("AEIOUCompany")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Jogn Maden")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("AEIOUCompany")]
[assembly: AssemblyTitle("AEIOUCompany")]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
public readonly struct Speak
{
	public readonly string ChatMessage;

	public readonly int PlayerId;

	public Speak(string chatMessage, int playerId)
	{
		ChatMessage = chatMessage;
		PlayerId = playerId;
	}
}
namespace AEIOU_Company
{
	[HarmonyPatch]
	public class Patches
	{
		private static int NEW_CHAT_SIZE = Plugin.ChatSize;

		private static TMP_InputField chatTextField = null;

		private static string lastChatMessage = "";

		private static readonly float[] emptySamples = new float[8388608];

		private static readonly List<Speak> pendingSpeech = new List<Speak>();

		private static Task<TTS.SpeechData> currentSpeechTask = null;

		[HarmonyPatch(typeof(HUDManager), "AddPlayerChatMessageClientRpc")]
		[HarmonyPostfix]
		public static void AddPlayerChatMessageClientRpcPostfix(HUDManager __instance, string chatMessage, int playerId)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			if (lastChatMessage == chatMessage || chatMessage.StartsWith(Plugin.BlacklistPrefix))
			{
				return;
			}
			lastChatMessage = chatMessage;
			if (playerId <= HUDManager.Instance.playersManager.allPlayerScripts.Length)
			{
				bool flag = GameNetworkManager.Instance.localPlayerController.holdingWalkieTalkie && StartOfRound.Instance.allPlayerScripts[playerId].holdingWalkieTalkie;
				float num = Vector3.Distance(((Component)GameNetworkManager.Instance.localPlayerController).transform.position, ((Component)HUDManager.Instance.playersManager.allPlayerScripts[playerId]).transform.position);
				if (num > 25f && !flag && GameNetworkManager.Instance.localPlayerController.isPlayerDead)
				{
					MethodInfo methodInfo = AccessTools.Method(typeof(HUDManager), "AddChatMessage", (Type[])null, (Type[])null);
					methodInfo.Invoke(HUDManager.Instance, new object[2]
					{
						chatMessage,
						HUDManager.Instance.playersManager.allPlayerScripts[playerId].playerUsername
					});
				}
				Plugin.Log($"AddTextToChatOnServer: {chatMessage} {playerId}");
				QueueSpeak(__instance, chatMessage, playerId);
			}
		}

		[HarmonyPatch(typeof(HUDManager), "Update")]
		[HarmonyPostfix]
		public static void UpdatePostfix(HUDManager __instance)
		{
			if (currentSpeechTask != null)
			{
				if (!currentSpeechTask.IsCompleted)
				{
					return;
				}
				if (!currentSpeechTask.IsCanceled && !currentSpeechTask.IsFaulted)
				{
					Speak(__instance.playersManager, currentSpeechTask.Result);
				}
				currentSpeechTask = null;
			}
			if (pendingSpeech.Count > 0)
			{
				Speak nextSpeech = pendingSpeech[0];
				pendingSpeech.RemoveAt(0);
				currentSpeechTask = Task.Run(() => TTS.SpeakToMemory(nextSpeech.PlayerId, nextSpeech.ChatMessage, 7.5f));
			}
		}

		private static void QueueSpeak(HUDManager __instance, string chatMessage, int playerId)
		{
			Plugin.Log("Speak");
			pendingSpeech.Add(new Speak(chatMessage, playerId));
		}

		private static void Speak(StartOfRound playersManager, TTS.SpeechData speechData)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d8: Unknown result type (might be due to invalid IL or missing references)
			int playerId = speechData.PlayerId;
			PlayerControllerB val = playersManager.allPlayerScripts[playerId];
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log("couldnt find player");
				return;
			}
			Plugin.Log("Found player");
			Transform obj = ((Component)val).gameObject.transform.Find("AEIOUSpeakObject");
			GameObject val2 = ((obj != null) ? ((Component)obj).gameObject : null);
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = new GameObject("AEIOUSpeakObject");
				val2.transform.parent = ((Component)val).transform;
				val2.transform.localPosition = Vector3.zero;
				val2.AddComponent<AudioSource>();
				val2.AddComponent<AudioHighPassFilter>();
				val2.AddComponent<AudioLowPassFilter>();
			}
			Plugin.Log("Found AEIOUSpeakObject");
			AudioSource component = val2.GetComponent<AudioSource>();
			if ((Object)(object)component == (Object)null)
			{
				Plugin.LogError("Couldn't speak, AudioSource was null");
				return;
			}
			if ((Object)(object)component.clip == (Object)null)
			{
				component.clip = AudioClip.Create("AEIOUCLIP", 8388608, 1, 11025, false);
			}
			Plugin.Log("Setting up clip");
			component.clip.SetData(emptySamples, 0);
			component.clip.SetData(speechData.AudioData, 0);
			component.outputAudioMixerGroup = SoundManager.Instance.playerVoiceMixers[val.playerClientId];
			component.playOnAwake = false;
			component.rolloffMode = (AudioRolloffMode)2;
			component.minDistance = 1f;
			component.maxDistance = 40f;
			component.dopplerLevel = Plugin.TTSDopperLevel;
			component.pitch = 1f;
			component.spatialize = true;
			component.spatialBlend = (val.isPlayerDead ? 0f : 1f);
			bool flag = !val.isPlayerDead || StartOfRound.Instance.localPlayerController.isPlayerDead;
			component.volume = (flag ? Plugin.TTSVolume : 0f);
			AudioHighPassFilter component2 = val2.GetComponent<AudioHighPassFilter>();
			if ((Object)(object)component2 != (Object)null)
			{
				((Behaviour)component2).enabled = false;
			}
			AudioLowPassFilter component3 = val2.GetComponent<AudioLowPassFilter>();
			if ((Object)(object)component3 != (Object)null)
			{
				component3.lowpassResonanceQ = 1f;
				component3.cutoffFrequency = 5000f;
			}
			if (component.isPlaying)
			{
				component.Stop(true);
			}
			Plugin.Log($"Playing audio: {component}{component.volume}");
			if (val.holdingWalkieTalkie)
			{
				GrabbableObject currentlyHeldObjectServer = val.currentlyHeldObjectServer;
				WalkieTalkie val3 = (WalkieTalkie)(object)((currentlyHeldObjectServer is WalkieTalkie) ? currentlyHeldObjectServer : null);
				if (val3 != null)
				{
					Plugin.Log("WalkieTalkie");
					bool flag2 = false;
					for (int i = 0; i < WalkieTalkie.allWalkieTalkies.Count; i++)
					{
						if ((Object)(object)((GrabbableObject)WalkieTalkie.allWalkieTalkies[i]).playerHeldBy == (Object)(object)StartOfRound.Instance.localPlayerController && ((GrabbableObject)WalkieTalkie.allWalkieTalkies[i]).isBeingUsed)
						{
							flag2 = true;
						}
					}
					if ((Object)(object)val3 != (Object)null && ((GrabbableObject)val3).isBeingUsed && flag2)
					{
						component.volume = Plugin.TTSVolume;
						if ((Object)(object)val == (Object)(object)StartOfRound.Instance.localPlayerController)
						{
							Plugin.Log("Pushing walkie button");
							val.playerBodyAnimator.SetBool("walkieTalkie", true);
							((MonoBehaviour)val3).StartCoroutine(WaitAndStopUsingWalkieTalkie(component.clip, val, speechData.AudioLengthInSeconds));
						}
						else
						{
							((Behaviour)component2).enabled = true;
							component3.lowpassResonanceQ = 3f;
							component3.cutoffFrequency = 4000f;
							component.spatialBlend = 0f;
						}
					}
				}
			}
			component.PlayOneShot(component.clip, 1f);
			RoundManager.Instance.PlayAudibleNoise(val2.transform.position, 25f, 0.7f, 0, false, 0);
		}

		private static IEnumerator WaitAndStopUsingWalkieTalkie(AudioClip clip, PlayerControllerB player, float audioLengthInSeconds)
		{
			Plugin.Log($"WalkieButton Length {audioLengthInSeconds}");
			yield return (object)new WaitForSeconds(audioLengthInSeconds);
			Plugin.Log("WalkieButton end");
			player.playerBodyAnimator.SetBool("walkieTalkie", false);
		}

		[HarmonyPatch(typeof(HUDManager), "EnableChat_performed")]
		[HarmonyPostfix]
		public static void EnableChat_performedPostfix(ref TMP_InputField ___chatTextField, HUDManager __instance)
		{
			___chatTextField.characterLimit = NEW_CHAT_SIZE;
			chatTextField = ___chatTextField;
			Plugin.Log("Enable Chat");
		}

		[HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")]
		[HarmonyPostfix]
		public static void KillPlayerPostfix(PlayerControllerB __instance)
		{
			if (!Plugin.EnableDeadChat)
			{
				Plugin.Log("EnableDeadChat false, skipping KillPlayerPostfix patch");
				return;
			}
			HUDManager.Instance.HideHUD(false);
			HUDManager.Instance.UpdateHealthUI(100, false);
			Plugin.Log("Player died, re-enabling UI");
		}

		[HarmonyPatch(typeof(HUDManager), "EnableChat_performed")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> EnableChat_performedTranspiler(IEnumerable<CodeInstruction> oldInstructions)
		{
			if (!Plugin.EnableDeadChat)
			{
				Plugin.Log("EnableDeadChat false, skipping EnableChat_performedTranspiler patch");
				return oldInstructions;
			}
			List<CodeInstruction> list = new List<CodeInstruction>(oldInstructions);
			for (int i = 0; i < list.Count - 3; i++)
			{
				if (list[i].opcode == OpCodes.Ldarg_0 && CodeInstructionExtensions.Is(list[i + 1], OpCodes.Ldfld, (MemberInfo)AccessTools.Field(typeof(HUDManager), "localPlayer")) && CodeInstructionExtensions.Is(list[i + 2], OpCodes.Ldfld, (MemberInfo)AccessTools.Field(typeof(PlayerControllerB), "isPlayerDead")) && list[i + 3].opcode == OpCodes.Brfalse)
				{
					Plugin.Log("Patching dead chat in EnableChat_performed");
					list[i].opcode = OpCodes.Br;
					list[i].operand = list[i + 3].operand;
					break;
				}
			}
			return list.AsEnumerable();
		}

		[HarmonyPatch(typeof(HUDManager), "SubmitChat_performed")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> SubmitChat_performedTranspiler(IEnumerable<CodeInstruction> oldInstructions)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(oldInstructions);
			patchMaxChatSize(list);
			if (Plugin.EnableDeadChat)
			{
				patchDeadChat(list);
			}
			else
			{
				Plugin.Log("EnableDeadChat false, skipping SubmitChat_performedTranspiler patch");
			}
			return list.AsEnumerable();
			static void patchDeadChat(List<CodeInstruction> newInstructions)
			{
				for (int i = 0; i < newInstructions.Count - 3; i++)
				{
					if (newInstructions[i].opcode == OpCodes.Ldarg_0 && CodeInstructionExtensions.Is(newInstructions[i + 1], OpCodes.Ldfld, (MemberInfo)AccessTools.Field(typeof(HUDManager), "localPlayer")) && CodeInstructionExtensions.Is(newInstructions[i + 2], OpCodes.Ldfld, (MemberInfo)AccessTools.Field(typeof(PlayerControllerB), "isPlayerDead")) && newInstructions[i + 3].opcode == OpCodes.Brfalse)
					{
						Plugin.Log("Patching dead chat in SubmitChat_performed");
						newInstructions[i].opcode = OpCodes.Br;
						newInstructions[i].operand = newInstructions[i + 3].operand;
						break;
					}
				}
			}
			static void patchMaxChatSize(List<CodeInstruction> newInstructions)
			{
				CodeInstruction val = null;
				bool flag = false;
				foreach (CodeInstruction newInstruction in newInstructions)
				{
					if (CodeInstructionExtensions.Is(newInstruction, OpCodes.Ldc_I4_S, (object)50))
					{
						flag = true;
						val = newInstruction;
					}
					else
					{
						if (newInstruction.opcode == OpCodes.Bge && flag)
						{
							val.opcode = OpCodes.Ldc_I4;
							val.operand = NEW_CHAT_SIZE + 1;
							Plugin.Log("Patched max chat length");
							break;
						}
						if (flag)
						{
							flag = false;
							val = null;
						}
					}
				}
			}
		}

		[HarmonyPatch(typeof(HUDManager), "AddPlayerChatMessageServerRpc")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> AddPlayerChatMessageServerRpcTranspiler(IEnumerable<CodeInstruction> oldInstructions)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(oldInstructions);
			foreach (CodeInstruction item in list)
			{
				if (CodeInstructionExtensions.Is(item, OpCodes.Ldc_I4_S, (object)50))
				{
					item.opcode = OpCodes.Ldc_I4;
					item.operand = NEW_CHAT_SIZE;
					Plugin.Log("Patched server max chat length");
					break;
				}
			}
			return list.AsEnumerable();
		}
	}
	public class LCModUtils
	{
		private static Harmony _harmony;

		private static bool _shouldHost;

		private static bool _shouldJoin;

		public LCModUtils(Harmony harmony)
		{
			_harmony = harmony;
		}

		public void DisableFullscreen()
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(IngamePlayerSettings), "SetFullscreenMode", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.LogError("Couldn't find method SetFullscreenMode");
			}
			MethodInfo methodInfo2 = SymbolExtensions.GetMethodInfo((Expression<Action>)(() => SetFullScreenModePrefix()));
			_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Plugin.Log("Disabled Fullscreen");
		}

		public void BootToLANMenu()
		{
			Plugin.Log("Attempting to load lan scene");
			SceneManager.sceneLoaded += OnSceneLoaded;
			SceneManager.LoadScene("InitSceneLANMode");
		}

		public void StartLANHost()
		{
			BootToLANMenu();
			_shouldHost = true;
		}

		public void StartLANClient()
		{
			BootToLANMenu();
			_shouldJoin = true;
		}

		private static bool SetFullScreenModePrefix()
		{
			return false;
		}

		private static void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Expected O, but got Unknown
			MenuManager val = Object.FindObjectOfType<MenuManager>();
			if (!(((Scene)(ref scene)).name == "MainMenu"))
			{
				return;
			}
			Plugin.Log("MainMenuLoaded");
			GameObject lanWarningContainer = val.lanWarningContainer;
			if (Object.op_Implicit((Object)(object)lanWarningContainer))
			{
				Plugin.Log("Destroy LAN Warning");
				Object.Destroy((Object)(object)lanWarningContainer);
			}
			else
			{
				Plugin.Log("LANWarning Null");
			}
			if (_shouldHost || _shouldJoin)
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(MenuManager), "Start", (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					Plugin.LogError("Couldn't find method \"Start\" in MenuManager");
				}
				MethodInfo methodInfo2 = SymbolExtensions.GetMethodInfo((Expression<Action>)(() => HostOrJoin()));
				_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			SceneManager.sceneLoaded -= OnSceneLoaded;
		}

		private static void HostOrJoin()
		{
			MenuManager val = Object.FindObjectOfType<MenuManager>();
			if (_shouldHost)
			{
				if (val != null)
				{
					val.ClickHostButton();
				}
				if (val != null)
				{
					val.ConfirmHostButton();
				}
			}
			else if (_shouldJoin)
			{
				AccessTools.Method(typeof(MenuManager), "ClickJoinButton", (Type[])null, (Type[])null)?.Invoke(val, null);
			}
		}
	}
	[BepInPlugin("AEIOUCompany", "AEIOUCompany", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public static Harmony Harmony = null;

		protected static ManualLogSource Logger = null;

		public static bool PlayStartingUpMessage = false;

		public static float TTSVolume = 0f;

		public static float TTSDopperLevel;

		public static int ChatSize;

		public static bool EnableDeadChat = true;

		public static string BlacklistPrefix = "/";

		public static void Log(object data)
		{
			ManualLogSource logger = Logger;
			if (logger != null)
			{
				logger.LogInfo(data);
			}
		}

		public static void LogError(object data)
		{
			ManualLogSource logger = Logger;
			if (logger != null)
			{
				logger.LogError(data);
			}
		}

		public void Awake()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			Harmony harmony = new Harmony("AEIOUCompany");
			Harmony = harmony;
			Logger = ((BaseUnityPlugin)this).Logger;
			PlayStartingUpMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "StartingUpMessage", true, "Enables \"starting up\" sound effect.").Value;
			TTSVolume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Volume", 1f, "Volume scale of text-to-speech-voice. Values range from 0 to 1").Value;
			TTSDopperLevel = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Doppler Effect Level", 1f, "Values range from 0 to 1").Value;
			ChatSize = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced", "Chat Character Limit", 1024, "WARNING: Everybody must have the same value set for this!").Value;
			EnableDeadChat = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable Dead Chat", true, "Enables chatting after dead").Value;
			BlacklistPrefix = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Blacklist Prefix", "/", "TTS Ignores messages starting with this").Value;
			TTS.Init();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin AEIOUCompany is loaded!");
			Harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Plugin total patches appled: {Harmony.GetPatchedMethods().Count()}");
		}

		public void OnDestroy()
		{
			if (PlayStartingUpMessage)
			{
				TTS.Speak("Starting Up");
			}
		}

		private void EnableTestMode()
		{
			LCModUtils lCModUtils = new LCModUtils(Harmony);
			lCModUtils.DisableFullscreen();
			lCModUtils.BootToLANMenu();
		}
	}
	internal static class TTS
	{
		public readonly struct SpeechData
		{
			public readonly int PlayerId;

			public readonly float[] AudioData;

			public readonly float AudioLengthInSeconds;

			public SpeechData(int playerPlayerId, float[] audioData, float audioLengthInSeconds)
			{
				PlayerId = playerPlayerId;
				AudioData = audioData;
				AudioLengthInSeconds = audioLengthInSeconds;
			}
		}

		private const int OUT_BUFFER_SIZE = 8192;

		public const int IN_BUFFER_SIZE = 8388608;

		private static readonly float[] audioFloatBuffer = new float[8388608];

		private static byte[] audioByteBuffer = new byte[16777216];

		private static NamedPipeClientStream _namedPipeClientStream;

		private static StreamWriter _streamWriter;

		private static BinaryReader _binaryReader;

		private static bool _initialized = false;

		public static void Init()
		{
			StartSpeakServer();
			try
			{
				_namedPipeClientStream = new NamedPipeClientStream("AEIOUCOMPANYMOD");
				_streamWriter = new StreamWriter(_namedPipeClientStream, Encoding.UTF8, 8192, leaveOpen: true);
				_binaryReader = new BinaryReader(_namedPipeClientStream, Encoding.UTF8, leaveOpen: true);
			}
			catch (IOException data)
			{
				Plugin.LogError(data);
			}
			ConnectToSpeakServer();
			_initialized = true;
		}

		public static void Speak(string message)
		{
			if (!_initialized)
			{
				Plugin.LogError("Tried to speak before initializing TTS!");
				return;
			}
			try
			{
				SendMsg(message, "msgA");
			}
			catch (IOException ex)
			{
				Plugin.LogError("Speak" + ex);
			}
		}

		public static SpeechData SpeakToMemory(int playerId, string message, float volumeScale = 1f)
		{
			if (!_initialized)
			{
				Plugin.LogError("Tried to speak before initializing TTS!");
				return default(SpeechData);
			}
			message = message.Replace("\r", "").Replace("\n", "");
			SendMsg(message, "msg");
			int num = _binaryReader.ReadInt32();
			if (num > audioByteBuffer.Length)
			{
				audioByteBuffer = new byte[num];
			}
			Array.Clear(audioFloatBuffer, 0, audioFloatBuffer.Length);
			int num2 = 0;
			_binaryReader.Read(audioByteBuffer, 0, num);
			for (int i = 0; i < Math.Min(num / 2, audioFloatBuffer.Length); i++)
			{
				float num3 = volumeScale * ((float)BitConverter.ToInt16(audioByteBuffer, i * 2) / 32767f);
				audioFloatBuffer[i] = num3;
				if (num3 != 0f)
				{
					num2 = i;
				}
			}
			float audioLengthInSeconds = (float)num2 / 11025f;
			Plugin.Log("END");
			return new SpeechData(playerId, audioFloatBuffer, audioLengthInSeconds);
		}

		private static void SendMsg(string message, string prefix)
		{
			if (!_namedPipeClientStream.IsConnected)
			{
				StartSpeakServer();
				ConnectToSpeakServer();
			}
			message = prefix + "=[:np]" + message + "]";
			Plugin.Log("Sending: " + message);
			try
			{
				_streamWriter.WriteLine(message);
				_streamWriter.Flush();
			}
			catch (Exception data)
			{
				Plugin.LogError(data);
			}
		}

		private static void ConnectToSpeakServer()
		{
			Plugin.Log("ConnectingToPipe");
			try
			{
				_namedPipeClientStream.Connect(7500);
			}
			catch (TimeoutException)
			{
				Plugin.LogError("Unable to connect to SpeakServer after timeout");
			}
			catch (IOException)
			{
				Plugin.LogError("IOException while trying to ConnectToSpeakServer");
			}
		}

		private static void StartSpeakServer()
		{
			if (CheckForAEIOUSPEAKProcess())
			{
				Process[] processesByName = Process.GetProcessesByName("AEIOUSpeak");
				Process[] array = processesByName;
				foreach (Process process in array)
				{
					process.Close();
				}
			}
			string text = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath.Replace("AEIOUCompany.dll", "");
			Plugin.Log(text + "AEIOUSpeak.exe");
			Process process2 = Process.Start(text + "AEIOUSpeak.exe");
			Plugin.Log("Started Speak Server");
			if (process2 == null)
			{
				Plugin.LogError("Failed to start Speak Server");
			}
		}

		private static bool CheckForAEIOUSPEAKProcess()
		{
			Process[] processesByName = Process.GetProcessesByName("AEIOUSpeak");
			if (processesByName.Length != 0)
			{
				return true;
			}
			return false;
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "AEIOUCompany";

		public const string PLUGIN_NAME = "AEIOUCompany";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}

aeioucompany/SharpTalk.dll

Decompiled 3 months ago
using System;
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;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("SharpTalk")]
[assembly: AssemblyDescription("A .NET wrapper for the DECtalk TTS engine.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SharpTalk")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("a8dbd00f-6cfd-4d00-a4d6-b0059b2eb887")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.8726.31713")]
[module: UnverifiableCode]
namespace SharpTalk;

public class FonixTalkEngine : IDisposable
{
	[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
	private delegate void DtCallbackRoutine(int lParam1, int lParam2, uint drCallbackParameter, uint uiMsg);

	private struct PhonemeMark
	{
		public byte ThisPhoneme;

		public byte NextPhoneme;

		public ushort Duration;
	}

	[StructLayout(LayoutKind.Explicit)]
	private struct PhonemeTag
	{
		[FieldOffset(0)]
		public PhonemeMark PMData;

		[FieldOffset(0)]
		public int DWData;
	}

	private struct InMemoryRaiiHelper : IDisposable
	{
		private readonly FonixTalkEngine _engine;

		public InMemoryRaiiHelper(FonixTalkEngine engine)
		{
			_engine = engine;
		}

		public void Dispose()
		{
			_engine.CloseInMemory();
		}
	}

	private struct BufferRaiiHelper : IDisposable
	{
		private readonly FonixTalkEngine _engine;

		public BufferRaiiHelper(FonixTalkEngine engine)
		{
			_engine = engine;
		}

		public void Dispose()
		{
			_engine.FreeBuffer();
		}
	}

	public const uint DefaultRate = 200u;

	public const TtsVoice DefaultSpeaker = TtsVoice.Paul;

	private const uint WaveFormat_1M16 = 4u;

	private const uint TtsNotSupported = 32767u;

	private const uint TtsNotAvailable = 32766u;

	private const uint TtsLangError = 16384u;

	private static readonly uint UiIndexMsg = RegisterWindowMessage("DECtalkIndexMessage");

	private static readonly uint UiErrorMsg = RegisterWindowMessage("DECtalkErrorMessage");

	private static readonly uint UiBufferMsg = RegisterWindowMessage("DECtalkBufferMessage");

	private static readonly uint UiPhonemeMsg = RegisterWindowMessage("DECtalkVisualMessage");

	private IntPtr _handle;

	private IntPtr _speakerParamsPtr;

	private IntPtr _dummy1;

	private IntPtr _dummy2;

	private IntPtr _dummy3;

	private DtCallbackRoutine _callback;

	private TtsBufferManaged _buffer;

	private Stream _bufferStream;

	public TtsVoice Voice
	{
		get
		{
			Check(TextToSpeechGetSpeaker(_handle, out var speaker));
			return speaker;
		}
		set
		{
			Check(TextToSpeechSetSpeaker(_handle, value));
		}
	}

	public uint Rate
	{
		get
		{
			Check(TextToSpeechGetRate(_handle, out var rate));
			return rate;
		}
		set
		{
			Check(TextToSpeechSetRate(_handle, value));
		}
	}

	public SpeakerParams SpeakerParams
	{
		get
		{
			Check(TextToSpeechGetSpeakerParams(_handle, 0u, out _speakerParamsPtr, out _dummy1, out _dummy2, out _dummy3));
			return (SpeakerParams)Marshal.PtrToStructure(_speakerParamsPtr, typeof(SpeakerParams));
		}
		set
		{
			Check(TextToSpeechGetSpeakerParams(_handle, 0u, out _speakerParamsPtr, out _dummy1, out _dummy2, out _dummy3));
			Marshal.StructureToPtr(value, _speakerParamsPtr, fDeleteOld: false);
			Check(TextToSpeechSetSpeakerParams(_handle, _speakerParamsPtr));
		}
	}

	public event EventHandler<PhonemeEventArgs> Phoneme;

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechStartupEx(out IntPtr handle, uint uiDeviceNumber, uint dwDeviceOptions, DtCallbackRoutine callback, ref IntPtr dwCallbackParameter);

	[DllImport("FonixTalk.dll")]
	[return: MarshalAs(UnmanagedType.Bool)]
	private static extern bool TextToSpeechSelectLang(IntPtr handle, uint lang);

	[DllImport("FonixTalk.dll")]
	private static extern uint TextToSpeechStartLang([MarshalAs(UnmanagedType.LPStr)] string lang);

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechSetSpeaker(IntPtr handle, TtsVoice speaker);

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechGetSpeaker(IntPtr handle, out TtsVoice speaker);

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechGetRate(IntPtr handle, out uint rate);

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechSetRate(IntPtr handle, uint rate);

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechSpeakA(IntPtr handle, [MarshalAs(UnmanagedType.LPStr)] string msg, uint flags);

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechShutdown(IntPtr handle);

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechPause(IntPtr handle);

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechResume(IntPtr handle);

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechReset(IntPtr handle, [MarshalAs(UnmanagedType.Bool)] bool bReset);

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechSync(IntPtr handle);

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechSetSpeakerParams(IntPtr handle, IntPtr spDefs);

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechGetSpeakerParams(IntPtr handle, uint uiIndex, out IntPtr ppspCur, out IntPtr ppspLoLimit, out IntPtr ppspHiLimit, out IntPtr ppspDefault);

	[DllImport("FonixTalk.dll")]
	private unsafe static extern MMRESULT TextToSpeechAddBuffer(IntPtr handle, TtsBufferManaged.TTS_BUFFER_T* buffer);

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechOpenInMemory(IntPtr handle, uint format);

	[DllImport("FonixTalk.dll")]
	private static extern MMRESULT TextToSpeechCloseInMemory(IntPtr handle);

	[DllImport("user32.dll")]
	private static extern uint RegisterWindowMessage([MarshalAs(UnmanagedType.LPStr)] string lpString);

	[DllImport("kernel32.dll")]
	private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

	public FonixTalkEngine(string language)
	{
		Init(language);
	}

	public FonixTalkEngine()
	{
		Init("US");
	}

	public FonixTalkEngine(string language, uint rate, TtsVoice speaker)
	{
		Init(language);
		Voice = speaker;
		Rate = rate;
	}

	public FonixTalkEngine(uint rate, TtsVoice speaker)
	{
		Init("US");
		Voice = speaker;
		Rate = rate;
	}

	private void Init(string lang)
	{
		_callback = TtsCallback;
		_buffer = null;
		_bufferStream = null;
		if (lang != "XX")
		{
			uint num = TextToSpeechStartLang(lang);
			if ((num & 0x4000u) != 0)
			{
				switch (num)
				{
				case 32767u:
					throw new FonixTalkException("This version of DECtalk does not support multiple languages.");
				case 32766u:
					throw new FonixTalkException("The specified language was not found.");
				}
			}
			if (!TextToSpeechSelectLang(IntPtr.Zero, num))
			{
				throw new FonixTalkException("The specified language failed to load.");
			}
		}
		Check(TextToSpeechStartupEx(out _handle, uint.MaxValue, 0u, _callback, ref _handle));
		Speak("[:phone on]");
	}

	private unsafe void TtsCallback(int lParam1, int lParam2, uint drCallbackParameter, uint uiMsg)
	{
		if (uiMsg == UiPhonemeMsg && this.Phoneme != null)
		{
			PhonemeTag phonemeTag = default(PhonemeTag);
			phonemeTag.DWData = lParam2;
			PhonemeTag phonemeTag2 = phonemeTag;
			this.Phoneme(this, new PhonemeEventArgs((char)phonemeTag2.PMData.ThisPhoneme, phonemeTag2.PMData.Duration));
		}
		else if (uiMsg == UiBufferMsg)
		{
			_bufferStream.Write(_buffer.GetBufferBytes(), 0, (int)_buffer.Length);
			_ = _buffer.Full;
			_buffer.Reset();
			Check(TextToSpeechAddBuffer(_handle, _buffer.ValuePointer));
		}
		else if (uiMsg != UiErrorMsg)
		{
			_ = UiIndexMsg;
		}
	}

	public byte[] SpeakToMemory(string input)
	{
		using (_bufferStream = new MemoryStream())
		{
			using (OpenInMemory(4u))
			{
				using (ReadyBuffer())
				{
					Speak(input);
					Sync();
					TextToSpeechReset(_handle, bReset: false);
				}
			}
			return ((MemoryStream)_bufferStream).ToArray();
		}
	}

	public void SpeakToStream(Stream stream, string input)
	{
		_bufferStream = stream;
		using (OpenInMemory(4u))
		{
			using (ReadyBuffer())
			{
				Speak(input);
				Sync();
				TextToSpeechReset(_handle, bReset: false);
			}
		}
		_bufferStream = null;
	}

	public void SpeakToWavFile(string path, string input)
	{
		using MemoryStream memoryStream = new MemoryStream();
		SpeakToStream(memoryStream, input);
		int num = (int)memoryStream.Length;
		using BinaryWriter binaryWriter = new BinaryWriter(File.Create(path), Encoding.ASCII);
		binaryWriter.Write("RIFF".ToCharArray());
		binaryWriter.Write(num + 44 - 8);
		binaryWriter.Write("WAVE".ToCharArray());
		binaryWriter.Write("fmt ".ToCharArray());
		binaryWriter.Write(16);
		binaryWriter.Write((short)1);
		binaryWriter.Write((short)1);
		binaryWriter.Write(11025);
		binaryWriter.Write(22050);
		binaryWriter.Write((short)2);
		binaryWriter.Write((short)16);
		binaryWriter.Write("data".ToCharArray());
		binaryWriter.Write(num);
		memoryStream.Position = 0L;
		memoryStream.CopyTo(binaryWriter.BaseStream);
	}

	public void Pause()
	{
		Check(TextToSpeechPause(_handle));
	}

	public void Resume()
	{
		Check(TextToSpeechResume(_handle));
	}

	public void Reset()
	{
		Check(TextToSpeechReset(_handle, bReset: false));
	}

	public void Sync()
	{
		Check(TextToSpeechSync(_handle));
	}

	public void Speak(string msg)
	{
		Check(TextToSpeechSpeakA(_handle, msg, 1u));
	}

	private static void Check(MMRESULT code)
	{
		if (code != 0)
		{
			throw new FonixTalkException(code);
		}
	}

	private InMemoryRaiiHelper OpenInMemory(uint format)
	{
		Check(TextToSpeechOpenInMemory(_handle, format));
		return new InMemoryRaiiHelper(this);
	}

	private void CloseInMemory()
	{
		Check(TextToSpeechCloseInMemory(_handle));
	}

	private unsafe BufferRaiiHelper ReadyBuffer()
	{
		if (_buffer != null)
		{
			throw new InvalidOperationException("Buffer already exists.");
		}
		_buffer = new TtsBufferManaged();
		Check(TextToSpeechAddBuffer(_handle, _buffer.ValuePointer));
		return new BufferRaiiHelper(this);
	}

	private void FreeBuffer()
	{
		_buffer.Dispose();
		_buffer = null;
	}

	~FonixTalkEngine()
	{
		Dispose(disposing: false);
	}

	public void Dispose()
	{
		Dispose(disposing: true);
	}

	private void Dispose(bool disposing)
	{
		TextToSpeechShutdown(_handle);
		if (_buffer != null)
		{
			_buffer.Dispose();
		}
		if (disposing)
		{
			GC.SuppressFinalize(this);
		}
	}
}
public sealed class FonixTalkException : Exception
{
	internal FonixTalkException(MMRESULT code)
		: base(GetMessage(code))
	{
	}

	internal FonixTalkException(string message)
		: base(message)
	{
	}

	private static string GetMessage(MMRESULT code)
	{
		return code switch
		{
			MMRESULT.MMSYSERR_INVALPARAM => "An invalid parameter was passed to the function.", 
			MMRESULT.MMSYSERR_INVALHANDLE => "The associated handle is invalid. Did you dispose it?", 
			MMRESULT.MMSYSERR_ERROR => "The function returned a generic error. Please check that you are using the functions correctly.", 
			MMRESULT.MMSYSERR_NOERROR => "The function did not throw an error. If you are seeing this, Berkin was obviously high while coding.", 
			MMRESULT.MMSYSERR_NOMEM => "There was insufficnent memory available to allocate the requested resources.", 
			MMRESULT.MMSYSERR_ALLOCATED => "The requested resources are already in use somewhere else.", 
			MMRESULT.WAVERR_BADFORMAT => "Wave output device does not support request format.", 
			MMRESULT.MMSYSERR_BADDEVICEID => "Device ID out of range.", 
			MMRESULT.MMSYSERR_NODRIVER => "No wave output device present.", 
			_ => code.ToString(), 
		};
	}
}
[Flags]
internal enum DeviceOptions : uint
{
	OwnAudioDevice = 1u,
	ReportOpenError = 2u,
	UseSapi5AudioDevice = 0x40000000u,
	DoNotUseAudioDevice = 0x80000000u
}
public static class LanguageCode
{
	public const string EnglishUS = "US";

	public const string EnglishUK = "UK";

	public const string SpanishCastilian = "SP";

	public const string SpanishLatinAmerican = "LA";

	public const string German = "GR";

	public const string French = "FR";

	public const string None = "XX";
}
internal enum MMRESULT : uint
{
	MMSYSERR_NOERROR = 0u,
	MMSYSERR_ERROR = 1u,
	MMSYSERR_BADDEVICEID = 2u,
	MMSYSERR_NOTENABLED = 3u,
	MMSYSERR_ALLOCATED = 4u,
	MMSYSERR_INVALHANDLE = 5u,
	MMSYSERR_NODRIVER = 6u,
	MMSYSERR_NOMEM = 7u,
	MMSYSERR_NOTSUPPORTED = 8u,
	MMSYSERR_BADERRNUM = 9u,
	MMSYSERR_INVALFLAG = 10u,
	MMSYSERR_INVALPARAM = 11u,
	MMSYSERR_HANDLEBUSY = 12u,
	MMSYSERR_INVALIDALIAS = 13u,
	MMSYSERR_BADDB = 14u,
	MMSYSERR_KEYNOTFOUND = 15u,
	MMSYSERR_READERROR = 16u,
	MMSYSERR_WRITEERROR = 17u,
	MMSYSERR_DELETEERROR = 18u,
	MMSYSERR_VALNOTFOUND = 19u,
	MMSYSERR_NODRIVERCB = 20u,
	WAVERR_BADFORMAT = 32u,
	WAVERR_STILLPLAYING = 33u,
	WAVERR_UNPREPARED = 34u
}
public class PhonemeEventArgs : EventArgs
{
	public readonly char Phoneme;

	public readonly uint Duration;

	internal PhonemeEventArgs(char phoneme, uint duration)
	{
		Phoneme = phoneme;
		Duration = duration;
	}
}
public enum TtsVoice : uint
{
	Paul,
	Betty,
	Harry,
	Frank,
	Dennis,
	Kit,
	Ursula,
	Rita,
	Wendy
}
public struct SpeakerParams
{
	[MarshalAs(UnmanagedType.I2)]
	public Sex Sex;

	public short Smoothness;

	public short Assertiveness;

	public short AveragePitch;

	public short Breathiness;

	public short Richness;

	public short NumFixedSampOG;

	public short Laryngealization;

	public short HeadSize;

	public short Formant4ResFreq;

	public short Formant4Bandwidth;

	public short Formant5ResFreq;

	public short Formant5Bandwidth;

	public short Parallel4Freq;

	public short Parallel5Freq;

	public short GainFrication;

	public short GainAspiration;

	public short GainVoicing;

	public short GainNasalization;

	public short GainCFR1;

	public short GainCFR2;

	public short GainCFR3;

	public short GainCFR4;

	public short Loudness;

	public short SpectralTilt;

	public short BaselineFall;

	public short LaxBreathiness;

	public short Quickness;

	public short HatRise;

	public short StressRise;

	public short GlottalSpeed;

	public short OutputGainMultiplier;
}
public enum Sex : short
{
	Female,
	Male
}
[Flags]
internal enum SpeakFlags : uint
{
	Normal = 0u,
	Force = 1u
}
internal enum TTSMessageType : uint
{
	Buffer,
	IndexMarker,
	Status,
	Visual
}
[StructLayout(LayoutKind.Sequential)]
internal class TtsBufferManaged : IDisposable
{
	public struct TTS_BUFFER_T
	{
		public const int BufferSize = 16384;

		public IntPtr DataPtr;

		public unsafe TTS_PHONEME_T* PhonemeArrayPtr;

		public unsafe TTS_INDEX_T* IndexArrayPtr;

		public uint MaxBufferLength;

		public uint MaxPhonemeChanges;

		public uint MaxIndexMarks;

		public uint BufferLength;

		public uint PhonemeChangeCount;

		public uint IndexMarkCount;

		public uint _reserved;
	}

	private TTS_BUFFER_T _value;

	private GCHandle _pinHandle;

	public bool Full => _value.BufferLength == _value.MaxBufferLength;

	public uint Length => _value.BufferLength;

	public unsafe TTS_BUFFER_T* ValuePointer
	{
		get
		{
			fixed (TTS_BUFFER_T* result = &_value)
			{
				return result;
			}
		}
	}

	public TtsBufferManaged()
	{
		_value = default(TTS_BUFFER_T);
		_pinHandle = GCHandle.Alloc(this, GCHandleType.Pinned);
		_value.MaxBufferLength = 16384u;
		_value.DataPtr = Marshal.AllocHGlobal(16384);
	}

	public byte[] GetBufferBytes()
	{
		byte[] array = new byte[_value.BufferLength];
		Marshal.Copy(_value.DataPtr, array, 0, (int)_value.BufferLength);
		return array;
	}

	public void Reset()
	{
		_value.BufferLength = 0u;
		_value.PhonemeChangeCount = 0u;
		_value.IndexMarkCount = 0u;
	}

	public void Dispose()
	{
		Marshal.FreeHGlobal(_value.DataPtr);
		_pinHandle.Free();
		GC.SuppressFinalize(this);
	}
}
internal struct TTS_INDEX_T
{
	public uint IndexValue;

	public uint SampleNumber;

	private readonly uint _reserved;
}
internal struct TTS_PHONEME_T
{
	public uint Phoneme;

	public uint PhonemeSampleNumber;

	public uint PhonemeDuration;

	private readonly uint _reserved;
}