Please disclose if any significant portion of your mod was created 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 ZundamonizedTTS v1.0.0
ZundamonizedTTS.dll
Decompiled 7 months agousing System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using Dissonance; using GameNetcodeStuff; using HarmonyLib; using Newtonsoft.Json.Linq; using Unity.Collections; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ChatReadMod")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ChatReadMod")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("fff60788-1a01-4f7f-b42b-cfae325d0334")] [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")] public static class WavUtility { private const int HEADER_SIZE = 44; public static bool Save(string filename, AudioClip clip) { if (!filename.ToLower().EndsWith(".wav")) { filename += ".wav"; } string text = Path.Combine(Application.persistentDataPath, filename); Debug.Log((object)text); Directory.CreateDirectory(Path.GetDirectoryName(text)); using (FileStream fileStream = CreateEmpty(text)) { ConvertAndWrite(fileStream, clip); WriteHeader(fileStream, clip); } return true; } public static AudioClip TrimSilence(AudioClip clip, float min) { float[] array = new float[clip.samples]; clip.GetData(array, 0); return TrimSilence(new List<float>(array), min, clip.channels, clip.frequency); } public static AudioClip TrimSilence(List<float> samples, float min, int channels, int hz) { return TrimSilence(samples, min, channels, hz, _3D: false, stream: false); } public static AudioClip TrimSilence(List<float> samples, float min, int channels, int hz, bool _3D, bool stream) { int i; for (i = 0; i < samples.Count && !(Mathf.Abs(samples[i]) > min); i++) { } samples.RemoveRange(0, i); i = samples.Count - 1; while (i > 0 && !(Mathf.Abs(samples[i]) > min)) { i--; } samples.RemoveRange(i, samples.Count - i); AudioClip obj = AudioClip.Create("TempClip", samples.Count, channels, hz, _3D, stream); obj.SetData(samples.ToArray(), 0); return obj; } private static FileStream CreateEmpty(string filepath) { FileStream fileStream = new FileStream(filepath, FileMode.Create); byte value = 0; for (int i = 0; i < 44; i++) { fileStream.WriteByte(value); } return fileStream; } private static void ConvertAndWrite(FileStream fileStream, AudioClip clip) { float[] array = new float[clip.samples]; clip.GetData(array, 0); short[] array2 = new short[array.Length]; byte[] array3 = new byte[array.Length * 2]; int num = 32767; for (int i = 0; i < array.Length; i++) { array2[i] = (short)(array[i] * (float)num); _ = new byte[2]; BitConverter.GetBytes(array2[i]).CopyTo(array3, i * 2); } fileStream.Write(array3, 0, array3.Length); } private static void WriteHeader(FileStream fileStream, AudioClip clip) { int frequency = clip.frequency; int channels = clip.channels; int samples = clip.samples; fileStream.Seek(0L, SeekOrigin.Begin); byte[] bytes = Encoding.UTF8.GetBytes("RIFF"); fileStream.Write(bytes, 0, 4); byte[] bytes2 = BitConverter.GetBytes(fileStream.Length - 8); fileStream.Write(bytes2, 0, 4); byte[] bytes3 = Encoding.UTF8.GetBytes("WAVE"); fileStream.Write(bytes3, 0, 4); byte[] bytes4 = Encoding.UTF8.GetBytes("fmt "); fileStream.Write(bytes4, 0, 4); byte[] bytes5 = BitConverter.GetBytes(16); fileStream.Write(bytes5, 0, 4); byte[] bytes6 = BitConverter.GetBytes((ushort)1); fileStream.Write(bytes6, 0, 2); byte[] bytes7 = BitConverter.GetBytes(channels); fileStream.Write(bytes7, 0, 2); byte[] bytes8 = BitConverter.GetBytes(frequency); fileStream.Write(bytes8, 0, 4); byte[] bytes9 = BitConverter.GetBytes(frequency * channels * 2); fileStream.Write(bytes9, 0, 4); ushort value = (ushort)(channels * 2); fileStream.Write(BitConverter.GetBytes(value), 0, 2); byte[] bytes10 = BitConverter.GetBytes((ushort)16); fileStream.Write(bytes10, 0, 2); byte[] bytes11 = Encoding.UTF8.GetBytes("data"); fileStream.Write(bytes11, 0, 4); byte[] bytes12 = BitConverter.GetBytes(samples * channels * 2); fileStream.Write(bytes12, 0, 4); } public static AudioClip ToAudioClip(byte[] wavFile, int offsetSamples, string name) { using MemoryStream input = new MemoryStream(wavFile); using BinaryReader binaryReader = new BinaryReader(input); binaryReader.ReadBytes(22); short num = binaryReader.ReadInt16(); int num2 = binaryReader.ReadInt32(); binaryReader.ReadBytes(6); short num3 = binaryReader.ReadInt16(); new string(binaryReader.ReadChars(4)); int num4 = binaryReader.ReadInt32() / (num3 / 8); float[] array = new float[num4]; for (int i = 0; i < num4; i++) { short num5 = binaryReader.ReadInt16(); array[i] = (float)num5 / 32768f; } AudioClip obj = AudioClip.Create(name, num4 / num, (int)num, num2, false); obj.SetData(array, 0); return obj; } } namespace VoicevoxInject; [BepInPlugin("com.example.voicevox.inject", "ZundamonizedTTS", "1.0.0")] public class VoicevoxInjectPlugin : BaseUnityPlugin { public static VoicevoxInjectPlugin Instance; public static Sprite GlobalSprite; public static readonly ConcurrentQueue<Action> ExecutionQueue = new ConcurrentQueue<Action>(); public static ConfigEntry<float> VolumeConfig; public static ConfigEntry<int> SpeakerIdConfig; public static ConfigEntry<int> VoicevoxTimeoutSecConfig; public static ConfigEntry<int> VoicevoxRetryCountConfig; private void Awake() { //IL_00f2: Unknown result type (might be due to invalid IL or missing references) Instance = this; VolumeConfig = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Volume", 1f, "Volume scale for the TTS voice (VOICEVOX volumeScale)."); SpeakerIdConfig = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SpeakerId", 3, "VOICEVOX Speaker ID (3 = Zundamon Normal)."); VoicevoxTimeoutSecConfig = ((BaseUnityPlugin)this).Config.Bind<int>("General", "VoicevoxTimeoutSec", 8, "VOICEVOX request timeout in seconds."); VoicevoxRetryCountConfig = ((BaseUnityPlugin)this).Config.Bind<int>("General", "VoicevoxRetryCount", 1, "Number of retries on VOICEVOX failure (0 = no retry)."); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"ZundamonizedTTS Loaded. Volume: {VolumeConfig.Value}, SpeakerID: {SpeakerIdConfig.Value}, Timeout: {VoicevoxTimeoutSecConfig.Value}s, Retries: {VoicevoxRetryCountConfig.Value}"); new Harmony("com.example.voicevox.inject").PatchAll(); LoadSprite(); } private void Update() { Action result; while (ExecutionQueue.TryDequeue(out result)) { try { result(); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Dispatcher Error: {arg}"); } } } public static void Enqueue(Action action) { ExecutionQueue.Enqueue(action); } private void LoadSprite() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) string path = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "zundamon.png"); if (File.Exists(path)) { try { byte[] array = File.ReadAllBytes(path); Texture2D val = new Texture2D(2, 2); if (ImageConversion.LoadImage(val, array)) { ((Texture)val).filterMode = (FilterMode)1; GlobalSprite = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Voicevox] Image loaded successfully."); } return; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Voicevox] Failed to load image: " + ex.Message)); return; } } string path2 = Path.Combine(Paths.PluginPath, "zundamon.png"); if (File.Exists(path2)) { try { byte[] array2 = File.ReadAllBytes(path2); Texture2D val2 = new Texture2D(2, 2); if (ImageConversion.LoadImage(val2, array2)) { ((Texture)val2).filterMode = (FilterMode)1; GlobalSprite = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.5f)); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Voicevox] Image loaded from fallback location."); } return; } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Voicevox] Failed to load fallback image: " + ex2.Message)); return; } } ((BaseUnityPlugin)this).Logger.LogWarning((object)"[Voicevox] Image not found. Face feature disabled."); } } public class VoicevoxDispatcher : MonoBehaviour { private static VoicevoxDispatcher _instance; public static void EnsureExists() { if (!((Object)(object)_instance != (Object)null) && (Object)(object)HUDManager.Instance != (Object)null) { VoicevoxDispatcher component = ((Component)HUDManager.Instance).gameObject.GetComponent<VoicevoxDispatcher>(); if ((Object)(object)component != (Object)null) { _instance = component; } else { _instance = ((Component)HUDManager.Instance).gameObject.AddComponent<VoicevoxDispatcher>(); } } } private void Update() { Action result; while (VoicevoxInjectPlugin.ExecutionQueue.TryDequeue(out result)) { try { result(); } catch (Exception arg) { Debug.LogError((object)$"[Voicevox] Dispatcher Error: {arg}"); } } } } [HarmonyPatch(typeof(HUDManager), "Update")] public class DispatcherPatch { private static void Postfix() { VoicevoxDispatcher.EnsureExists(); } } public class ZundamonFaceController : MonoBehaviour { [CompilerGenerated] private sealed class <HideAfter>d__8 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public float duration; public ZundamonFaceController <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <HideAfter>d__8(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown int num = <>1__state; ZundamonFaceController zundamonFaceController = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(duration + 0.5f); <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)zundamonFaceController._faceObj != (Object)null) { zundamonFaceController._faceObj.SetActive(false); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private GameObject _faceObj; private Coroutine _hideCoroutine; private PlayerControllerB _ownerPlayer; private void Awake() { _ownerPlayer = ((Component)this).GetComponent<PlayerControllerB>(); } private void Start() { if (!IsLocalPlayer()) { InitializeFaceObject(); } } private bool IsLocalPlayer() { if ((Object)(object)_ownerPlayer != (Object)null && (Object)(object)StartOfRound.Instance != (Object)null) { return (Object)(object)_ownerPlayer == (Object)(object)StartOfRound.Instance.localPlayerController; } return false; } private void InitializeFaceObject() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown if (!IsLocalPlayer()) { Transform val = ((Component)this).transform.Find("ZundamonFace"); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } Transform val2 = FindBoneRecursive(((Component)this).transform, "spine.004"); if ((Object)(object)val2 == (Object)null) { val2 = ((Component)this).transform; } _faceObj = new GameObject("ZundamonFace"); _faceObj.transform.SetParent(val2); _faceObj.transform.localPosition = new Vector3(0f, 0.3f, 0.2f); _faceObj.transform.localRotation = Quaternion.identity; _faceObj.transform.localScale = Vector3.one * 0.14f; SpriteRenderer val3 = _faceObj.AddComponent<SpriteRenderer>(); if ((Object)(object)VoicevoxInjectPlugin.GlobalSprite != (Object)null) { val3.sprite = VoicevoxInjectPlugin.GlobalSprite; ((Renderer)val3).material = new Material(Shader.Find("Sprites/Default")); } _faceObj.SetActive(false); } } public void Show(float duration) { if (IsLocalPlayer()) { return; } if ((Object)(object)_faceObj == (Object)null) { InitializeFaceObject(); } if (!((Object)(object)_faceObj == (Object)null)) { _faceObj.SetActive(true); if (_hideCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_hideCoroutine); } _hideCoroutine = ((MonoBehaviour)this).StartCoroutine(HideAfter(duration)); } } [IteratorStateMachine(typeof(<HideAfter>d__8))] private IEnumerator HideAfter(float duration) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <HideAfter>d__8(0) { <>4__this = this, duration = duration }; } private Transform FindBoneRecursive(Transform parent, string boneName) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown if (((Object)parent).name.Equals(boneName)) { return parent; } foreach (Transform item in parent) { Transform parent2 = item; Transform val = FindBoneRecursive(parent2, boneName); if ((Object)(object)val != (Object)null) { return val; } } return null; } } [HarmonyPatch(typeof(PlayerControllerB), "Start")] public class PlayerSetupPatch { private static void Postfix(PlayerControllerB __instance) { if ((Object)(object)((Component)__instance).gameObject.GetComponent<ZundamonFaceController>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<ZundamonFaceController>(); } } } [HarmonyPatch(typeof(HUDManager), "AddChatMessage")] public class ChatPatch { private static void Postfix(HUDManager __instance, string chatMessage, string nameOfUserWhoTyped, int playerWhoSent) { HandleChatAsync(__instance, chatMessage, nameOfUserWhoTyped, playerWhoSent); } private static async Task HandleChatAsync(HUDManager __instance, string chatMessage, string nameOfUserWhoTyped, int playerWhoSent) { using CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(5.0)); _ = 1; try { if ((Object)(object)NetworkManager.Singleton == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null || playerWhoSent == -1) { return; } PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[playerWhoSent]; if ((Object)(object)val == (Object)null) { return; } ulong actualClientId = val.actualClientId; if (actualClientId == NetworkManager.Singleton.LocalClientId) { float[] samples2 = await VoicevoxClient.GenerateVoicePcm(chatMessage, cts.Token); if (samples2 == null || samples2.Length == 0) { return; } VoicevoxInjectPlugin.Enqueue(delegate { DissonanceInjector.EnqueueAudio(samples2); HostSpeaker.Play(samples2); float duration3 = (float)samples2.Length / 48000f; if (NetworkManager.Singleton.IsServer) { HybridNetworkHandler.BroadcastFaceShow(playerWhoSent, duration3); } else { HybridNetworkHandler.SendFaceRequest(duration3); } }); return; } if (!NetworkManager.Singleton.IsServer) { return; } if (ModManager.ModdedClientIds.ContainsKey(actualClientId)) { VoicevoxInjectPlugin.Enqueue(delegate { float duration2 = 2f + (float)chatMessage.Length * 0.1f; HybridNetworkHandler.BroadcastFaceShow(playerWhoSent, duration2); }); return; } float[] samples = await VoicevoxClient.GenerateVoicePcm(chatMessage, cts.Token); if (samples == null || samples.Length == 0) { return; } VoicevoxInjectPlugin.Enqueue(delegate { float duration = (float)samples.Length / 48000f; HybridNetworkHandler.BroadcastFaceShow(playerWhoSent, duration); DissonanceInjector.EnqueueAudio(samples); HostSpeaker.Play(samples); }); } catch (OperationCanceledException) { } catch (Exception arg) { Debug.LogError((object)$"[Voicevox] Chat Error: {arg}"); } } } public static class HybridNetworkHandler { private static bool _handlersRegistered; public static void Register() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown if (!((Object)(object)NetworkManager.Singleton == (Object)null) && !_handlersRegistered) { CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager; if (customMessagingManager != null) { customMessagingManager.RegisterNamedMessageHandler("VoicevoxFace", new HandleNamedMessageDelegate(ReceiveFaceCommand)); customMessagingManager.RegisterNamedMessageHandler("VoicevoxFaceReq", new HandleNamedMessageDelegate(ReceiveFaceRequest)); _handlersRegistered = true; } } } public static void Unregister() { if ((Object)(object)NetworkManager.Singleton == (Object)null || !_handlersRegistered) { return; } CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager; if (customMessagingManager != null) { try { customMessagingManager.UnregisterNamedMessageHandler("VoicevoxFace"); customMessagingManager.UnregisterNamedMessageHandler("VoicevoxFaceReq"); } catch { } } _handlersRegistered = false; } public static void BroadcastFaceShow(int playerId, float duration) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)StartOfRound.Instance != (Object)null && playerId >= 0 && playerId < StartOfRound.Instance.allPlayerScripts.Length) { ZundamonFaceController component = ((Component)StartOfRound.Instance.allPlayerScripts[playerId]).GetComponent<ZundamonFaceController>(); if ((Object)(object)component != (Object)null && StartOfRound.Instance.allPlayerScripts[playerId].actualClientId != NetworkManager.Singleton.LocalClientId) { component.Show(duration); } } if ((Object)(object)NetworkManager.Singleton == (Object)null) { return; } FastBufferWriter val = default(FastBufferWriter); foreach (ulong key in ModManager.ModdedClientIds.Keys) { if (key != NetworkManager.Singleton.LocalClientId) { ((FastBufferWriter)(ref val))..ctor(8, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref playerId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref duration, default(ForPrimitives)); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("VoicevoxFace", key, val, (NetworkDelivery)2); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } } } public static void SendFaceRequest(float duration) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsClient) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(4, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref duration, default(ForPrimitives)); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("VoicevoxFaceReq", 0uL, val, (NetworkDelivery)2); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } private static void ReceiveFaceRequest(ulong sender, FastBufferReader reader) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (!NetworkManager.Singleton.IsServer) { return; } float duration = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref duration, default(ForPrimitives)); int num = -1; for (int i = 0; i < StartOfRound.Instance.allPlayerScripts.Length; i++) { if (StartOfRound.Instance.allPlayerScripts[i].actualClientId == sender) { num = i; break; } } if (num != -1) { ModManager.ModdedClientIds.TryAdd(sender, 0); BroadcastFaceShow(num, duration); } } private static void ReceiveFaceCommand(ulong sender, FastBufferReader reader) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) try { int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); float duration = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref duration, default(ForPrimitives)); if ((Object)(object)StartOfRound.Instance != (Object)null && num >= 0 && num < StartOfRound.Instance.allPlayerScripts.Length) { ZundamonFaceController component = ((Component)StartOfRound.Instance.allPlayerScripts[num]).gameObject.GetComponent<ZundamonFaceController>(); if ((Object)(object)component != (Object)null) { component.Show(duration); } } } catch (Exception ex) { Debug.LogError((object)("[Voicevox] FaceCmd Error: " + ex.Message)); } } } [HarmonyPatch] public class ModManager { [CompilerGenerated] private sealed class <RetryHandshake>d__8 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private int <attempts>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <RetryHandshake>d__8(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <attempts>5__2 = 0; break; case 1: <>1__state = -1; break; } if (!IsHostModded && <attempts>5__2 < 10) { SendHelloToHost(); <attempts>5__2++; <>2__current = (object)new WaitForSeconds(2f); <>1__state = 1; return true; } if (!IsHostModded) { Debug.LogWarning((object)"[Voicevox] Handshake failed."); } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public static ConcurrentDictionary<ulong, byte> ModdedClientIds = new ConcurrentDictionary<ulong, byte>(); public static bool IsHostModded = false; private static Coroutine _handshakeCoroutine; [HarmonyPatch(typeof(StartOfRound), "Awake")] [HarmonyPostfix] private static void Initialize() { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown ModdedClientIds.Clear(); IsHostModded = false; HybridNetworkHandler.Register(); if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer) { NetworkManager.Singleton.CustomMessagingManager.RegisterNamedMessageHandler("VoicevoxHello", new HandleNamedMessageDelegate(OnHelloReceived)); } else if ((Object)(object)NetworkManager.Singleton != (Object)null) { NetworkManager.Singleton.CustomMessagingManager.RegisterNamedMessageHandler("VoicevoxWelcome", new HandleNamedMessageDelegate(OnWelcomeReceived)); } } [HarmonyPatch(typeof(StartOfRound), "OnDestroy")] [HarmonyPostfix] private static void OnDestroyCleanup() { HybridNetworkHandler.Unregister(); if (_handshakeCoroutine != null && (Object)(object)StartOfRound.Instance != (Object)null) { StopCoroutine(_handshakeCoroutine); } try { if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.CustomMessagingManager != null) { NetworkManager.Singleton.CustomMessagingManager.UnregisterNamedMessageHandler("VoicevoxHello"); NetworkManager.Singleton.CustomMessagingManager.UnregisterNamedMessageHandler("VoicevoxWelcome"); } } catch { } } [HarmonyPatch(typeof(PlayerControllerB), "Start")] [HarmonyPostfix] private static void OnPlayerStart(PlayerControllerB __instance) { if (((NetworkBehaviour)__instance).IsOwner && ((NetworkBehaviour)__instance).IsClient && !((NetworkBehaviour)__instance).IsServer) { if (_handshakeCoroutine != null) { StopCoroutine(_handshakeCoroutine); } _handshakeCoroutine = StartCoroutine(RetryHandshake()); } } private static Coroutine StartCoroutine(IEnumerator routine) { if ((Object)(object)StartOfRound.Instance != (Object)null) { return ((MonoBehaviour)StartOfRound.Instance).StartCoroutine(routine); } return null; } private static void StopCoroutine(Coroutine routine) { if ((Object)(object)StartOfRound.Instance != (Object)null) { ((MonoBehaviour)StartOfRound.Instance).StopCoroutine(routine); } } [IteratorStateMachine(typeof(<RetryHandshake>d__8))] private static IEnumerator RetryHandshake() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <RetryHandshake>d__8(0); } private static void SendHelloToHost() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)NetworkManager.Singleton == (Object)null) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(0, (Allocator)2, -1); try { NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("VoicevoxHello", 0uL, val, (NetworkDelivery)2); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } private static void OnHelloReceived(ulong senderClientId, FastBufferReader reader) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (!NetworkManager.Singleton.IsServer) { return; } ModdedClientIds.TryAdd(senderClientId, 0); FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(0, (Allocator)2, -1); try { NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("VoicevoxWelcome", senderClientId, val, (NetworkDelivery)2); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } private static void OnWelcomeReceived(ulong senderClientId, FastBufferReader reader) { IsHostModded = true; Debug.Log((object)"[Voicevox] Welcome received."); } } [HarmonyPatch] public class CreditHandler { [CompilerGenerated] private sealed class <ShowCreditWithDelay>d__3 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ShowCreditWithDelay>d__3(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(3f); <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.AddTextToChatOnServer("<color=#52e052>[System] VOICEVOX:Zundamon is working for the company.(1.0.0)</color>", -1); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private const string CreditText = "<color=#52e052>[System] VOICEVOX:Zundamon is working for the company.(1.0.0)</color>"; [HarmonyPatch(typeof(StartOfRound), "SceneManager_OnLoadComplete1")] [HarmonyPostfix] private static void OnSceneLoaded() { if (NetworkManager.Singleton.IsServer) { ((MonoBehaviour)StartOfRound.Instance).StartCoroutine(ShowCreditWithDelay()); } } [HarmonyPatch(typeof(StartOfRound), "OnPlayerConnectedClientRpc")] [HarmonyPostfix] private static void OnPlayerJoin() { if (NetworkManager.Singleton.IsServer) { ((MonoBehaviour)StartOfRound.Instance).StartCoroutine(ShowCreditWithDelay()); } } [IteratorStateMachine(typeof(<ShowCreditWithDelay>d__3))] private static IEnumerator ShowCreditWithDelay() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <ShowCreditWithDelay>d__3(0); } } public class HostSpeaker : MonoBehaviour { private static HostSpeaker _instance; private AudioSource _source; public static void Play(float[] samples) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if ((Object)(object)_instance == (Object)null) { GameObject val = new GameObject("VoicevoxHostSpeaker"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent<HostSpeaker>(); _instance.Initialize(); } _instance.PlayInternal(samples); } private void Initialize() { _source = ((Component)this).gameObject.AddComponent<AudioSource>(); _source.spatialBlend = 0f; _source.volume = 1f; } private void PlayInternal(float[] samples) { if (!((Object)(object)_source == (Object)null)) { AudioClip val = AudioClip.Create("HostVoice", samples.Length, 1, 48000, false); val.SetData(samples, 0); _source.PlayOneShot(val); } } } public class DissonanceInjector : MonoBehaviour { private static DissonanceInjector _instance; private static Queue<float[]> _chunkQueue = new Queue<float[]>(); private static object _queueLock = new object(); private float[] _currentChunk; private int _currentChunkOffset; private static object _micCaptureObject; private static MethodInfo _consumeMethod; private float _timeAccumulator; public static void EnqueueAudio(float[] samples) { if ((Object)(object)_instance == (Object)null) { StartOfRound val = Object.FindObjectOfType<StartOfRound>(); if ((Object)(object)val != (Object)null) { _instance = ((Component)val).gameObject.AddComponent<DissonanceInjector>(); _instance.Initialize(); } } int num; for (int i = 0; i < samples.Length; i += num) { num = Math.Min(4800, samples.Length - i); float[] array = new float[num]; Array.Copy(samples, i, array, 0, num); lock (_queueLock) { _chunkQueue.Enqueue(array); } } } private void Initialize() { DissonanceComms val = Object.FindObjectOfType<DissonanceComms>(); if ((Object)(object)val == (Object)null) { return; } PropertyInfo property = ((object)val).GetType().GetProperty("MicrophoneCapture", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { _micCaptureObject = property.GetValue(val); if (_micCaptureObject != null) { _consumeMethod = _micCaptureObject.GetType().GetMethod("ConsumeSamples", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } } } private void Update() { if (_micCaptureObject != null) { _timeAccumulator += Time.deltaTime; int num = 0; while (_timeAccumulator >= 0.02f && num < 5) { PushFrame(); _timeAccumulator -= 0.02f; num++; } if (_timeAccumulator > 0.5f) { _timeAccumulator = 0f; } } } private void PushFrame() { float[] array = new float[960]; int num = 0; lock (_queueLock) { while (num < 960) { if (_currentChunk == null) { if (_chunkQueue.Count <= 0) { break; } _currentChunk = _chunkQueue.Dequeue(); _currentChunkOffset = 0; } int num2 = Math.Min(_currentChunk.Length - _currentChunkOffset, 960 - num); Array.Copy(_currentChunk, _currentChunkOffset, array, num, num2); num += num2; _currentChunkOffset += num2; if (_currentChunkOffset >= _currentChunk.Length) { _currentChunk = null; _currentChunkOffset = 0; } } } if (num > 0 && _consumeMethod != null) { if (num < 960) { Array.Clear(array, num, 960 - num); } ArraySegment<float> arraySegment = new ArraySegment<float>(array); _consumeMethod.Invoke(_micCaptureObject, new object[1] { arraySegment }); } } } public static class VoicevoxClient { private static readonly HttpClient client = new HttpClient(); public static async Task<float[]> GenerateVoicePcm(string text, CancellationToken token) { _ = 4; try { int speakerId = VoicevoxInjectPlugin.SpeakerIdConfig.Value; int timeoutSec = VoicevoxInjectPlugin.VoicevoxTimeoutSecConfig.Value; int num = Math.Max(0, VoicevoxInjectPlugin.VoicevoxRetryCountConfig.Value); int attempt = 0; int maxAttempts = num + 1; int backoffMs = 300; while (attempt < maxAttempts) { attempt++; using (CancellationTokenSource ctsTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSec))) { using CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ctsTimeout.Token, token); CancellationToken linkedToken = linkedCts.Token; try { HttpResponseMessage val = await client.PostAsync($"http://localhost:50021/audio_query?text={Uri.EscapeDataString(text)}&speaker={speakerId}", (HttpContent)null, linkedToken); if (!val.IsSuccessStatusCode) { throw new HttpRequestException($"audio_query status {(int)val.StatusCode}"); } JObject obj = JObject.Parse(await val.Content.ReadAsStringAsync()); obj["volumeScale"] = JToken.op_Implicit(VoicevoxInjectPlugin.VolumeConfig.Value); StringContent val2 = new StringContent(((object)obj).ToString(), Encoding.UTF8, "application/json"); HttpResponseMessage val3 = await client.PostAsync($"http://localhost:50021/synthesis?speaker={speakerId}", (HttpContent)(object)val2, linkedToken); if (!val3.IsSuccessStatusCode) { throw new HttpRequestException($"synthesis status {(int)val3.StatusCode}"); } float[] array = ConvertWavTo48kMono(await val3.Content.ReadAsByteArrayAsync()); if (array == null || array.Length == 0) { return new float[0]; } return array; } catch (OperationCanceledException) { if (token != default(CancellationToken) && token.IsCancellationRequested) { throw; } Debug.LogWarning((object)$"[Voicevox] Generation attempt {attempt} timed out after {timeoutSec}s."); } catch (Exception ex2) { Debug.LogWarning((object)$"[Voicevox] Generation attempt {attempt} failed: {ex2.Message}"); } } if (attempt < maxAttempts) { await Task.Delay(backoffMs); backoffMs *= 2; } } Debug.LogError((object)"[Voicevox] All attempts to generate voice failed."); return null; } catch (OperationCanceledException) { throw; } catch (Exception ex4) { Debug.LogError((object)("[Voicevox] Gen Error: " + ex4.Message)); return null; } } private static float[] ConvertWavTo48kMono(byte[] wavData) { if (wavData == null || wavData.Length < 44) { return new float[0]; } try { int num = BitConverter.ToInt16(wavData, 22); int num2 = BitConverter.ToInt32(wavData, 24); int num3 = -1; for (int i = 12; i < wavData.Length - 4; i++) { if (wavData[i] == 100 && wavData[i + 1] == 97 && wavData[i + 2] == 116 && wavData[i + 3] == 97) { num3 = i; break; } } if (num3 == -1) { num3 = 36; } int num4 = num3 + 8; int num5 = wavData.Length - num4; if (num5 <= 0) { return new float[0]; } int num6 = num5 / 2; float[] array = new float[num6]; for (int j = 0; j < num6; j++) { short num7 = BitConverter.ToInt16(wavData, num4 + j * 2); array[j] = (float)num7 / 32768f; } float[] array2; if (num == 2) { array2 = new float[num6 / 2]; for (int k = 0; k < array2.Length; k++) { array2[k] = (array[k * 2] + array[k * 2 + 1]) * 0.5f; } } else { array2 = array; } if (num2 == 48000) { return array2; } float num8 = (float)num2 / 48000f; int num9 = (int)((float)array2.Length / num8); float[] array3 = new float[num9]; for (int l = 0; l < num9; l++) { float num10 = (float)l * num8; int num11 = (int)num10; if (num11 < array2.Length - 1) { array3[l] = Mathf.Lerp(array2[num11], array2[num11 + 1], num10 - (float)num11); } else if (num11 < array2.Length) { array3[l] = array2[num11]; } } return array3; } catch (Exception ex) { Debug.LogError((object)("[Voicevox] WAV Parse Error: " + ex.Message)); return new float[0]; } } }