Decompiled source of ZundamonizedTTS v1.0.0

ZundamonizedTTS.dll

Decompiled 2 days ago
using 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];
		}
	}
}