Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LethalSpeechOutput v2.0.2
BepInEx/plugins/Lethal Speech Output/Lethal Speech Output.dll
Decompiled 2 years agousing 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 BepInEx; using BepInEx.Logging; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Lethal Speech Output")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Lethal Speech Output")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("52a032fb-afa9-4869-86e1-c69b91215802")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace GreenBean.LethalSpeechOutput; [BepInPlugin("GreenBean.LethalSpeechOutput", "Lethal Speech Output", "1.0.0")] public class LethalSpeechOutput : BaseUnityPlugin { private static LethalSpeechOutput Instance; internal ManualLogSource mls; private const string modGUID = "GreenBean.LethalSpeechOutput"; private const string modName = "Lethal Speech Output"; private const string modVersion = "1.0.0"; public static bool ForceLogSpeech { get; set; } private void Awake() { if ((Object)(object)Instance == (Object)null) { Instance = this; } mls = Logger.CreateLogSource("GreenBean.LethalSpeechOutput"); mls.LogInfo((object)"Lethal Speech Output running."); SpeechSynthesizer.Initialize(); } private void OnDestroy() { SpeechSynthesizer.Shutdown(); } public static void SpeakText(string text) { try { SpeechSynthesizer.SpeakText(text); if (ForceLogSpeech) { Instance.mls.LogInfo((object)("Spoke: '" + text + "' using Tolk or SAPI.")); } } catch (Exception ex) { Instance.mls.LogError((object)("Failed to speak using Tolk or SAPI: " + ex.Message)); } } } public static class SpeechSynthesizer { private delegate void LoadDelegate(); private delegate void UnloadDelegate(); private delegate bool OutputDelegate([MarshalAs(UnmanagedType.LPWStr)] string str, bool interrupt); private delegate IntPtr DetectScreenReaderDelegate(); private const string TolkDllName = "Tolk.dll"; private const string TolkDotNetDllName = "TolkDotNet.dll"; private const string NvdaClientDllName = "nvdaControllerClient64.dll"; private const string SapiDllName = "SAAPI64.dll"; private static Dictionary<string, IntPtr> loadedDlls = new Dictionary<string, IntPtr>(); private static string tempDirectory; private static LoadDelegate Tolk_Load; private static UnloadDelegate Tolk_Unload; private static OutputDelegate Tolk_Output; private static DetectScreenReaderDelegate Tolk_DetectScreenReader; [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern IntPtr LoadLibrary(string lpFileName); [DllImport("kernel32.dll")] private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); [DllImport("kernel32.dll")] private static extern bool FreeLibrary(IntPtr hModule); public static void Initialize() { try { tempDirectory = Path.Combine(Path.GetTempPath(), "LethalSpeechOutput"); Directory.CreateDirectory(tempDirectory); ExtractAndLoadDll("Tolk.dll"); ExtractAndLoadDll("TolkDotNet.dll"); ExtractAndLoadDll("nvdaControllerClient64.dll"); ExtractAndLoadDll("SAAPI64.dll"); string text = Environment.GetEnvironmentVariable("PATH") ?? string.Empty; Environment.SetEnvironmentVariable("PATH", tempDirectory + ";" + text); if (loadedDlls.ContainsKey("Tolk.dll")) { InitializeTolk(loadedDlls["Tolk.dll"]); } else { Debug.LogError((object)"Failed to initialize Tolk. Speech synthesis will not be available."); } } catch (Exception ex) { Debug.LogError((object)("Error initializing speech synthesizer: " + ex.Message)); Debug.LogError((object)("Stack trace: " + ex.StackTrace)); } } private static void ExtractAndLoadDll(string dllName) { string text = "Lethal_Speech_Output." + dllName; using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(text); if (stream == null) { Debug.LogError((object)("Failed to find embedded resource: " + text)); return; } string text2 = Path.Combine(tempDirectory, dllName); using (FileStream destination = new FileStream(text2, FileMode.Create, FileAccess.Write)) { stream.CopyTo(destination); } IntPtr intPtr = LoadLibrary(text2); if (intPtr != IntPtr.Zero) { loadedDlls[dllName] = intPtr; return; } int lastWin32Error = Marshal.GetLastWin32Error(); Debug.LogWarning((object)$"Failed to load {dllName} from embedded resources. Error code: {lastWin32Error}"); } private static void InitializeTolk(IntPtr tolkDllHandle) { Tolk_Load = GetDelegate<LoadDelegate>(tolkDllHandle, "Tolk_Load"); Tolk_Unload = GetDelegate<UnloadDelegate>(tolkDllHandle, "Tolk_Unload"); Tolk_Output = GetDelegate<OutputDelegate>(tolkDllHandle, "Tolk_Output"); Tolk_DetectScreenReader = GetDelegate<DetectScreenReaderDelegate>(tolkDllHandle, "Tolk_DetectScreenReader"); Tolk_Load(); IntPtr intPtr = Tolk_DetectScreenReader(); string text = ((intPtr != IntPtr.Zero) ? Marshal.PtrToStringUni(intPtr) : "None"); Debug.Log((object)("Tolk initialized. Detected screen reader: " + text)); } private static T GetDelegate<T>(IntPtr module, string procName) where T : class { IntPtr procAddress = GetProcAddress(module, procName); if (procAddress == IntPtr.Zero) { throw new Exception("Failed to get address of " + procName); } return Marshal.GetDelegateForFunctionPointer(procAddress, typeof(T)) as T; } public static void Shutdown() { if (Tolk_Unload != null) { Tolk_Unload(); Debug.Log((object)"Tolk unloaded."); } foreach (IntPtr value in loadedDlls.Values) { FreeLibrary(value); } loadedDlls.Clear(); if (Directory.Exists(tempDirectory)) { Directory.Delete(tempDirectory, recursive: true); } Debug.Log((object)"All DLLs unloaded."); } public static void SpeakText(string text, bool interrupt = false) { if (string.IsNullOrEmpty(text)) { Debug.LogWarning((object)"Attempted to speak empty or null text."); } else if (Tolk_Output != null) { bool flag = Tolk_Output(text, interrupt); Debug.Log((object)$"Tolk speech result: {flag}"); } else { Debug.LogWarning((object)"Tolk_Output is null. Speech synthesis failed."); } } public static string DetectScreenReader() { if (Tolk_DetectScreenReader != null) { IntPtr intPtr = Tolk_DetectScreenReader(); if (!(intPtr != IntPtr.Zero)) { return "None"; } return Marshal.PtrToStringUni(intPtr); } return "No screen reader detected"; } }