Decompiled source of Tongue v1.0.0

tongue.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using REPOLib.Modules;
using Strobotnik.Klattersynth;
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: AssemblyCompany("tongue")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1+0616cb263e2b8da20975f29505302f335ec69195")]
[assembly: AssemblyProduct("Tongue")]
[assembly: AssemblyTitle("tongue")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.1.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;
		}
	}
}
namespace tongue
{
	[BepInPlugin("sane.tongue", "Tongue", "1.0.0")]
	[BepInProcess("REPO.exe")]
	[BepInIncompatibility("Lavighju.espeakTTS")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[HarmonyPatch]
	public class Tongue : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		public static NetworkedEvent LanguageChangeEvent;

		private ConfigEntry<string> languageSetting;

		private const int AUDIO_OUTPUT_SYNCHRONOUS = 0;

		private const int espeakCHARS_UTF8 = 1;

		private const int PHONEME_MODE_ASCII = 0;

		private string currentSpeaker = "";

		private Dictionary<string, string> languagePerPlayer = new Dictionary<string, string>();

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

		public static Tongue Instance { get; private set; }

		[DllImport("libespeak-ng.dll", CallingConvention = CallingConvention.Cdecl)]
		public static extern int espeak_Initialize(int output, int buflength, string path, int options);

		[DllImport("libespeak-ng.dll", CallingConvention = CallingConvention.Cdecl)]
		public static extern int espeak_SetVoiceByName(string name);

		[DllImport("libespeak-ng.dll", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr espeak_TextToPhonemes(ref IntPtr textPtr, int textMode, int phonemeMode);

		public static string phoneticize(string text, string language)
		{
			if (language == "en")
			{
				return text;
			}
			espeak_SetVoiceByName(language);
			byte[] bytes = Encoding.UTF8.GetBytes(text + "\0");
			IntPtr intPtr = Marshal.AllocHGlobal(bytes.Length);
			Marshal.Copy(bytes, 0, intPtr, bytes.Length);
			IntPtr textPtr = intPtr;
			IntPtr intPtr2 = espeak_TextToPhonemes(ref textPtr, 1, 0);
			if (intPtr2 != IntPtr.Zero)
			{
				string text2 = Marshal.PtrToStringAnsi(intPtr2);
				Marshal.FreeHGlobal(intPtr);
				return "[" + text2 + "]";
			}
			Marshal.FreeHGlobal(intPtr);
			return "";
		}

		private void Awake()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Expected O, but got Unknown
			Instance = this;
			((Object)((Component)Instance).gameObject).hideFlags = (HideFlags)61;
			Logger = ((BaseUnityPlugin)this).Logger;
			LanguageChangeEvent = new NetworkedEvent("LanguageChangeEvent", (Action<EventData>)HandleLanguageChangeEvent);
			languageSetting = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Language", "de", new ConfigDescription("What language should the TTS emulate?", (AcceptableValueBase)(object)new AcceptableValueList<string>(language_list), Array.Empty<object>()));
			string text;
			try
			{
				text = Directory.GetFiles(Paths.PluginPath, "phondata-manifest", SearchOption.AllDirectories)[0].Replace("phondata-manifest", "");
			}
			catch
			{
				Logger.LogError((object)"espeak-ng-data not found!");
				return;
			}
			Logger.LogInfo((object)("Attempting to load eSpeak data from " + text));
			int num = espeak_Initialize(0, 0, text, 0);
			if (num == -1)
			{
				Logger.LogError((object)("eSpeak failed to initialize from " + text + "! Not patching!"));
				return;
			}
			Logger.LogInfo((object)"eSpeak loaded successfully! Patching Speak Method...");
			Harmony val = new Harmony("sane.tongue");
			val.PatchAll();
		}

		private static void HandleLanguageChangeEvent(EventData eventData)
		{
			string[] array = (string[])eventData.CustomData;
			string key = array[0];
			string value = array[1];
			Instance.languagePerPlayer[key] = value;
		}

		[HarmonyPatch(typeof(WorldSpaceUIParent), "TTS")]
		[HarmonyPrefix]
		private static void TTSPrefix(PlayerAvatar _player, string _text, float _time)
		{
			string text = (string)typeof(PlayerAvatar).GetField("steamID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).GetValue(_player);
			Logger.LogDebug((object)("Current Speaker: " + text));
			Instance.currentSpeaker = text;
		}

		public void ChangeTTSLanguageForSteamID(string steamID, string language)
		{
			if (!language_list.Contains(language))
			{
				Logger.LogError((object)("Language " + language + " is not valid. Perhaps someone is using a different version of Tongue."));
				return;
			}
			PlayerAvatar val = SemiFunc.PlayerAvatarGetFromSteamID(steamID);
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogError((object)"Someone wants to change language but provided an invalid steamID!");
			}
			else if (languagePerPlayer.ContainsKey(steamID))
			{
				languagePerPlayer[steamID] = language;
			}
			else
			{
				languagePerPlayer.Add(steamID, language);
			}
		}

		[HarmonyPatch(typeof(PlayerAvatar), "ChatMessageSend")]
		[HarmonyPrefix]
		private static void ChatMessageSendPrefix(PlayerAvatar __instance)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			string text = (string)typeof(PlayerAvatar).GetField("steamID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).GetValue(__instance);
			string[] array = new string[2]
			{
				text,
				Instance.languageSetting.Value
			};
			LanguageChangeEvent.RaiseEvent((object)array, NetworkingEvents.RaiseAll, SendOptions.SendReliable);
		}

		[HarmonyPatch(typeof(SpeechSynth), "speak", new Type[]
		{
			typeof(StringBuilder),
			typeof(int),
			typeof(VoicingSource),
			typeof(bool)
		})]
		[HarmonyPrefix]
		private static void SpeakPrefix(ref StringBuilder text, int voiceFundamentalFreq, VoicingSource voicingSource, ref bool bracketsAsPhonemes)
		{
			bracketsAsPhonemes = true;
			string text2 = text.ToString();
			text.Length = 0;
			string valueOrDefault = Instance.languagePerPlayer.GetValueOrDefault(Instance.currentSpeaker, Instance.languageSetting.Value);
			text.Append(phoneticize(text2, valueOrDefault));
			if (valueOrDefault == "en")
			{
				return;
			}
			while (text.ToString().IndexOf(':') != -1)
			{
				int num = text.ToString().IndexOf(':');
				text[num] = text[num - 1];
			}
			Dictionary<char, char> dictionary = new Dictionary<char, char>
			{
				{ 'C', 'S' },
				{ 'Y', '3' },
				{ 'E', 'e' },
				{ 'a', 'A' },
				{ 'W', '3' }
			};
			foreach (var (oldChar, newChar) in dictionary)
			{
				text.Replace(oldChar, newChar);
			}
			text2 = Regex.Replace(text.ToString(), "\\([^)]*\\)", "").Trim();
			text.Length = 0;
			text.Append(text2);
			Logger.LogDebug((object)$"Saying word: {text}");
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "tongue";

		public const string PLUGIN_NAME = "Tongue";

		public const string PLUGIN_VERSION = "0.1.1";
	}
}