Decompiled source of loaforcsSoundAPI Beta v2.0.0

BepInEx/plugins/loaforcsSoundAPI/me.loaforc.soundapi.dll

Decompiled 4 hours ago
using System;
using System.Buffers;
using System.Collections.Concurrent;
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 System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using loaforcsSoundAPI.Core;
using loaforcsSoundAPI.Core.Data;
using loaforcsSoundAPI.Core.JSON;
using loaforcsSoundAPI.Core.Networking;
using loaforcsSoundAPI.Core.Patches;
using loaforcsSoundAPI.Core.Util;
using loaforcsSoundAPI.Reporting;
using loaforcsSoundAPI.Reporting.Data;
using loaforcsSoundAPI.SoundPacks;
using loaforcsSoundAPI.SoundPacks.Data;
using loaforcsSoundAPI.SoundPacks.Data.Conditions;
using loaforcsSoundAPI.Util.Extensions;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("loaforc")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("2.0.0.0")]
[assembly: AssemblyInformationalVersion("2.0.0+1c5da9aa0ecea20002b9c72ce776120988276ecd")]
[assembly: AssemblyProduct("loaforcsSoundAPI")]
[assembly: AssemblyTitle("me.loaforc.soundapi")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/LoafOrc/loaforcsSoundAPI")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.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;
		}
	}
}
namespace loaforcsSoundAPI
{
	[BepInPlugin("me.loaforc.soundapi", "loaforcsSoundAPI", "2.0.0")]
	internal class loaforcsSoundAPI : BaseUnityPlugin
	{
		private static loaforcsSoundAPI _instance;

		internal static ManualLogSource Logger { get; private set; }

		private void Awake()
		{
			_instance = this;
			Logger = Logger.CreateLogSource("me.loaforc.soundapi");
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			Logger.LogInfo((object)"Setting up config");
			Debuggers.Bind(((BaseUnityPlugin)this).Config);
			SoundReportHandler.Bind(((BaseUnityPlugin)this).Config);
			Logger.LogInfo((object)"Running patches");
			Harmony harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "me.loaforc.soundapi");
			UnityObjectPatch.Init(harmony);
			Logger.LogInfo((object)"Registering data");
			SoundAPI.RegisterAll(Assembly.GetExecutingAssembly());
			SoundReplacementHandler.Register();
			((BaseUnityPlugin)this).Config.Save();
			Logger.LogInfo((object)"me.loaforc.soundapi by loaforc has loaded :3");
		}

		internal static ConfigFile GenerateConfigFile(string name)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			return new ConfigFile(Utility.CombinePaths(new string[2]
			{
				Paths.ConfigPath,
				name + ".cfg"
			}), false, MetadataHelper.GetMetadata((object)_instance));
		}
	}
	public static class SoundAPI
	{
		public const string PLUGIN_GUID = "me.loaforc.soundapi";

		internal static NetworkAdapter CurrentNetworkAdapter { get; private set; }

		public static void RegisterAll(Assembly assembly)
		{
			foreach (Type loadableType in assembly.GetLoadableTypes())
			{
				if (loadableType.IsNested)
				{
					continue;
				}
				SoundAPIConditionAttribute soundAPIConditionAttribute = (SoundAPIConditionAttribute)loadableType.GetCustomAttribute(typeof(SoundAPIConditionAttribute));
				if (soundAPIConditionAttribute == null)
				{
					continue;
				}
				if (loadableType.BaseType == null || (loadableType.BaseType != typeof(Condition) && loadableType.BaseType.GetGenericTypeDefinition() != typeof(Condition<>)))
				{
					loaforcsSoundAPI.Logger.LogError((object)("Condition: '" + loadableType.FullName + "' has been marked with [SoundAPICondition] but does not extend Condition!"));
					continue;
				}
				ConstructorInfo info = loadableType.GetConstructor(Array.Empty<Type>());
				if (info == null)
				{
					loaforcsSoundAPI.Logger.LogError((object)("Condition: '" + loadableType.FullName + "' has no valid constructor! It must have a constructor with no parameters! If you need extra parameters do not mark it with [SoundAPICondition] and Register it manually."));
					continue;
				}
				RegisterCondition(soundAPIConditionAttribute.ID, () => (Condition)info.Invoke(Array.Empty<object>()));
			}
		}

		public static void RegisterCondition(string id, Func<Condition> factory)
		{
			SoundPackDataHandler.Register(id, factory);
		}

		public static void RegisterNetworkAdapter(NetworkAdapter adapter)
		{
			CurrentNetworkAdapter = adapter;
			loaforcsSoundAPI.Logger.LogInfo((object)("Registered network adapter: '" + CurrentNetworkAdapter.Name + "'"));
			CurrentNetworkAdapter.OnRegister();
		}

		public static void RegisterSoundPack(SoundPack pack)
		{
			if (SoundPackDataHandler.LoadedPacks.Contains(pack))
			{
				throw new InvalidOperationException("Already registered sound-pack: '" + pack.Name + "'!");
			}
			SoundPackDataHandler.AddLoadedPack(pack);
			foreach (SoundReplacementCollection replacementCollection in pack.ReplacementCollections)
			{
				foreach (SoundReplacementGroup replacement in replacementCollection.Replacements)
				{
					SoundPackDataHandler.AddReplacement(replacement);
				}
			}
		}
	}
	internal static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "me.loaforc.soundapi";

		public const string PLUGIN_NAME = "loaforcsSoundAPI";

		public const string PLUGIN_VERSION = "2.0.0";
	}
}
namespace loaforcsSoundAPI.Util.Extensions
{
	internal static class AssemblyExtensions
	{
		internal static IEnumerable<Type> GetLoadableTypes(this Assembly assembly)
		{
			if (assembly == null)
			{
				throw new ArgumentNullException("assembly");
			}
			try
			{
				return assembly.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				return ex.Types.Where((Type t) => t != null);
			}
		}
	}
}
namespace loaforcsSoundAPI.SoundPacks
{
	internal class LoadSoundOperation
	{
		public readonly UnityWebRequest WebRequest = webRequest.webRequest;

		public readonly SoundInstance Sound;

		public bool IsReady => WebRequest.isDone;

		public bool IsDone { get; set; }

		public LoadSoundOperation(SoundInstance soundInstance, UnityWebRequestAsyncOperation webRequest)
		{
			Sound = soundInstance;
			base..ctor();
		}
	}
	internal static class SoundPackDataHandler
	{
		private static List<SoundPack> _loadedPacks = new List<SoundPack>();

		internal static Dictionary<string, List<SoundReplacementGroup>> SoundReplacements = new Dictionary<string, List<SoundReplacementGroup>>();

		internal static Dictionary<string, Func<Condition>> conditionFactories = new Dictionary<string, Func<Condition>>();

		internal static List<AudioClip> allLoadedClips = new List<AudioClip>();

		internal static IReadOnlyList<SoundPack> LoadedPacks => _loadedPacks.AsReadOnly();

		internal static void Register(string id, Func<Condition> factory)
		{
			conditionFactories[id] = factory;
		}

		public static Condition CreateCondition(string id)
		{
			if (conditionFactories.TryGetValue(id, out var value))
			{
				return value();
			}
			return new InvalidCondition(id);
		}

		internal static void AddLoadedPack(SoundPack pack)
		{
			_loadedPacks.Add(pack);
		}

		internal static void AddReplacement(SoundReplacementGroup group)
		{
			foreach (string match in group.Matches)
			{
				string key = match.Split(":").Last();
				if (!SoundReplacements.TryGetValue(key, out var value))
				{
					value = new List<SoundReplacementGroup>();
				}
				if (!value.Contains(group))
				{
					value.Add(group);
					SoundReplacements[key] = value;
				}
			}
		}
	}
	internal static class SoundPackLoadPipeline
	{
		private static volatile int _activeThreads;

		private static Dictionary<string, List<string>> mappings;

		internal static event Action OnFinishedPipeline;

		internal static async void StartPipeline()
		{
			Stopwatch completeLoadingTimer = Stopwatch.StartNew();
			Stopwatch timer = Stopwatch.StartNew();
			List<SoundPack> list = FindAndLoadPacks();
			loaforcsSoundAPI.Logger.LogInfo((object)$"(Step 1) Loading Sound-pack definitions took {timer.ElapsedMilliseconds}ms");
			timer.Restart();
			List<LoadSoundOperation> webRequestOperations = new List<LoadSoundOperation>();
			foreach (SoundPack item2 in list)
			{
				string path = Path.Combine(item2.PackFolder, "soundapi_mappings.json");
				if (!File.Exists(path))
				{
					continue;
				}
				Dictionary<string, List<string>> dictionary = JSONDataLoader.LoadFromFile<Dictionary<string, List<string>>>(path);
				foreach (KeyValuePair<string, List<string>> item3 in dictionary)
				{
					if (mappings.ContainsKey(item3.Key))
					{
						mappings[item3.Key].AddRange(item3.Value);
					}
					else
					{
						mappings[item3.Key] = item3.Value;
					}
				}
			}
			loaforcsSoundAPI.Logger.LogInfo((object)$"(Step 2) Loading Sound-pack mappings ('{mappings.Count}') took {timer.ElapsedMilliseconds}ms");
			timer.Restart();
			foreach (SoundPack item4 in list)
			{
				foreach (SoundReplacementCollection item5 in LoadSoundReplacementCollections(item4))
				{
					item5.Pack = item4;
					foreach (SoundReplacementGroup replacement in item5.Replacements)
					{
						replacement.Parent = item5;
						List<IValidatable.ValidationResult> results = replacement.Validate();
						if (!IValidatable.LogAndCheckValidationResult("loading a replacement group in '" + LogFormats.FormatFilePath(item5.FilePath) + "'", results))
						{
							continue;
						}
						List<IValidatable.ValidationResult> list2 = new List<IValidatable.ValidationResult>();
						foreach (SoundInstance sound in replacement.Sounds)
						{
							sound.Parent = replacement;
							list2.AddRange(sound.Validate());
						}
						if (!IValidatable.LogAndCheckValidationResult("loading a replacement group in '" + LogFormats.FormatFilePath(item5.FilePath) + "'", list2))
						{
							continue;
						}
						SoundPackDataHandler.AddReplacement(replacement);
						foreach (SoundInstance sound2 in replacement.Sounds)
						{
							webRequestOperations.Add(StartWebRequestOperation(item4, sound2));
						}
					}
				}
			}
			int amountOfOperations = webRequestOperations.Count;
			loaforcsSoundAPI.Logger.LogInfo((object)$"(Step 3) Loading sound replacement collections took {timer.ElapsedMilliseconds}ms");
			if (SoundReportHandler.CurrentReport != null)
			{
				SoundReportHandler.CurrentReport.AudioClipsLoaded = amountOfOperations;
			}
			loaforcsSoundAPI.Logger.LogInfo((object)$"(Step 4) Started loading {amountOfOperations} audio file(s)");
			loaforcsSoundAPI.Logger.LogInfo((object)"Waiting for splash screens to complete to continue...");
			completeLoadingTimer.Stop();
			await Task.Delay(1);
			loaforcsSoundAPI.Logger.LogInfo((object)"Splash screens done! Continuing pipeline");
			loaforcsSoundAPI.Logger.LogWarning((object)"The game will freeze for a moment!");
			timer.Restart();
			completeLoadingTimer.Start();
			bool flag = false;
			ConcurrentQueue<LoadSoundOperation> queuedOperations = new ConcurrentQueue<LoadSoundOperation>();
			ConcurrentBag<Exception> threadPoolExceptions = new ConcurrentBag<Exception>();
			for (int i = 0; i < 8; i++)
			{
				Task.Run(delegate
				{
					while (queuedOperations.Count == 0)
					{
						Thread.Sleep(10);
					}
					Interlocked.Increment(ref _activeThreads);
					Debuggers.SoundReplacementLoader?.Log($"active threads at {_activeThreads}");
					LoadSoundOperation result;
					while (queuedOperations.TryDequeue(out result))
					{
						try
						{
							AudioClip content2 = DownloadHandlerAudioClip.GetContent(result.WebRequest);
							result.Sound.Clip = content2;
							result.WebRequest.Dispose();
							Debuggers.SoundReplacementLoader?.Log("clip generated");
							result.IsDone = true;
						}
						catch (Exception item)
						{
							threadPoolExceptions.Add(item);
						}
					}
					Interlocked.Decrement(ref _activeThreads);
				});
			}
			bool flag2 = true;
			try
			{
				while (webRequestOperations.Count > 0)
				{
					foreach (LoadSoundOperation item6 in from operation in webRequestOperations.ToList()
						where operation.IsReady
						select operation)
					{
						if (flag2)
						{
							queuedOperations.Enqueue(item6);
						}
						else
						{
							AudioClip content = DownloadHandlerAudioClip.GetContent(item6.WebRequest);
							item6.Sound.Clip = content;
							item6.WebRequest.Dispose();
							Debuggers.SoundReplacementLoader?.Log("clip generated");
						}
						webRequestOperations.Remove(item6);
					}
					if (!flag && webRequestOperations.Count < amountOfOperations / 2)
					{
						flag = true;
						loaforcsSoundAPI.Logger.LogInfo((object)"(Step 5) Queued half of the needed operations!");
					}
					Thread.Sleep(100);
				}
				loaforcsSoundAPI.Logger.LogInfo((object)"(Step 5) All file reads are done, waiting for the audio clips conversions.");
				while (_activeThreads > 0 || webRequestOperations.Any((LoadSoundOperation operation) => !operation.IsDone))
				{
					Thread.Sleep(100);
				}
				loaforcsSoundAPI.Logger.LogInfo((object)$"(Step 6) Took {timer.ElapsedMilliseconds}ms to finish loading audio clips from files");
				if (threadPoolExceptions.Count != 0)
				{
					loaforcsSoundAPI.Logger.LogError((object)$"(Step 6) {threadPoolExceptions.Count} internal error(s) happened while loading:");
					foreach (Exception item7 in threadPoolExceptions)
					{
						loaforcsSoundAPI.Logger.LogError((object)item7.ToString());
					}
				}
				SoundPackLoadPipeline.OnFinishedPipeline();
				mappings = null;
				loaforcsSoundAPI.Logger.LogDebug((object)$"Active Threads that are left over: {_activeThreads}");
				loaforcsSoundAPI.Logger.LogInfo((object)$"Entire load process took an effective {completeLoadingTimer.ElapsedMilliseconds}ms");
			}
			catch (Exception ex)
			{
				loaforcsSoundAPI.Logger.LogError((object)ex);
			}
		}

		private static List<SoundPack> FindAndLoadPacks(string entryPoint = "sound_pack.json")
		{
			List<SoundPack> list = new List<SoundPack>();
			string[] files = Directory.GetFiles(Paths.PluginPath, entryPoint, SearchOption.AllDirectories);
			foreach (string text in files)
			{
				Debuggers.SoundReplacementLoader?.Log("found entry point: '" + text + "'!");
				SoundPack soundPack = JSONDataLoader.LoadFromFile<SoundPack>(text);
				if (soundPack != null)
				{
					soundPack.PackFolder = Path.GetDirectoryName(text);
					Debuggers.SoundReplacementLoader?.Log("json loaded, validating");
					List<IValidatable.ValidationResult> results = soundPack.Validate();
					if (IValidatable.LogAndCheckValidationResult("loading '" + text + "'", results))
					{
						ConfigFile val = loaforcsSoundAPI.GenerateConfigFile("soundpack." + soundPack.Name);
						val.SaveOnConfigSet = false;
						soundPack.Bind(val);
						val.Save();
						list.Add(soundPack);
						SoundPackDataHandler.AddLoadedPack(soundPack);
						Debuggers.SoundReplacementLoader?.Log("pack folder: " + soundPack.PackFolder);
					}
				}
			}
			Debuggers.SoundReplacementLoader?.Log($"loaded '{list.Count}' packs.");
			return list;
		}

		private static List<SoundReplacementCollection> LoadSoundReplacementCollections(SoundPack pack)
		{
			List<SoundReplacementCollection> list = new List<SoundReplacementCollection>();
			if (!Directory.Exists(Path.Combine(pack.PackFolder, "replacers")))
			{
				return list;
			}
			Debuggers.SoundReplacementLoader?.Log("start loading '" + pack.Name + "'!");
			string[] files = Directory.GetFiles(Path.Combine(pack.PackFolder, "replacers"), "*.json", SearchOption.AllDirectories);
			foreach (string text in files)
			{
				Debuggers.SoundReplacementLoader?.Log("found replacer: '" + text + "'!");
				SoundReplacementCollection soundReplacementCollection = JSONDataLoader.LoadFromFile<SoundReplacementCollection>(text);
				if (soundReplacementCollection == null)
				{
					continue;
				}
				foreach (SoundReplacementGroup replacement in soundReplacementCollection.Replacements)
				{
					foreach (string item in replacement.Matches.ToList())
					{
						if (item.StartsWith("#"))
						{
							replacement.Matches.Remove(item);
							List<string> matches = replacement.Matches;
							Dictionary<string, List<string>> dictionary = mappings;
							string text2 = item;
							matches.AddRange(dictionary[text2.Substring(1, text2.Length - 1)]);
						}
					}
					List<string> collection = replacement.Matches.Select((string match) => (match.Split(":").Length != 2) ? match : ("*:" + match)).ToList();
					replacement.Matches.Clear();
					replacement.Matches.AddRange(collection);
				}
				list.Add(soundReplacementCollection);
			}
			return list;
		}

		private static LoadSoundOperation StartWebRequestOperation(SoundPack pack, SoundInstance sound)
		{
			string text = Path.Combine(pack.PackFolder, "sounds", sound.Sound);
			UnityWebRequest audioClip = UnityWebRequestMultimedia.GetAudioClip(text, (AudioType)14);
			return new LoadSoundOperation(sound, audioClip.SendWebRequest());
		}

		static SoundPackLoadPipeline()
		{
			SoundPackLoadPipeline.OnFinishedPipeline = delegate
			{
			};
			mappings = new Dictionary<string, List<string>>();
		}
	}
	internal static class SoundReplacementHandler
	{
		private const int TOKEN_PARENT_NAME = 0;

		private const int TOKEN_OBJECT_NAME = 1;

		private const int TOKEN_CLIP_NAME = 2;

		private static readonly string[] _suffixesToRemove = new string[1] { "(Clone)" };

		private static readonly Dictionary<int, string> _cachedObjectNames = new Dictionary<int, string>();

		private static readonly StringBuilder _builder = new StringBuilder();

		internal static readonly DefaultConditionContext DEFAULT_CONTEXT = new DefaultConditionContext();

		internal static void Register()
		{
			SceneManager.sceneLoaded += delegate(Scene scene, LoadSceneMode _)
			{
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				_cachedObjectNames.Clear();
				AudioSource[] array = Object.FindObjectsOfType<AudioSource>(true);
				foreach (AudioSource val in array)
				{
					if (!(((Component)val).gameObject.scene != scene) && val.playOnAwake && TryReplaceAudio(val, val.clip, out var replacement))
					{
						val.Stop();
						if (!((Object)(object)replacement == (Object)null))
						{
							val.clip = replacement;
						}
					}
				}
			};
		}

		internal static bool TryReplaceAudio(AudioSource source, AudioClip clip, out AudioClip replacement)
		{
			replacement = null;
			if ((Object)(object)((Component)source).gameObject == (Object)null)
			{
				return false;
			}
			AudioSourceAdditionalData orCreate = AudioSourceAdditionalData.GetOrCreate(source);
			if (orCreate.ReplacedWith != null && orCreate.ReplacedWith.Parent.UpdateEveryFrame)
			{
				return false;
			}
			string[] name = ArrayPool<string>.Shared.Rent(3);
			if (!TryProcessName(ref name, source, clip) || !TryGetReplacementClip(name, out var group, out var clip2, orCreate.CurrentContext ?? DEFAULT_CONTEXT))
			{
				ArrayPool<string>.Shared.Return(name);
				return false;
			}
			ArrayPool<string>.Shared.Return(name);
			((Object)clip2).name = ((Object)clip).name;
			replacement = clip2;
			orCreate.ReplacedWith = group;
			return true;
		}

		private static string TrimObjectName(GameObject gameObject)
		{
			if (_cachedObjectNames.ContainsKey(((object)gameObject).GetHashCode()))
			{
				return _cachedObjectNames[((object)gameObject).GetHashCode()];
			}
			_builder.Clear();
			_builder.Append(((Object)gameObject).name);
			string[] suffixesToRemove = _suffixesToRemove;
			foreach (string oldValue in suffixesToRemove)
			{
				_builder.Replace(oldValue, string.Empty);
			}
			for (int j = 0; j < _builder.Length; j++)
			{
				if (_builder[j] == '(')
				{
					int num = j;
					for (j++; j < _builder.Length && char.IsDigit(_builder[j]); j++)
					{
					}
					if (j < _builder.Length && _builder[j] == ')')
					{
						_builder.Remove(num, j - num + 1);
						j = num - 1;
					}
				}
			}
			int num2 = _builder.Length;
			while (num2 > 0 && _builder[num2 - 1] == ' ')
			{
				num2--;
			}
			_builder.Remove(num2, _builder.Length - num2);
			string text = _builder.ToString();
			_cachedObjectNames[((object)gameObject).GetHashCode()] = text;
			return text;
		}

		private static bool TryProcessName(ref string[] name, AudioSource source, AudioClip clip)
		{
			if ((Object)(object)clip == (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)source).transform.parent == (Object)null)
			{
				name[0] = "*";
			}
			else
			{
				name[0] = TrimObjectName(((Component)((Component)source).transform.parent).gameObject);
			}
			name[1] = TrimObjectName(((Component)source).gameObject);
			name[2] = ((Object)clip).name;
			if (SoundReportHandler.CurrentReport != null)
			{
				string text;
				try
				{
					text = new StackTrace(fNeedFileInfo: true).GetFrame(5).GetMethod().DeclaringType.Name;
				}
				catch
				{
					text = "<unknown>";
				}
				string text2 = name[0] + ":" + name[1] + ":" + name[2];
				if (!SoundReportHandler.CurrentReport.AllMatchStrings.Contains(text2))
				{
					SoundReportHandler.CurrentReport.AllMatchStrings.Add(text + " > " + text2);
					SoundReportHandler.CurrentReport.RawMatchStrings.Add(text2);
					Debuggers.MatchStrings?.Log(text2);
				}
			}
			return true;
		}

		private static bool TryGetReplacementClip(string[] name, out SoundReplacementGroup group, out AudioClip clip, IContext context)
		{
			group = null;
			clip = null;
			if (name == null)
			{
				return false;
			}
			Debuggers.SoundReplacementHandler?.Log("beginning replacement attempt for " + name[2]);
			if (!SoundPackDataHandler.SoundReplacements.TryGetValue(name[2], out var value))
			{
				return false;
			}
			Debuggers.SoundReplacementHandler?.Log("sound dictionary hit");
			value = value.Where((SoundReplacementGroup it) => it.Parent.Evaluate(context) && it.Evaluate(context) && CheckGroupMatches(it, name)).ToList();
			if (value.Count == 0)
			{
				return false;
			}
			Debuggers.SoundReplacementHandler?.Log("sound group that matches");
			group = value[Random.Range(0, value.Count)];
			List<SoundInstance> list = group.Sounds.Where((SoundInstance it) => it.Evaluate(context)).ToList();
			if (list.Count == 0)
			{
				return false;
			}
			Debuggers.SoundReplacementHandler?.Log("has valid sounds");
			int totalWeight = 0;
			list.ForEach(delegate(SoundInstance replacement)
			{
				totalWeight += replacement.Weight;
			});
			int num = Random.Range(0, totalWeight);
			SoundInstance soundInstance = null;
			foreach (SoundInstance item in list)
			{
				soundInstance = item;
				num -= soundInstance.Weight;
				if (num <= 0)
				{
					break;
				}
			}
			clip = soundInstance.Clip;
			Debuggers.SoundReplacementHandler?.Log("done, dumping stack trace!");
			Debuggers.SoundReplacementHandler?.Log(string.Join(", ", group.Matches));
			Debuggers.SoundReplacementHandler?.Log(((Object)clip).name);
			Debuggers.SoundReplacementHandler?.Log(new StackTrace(fNeedFileInfo: true).ToString().Trim());
			return true;
		}

		private static bool CheckGroupMatches(SoundReplacementGroup group, string[] a)
		{
			foreach (string match in group.Matches)
			{
				if (MatchStrings(a, match))
				{
					return true;
				}
			}
			return false;
		}

		private static bool MatchStrings(string[] a, string b)
		{
			string[] array = b.Split(":");
			if (array[0] != "*" && array[0] != a[0])
			{
				return false;
			}
			if (array[1] != "*" && array[1] != a[1])
			{
				return false;
			}
			return a[2] == array[2];
		}
	}
}
namespace loaforcsSoundAPI.SoundPacks.Data
{
	public interface IPackData
	{
		SoundPack Pack { get; internal set; }
	}
	public class SoundInstance : Conditional
	{
		[field: NonSerialized]
		public SoundReplacementGroup Parent { get; internal set; }

		public string Sound { get; private set; }

		public int Weight { get; private set; }

		[field: NonSerialized]
		public AudioClip Clip { get; internal set; }

		public override SoundPack Pack
		{
			get
			{
				return Parent.Pack;
			}
			set
			{
				if (Parent.Pack != null)
				{
					throw new InvalidOperationException("Pack has already been set.");
				}
				Parent.Pack = value;
			}
		}

		[JsonConstructor]
		internal SoundInstance()
		{
		}

		public SoundInstance(SoundReplacementGroup parent, int weight, AudioClip clip)
		{
			Parent = parent;
			Weight = weight;
			Clip = clip;
			parent.AddSoundReplacement(this);
		}

		public override List<IValidatable.ValidationResult> Validate()
		{
			List<IValidatable.ValidationResult> list = base.Validate();
			if (!File.Exists(Path.Combine(Parent.Parent.Pack.PackFolder, "sounds", Sound)))
			{
				list.Add(new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "Sound '" + Sound + "' couldn't be found or doesn't exist!"));
			}
			return list;
		}
	}
	public class SoundPack : IValidatable
	{
		[NonSerialized]
		private readonly Dictionary<string, object> _configData = new Dictionary<string, object>();

		[JsonProperty]
		private Dictionary<string, JObject> config { get; set; }

		public string Name { get; private set; }

		public string PackFolder { get; internal set; }

		[field: NonSerialized]
		public List<SoundReplacementCollection> ReplacementCollections { get; private set; } = new List<SoundReplacementCollection>();


		[JsonConstructor]
		internal SoundPack()
		{
		}

		internal void Bind(ConfigFile file)
		{
			//IL_0095: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Invalid comparison between Unknown and I4
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Invalid comparison between Unknown and I4
			if (config == null || config.Count == 0)
			{
				return;
			}
			loaforcsSoundAPI.Logger.LogDebug((object)"handling config");
			JToken val2 = default(JToken);
			foreach (KeyValuePair<string, JObject> item in config)
			{
				string[] array = item.Key.Split(":");
				string text = array[0];
				string text2 = array[1];
				JToken val = item.Value["default"];
				string text3 = (item.Value.TryGetValue("description", ref val2) ? ((object)val2).ToString() : "no description defined!");
				JTokenType type = val.Type;
				if ((int)type != 8)
				{
					if ((int)type != 9)
					{
						throw new NotImplementedException("WHAT");
					}
					_configData[item.Key] = file.Bind<bool>(text, text2, (bool)val, text3).Value;
				}
				else
				{
					_configData[item.Key] = file.Bind<string>(text, text2, (string)val, text3).Value;
				}
			}
		}

		public SoundPack(string name, string packFolder)
		{
			Name = name;
			PackFolder = packFolder;
		}

		internal bool TryGetConfigValue(string id, out object returnValue)
		{
			returnValue = null;
			if (!_configData.TryGetValue(id, out var value))
			{
				return false;
			}
			returnValue = value;
			return true;
		}

		public List<IValidatable.ValidationResult> Validate()
		{
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Invalid comparison between Unknown and I4
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Invalid comparison between Unknown and I4
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			List<IValidatable.ValidationResult> list = new List<IValidatable.ValidationResult>();
			if (string.IsNullOrEmpty(Name))
			{
				list.Add(new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "'name' can not be missing or empty!"));
				return list;
			}
			string name = Name;
			foreach (char c in name)
			{
				if (!char.IsLetter(c) && c != '_')
				{
					list.Add(new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, $"'name' can not contain special character '{c}'!"));
				}
			}
			if (config == null)
			{
				return list;
			}
			JToken val = default(JToken);
			foreach (KeyValuePair<string, JObject> item in config)
			{
				string[] array = item.Key.Split(":");
				if (array.Length != 2)
				{
					list.Add(new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "'" + item.Key + "' is not a valid key for config! It must be 'section:name' with exactly one colon!"));
				}
				if (!item.Value.TryGetValue("default", ref val))
				{
					list.Add(new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "'" + item.Key + "' does not have a 'default' value! This is needed to get what type the config is!"));
				}
				else if ((int)val.Type != 9 && (int)val.Type != 8)
				{
					list.Add(new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, $"'{item.Key}' is of unsupported type: '{val.Type}'! Only supported types are strings/text or booleans!"));
				}
				if (!item.Value.ContainsKey("description"))
				{
					list.Add(new IValidatable.ValidationResult(IValidatable.ResultType.WARN, "'" + item.Key + "' does not have a description."));
				}
			}
			return list;
		}
	}
	public class SoundReplacementCollection : Conditional, IFilePathAware, IPackData
	{
		[field: NonSerialized]
		public override SoundPack Pack { get; set; }

		[JsonProperty("update_every_frame")]
		public bool UpdateEveryFrame { get; private set; }

		public bool Synced { get; private set; }

		public List<SoundReplacementGroup> Replacements { get; private set; } = new List<SoundReplacementGroup>();


		public string FilePath { get; set; }

		[JsonConstructor]
		internal SoundReplacementCollection()
		{
		}

		public SoundReplacementCollection(SoundPack pack)
		{
			Pack = pack;
			pack.ReplacementCollections.Add(this);
		}

		internal void AddSoundReplacementGroup(SoundReplacementGroup group)
		{
			Replacements.Add(group);
		}
	}
	public class SoundReplacementGroup : Conditional
	{
		[field: NonSerialized]
		public SoundReplacementCollection Parent { get; internal set; }

		public List<string> Matches { get; private set; }

		public List<SoundInstance> Sounds { get; private set; } = new List<SoundInstance>();


		public override SoundPack Pack
		{
			get
			{
				return Parent.Pack;
			}
			set
			{
				if (Parent.Pack != null)
				{
					throw new InvalidOperationException("Pack has already been set.");
				}
				Parent.Pack = value;
			}
		}

		[JsonConstructor]
		internal SoundReplacementGroup()
		{
		}

		public SoundReplacementGroup(SoundReplacementCollection parent, List<string> matches)
		{
			Parent = parent;
			Matches = matches;
			if (SoundPackDataHandler.LoadedPacks.Contains(parent.Pack))
			{
				throw new InvalidOperationException("SoundPack has already been registered, trying to add a new SoundReplacementGroup does not work!");
			}
			parent.AddSoundReplacementGroup(this);
		}

		internal void AddSoundReplacement(SoundInstance sound)
		{
			Sounds.Add(sound);
		}

		public override List<IValidatable.ValidationResult> Validate()
		{
			List<IValidatable.ValidationResult> list = base.Validate();
			foreach (string match in Matches)
			{
				if (string.IsNullOrEmpty(match))
				{
					list.Add(new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "Match string can not be empty!"));
					continue;
				}
				string[] array = match.Split(":");
				if (array.Length == 1)
				{
					list.Add(new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "'" + match + "' is not valid! If you mean to match to all Audio clips with this name you must explicitly do '*:" + match + "'."));
				}
			}
			return list;
		}
	}
}
namespace loaforcsSoundAPI.SoundPacks.Data.Conditions
{
	public abstract class Condition : IValidatable
	{
		[field: NonSerialized]
		public Conditional Parent { get; internal set; }

		protected SoundPack Pack => Parent.Pack;

		public abstract bool Evaluate(IContext context);

		public virtual List<IValidatable.ValidationResult> Validate()
		{
			return new List<IValidatable.ValidationResult>();
		}

		protected bool EvaluateRangeOperator(int number, string condition)
		{
			return EvaluateRangeOperator((double)number, condition);
		}

		protected bool EvaluateRangeOperator(float number, string condition)
		{
			return EvaluateRangeOperator((double)number, condition);
		}

		protected bool EvaluateRangeOperator(double value, string condition)
		{
			string[] array = condition.Split("..");
			if (array.Length == 1)
			{
				if (double.TryParse(array[0], out var result))
				{
					return value == result;
				}
				return false;
			}
			if (array.Length == 2)
			{
				double result2;
				if (array[0] == "")
				{
					result2 = double.MinValue;
				}
				else if (!double.TryParse(array[0], out result2))
				{
					return false;
				}
				double result3;
				if (array[1] == "")
				{
					result3 = double.MaxValue;
				}
				else if (!double.TryParse(array[1], out result3))
				{
					return false;
				}
				if (value >= result2)
				{
					return value <= result3;
				}
				return false;
			}
			return false;
		}

		protected bool ValidateRangeOperator(string condition, out IValidatable.ValidationResult result)
		{
			result = null;
			if (string.IsNullOrEmpty(condition))
			{
				result = new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "Range operator can not be missing or empty!");
				return false;
			}
			string[] array = condition.Split("..");
			int num = array.Length;
			if (num <= 2)
			{
				switch (num)
				{
				case 1:
				{
					if (!double.TryParse(array[0], out var _))
					{
						result = new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "Failed to parse: '" + array[0] + "' as a number!");
					}
					break;
				}
				case 2:
				{
					double num2;
					if (array[0] == "")
					{
						num2 = double.MinValue;
					}
					else if (!double.TryParse(array[0], out num2))
					{
						result = new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "Failed to parse: '" + array[0] + "' as a number!");
					}
					double num3;
					if (array[1] == "")
					{
						num3 = double.MaxValue;
					}
					else if (!double.TryParse(array[1], out num3))
					{
						result = new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "Failed to parse: '" + array[1] + "' as a number!");
					}
					break;
				}
				}
			}
			else
			{
				result = new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "Range operator: '" + condition + "' uses .. more than once!");
			}
			return result == null;
		}
	}
	internal sealed class InvalidCondition : Condition
	{
		[CompilerGenerated]
		private string <type>P;

		public InvalidCondition(string type)
		{
			<type>P = type;
			base..ctor();
		}

		public override bool Evaluate(IContext context)
		{
			return false;
		}

		public override List<IValidatable.ValidationResult> Validate()
		{
			if (string.IsNullOrEmpty(<type>P))
			{
				return new List<IValidatable.ValidationResult>(1)
				{
					new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "Condition must have a type!")
				};
			}
			return new List<IValidatable.ValidationResult>(1)
			{
				new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "'" + <type>P + "' is not a valid condition type!")
			};
		}
	}
	public abstract class Condition<TContext> : Condition where TContext : IContext
	{
		public override bool Evaluate(IContext context)
		{
			if (!(context is TContext context2))
			{
				return EvaluateFallback(context);
			}
			return EvaluateWithContext(context2);
		}

		protected abstract bool EvaluateWithContext(TContext context);

		protected virtual bool EvaluateFallback(IContext context)
		{
			return false;
		}
	}
	public abstract class Conditional : IValidatable, IPackData
	{
		public Condition Condition { get; set; }

		public abstract SoundPack Pack { get; set; }

		public bool Evaluate(IContext context)
		{
			if (Condition == null)
			{
				return true;
			}
			return Condition.Evaluate(context);
		}

		public virtual List<IValidatable.ValidationResult> Validate()
		{
			if (Condition == null)
			{
				return new List<IValidatable.ValidationResult>();
			}
			Condition.Parent = this;
			return Condition.Validate();
		}
	}
	public interface IContext
	{
	}
	internal class DefaultConditionContext : IContext
	{
	}
	[AttributeUsage(AttributeTargets.Class)]
	[MeansImplicitUse]
	public class SoundAPIConditionAttribute : Attribute
	{
		public string ID { get; private set; }

		public SoundAPIConditionAttribute(string id)
		{
			ID = id;
			base..ctor();
		}
	}
}
namespace loaforcsSoundAPI.SoundPacks.Conditions
{
	[SoundAPICondition("and")]
	internal class AndCondition : Condition<DefaultConditionContext>
	{
		public Condition[] Conditions { get; private set; }

		protected override bool EvaluateWithContext(DefaultConditionContext context)
		{
			Condition[] conditions = Conditions;
			foreach (Condition condition in conditions)
			{
				if (condition is InvalidCondition)
				{
					return false;
				}
				if (!condition.Evaluate(context))
				{
					return false;
				}
			}
			return true;
		}

		public override List<IValidatable.ValidationResult> Validate()
		{
			if (Conditions.Length == 0)
			{
				return new List<IValidatable.ValidationResult>(1)
				{
					new IValidatable.ValidationResult(IValidatable.ResultType.WARN, "'and' condition has no conditions and will always return true!")
				};
			}
			return new List<IValidatable.ValidationResult>();
		}
	}
	[SoundAPICondition("config")]
	internal class ConfigCondition : Condition<DefaultConditionContext>
	{
		public string Config { get; private set; }

		public object Value { get; private set; }

		protected override bool EvaluateWithContext(DefaultConditionContext context)
		{
			if (!base.Pack.TryGetConfigValue(Config, out var returnValue))
			{
				return false;
			}
			if (Value == null)
			{
				if (returnValue is bool)
				{
					return (bool)returnValue;
				}
				if (returnValue is string value)
				{
					return string.IsNullOrEmpty(value);
				}
				return false;
			}
			if (returnValue is bool flag)
			{
				return flag == (bool)Value;
			}
			if (returnValue is string text)
			{
				return text == (string)Value;
			}
			return false;
		}

		public override List<IValidatable.ValidationResult> Validate()
		{
			if (!base.Pack.TryGetConfigValue(Config, out var returnValue))
			{
				List<IValidatable.ValidationResult> list = new List<IValidatable.ValidationResult>(1);
				list.Add(new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "Config '" + Config + "' does not exist on SoundPack '" + base.Pack.Name + "'"));
				return list;
			}
			if (Value != null && returnValue.GetType() != Value.GetType())
			{
				return new List<IValidatable.ValidationResult>(1)
				{
					new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, $"Config '{Config}' has a type of: '{returnValue.GetType()}' but the Value type is '{Value.GetType()}'!")
				};
			}
			return new List<IValidatable.ValidationResult>();
		}
	}
	[SoundAPICondition("mod_installed")]
	internal class ModInstalledCondition : Condition<DefaultConditionContext>
	{
		public string Value { get; private set; }

		protected override bool EvaluateWithContext(DefaultConditionContext context)
		{
			return Chainloader.PluginInfos.ContainsKey(Value);
		}

		public override List<IValidatable.ValidationResult> Validate()
		{
			if (string.IsNullOrEmpty(Value))
			{
				return new List<IValidatable.ValidationResult>(1)
				{
					new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "Value on 'mod_installed' must be there and must not be empty.")
				};
			}
			return new List<IValidatable.ValidationResult>();
		}
	}
	[SoundAPICondition("not")]
	internal class NotCondition : Condition<DefaultConditionContext>
	{
		[JsonProperty("condition")]
		public Condition Condition { get; private set; }

		protected override bool EvaluateWithContext(DefaultConditionContext context)
		{
			if (Condition is InvalidCondition)
			{
				return false;
			}
			return !Condition.Evaluate(context);
		}

		public override List<IValidatable.ValidationResult> Validate()
		{
			if (Condition == null)
			{
				return new List<IValidatable.ValidationResult>(1)
				{
					new IValidatable.ValidationResult(IValidatable.ResultType.FAIL, "'not' condition has no valid condition to invert!")
				};
			}
			return new List<IValidatable.ValidationResult>();
		}
	}
	[SoundAPICondition("or")]
	internal class OrCondition : Condition<DefaultConditionContext>
	{
		public Condition[] Conditions { get; private set; }

		protected override bool EvaluateWithContext(DefaultConditionContext context)
		{
			Condition[] conditions = Conditions;
			foreach (Condition condition in conditions)
			{
				if (condition is InvalidCondition)
				{
					return false;
				}
				if (condition.Evaluate(context))
				{
					return true;
				}
			}
			return false;
		}

		public override List<IValidatable.ValidationResult> Validate()
		{
			if (Conditions.Length == 0)
			{
				return new List<IValidatable.ValidationResult>(1)
				{
					new IValidatable.ValidationResult(IValidatable.ResultType.WARN, "'or' condition has no conditions and will always return false!")
				};
			}
			return new List<IValidatable.ValidationResult>();
		}
	}
}
namespace loaforcsSoundAPI.Reporting
{
	public static class SoundReportHandler
	{
		private const string _datetimeFormat = "dd_MM_yyyy-HH_mm";

		private static Action<StreamWriter, SoundReport> _reportSections = delegate
		{
		};

		public static SoundReport CurrentReport { get; private set; }

		public static Action OnStartReport { get; set; } = delegate
		{
		};


		public static void AddReportSection(string header, Action<StreamWriter, SoundReport> callback)
		{
			_reportSections = (Action<StreamWriter, SoundReport>)Delegate.Combine(_reportSections, (Action<StreamWriter, SoundReport>)delegate(StreamWriter stream, SoundReport report)
			{
				stream.WriteLine("## " + header);
				callback(stream, report);
				stream.WriteLine("");
				stream.WriteLine("");
			});
		}

		internal static void Register()
		{
			Directory.CreateDirectory(GetFolder());
			CurrentReport = new SoundReport();
			loaforcsSoundAPI.Logger.LogWarning((object)"SoundAPI is generating a report!");
			loaforcsSoundAPI.Logger.LogInfo((object)("The report will be located at '" + LogFormats.FormatFilePath(Path.Combine(GetFolder(), GetFileName(CurrentReport, ".md")))));
			Application.quitting += delegate
			{
				WriteReportToFile(CurrentReport);
			};
			SoundPackLoadPipeline.OnFinishedPipeline += delegate
			{
				foreach (SoundPack loadedPack in SoundPackDataHandler.LoadedPacks)
				{
					CurrentReport.SoundPackNames.Add(loadedPack.Name);
				}
			};
			AddReportSection("General Information", delegate(StreamWriter stream, SoundReport report)
			{
				stream.WriteLine("SoundAPI version: `2.0.0` <br/><br/>");
				stream.WriteLine($"Audio-clips loaded: `{report.AudioClipsLoaded}` <br/>");
				stream.WriteLine($"Match strings registered: `{SoundPackDataHandler.SoundReplacements.Values.Sum((List<SoundReplacementGroup> it) => it.Count)}` <br/>");
				WriteList("Loaded sound-packs", stream, report.SoundPackNames);
			});
			AddReportSection("Dynamic Data", delegate(StreamWriter stream, SoundReport _)
			{
				if (SoundAPI.CurrentNetworkAdapter != null)
				{
					stream.WriteLine("Network Adapter: `" + SoundAPI.CurrentNetworkAdapter.Name + "` <br/><br/>");
				}
				WriteList("Registered Conditions", stream, SoundPackDataHandler.conditionFactories.Keys.ToList());
			});
			AddReportSection("All Match Strings", delegate(StreamWriter stream, SoundReport report)
			{
				WriteList(null, stream, report.AllMatchStrings);
			});
			OnStartReport();
		}

		internal static void Bind(ConfigFile file)
		{
			if (file.Bind<bool>("Developer", "GenerateReports", false, "While true SoundAPI will generate a json and markdown file per session that records information SoundAPI and related mods find.").Value)
			{
				Register();
			}
		}

		private static string GetFileName(SoundReport report, string extension)
		{
			return "generated_report-" + report.StartedAt.ToString("dd_MM_yyyy-HH_mm") + extension;
		}

		private static string GetFolder()
		{
			return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "reports");
		}

		private static void WriteReportToFile(SoundReport report)
		{
			using StreamWriter streamWriter = new StreamWriter(Path.Combine(GetFolder(), GetFileName(report, ".md")));
			streamWriter.WriteLine("# Generated Report");
			streamWriter.WriteLine($"At {report.StartedAt} :3");
			streamWriter.WriteLine("");
			_reportSections(streamWriter, report);
			streamWriter.Flush();
			streamWriter.Close();
			using StreamWriter streamWriter2 = new StreamWriter(Path.Combine(GetFolder(), GetFileName(report, ".json")));
			streamWriter2.WriteLine(JsonConvert.SerializeObject((object)report, (Formatting)1));
		}

		public static void WriteList(string header, StreamWriter stream, ICollection<string> list)
		{
			if (!string.IsNullOrEmpty(header))
			{
				stream.WriteLine($"### {header} (`{list.Count}`)");
			}
			stream.WriteLine(string.Join("<br/>\n", list.Select((string it) => "- " + it)));
		}

		public static void WriteEnum<T>(string header, StreamWriter stream) where T : Enum
		{
			WriteList(header, stream, (from it in Enum.GetValues(typeof(T)).OfType<T>()
				select it.ToString().ToLowerInvariant()).ToList());
		}
	}
}
namespace loaforcsSoundAPI.Reporting.Data
{
	public class SoundReport
	{
		public DateTime StartedAt { get; private set; } = DateTime.Now;


		public List<string> AllMatchStrings { get; private set; } = new List<string>();


		public List<string> RawMatchStrings { get; private set; } = new List<string>();


		public List<string> SoundPackNames { get; private set; } = new List<string>();


		public int AudioClipsLoaded { get; set; }
	}
}
namespace loaforcsSoundAPI.Core
{
	public class AudioSourceAdditionalData
	{
		public AudioSource Source { get; }

		internal SoundReplacementGroup ReplacedWith { get; set; }

		public IContext CurrentContext { get; set; }

		public AudioSourceAdditionalData(AudioSource source)
		{
			Source = source;
			base..ctor();
		}

		internal void Update()
		{
			if (!Object.op_Implicit((Object)(object)Source) || !((Behaviour)Source).enabled || ReplacedWith == null)
			{
				return;
			}
			if (!Source.isPlaying)
			{
				ReplacedWith = null;
			}
			else if (ReplacedWith.Parent.UpdateEveryFrame)
			{
				IContext context = CurrentContext ?? SoundReplacementHandler.DEFAULT_CONTEXT;
				SoundInstance soundInstance = ReplacedWith.Sounds.FirstOrDefault((SoundInstance x) => x.Evaluate(context));
				if (soundInstance != null && !((Object)(object)soundInstance.Clip == (Object)(object)Source.clip))
				{
					float time = Source.time;
					Source.clip = soundInstance.Clip;
					Source.Play();
					Source.time = time;
				}
			}
		}

		public static AudioSourceAdditionalData GetOrCreate(AudioSource source)
		{
			if (SoundAPIAudioManager.audioSourceData.TryGetValue(source, out var value))
			{
				return value;
			}
			value = new AudioSourceAdditionalData(source);
			SoundAPIAudioManager.audioSourceData[source] = value;
			return value;
		}
	}
	internal static class Debuggers
	{
		internal static DebugLogSource AudioSourceAdditionalData;

		internal static DebugLogSource SoundReplacementLoader;

		internal static DebugLogSource SoundReplacementHandler;

		internal static DebugLogSource MatchStrings;

		internal static void Bind(ConfigFile file)
		{
			FieldInfo[] fields = typeof(Debuggers).GetFields(BindingFlags.Static | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (file.Bind<bool>("InternalDebugging", fieldInfo.Name, false, "Enable/Disable this DebugLogSource. Should only be true if you know what you are doing or have been asked to.").Value)
				{
					fieldInfo.SetValue(null, new DebugLogSource(fieldInfo.Name));
				}
				else
				{
					fieldInfo.SetValue(null, null);
				}
			}
		}
	}
	internal class DebugLogSource
	{
		[CompilerGenerated]
		private string <title>P;

		public DebugLogSource(string title)
		{
			<title>P = title;
			base..ctor();
		}

		internal void Log(object message)
		{
			loaforcsSoundAPI.Logger.LogDebug((object)$"[Debug-{<title>P}] {message}");
		}
	}
	internal class SoundAPIAudioManager : MonoBehaviour
	{
		internal static readonly Dictionary<AudioSource, AudioSourceAdditionalData> audioSourceData = new Dictionary<AudioSource, AudioSourceAdditionalData>();

		private static SoundAPIAudioManager Instance;

		private void Awake()
		{
			SceneManager.sceneLoaded += delegate
			{
				if (!Object.op_Implicit((Object)(object)Instance))
				{
					SpawnManager();
				}
				RunCleanup();
			};
		}

		private static void SpawnManager()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			loaforcsSoundAPI.Logger.LogInfo((object)"Starting AudioManager.");
			GameObject val = new GameObject("SoundAPI_AudioManager");
			Object.DontDestroyOnLoad((Object)(object)val);
			Instance = val.AddComponent<SoundAPIAudioManager>();
		}

		private void Update()
		{
			foreach (AudioSourceAdditionalData value in audioSourceData.Values)
			{
				value.Update();
			}
		}

		private void OnDisable()
		{
			loaforcsSoundAPI.Logger.LogDebug((object)"manager disabled");
		}

		private void OnDestroy()
		{
			loaforcsSoundAPI.Logger.LogDebug((object)"manager destroyed");
		}

		private static void RunCleanup()
		{
			loaforcsSoundAPI.Logger.LogDebug((object)"cleaning up old audio source entries");
			AudioSource[] array = audioSourceData.Keys.ToArray();
			foreach (AudioSource val in array)
			{
				if (!Object.op_Implicit((Object)(object)val))
				{
					audioSourceData.Remove(val);
				}
			}
		}
	}
}
namespace loaforcsSoundAPI.Core.Util
{
	internal static class LogFormats
	{
		internal static string FormatFilePath(string path)
		{
			return $"plugins{Path.DirectorySeparatorChar}{Path.Combine(Path.GetRelativePath(Paths.PluginPath, path))}";
		}
	}
}
namespace loaforcsSoundAPI.Core.Patches
{
	[HarmonyPatch(typeof(AudioSource))]
	internal static class AudioSourcePatch
	{
		[HarmonyPrefix]
		[HarmonyPatch("Play", new Type[] { })]
		[HarmonyPatch("Play", new Type[] { typeof(ulong) })]
		[HarmonyPatch("Play", new Type[] { typeof(double) })]
		private static bool Play(AudioSource __instance)
		{
			if (SoundReplacementHandler.TryReplaceAudio(__instance, __instance.clip, out var replacement))
			{
				if ((Object)(object)replacement == (Object)null)
				{
					return false;
				}
				__instance.clip = replacement;
			}
			return true;
		}

		[HarmonyPrefix]
		[HarmonyPatch("PlayOneShot", new Type[]
		{
			typeof(AudioClip),
			typeof(float)
		})]
		private static bool PlayOneShot(AudioSource __instance, ref AudioClip clip)
		{
			if (SoundReplacementHandler.TryReplaceAudio(__instance, clip, out var replacement))
			{
				if ((Object)(object)replacement == (Object)null)
				{
					return false;
				}
				clip = replacement;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(GameObject))]
	internal static class GameObjectPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("AddComponent", new Type[] { typeof(Type) })]
		internal static void NewAudioSource(GameObject __instance, ref Component __result)
		{
			Component obj = __result;
			AudioSource val = (AudioSource)(object)((obj is AudioSource) ? obj : null);
			if (val != null)
			{
				AudioSourceAdditionalData.GetOrCreate(val);
			}
		}
	}
	[HarmonyPatch(typeof(Logger))]
	internal static class LoggerPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch("LogMessage")]
		private static void ReenableAndSaveConfigs(object data)
		{
			if (data is string text && text == "Chainloader startup complete")
			{
				loaforcsSoundAPI.Logger.LogInfo((object)"Starting Sound-pack loading pipeline");
				SoundPackLoadPipeline.StartPipeline();
			}
		}
	}
	internal static class UnityObjectPatch
	{
		private static void InstantiatePatch(Object __result)
		{
			Debuggers.AudioSourceAdditionalData?.Log("aghuobr: " + __result.name);
			GameObject val = (GameObject)(object)((__result is GameObject) ? __result : null);
			if (val != null)
			{
				CheckInstantiationRecursively(val);
			}
		}

		internal static void Init(Harmony harmony)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			HarmonyMethod val = new HarmonyMethod(typeof(UnityObjectPatch).GetMethod("InstantiatePatch", BindingFlags.Static | BindingFlags.NonPublic));
			MethodInfo[] methods = typeof(Object).GetMethods();
			foreach (MethodInfo methodInfo in methods)
			{
				if (!(methodInfo.Name != "Instantiate"))
				{
					Debuggers.AudioSourceAdditionalData?.Log($"patching {methodInfo}");
					if (methodInfo.IsGenericMethod)
					{
						harmony.Patch((MethodBase)methodInfo.MakeGenericMethod(typeof(Object)), (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
					else
					{
						harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
				}
			}
		}

		private static void CheckInstantiationRecursively(GameObject gameObject)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			AudioSourceAdditionalData audioSourceAdditionalData = default(AudioSourceAdditionalData);
			if (gameObject.TryGetComponent<AudioSourceAdditionalData>(ref audioSourceAdditionalData))
			{
				return;
			}
			AudioSource[] components = gameObject.GetComponents<AudioSource>();
			foreach (AudioSource val in components)
			{
				AudioSourceAdditionalData.GetOrCreate(val);
				if (!val.playOnAwake)
				{
					continue;
				}
				AudioClip clip = val.clip;
				if (SoundReplacementHandler.TryReplaceAudio(val, clip, out var replacement))
				{
					val.Stop();
					if (!((Object)(object)replacement == (Object)null))
					{
						val.clip = replacement;
						val.Play();
					}
				}
				else
				{
					val.clip = clip;
					val.Play();
				}
			}
			foreach (Transform item in gameObject.transform)
			{
				Transform val2 = item;
				CheckInstantiationRecursively(((Component)val2).gameObject);
			}
		}
	}
}
namespace loaforcsSoundAPI.Core.Networking
{
	public abstract class NetworkAdapter
	{
		public abstract string Name { get; }

		public abstract void OnRegister();
	}
}
namespace loaforcsSoundAPI.Core.JSON
{
	public static class JSONDataLoader
	{
		private class MatchesJSONConverter : JsonConverter
		{
			public override bool CanConvert(Type objectType)
			{
				return objectType == typeof(List<string>);
			}

			public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Invalid comparison between Unknown and I4
				JToken val = JToken.Load(reader);
				if ((int)val.Type == 2)
				{
					return val.ToObject<List<string>>();
				}
				return new List<string> { ((object)val).ToString() };
			}

			public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
			{
				serializer.Serialize(writer, value);
			}
		}

		private class IncludePrivatePropertiesContractResolver : DefaultContractResolver
		{
			protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				JsonProperty val = ((DefaultContractResolver)this).CreateProperty(member, memberSerialization);
				if (!val.Writable && member is PropertyInfo propertyInfo)
				{
					val.Writable = propertyInfo.GetSetMethod(nonPublic: true) != null;
				}
				return val;
			}
		}

		private class ConditionConverter : JsonConverter<Condition>
		{
			public override Condition ReadJson(JsonReader reader, Type objectType, Condition existingValue, bool hasExistingValue, JsonSerializer serializer)
			{
				JObject val = JObject.Load(reader);
				string text = ((object)val["type"])?.ToString();
				if (string.IsNullOrEmpty(text))
				{
					return new InvalidCondition(null);
				}
				Condition condition = SoundPackDataHandler.CreateCondition(text);
				if (condition == null)
				{
					return null;
				}
				serializer.Populate(((JToken)val).CreateReader(), (object)condition);
				return condition;
			}

			public override void WriteJson(JsonWriter writer, Condition value, JsonSerializer serializer)
			{
				throw new NotImplementedException("no.");
			}
		}

		private static readonly JsonSerializerSettings _settings = new JsonSerializerSettings
		{
			ContractResolver = (IContractResolver)(object)new IncludePrivatePropertiesContractResolver(),
			Converters = new List<JsonConverter>(2)
			{
				(JsonConverter)(object)new MatchesJSONConverter(),
				(JsonConverter)(object)new ConditionConverter()
			}
		};

		public static T LoadFromFile<T>(string path)
		{
			//IL_0056: Expected O, but got Unknown
			string text = File.ReadAllText(path);
			try
			{
				T val = JsonConvert.DeserializeObject<T>(text, _settings);
				if ((object)val is IFilePathAware filePathAware)
				{
					filePathAware.FilePath = path;
				}
				if ((object)val is Conditional conditional && conditional.Condition != null)
				{
					conditional.Condition.Parent = conditional;
				}
				return val;
			}
			catch (JsonReaderException val2)
			{
				JsonReaderException val3 = val2;
				loaforcsSoundAPI.Logger.LogError((object)$"Failed to read json file: 'plugins{Path.DirectorySeparatorChar}{Path.GetRelativePath(Paths.PluginPath, path)}'");
				loaforcsSoundAPI.Logger.LogError((object)((Exception)(object)val3).Message);
				string[] array = text.Split("\n");
				int num = int.MaxValue;
				for (int i = Mathf.Max(0, val3.LineNumber - 3); i < Mathf.Min(array.Length, val3.LineNumber + 3); i++)
				{
					int num2 = array[i].TakeWhile(char.IsWhiteSpace).Count();
					num = Mathf.Min(num, num2);
				}
				for (int j = Mathf.Max(0, val3.LineNumber - 3); j < Mathf.Min(array.Length, val3.LineNumber + 3); j++)
				{
					string text2 = $"{(j + 1).ToString(),-5}|  ";
					string text3 = array[j];
					int num3 = Mathf.Min(array[j].Length, num);
					string text4 = text2 + text3.Substring(num3, text3.Length - num3).TrimEnd();
					if (j + 1 == val3.LineNumber)
					{
						text4 += " // <- HERE";
					}
					loaforcsSoundAPI.Logger.LogError((object)text4);
				}
			}
			return default(T);
		}
	}
}
namespace loaforcsSoundAPI.Core.Data
{
	public interface IFilePathAware
	{
		string FilePath { get; internal set; }
	}
	public interface IValidatable
	{
		public enum ResultType
		{
			WARN,
			FAIL
		}

		public class ValidationResult
		{
			public ResultType Status { get; private set; }

			public string Reason { get; private set; }

			public ValidationResult(ResultType resultType, string reason = null)
			{
				Status = resultType;
				Reason = reason ?? string.Empty;
				base..ctor();
			}
		}

		private static readonly StringBuilder _stringBuilder = new StringBuilder();

		List<ValidationResult> Validate();

		internal static bool LogAndCheckValidationResult(string context, List<ValidationResult> results)
		{
			if (results.Count == 0)
			{
				return true;
			}
			int num = 0;
			int num2 = 0;
			foreach (ValidationResult result in results)
			{
				switch (result.Status)
				{
				case ResultType.WARN:
					num++;
					break;
				case ResultType.FAIL:
					num2++;
					break;
				default:
					throw new ArgumentOutOfRangeException();
				}
			}
			_stringBuilder.Clear();
			if (num2 != 0)
			{
				_stringBuilder.Append(num2);
				_stringBuilder.Append(" fail(s)");
			}
			if (num != 0)
			{
				if (num2 != 0)
				{
					_stringBuilder.Append(" and ");
				}
				_stringBuilder.Append(num);
				_stringBuilder.Append(" warning(s)");
			}
			_stringBuilder.Append(" while ");
			_stringBuilder.Append(context);
			_stringBuilder.Append(": ");
			if (num2 != 0)
			{
				loaforcsSoundAPI.Logger.LogError((object)_stringBuilder);
			}
			else
			{
				loaforcsSoundAPI.Logger.LogWarning((object)_stringBuilder);
			}
			foreach (ValidationResult result2 in results)
			{
				switch (result2.Status)
				{
				case ResultType.WARN:
					loaforcsSoundAPI.Logger.LogWarning((object)("WARN: " + result2.Reason));
					break;
				case ResultType.FAIL:
					loaforcsSoundAPI.Logger.LogError((object)("FAIL: " + result2.Reason));
					break;
				default:
					throw new ArgumentOutOfRangeException();
				}
			}
			return num2 != 0;
		}
	}
}