Decompiled source of FenchikMemeSounds v3.1.11

FenchikMemeSounds.dll

Decompiled 2 months 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.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using MenuLib;
using MenuLib.MonoBehaviors;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("FenchikMemeSounds")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("FenchikMemeSounds")]
[assembly: AssemblyTitle("FenchikMemeSounds")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
[BepInPlugin("fenchik.memesounds", "Fenchik Meme Sounds", "3.5.0")]
public class FenchikMemeSounds : BaseUnityPlugin
{
	private enum AllAudioToggle
	{
		Enable,
		Disable
	}

	[HarmonyPatch(typeof(AudioSource), "PlayOneShot", new Type[] { typeof(AudioClip) })]
	private class Patch_PlayOneShot
	{
		private static bool Prefix(AudioSource __instance, AudioClip clip)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)clip == (Object)null)
			{
				return true;
			}
			if (clipCache.ContainsKey(((Object)clip).name))
			{
				return true;
			}
			if (lastCheckedFrame.TryGetValue(__instance, out var value) && value == Time.frameCount)
			{
				return true;
			}
			lastCheckedFrame[__instance] = Time.frameCount;
			if ((Object)(object)__instance == (Object)null || !((Behaviour)__instance).isActiveAndEnabled)
			{
				lastCheckedFrame.Remove(__instance);
			}
			if (TryReplace(__instance, ((Object)clip).name, out var newClip, out var volume, out var enabled) && enabled)
			{
				__instance.Stop();
				__instance.time = 0f;
				__instance.timeSamples = 0;
				__instance.PlayOneShot(newClip, Mathf.Clamp01(volume));
				if (!replacedSources.Contains(__instance))
				{
					replacedSources.Add(__instance);
				}
				return false;
			}
			return true;
		}
	}

	[HarmonyPatch(typeof(AudioSource), "Stop", new Type[] { })]
	private class Patch_AudioSource_Stop
	{
		private static void Prefix(AudioSource __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && __instance.isPlaying && !((Object)(object)__instance.clip == (Object)null) && __instance.loop && !(__instance.time < 0.2f) && !(__instance.time >= __instance.clip.length - 0.2f))
			{
				savedPositions[__instance] = __instance.time;
			}
		}
	}

	[HarmonyPatch(typeof(AudioSource), "Play", new Type[] { })]
	private class Patch_AudioSource_Play
	{
		private static void Prefix(AudioSource __instance)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.clip == (Object)null || clipCache.ContainsKey(((Object)__instance.clip).name) || (lastCheckedFrame.TryGetValue(__instance, out var value) && value == Time.frameCount))
			{
				return;
			}
			lastCheckedFrame[__instance] = Time.frameCount;
			if ((Object)(object)__instance == (Object)null || !((Behaviour)__instance).isActiveAndEnabled)
			{
				lastCheckedFrame.Remove(__instance);
			}
			if (TryReplace(__instance, ((Object)__instance.clip).name, out var newClip, out var volume, out var enabled) && enabled)
			{
				if ((Object)(object)__instance.clip != (Object)(object)newClip)
				{
					__instance.clip = newClip;
				}
				__instance.volume = Mathf.Clamp01(volume);
				if (!replacedSources.Contains(__instance))
				{
					replacedSources.Add(__instance);
				}
			}
		}
	}

	[HarmonyPatch(typeof(Sound), "PlayLoop")]
	private class Patch_SoundPlayLoop
	{
		private static void Postfix(Sound __instance)
		{
			if ((Object)(object)__instance?.Source == (Object)null || __instance.Sounds == null || __instance.Sounds.Length == 0)
			{
				return;
			}
			AudioClip obj = __instance.Sounds[0];
			string text = ((obj != null) ? ((Object)obj).name : null);
			if (!string.IsNullOrEmpty(text) && TryReplace(__instance.Source, text, out var newClip, out var volume, out var enabled) && enabled)
			{
				__instance.Sounds[0] = newClip;
				__instance.Source.clip = newClip;
				__instance.Source.volume = volume;
				if (!replacedSources.Contains(__instance.Source))
				{
					replacedSources.Add(__instance.Source);
				}
			}
		}
	}

	private class ReplacementContainer
	{
		public List<Replacement> replacements = new List<Replacement>();
	}

	private class Replacement
	{
		public string category;

		public List<string> matches = new List<string>();

		public List<SoundEntry> sounds = new List<SoundEntry>();
	}

	private class SoundEntry
	{
		public string sound;

		public string category;

		public string displayName;

		public float weight = 1f;

		public bool loop = false;
	}

	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static Func<AudioSource, bool> <>9__2_0;

		public static Func<AudioSource, bool> <>9__3_0;

		public static Func<KeyValuePair<ConfigDefinition, ConfigEntryBase>, string> <>9__16_0;

		public static Func<string, bool> <>9__16_1;

		public static Func<string, string> <>9__16_2;

		public static ScrollViewBuilderDelegate <>9__24_0;

		public static Func<KeyValuePair<ConfigDefinition, ConfigEntryBase>, bool> <>9__24_10;

		public static Func<KeyValuePair<ConfigDefinition, ConfigEntryBase>, bool> <>9__24_11;

		public static ScrollViewBuilderDelegate <>9__24_5;

		public static Func<KeyValuePair<ConfigDefinition, ConfigEntryBase>, string> <>9__25_0;

		public static Func<string, bool> <>9__25_1;

		public static Predicate<AudioSource> <>9__28_0;

		public static Func<AudioSource, bool> <>9__52_0;

		internal bool <Update>b__2_0(AudioSource a)
		{
			return (Object)(object)a != (Object)null && a.isPlaying;
		}

		internal bool <CleanDeadAudioSources>b__3_0(AudioSource src)
		{
			return (Object)(object)src != (Object)null && (Object)(object)((Component)src).gameObject != (Object)null;
		}

		internal string <CreateCategoryNavigation>b__16_0(KeyValuePair<ConfigDefinition, ConfigEntryBase> kvp)
		{
			return kvp.Key.Section;
		}

		internal bool <CreateCategoryNavigation>b__16_1(string section)
		{
			return !section.StartsWith("__");
		}

		internal string <CreateCategoryNavigation>b__16_2(string s)
		{
			return s;
		}

		internal RectTransform <OpenCategoryPage>b__24_0(Transform scroll)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			REPOLabel val = MenuAPI.CreateREPOLabel("Volume Presets:", scroll, new Vector2(0f, 10f));
			return ((REPOElement)val).rectTransform;
		}

		internal bool <OpenCategoryPage>b__24_10(KeyValuePair<ConfigDefinition, ConfigEntryBase> kvp)
		{
			ConfigEntry<bool> val = kvp.Value as ConfigEntry<bool>;
			return val != null;
		}

		internal bool <OpenCategoryPage>b__24_11(KeyValuePair<ConfigDefinition, ConfigEntryBase> kvp)
		{
			return ((ConfigEntry<bool>)(object)kvp.Value).Value;
		}

		internal RectTransform <OpenCategoryPage>b__24_5(Transform scroll)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			REPOLabel val = MenuAPI.CreateREPOLabel("Sounds of category", scroll, new Vector2(0f, 30f));
			return ((REPOElement)val).rectTransform;
		}

		internal string <GetAllCategories>b__25_0(KeyValuePair<ConfigDefinition, ConfigEntryBase> kvp)
		{
			return kvp.Key.Section;
		}

		internal bool <GetAllCategories>b__25_1(string section)
		{
			return !section.StartsWith("__");
		}

		internal bool <UpdateVolumes>b__28_0(AudioSource src)
		{
			return (Object)(object)src == (Object)null || (Object)(object)src.clip == (Object)null;
		}

		internal bool <CleanupSavedPositions>b__52_0(AudioSource src)
		{
			return (Object)(object)src == (Object)null || (Object)(object)((Component)src).gameObject == (Object)null;
		}
	}

	private static float processedClipsCleanupTimer = 0f;

	private static readonly HashSet<string> noReplacementCache = new HashSet<string>();

	private REPOPopupPage _mainSettingsPage;

	private Dictionary<string, REPOPopupPage> _categoryPages = new Dictionary<string, REPOPopupPage>();

	private REPOPopupPage _currentRightPage = null;

	private string _currentCategory = null;

	private static ManualLogSource log;

	private static ConfigFile config;

	private static AudioSource globalSource;

	private static readonly Dictionary<AudioSource, float> savedPositions = new Dictionary<AudioSource, float>();

	private static readonly Dictionary<AudioSource, string> lastCheckedClips = new Dictionary<AudioSource, string>();

	private static readonly Dictionary<AudioSource, int> lastCheckedFrame = new Dictionary<AudioSource, int>();

	private static readonly Dictionary<AudioSource, string> audioSourceCache = new Dictionary<AudioSource, string>();

	private static readonly Dictionary<string, List<SoundEntry>> replacementMap = new Dictionary<string, List<SoundEntry>>();

	private static readonly Dictionary<string, bool> processedResultCache = new Dictionary<string, bool>();

	private static readonly Dictionary<string, (ConfigEntry<bool>, ConfigEntry<float>)> configEntryCache = new Dictionary<string, (ConfigEntry<bool>, ConfigEntry<float>)>();

	private static readonly Dictionary<string, AudioClip> clipCache = new Dictionary<string, AudioClip>();

	private static readonly Dictionary<string, (AudioClip, float, bool)> tryReplaceCache = new Dictionary<string, (AudioClip, float, bool)>();

	private static readonly Dictionary<string, (bool, float)> configCache = new Dictionary<string, (bool, float)>();

	private static readonly Dictionary<string, SoundEntry> directMatchCache = new Dictionary<string, SoundEntry>();

	private static readonly Dictionary<string, (AudioClip clip, float volume, bool enabled)> processedClips = new Dictionary<string, (AudioClip, float, bool)>();

	private static readonly List<AudioSource> replacedSources = new List<AudioSource>();

	private static readonly Queue<AudioSource> audioPool = new Queue<AudioSource>();

	private const int PoolInitialSize = 20;

	private const int PoolMaxSize = 50;

	private float poolCleanupTimer = 0f;

	public static FenchikMemeSounds Instance { get; private set; }

	private void Awake()
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Expected O, but got Unknown
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Expected O, but got Unknown
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Expected O, but got Unknown
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		Instance = this;
		MenuAPI.AddElementToMainMenu((BuilderDelegate)delegate(Transform parent)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			REPOButton val2 = MenuAPI.CreateREPOButton("♪ Sound replacers", (Action)delegate
			{
				CreateCategoryNavigation();
			}, parent, new Vector2(550f, 0f));
			TextMeshProUGUI labelTMP2 = val2.labelTMP;
			if ((Object)(object)labelTMP2 != (Object)null)
			{
				((TMP_Text)labelTMP2).enableVertexGradient = true;
				((TMP_Text)labelTMP2).colorGradient = new VertexGradient(Color32.op_Implicit(new Color32((byte)210, (byte)166, (byte)121, byte.MaxValue)), Color32.op_Implicit(new Color32((byte)210, (byte)166, (byte)121, byte.MaxValue)), Color32.op_Implicit(new Color32((byte)90, (byte)56, (byte)37, byte.MaxValue)), Color32.op_Implicit(new Color32((byte)90, (byte)56, (byte)37, byte.MaxValue)));
			}
		});
		MenuAPI.AddElementToEscapeMenu((BuilderDelegate)delegate(Transform parent)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			REPOButton val = MenuAPI.CreateREPOButton("Sound Replacers", (Action)delegate
			{
				CreateCategoryNavigation();
			}, parent, new Vector2(126f, 61f));
			TextMeshProUGUI labelTMP = val.labelTMP;
			if ((Object)(object)labelTMP != (Object)null)
			{
				((TMP_Text)labelTMP).enableVertexGradient = true;
				((TMP_Text)labelTMP).colorGradient = new VertexGradient(Color32.op_Implicit(new Color32((byte)210, (byte)166, (byte)121, byte.MaxValue)), Color32.op_Implicit(new Color32((byte)210, (byte)166, (byte)121, byte.MaxValue)), Color32.op_Implicit(new Color32((byte)90, (byte)56, (byte)37, byte.MaxValue)), Color32.op_Implicit(new Color32((byte)90, (byte)56, (byte)37, byte.MaxValue)));
			}
		});
		poolCleanupTimer += Time.deltaTime;
		if (poolCleanupTimer > 5f)
		{
			CleanDeadAudioSources();
			CleanupSavedPositions();
			poolCleanupTimer = 0f;
		}
		log = ((BaseUnityPlugin)this).Logger;
		config = new ConfigFile(Path.Combine(Paths.ConfigPath, "FenchikMemeSound.cfg"), true);
		log.LogInfo((object)"\ud83d\udd27 FenchikMemeSounds инициализируется...");
		LoadReplacements();
		InitAudioPool();
		globalSource = ((Component)this).gameObject.AddComponent<AudioSource>();
		globalSource.spatialBlend = 0f;
		globalSource.playOnAwake = false;
		new Harmony("fenchik.memesounds").PatchAll();
	}

	private void Update()
	{
		if ((Input.GetKeyDown((KeyCode)27) || Input.GetKeyDown((KeyCode)8)) && ((Object)(object)_mainSettingsPage != (Object)null || (Object)(object)_currentRightPage != (Object)null))
		{
			CloseAllMenuPages();
		}
		if ((Object)(object)_mainSettingsPage == (Object)null && (Object)(object)_currentRightPage == (Object)null && !Cursor.visible)
		{
			Cursor.visible = true;
			Cursor.lockState = (CursorLockMode)0;
			log.LogWarning((object)"\ud83e\uddef Курсор вернули автоматически (меню закрыты)");
		}
		poolCleanupTimer += Time.deltaTime;
		if (poolCleanupTimer > 5f)
		{
			CleanDeadAudioSources();
			CleanupSavedPositions();
			poolCleanupTimer = 0f;
		}
		if (Time.frameCount % 60 == 0)
		{
			int num = audioPool.Count((AudioSource a) => (Object)(object)a != (Object)null && a.isPlaying);
			log.LogInfo((object)$"\ud83d\udd0a Активных AudioSource: {num}/{audioPool.Count}");
		}
		processedClipsCleanupTimer += Time.deltaTime;
		if (processedClipsCleanupTimer > 300f)
		{
			processedClips.Clear();
			processedClipsCleanupTimer = 0f;
		}
		if (Time.frameCount % 300 == 0)
		{
			lastCheckedFrame.Clear();
		}
	}

	private void CleanDeadAudioSources()
	{
		int count = audioPool.Count;
		List<AudioSource> list = audioPool.Where((AudioSource src) => (Object)(object)src != (Object)null && (Object)(object)((Component)src).gameObject != (Object)null).ToList();
		audioPool.Clear();
		foreach (AudioSource item in list)
		{
			audioPool.Enqueue(item);
		}
		log.LogInfo((object)$"\ud83e\uddf9 Очищено {count - list.Count} мёртвых источников");
	}

	public static void ReloadConfigAndClearCache()
	{
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Expected O, but got Unknown
		config.Reload();
		tryReplaceCache.Clear();
		configCache.Clear();
		processedClips.Clear();
		foreach (KeyValuePair<string, List<SoundEntry>> item in replacementMap)
		{
			foreach (SoundEntry item2 in item.Value)
			{
				string text = item2.category ?? "Default";
				string text2 = item2.displayName ?? Path.GetFileNameWithoutExtension(item2.sound);
				string key = text + "::" + text2;
				ConfigEntry<bool> val = config.Bind<bool>(text, text2, true, "Включить звук");
				ConfigEntry<float> val2 = config.Bind<float>(text, text2 + "_volume", 1f, new ConfigDescription("Громкость от 0.0 до 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
				configCache[key] = (val.Value, val2.Value);
			}
		}
		log.LogInfo((object)"\ud83d\udd01 Конфиг перезагружен и ConfigEntry пересозданы");
		UpdateVolumes();
	}

	public static bool TryReplace(AudioSource source, string clipName, out AudioClip newClip, out float volume, out bool enabled)
	{
		newClip = null;
		volume = 1f;
		enabled = true;
		if (string.IsNullOrEmpty(clipName))
		{
			return false;
		}
		if (noReplacementCache.Contains(clipName))
		{
			return false;
		}
		if (!processedClips.TryGetValue(clipName, out (AudioClip, float, bool) value))
		{
			string key = clipName.ToLowerInvariant();
			if (directMatchCache.TryGetValue(key, out var value2))
			{
				newClip = GetClip(value2.sound);
				bool result = ValidateConfig(value2, clipName, newClip, out volume, out enabled);
				processedClips[clipName] = (newClip, volume, enabled);
				return result;
			}
			foreach (KeyValuePair<string, List<SoundEntry>> item in replacementMap)
			{
				if (clipName.IndexOf(item.Key, StringComparison.OrdinalIgnoreCase) < 0)
				{
					continue;
				}
				List<SoundEntry> value3 = item.Value;
				SoundEntry soundEntry = PickWeighted(value3);
				if (soundEntry == null)
				{
					break;
				}
				newClip = GetClip(soundEntry.sound);
				bool result2 = ValidateConfig(soundEntry, clipName, newClip, out volume, out enabled);
				if (value3.Count == 1)
				{
					processedClips[clipName] = (newClip, volume, enabled);
				}
				return result2;
			}
			noReplacementCache.Add(clipName);
			return false;
		}
		(newClip, volume, enabled) = value;
		return enabled && (Object)(object)newClip != (Object)null;
	}

	private static bool ValidateConfig(SoundEntry entry, string cacheKey, AudioClip clip, out float volume, out bool enabled)
	{
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Expected O, but got Unknown
		volume = 1f;
		enabled = true;
		string key = entry.category + "::" + entry.displayName;
		if (!configEntryCache.TryGetValue(key, out var value))
		{
			ConfigEntry<bool> item = config.Bind<bool>(entry.category, entry.displayName, true, "Включить звук");
			ConfigEntry<float> item2 = config.Bind<float>(entry.category, entry.displayName + "_volume", 1f, new ConfigDescription("Громкость от 0.0 до 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			value = (item, item2);
			configEntryCache[key] = value;
		}
		enabled = value.Item1.Value;
		volume = value.Item2.Value;
		if (!enabled || (Object)(object)clip == (Object)null)
		{
			return false;
		}
		tryReplaceCache[cacheKey] = (clip, volume, enabled);
		return true;
	}

	private static void InitAudioPool()
	{
		for (int i = 0; i < 20; i++)
		{
			AudioSource item = CreateAudioSource($"PooledAudioSource_{i}");
			audioPool.Enqueue(item);
		}
		log.LogInfo((object)$"\ud83d\udd04 Инициализирован пул аудио: {audioPool.Count} источников");
	}

	private static AudioSource CreateAudioSource(string name)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Expected O, but got Unknown
		GameObject val = new GameObject(name);
		Object.DontDestroyOnLoad((Object)(object)val);
		AudioSource val2 = val.AddComponent<AudioSource>();
		val2.playOnAwake = false;
		val2.spatialBlend = 0f;
		return val2;
	}

	public static void PlayPooledSound(AudioClip clip, float volume = 1f, bool loop = false)
	{
		if ((Object)(object)clip == (Object)null)
		{
			return;
		}
		AudioSource val = null;
		foreach (AudioSource item in audioPool)
		{
			if (!item.isPlaying)
			{
				val = item;
				break;
			}
		}
		if ((Object)(object)val == (Object)null)
		{
			if (audioPool.Count >= 50)
			{
				return;
			}
			val = CreateAudioSource("PooledAudioSource_Dynamic");
			audioPool.Enqueue(val);
		}
		val.loop = loop;
		val.clip = clip;
		val.volume = Mathf.Clamp01(volume);
		val.time = 0f;
		val.timeSamples = 0;
		val.Play();
	}

	private static AudioClip LoadClipSync(string path)
	{
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Invalid comparison between Unknown and I4
		string text = "file:///" + path.Replace("\\", "/");
		AudioType val = (AudioType)(path.EndsWith(".mp3") ? 13 : (path.EndsWith(".wav") ? 20 : 14));
		UnityWebRequest audioClip = UnityWebRequestMultimedia.GetAudioClip(text, val);
		try
		{
			UnityWebRequestAsyncOperation val2 = audioClip.SendWebRequest();
			while (!((AsyncOperation)val2).isDone)
			{
			}
			if ((int)audioClip.result != 1)
			{
				log.LogWarning((object)("Не удалось загрузить звук: " + path));
				return null;
			}
			AudioClip content = DownloadHandlerAudioClip.GetContent(audioClip);
			((Object)content).name = Path.GetFileName(path);
			return content;
		}
		finally
		{
			((IDisposable)audioClip)?.Dispose();
		}
	}

	private static AudioClip GetClip(string filename)
	{
		if (clipCache.TryGetValue(filename, out var value))
		{
			return value;
		}
		string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "sounds", filename);
		if (!File.Exists(text))
		{
			log.LogWarning((object)("❌ Звук не найден: " + text));
			return null;
		}
		AudioClip val = LoadClipSync(text);
		if ((Object)(object)val != (Object)null)
		{
			clipCache[filename] = val;
		}
		return val;
	}

	private static SoundEntry PickWeighted(List<SoundEntry> entries)
	{
		if (entries == null || entries.Count == 0)
		{
			return null;
		}
		if (entries.Count == 1)
		{
			return entries[0];
		}
		float num = 0f;
		foreach (SoundEntry entry in entries)
		{
			num += ((entry.weight > 0f) ? entry.weight : 1f);
		}
		float num2 = Random.Range(0f, num);
		float num3 = 0f;
		foreach (SoundEntry entry2 in entries)
		{
			num3 += ((entry2.weight > 0f) ? entry2.weight : 1f);
			if (num2 <= num3)
			{
				return entry2;
			}
		}
		return entries[entries.Count - 1];
	}

	private void CreateCategoryNavigation()
	{
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Expected O, but got Unknown
		List<string> list = (from s in (from kvp in (IEnumerable<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)config
				select kvp.Key.Section into section
				where !section.StartsWith("__")
				select section).Distinct()
			orderby s
			select s).ToList();
		_mainSettingsPage = MenuAPI.CreateREPOPopupPage("Fenchik Meme Sounds", true, true, 1.5f, (Vector2?)new Vector2(-300f, 10f));
		_mainSettingsPage.OpenPage(false);
		foreach (string category in list)
		{
			_mainSettingsPage.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scroll)
			{
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				REPOButton val = MenuAPI.CreateREPOButton("♫ " + category, (Action)delegate
				{
					OpenCategoryPage(category);
				}, scroll, Vector2.zero);
				return ((REPOElement)val).rectTransform;
			}, 0f, 0f);
		}
	}

	private void CloseAllMenuPages()
	{
		_currentCategory = null;
		Cursor.visible = true;
		Cursor.lockState = (CursorLockMode)0;
		log.LogInfo((object)"\ud83d\udd1a Меню полностью закрыто, курсор восстановлен");
	}

	private void OpenCategoryPage(string category)
	{
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Expected O, but got Unknown
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Expected O, but got Unknown
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Expected O, but got Unknown
		//IL_013e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Expected O, but got Unknown
		//IL_00b1: 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: Expected O, but got Unknown
		//IL_0168: Unknown result type (might be due to invalid IL or missing references)
		//IL_016d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Expected O, but got Unknown
		//IL_0233: Unknown result type (might be due to invalid IL or missing references)
		//IL_0247: Expected O, but got Unknown
		//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b8: Expected O, but got Unknown
		config.Reload();
		if ((Object)(object)_currentRightPage != (Object)null)
		{
			_currentRightPage.ClosePage(true);
			Object.Destroy((Object)(object)((Component)_currentRightPage).gameObject);
			_currentRightPage = null;
		}
		_currentCategory = category;
		REPOPopupPage val = MenuAPI.CreateREPOPopupPage("Settings: " + category, true, false, 1.5f, (Vector2?)new Vector2(70f, 10f));
		val.OpenPage(true);
		_currentRightPage = val;
		object obj = <>c.<>9__24_0;
		if (obj == null)
		{
			ScrollViewBuilderDelegate val2 = delegate(Transform scroll)
			{
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				REPOLabel val13 = MenuAPI.CreateREPOLabel("Volume Presets:", scroll, new Vector2(0f, 10f));
				return ((REPOElement)val13).rectTransform;
			};
			<>c.<>9__24_0 = val2;
			obj = (object)val2;
		}
		val.AddElementToScrollView((ScrollViewBuilderDelegate)obj, 0f, 0f);
		val.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scroll)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			REPOButton val12 = MenuAPI.CreateREPOButton("■ Volume 1.0", (Action)delegate
			{
				ApplyVolumePreset(category, 1f);
			}, scroll, new Vector2(0f, 10f));
			return ((REPOElement)val12).rectTransform;
		}, 0f, 0f);
		val.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scroll)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			REPOButton val11 = MenuAPI.CreateREPOButton("● Volume 0.5", (Action)delegate
			{
				ApplyVolumePreset(category, 0.5f);
			}, scroll, new Vector2(0f, 10f));
			return ((REPOElement)val11).rectTransform;
		}, 0f, 0f);
		val.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scroll)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			REPOButton val10 = MenuAPI.CreateREPOButton("◦ Volume 0.1", (Action)delegate
			{
				ApplyVolumePreset(category, 0.1f);
			}, scroll, new Vector2(0f, 10f));
			return ((REPOElement)val10).rectTransform;
		}, 0f, 0f);
		_categoryPages[category] = val;
		val.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scroll)
		{
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			string text = (((IEnumerable<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)config).Where((KeyValuePair<ConfigDefinition, ConfigEntryBase> kvp) => kvp.Key.Section == category && !kvp.Key.Key.StartsWith("__") && !kvp.Key.Key.EndsWith("_volume")).Where(delegate(KeyValuePair<ConfigDefinition, ConfigEntryBase> kvp)
			{
				ConfigEntry<bool> val9 = kvp.Value as ConfigEntry<bool>;
				return val9 != null;
			}).All((KeyValuePair<ConfigDefinition, ConfigEntryBase> kvp) => ((ConfigEntry<bool>)(object)kvp.Value).Value) ? "Enable" : "Disable");
			REPOSlider val7 = MenuAPI.CreateREPOSlider("Category", "Enable/Disable all", (Action<string>)delegate(string choice)
			{
				bool value2 = choice == "Enable";
				foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
				{
					if (!(item.Key.Section != category) && !item.Key.Key.StartsWith("__") && !item.Key.Key.EndsWith("_volume") && item.Value is ConfigEntry<bool> val8)
					{
						val8.Value = value2;
					}
				}
				config.Save();
				UpdateVolumes();
			}, scroll, new string[2] { "Enable", "Disable" }, text, Vector2.zero, "", "", (BarBehavior)0);
			return ((REPOElement)val7).rectTransform;
		}, 0f, 0f);
		object obj2 = <>c.<>9__24_5;
		if (obj2 == null)
		{
			ScrollViewBuilderDelegate val3 = delegate(Transform scroll)
			{
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				REPOLabel val6 = MenuAPI.CreateREPOLabel("Sounds of category", scroll, new Vector2(0f, 30f));
				return ((REPOElement)val6).rectTransform;
			};
			<>c.<>9__24_5 = val3;
			obj2 = (object)val3;
		}
		val.AddElementToScrollView((ScrollViewBuilderDelegate)obj2, 0f, 0f);
		foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item2 in config)
		{
			if (item2.Key.Section != category)
			{
				continue;
			}
			string key = item2.Key.Key;
			ConfigEntryBase value = item2.Value;
			ConfigEntry<bool> boolEntry = value as ConfigEntry<bool>;
			if (boolEntry != null && !key.EndsWith("_volume") && !key.StartsWith("__"))
			{
				val.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scroll)
				{
					//IL_0027: Unknown result type (might be due to invalid IL or missing references)
					REPOToggle val5 = MenuAPI.CreateREPOToggle(key, (Action<bool>)delegate(bool b)
					{
						boolEntry.Value = b;
						config.Save();
					}, scroll, Vector2.zero, "On", "Off", boolEntry.Value);
					return ((REPOElement)val5).rectTransform;
				}, 0f, 0f);
				continue;
			}
			value = item2.Value;
			ConfigEntry<float> floatEntry = value as ConfigEntry<float>;
			if (floatEntry == null || !key.EndsWith("_volume"))
			{
				continue;
			}
			val.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scroll)
			{
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				REPOSlider val4 = MenuAPI.CreateREPOSlider(key, "Volume", (Action<float>)delegate(float f)
				{
					floatEntry.Value = f;
					config.Save();
				}, scroll, Vector2.zero, 0f, 1f, 2, floatEntry.Value, "", "", (BarBehavior)0);
				return ((REPOElement)val4).rectTransform;
			}, 0f, 0f);
		}
	}

	private List<string> GetAllCategories()
	{
		return (from kvp in (IEnumerable<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)config
			select kvp.Key.Section into section
			where !section.StartsWith("__")
			select section).Distinct().ToList();
	}

	private static void ApplyVolumePreset(string category, float volume)
	{
		foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
		{
			if (item.Key.Section == category && item.Key.Key.EndsWith("_volume") && item.Value is ConfigEntry<float> val)
			{
				val.Value = volume;
			}
		}
		config.Save();
		UpdateVolumes();
		log.LogInfo((object)$"\ud83d\udce2 Пресет громкости {volume} применён к категории [{category}]");
	}

	private void AddPresetButtons(string category)
	{
		ConfigEntry<bool> def = config.Bind<bool>(category, "__Preset_Default", false, "Установить громкость всех звуков этой категории на 1.0");
		ConfigEntry<bool> mid = config.Bind<bool>(category, "__Preset_Mid", false, "Установить громкость всех звуков этой категории на 0.5");
		ConfigEntry<bool> low = config.Bind<bool>(category, "__Preset_Quiet", false, "Установить громкость всех звуков этой категории на 0.1");
		ConfigEntry<AllAudioToggle> allToggle = config.Bind<AllAudioToggle>(category, "All Audio", AllAudioToggle.Enable, "Включить или выключить все звуки в категории");
		def.SettingChanged += delegate
		{
			ApplyPresetVolume(def, 1f);
		};
		mid.SettingChanged += delegate
		{
			ApplyPresetVolume(mid, 0.5f);
		};
		low.SettingChanged += delegate
		{
			ApplyPresetVolume(low, 0.1f);
		};
		allToggle.SettingChanged += delegate
		{
			HandleAllToggle(allToggle);
		};
		void ApplyPresetVolume(ConfigEntry<bool> entry, float volume)
		{
			if (entry.Value)
			{
				log.LogInfo((object)$"\ud83d\udd27 Применяется пресет громкости {volume} для категории [{category}]");
				foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
				{
					if (!(item.Key.Section != category) && item.Key.Key.EndsWith("_volume") && item.Value is ConfigEntry<float> val3)
					{
						val3.Value = volume;
					}
				}
				entry.Value = false;
				config.Save();
				UpdateVolumes();
			}
		}
		void HandleAllToggle(ConfigEntry<AllAudioToggle> entry)
		{
			switch (entry.Value)
			{
			case AllAudioToggle.Enable:
				log.LogInfo((object)("✅ Включаются все звуки в категории [" + category + "]"));
				foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item2 in config)
				{
					if (!(item2.Key.Section != category) && !item2.Key.Key.StartsWith("__") && !item2.Key.Key.EndsWith("_volume") && item2.Value is ConfigEntry<bool> val2)
					{
						val2.Value = true;
					}
				}
				break;
			case AllAudioToggle.Disable:
				log.LogInfo((object)("\ud83d\udeab Отключаются все звуки в категории [" + category + "]"));
				foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item3 in config)
				{
					if (!(item3.Key.Section != category) && !item3.Key.Key.StartsWith("__") && !item3.Key.Key.EndsWith("_volume") && item3.Value is ConfigEntry<bool> val)
					{
						val.Value = false;
					}
				}
				break;
			}
			config.Save();
			UpdateVolumes();
		}
	}

	public static void UpdateVolumes()
	{
		if (replacedSources.Count == 0)
		{
			return;
		}
		tryReplaceCache.Clear();
		processedClips.Clear();
		noReplacementCache.Clear();
		configCache.Clear();
		replacedSources.RemoveAll((AudioSource src) => (Object)(object)src == (Object)null || (Object)(object)src.clip == (Object)null);
		foreach (AudioSource replacedSource in replacedSources)
		{
			if (!((Object)(object)replacedSource.clip == (Object)null))
			{
				string name = ((Object)replacedSource.clip).name;
				if (TryReplace(replacedSource, name, out var _, out var volume, out var enabled) && enabled)
				{
					float volume2 = Mathf.Clamp01(Mathf.Pow(Mathf.Clamp(volume, 0.0001f, 1f), 2.2f));
					replacedSource.volume = volume2;
				}
				else
				{
					replacedSource.volume = 0f;
				}
			}
		}
		log.LogInfo((object)"\ud83d\udd04 Громкости обновлены");
	}

	private void LoadReplacements()
	{
		//IL_031b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0325: Expected O, but got Unknown
		string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "replacers");
		if (!Directory.Exists(text))
		{
			log.LogWarning((object)("❌ Папка с заменами не найдена: " + text));
			return;
		}
		HashSet<string> hashSet = new HashSet<string>();
		string[] files = Directory.GetFiles(text, "*.json");
		foreach (string text2 in files)
		{
			try
			{
				ReplacementContainer replacementContainer = JsonConvert.DeserializeObject<ReplacementContainer>(File.ReadAllText(text2));
				foreach (Replacement replacement in replacementContainer.replacements)
				{
					foreach (string match in replacement.matches)
					{
						if (!replacementMap.ContainsKey(match))
						{
							replacementMap[match] = new List<SoundEntry>();
						}
						foreach (SoundEntry sound in replacement.sounds)
						{
							SoundEntry soundEntry = sound;
							if (soundEntry.category == null)
							{
								soundEntry.category = replacement.category ?? "Default";
							}
							soundEntry = sound;
							if (soundEntry.displayName == null)
							{
								soundEntry.displayName = Path.GetFileNameWithoutExtension(sound.sound);
							}
							if (hashSet.Add(sound.category))
							{
								AddPresetButtons(sound.category);
							}
							if (!clipCache.ContainsKey(sound.sound))
							{
								AudioClip clip = GetClip(sound.sound);
								if ((Object)(object)clip != (Object)null)
								{
									clipCache[sound.sound] = clip;
								}
							}
							string key = sound.sound.ToLowerInvariant();
							directMatchCache[key] = sound;
							replacementMap[match].Add(sound);
						}
					}
				}
			}
			catch (Exception ex)
			{
				log.LogError((object)("⚠\ufe0f Ошибка при чтении " + text2 + ": " + ex.Message));
			}
		}
		foreach (KeyValuePair<string, List<SoundEntry>> item3 in replacementMap)
		{
			foreach (SoundEntry item4 in item3.Value)
			{
				string text3 = item4.category ?? "Default";
				string text4 = item4.displayName ?? Path.GetFileNameWithoutExtension(item4.sound);
				ConfigEntry<bool> item = config.Bind<bool>(text3, text4, true, "Включить звук");
				ConfigEntry<float> item2 = config.Bind<float>(text3, text4 + "_volume", 1f, new ConfigDescription("Громкость от 0.0 до 1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
				configEntryCache[text3 + "::" + text4] = (item, item2);
			}
		}
	}

	private static void CleanupSavedPositions()
	{
		List<AudioSource> list = savedPositions.Keys.Where((AudioSource src) => (Object)(object)src == (Object)null || (Object)(object)((Component)src).gameObject == (Object)null).ToList();
		foreach (AudioSource item in list)
		{
			savedPositions.Remove(item);
		}
	}
}