Decompiled source of HornetAudioEditor v1.2.0

patchers/AudioTablePatcher.dll

Decompiled 5 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Mono.Cecil;
using Mono.Cecil.Cil;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace HornetAudioEditor.Patcher;

public class AudioTableOnEnablePatcher
{
	public static IEnumerable<string> TargetDLLs => new string[1] { "Assembly-CSharp.dll" };

	public static void Patch(AssemblyDefinition assembly)
	{
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Expected O, but got Unknown
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		ModuleDefinition mainModule = assembly.MainModule;
		TypeDefinition type = mainModule.GetType("RandomAudioClipTable");
		if (type != null && !((IEnumerable<MethodDefinition>)type.Methods).Any((MethodDefinition m) => ((MemberReference)m).Name == "OnEnable"))
		{
			MethodDefinition val = new MethodDefinition("OnEnable", (MethodAttributes)129, mainModule.ImportReference(typeof(void)));
			val.Body.GetILProcessor().Append(Instruction.Create(OpCodes.Ret));
			type.Methods.Add(val);
			if (((IEnumerable<MethodDefinition>)type.Methods).Any((MethodDefinition m) => ((MemberReference)m).Name == "OnEnable"))
			{
				Console.WriteLine("Injected OnEnable into RandomAudioClipTable.");
			}
		}
	}
}

plugins/HornetAudioEditor.dll

Decompiled 5 days ago
using System;
using System.Collections;
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 HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
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(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("HornetAudioEditor")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0+89ebe01822f494276854572a6842b749907a93e6")]
[assembly: AssemblyProduct("HornetAudioEditor")]
[assembly: AssemblyTitle("HornetAudioEditor")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace HornetAudioEditor
{
	[BepInPlugin("alphalul.HornetAudioEditor", "Hornet Audio Editor", "1.2.0")]
	public class HornetAudioEditorPlugin : BaseUnityPlugin
	{
		private class AudioCollection
		{
			public HashSet<string> folders;

			public ProbabilityAudioClip[] vanillaClips;

			public bool includeVanillaClips;

			public AudioCollection(HashSet<string> folders, bool includeVanillaClips)
			{
				this.folders = folders;
				this.includeVanillaClips = includeVanillaClips;
				base..ctor();
			}
		}

		[HarmonyPatch(typeof(RandomAudioClipTable), "OnEnable")]
		private class AudioTableOnEnable_Patch
		{
			[HarmonyPrefix]
			private static void OnEnable_Prefix(RandomAudioClipTable __instance)
			{
				Instance.LoadAudioTable(__instance);
			}
		}

		[HarmonyPatch(typeof(GameManager), "Start")]
		private class GameManagerStart_Patch
		{
			[HarmonyPostfix]
			private static void Start_Postfix()
			{
				((MonoBehaviour)Instance).StartCoroutine(Instance.RefreshAudioCollectionsRoutine());
			}
		}

		[HarmonyPatch(typeof(RandomAudioClipTable), "SelectRandomClip")]
		private class AudioLog_Patch
		{
			[HarmonyPrefix]
			private static void SelectRandomClip_Prefix(RandomAudioClipTable __instance)
			{
				Instance.LogAudio(((Object)__instance).name);
			}
		}

		private Dictionary<string, List<ProbabilityAudioClip>> folderClips;

		private Dictionary<string, AudioCollection> audioCollections;

		private string clipsPath;

		private string audioCollectionsPath;

		private string collectionPresetsPath;

		private Harmony harmony = new Harmony("alphalul.HornetAudioEditor");

		private ConfigEntry<bool> configModEnabled;

		private ConfigEntry<bool> configLogAudio;

		private ConfigEntry<bool> configRefreshOnSaveQuit;

		public const string Id = "alphalul.HornetAudioEditor";

		private static HornetAudioEditorPlugin Instance { get; set; }

		public static string Name => "Hornet Audio Editor";

		public static string Version => "1.2.0";

		private void Awake()
		{
			if ((Object)(object)Instance == (Object)null)
			{
				Instance = this;
			}
			configModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Whether the mod is active. Set to false to disable the mod.");
			configLogAudio = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LogAudio", false, "Whether to log the name of a RandomAudioClipTable when it plays a sound. Useful for finding table names.");
			configRefreshOnSaveQuit = ((BaseUnityPlugin)this).Config.Bind<bool>("Loading", "RefreshOnSaveQuit", true, "Whether to re-apply audioCollections.json upon returning to the title screen.");
			if (configModEnabled.Value)
			{
				clipsPath = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "Clips");
				audioCollectionsPath = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "audioCollections.json");
				collectionPresetsPath = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "Collection Presets");
				Directory.CreateDirectory(clipsPath);
				harmony.PatchAll(typeof(GameManagerStart_Patch));
				if (configRefreshOnSaveQuit.Value)
				{
					harmony.PatchAll(typeof(AudioTableOnEnable_Patch));
				}
				if (configLogAudio.Value)
				{
					harmony.PatchAll(typeof(AudioLog_Patch));
				}
				((MonoBehaviour)this).StartCoroutine(RefreshAudioCollectionsRoutine());
			}
		}

		private IEnumerator RefreshAudioCollectionsRoutine()
		{
			if (!RetrieveAudioCollectionsData())
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Something is wrong with 'audioCollections.json', unable to initialize HornetAudioEditor mod.");
				yield break;
			}
			yield return LoadClipsRoutine();
			RandomAudioClipTable[] loadedAudioTables = Resources.FindObjectsOfTypeAll<RandomAudioClipTable>();
			RandomAudioClipTable[] array = loadedAudioTables;
			foreach (RandomAudioClipTable table in array)
			{
				if (audioCollections.TryGetValue(((Object)table).name, out var audioCollection))
				{
					ApplyClips(table, audioCollection);
				}
				audioCollection = null;
			}
		}

		private IEnumerator LoadClipsRoutine()
		{
			foreach (string folder in folderClips.Keys)
			{
				List<AudioClip> streamedFolderClips = new List<AudioClip>();
				string folderPath = Path.Combine(clipsPath, folder);
				if (!Directory.Exists(folderPath))
				{
					yield break;
				}
				string[] collectionWavFiles = Directory.GetFiles(folderPath, "*.wav", SearchOption.TopDirectoryOnly);
				string[] array = collectionWavFiles;
				foreach (string wavFile in array)
				{
					yield return WavToAudioClipRoutine(wavFile, streamedFolderClips);
				}
				foreach (AudioClip clip in streamedFolderClips)
				{
					List<ProbabilityAudioClip> list = folderClips[folder];
					ProbabilityAudioClip val = new ProbabilityAudioClip
					{
						Clip = clip
					};
					((ProbabilityBase<AudioClip>)val).Probability = 1f;
					list.Add(val);
				}
			}
		}

		private IEnumerator WavToAudioClipRoutine(string wavFile, List<AudioClip> clips)
		{
			string uri = new Uri(wavFile).AbsoluteUri;
			UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(uri, (AudioType)20);
			try
			{
				yield return req.SendWebRequest();
				AudioClip streamedClip = DownloadHandlerAudioClip.GetContent(req);
				((Object)streamedClip).name = Path.GetFileNameWithoutExtension(wavFile);
				streamedClip.LoadAudioData();
				clips.Add(streamedClip);
			}
			finally
			{
				((IDisposable)req)?.Dispose();
			}
		}

		private void ApplyClips(RandomAudioClipTable table, AudioCollection audioCollection)
		{
			List<ProbabilityAudioClip> list = new List<ProbabilityAudioClip>();
			foreach (string folder in audioCollection.folders)
			{
				list.AddRange(folderClips[folder]);
			}
			if (list.Count != 0)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Applied mod to " + ((Object)table).name));
			}
			if (audioCollection.includeVanillaClips || list.Count == 0)
			{
				list.AddRange(audioCollection.vanillaClips);
			}
			table.clips = list.ToArray();
		}

		private void LoadAudioTable(RandomAudioClipTable table)
		{
			if (audioCollections != null && folderClips != null && audioCollections.TryGetValue(((Object)table).name, out var value))
			{
				value.vanillaClips = (ProbabilityAudioClip[])table.clips.Clone();
				ApplyClips(table, value);
			}
		}

		private void LogAudio(string message)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)message);
		}

		private bool RetrieveAudioCollectionsData()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			JsonSerializerSettings val = new JsonSerializerSettings
			{
				Formatting = (Formatting)1,
				NullValueHandling = (NullValueHandling)1,
				DefaultValueHandling = (DefaultValueHandling)1
			};
			Dictionary<string, List<string>> dictionary;
			if (File.Exists(audioCollectionsPath))
			{
				try
				{
					dictionary = JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(File.ReadAllText(audioCollectionsPath), val);
				}
				catch (Exception ex)
				{
					Debug.LogError((object)("Hornet Audio Editor: " + ex.Message));
					return false;
				}
			}
			else
			{
				dictionary = new Dictionary<string, List<string>> { [""] = new List<string>(3) { "Taunt Hornet Voice", "Taunt Seriously Hornet Voice", "Hornet_poshanka" } };
				string contents = JsonConvert.SerializeObject((object)dictionary, val);
				File.WriteAllText(audioCollectionsPath, contents);
			}
			ApplyCollectionPresets(dictionary, val);
			ParseRawAudioCollectionsData(dictionary);
			return true;
		}

		private void ApplyCollectionPresets(Dictionary<string, List<string>> rawAudioCollectionsData, JsonSerializerSettings jsonSettings)
		{
			Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>();
			foreach (List<string> value2 in rawAudioCollectionsData.Values)
			{
				string[] array = value2.Where((string t) => t.EndsWith(".json")).ToArray();
				if (array.Length == 0)
				{
					continue;
				}
				string[] array2 = array;
				foreach (string text in array2)
				{
					try
					{
						if (dictionary.TryGetValue(text, out var value))
						{
							value2.AddRange(value);
							continue;
						}
						value = (dictionary[text] = JsonConvert.DeserializeObject<string[]>(File.ReadAllText(Path.Combine(collectionPresetsPath, text.TrimStart('+'))), jsonSettings));
						if (text.StartsWith('+'))
						{
							for (int j = 0; j < value.Length; j++)
							{
								if (!value[j].StartsWith('+'))
								{
									value[j] = "+" + value[j];
								}
							}
						}
						value2.AddRange(value);
						goto IL_013b;
					}
					catch (Exception ex)
					{
						Debug.LogError((object)("Hornet Audio Editor: " + ex.Message));
						goto IL_013b;
					}
					IL_013b:
					value2.Remove(text);
				}
			}
		}

		private void ParseRawAudioCollectionsData(Dictionary<string, List<string>> rawAudioCollectionsData)
		{
			folderClips = rawAudioCollectionsData.ToDictionary((KeyValuePair<string, List<string>> kvp) => kvp.Key, (KeyValuePair<string, List<string>> _) => new List<ProbabilityAudioClip>());
			audioCollections = new Dictionary<string, AudioCollection>();
			foreach (var (item, list2) in rawAudioCollectionsData)
			{
				foreach (string item2 in list2)
				{
					string key = item2.TrimStart('+');
					if (audioCollections.TryGetValue(key, out var value))
					{
						value.folders.Add(item);
						value.includeVanillaClips |= item2.StartsWith('+');
					}
					else
					{
						audioCollections[key] = new AudioCollection(new HashSet<string> { item }, item2.StartsWith('+'));
					}
				}
			}
		}
	}
}