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.1.0")]
[assembly: AssemblyInformationalVersion("1.2.1+910066f8bfcc92b17f3c026b4aff96fd4dd9f6fc")]
[assembly: AssemblyProduct("HornetAudioEditor")]
[assembly: AssemblyTitle("HornetAudioEditor")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.1.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.1")]
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 Start_Patch
{
[HarmonyPostfix]
private static void Start_Postfix()
{
((MonoBehaviour)Instance).StartCoroutine(Instance.RefreshAudioCollectionsRoutine());
}
}
[HarmonyPatch(typeof(RandomAudioClipTable), "CanPlay")]
private class AudioLog_Patch
{
[HarmonyPostfix]
private static void PlayAudio_Postfix(RandomAudioClipTable __instance, bool __result)
{
if (__result)
{
Instance.TryLogAudio(((Object)__instance).name);
}
}
}
private Dictionary<string, List<ProbabilityAudioClip>> folderClips;
private Dictionary<string, AudioCollection> audioCollections;
private string clipsPath;
private string audioCollectionsPath;
private string collectionPresetsPath;
private static JsonSerializerSettings jsonSettings = new JsonSerializerSettings
{
Formatting = (Formatting)1,
NullValueHandling = (NullValueHandling)1,
DefaultValueHandling = (DefaultValueHandling)1
};
private Harmony harmony = new Harmony("alphalul.HornetAudioEditor");
private ConfigEntry<bool> configModEnabled;
private ConfigEntry<bool> configRefreshOnSaveQuit;
private ConfigEntry<bool> configLogAudio;
private ConfigEntry<float> configLogSpamCooldown;
private HashSet<string> recentLogs = new HashSet<string>();
private Queue<(string log, float time)> logBufferQueue = new Queue<(string, float)>();
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.1";
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 modification of audio.");
configRefreshOnSaveQuit = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RefreshOnSaveQuit", true, "Whether to reapply audioCollections.json upon returning to the title screen.");
configLogAudio = ((BaseUnityPlugin)this).Config.Bind<bool>("Logging", "LogAudio", true, "Whether to log the name of a RandomAudioClipTable when it plays a sound. Useful for finding table names.");
configLogSpamCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Logging", "LogSpamCooldown", 0.2f, "How many seconds to wait until logging the same RandomAudioClipTable again. Helps reduce console spam.");
if (configLogAudio.Value)
{
harmony.PatchAll(typeof(AudioLog_Patch));
}
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(AudioTableOnEnable_Patch));
if (configRefreshOnSaveQuit.Value)
{
harmony.PatchAll(typeof(Start_Patch));
}
}
}
private IEnumerator RefreshAudioCollectionsRoutine()
{
RandomAudioClipTable[] loadedAudioTables = Resources.FindObjectsOfTypeAll<RandomAudioClipTable>();
ResetPersistentTables(loadedAudioTables);
if (!RetrieveAudioCollectionsData())
{
((BaseUnityPlugin)this).Logger.LogError((object)"Something is wrong with 'audioCollections.json', unable to initialize HornetAudioEditor mod.");
yield break;
}
foreach (string folder in folderClips.Keys)
{
List<AudioClip> streamedFolderClips = new List<AudioClip>();
string folderPath = Path.Combine(clipsPath, folder);
if (!Directory.Exists(folderPath))
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Folder " + folder + " doesn't exist"));
continue;
}
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);
}
}
RandomAudioClipTable[] array2 = loadedAudioTables;
foreach (RandomAudioClipTable table in array2)
{
LoadAudioTable(table);
}
}
private void ResetPersistentTables(RandomAudioClipTable[] loadedAudioTables)
{
if (audioCollections == null || folderClips == null)
{
return;
}
foreach (RandomAudioClipTable val in loadedAudioTables)
{
if (audioCollections.TryGetValue(((Object)val).name, out var value))
{
val.clips = value.vanillaClips;
}
}
}
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 LoadAudioTable(RandomAudioClipTable table)
{
if (audioCollections == null || folderClips == null || !audioCollections.TryGetValue(((Object)table).name, out var value))
{
return;
}
value.vanillaClips = (ProbabilityAudioClip[])table.clips.Clone();
List<ProbabilityAudioClip> list = new List<ProbabilityAudioClip>();
foreach (string folder in value.folders)
{
list.AddRange(folderClips[folder]);
}
if (list.Count != 0)
{
if (value.includeVanillaClips)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)$"Applied {list.Count} modded clip(s) and {table.clips.Length} vanilla clip(s) to {((Object)table).name}");
list.AddRange(table.clips);
}
else
{
((BaseUnityPlugin)this).Logger.LogInfo((object)$"Applied {list.Count} modded clip(s) to {((Object)table).name}");
}
table.clips = list.ToArray();
}
}
private bool RetrieveAudioCollectionsData()
{
Dictionary<string, List<string>> dictionary;
if (File.Exists(audioCollectionsPath))
{
try
{
dictionary = JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(File.ReadAllText(audioCollectionsPath), jsonSettings);
}
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, jsonSettings);
File.WriteAllText(audioCollectionsPath, contents);
}
ApplyCollectionPresets(dictionary);
ParseRawAudioCollectionsData(dictionary);
return true;
}
private void ApplyCollectionPresets(Dictionary<string, List<string>> rawAudioCollectionsData)
{
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();
foreach (string text in array)
{
try
{
bool flag = text.StartsWith('+');
string text2 = text.TrimStart('+');
if (!dictionary.TryGetValue(text2, out var value))
{
value = (dictionary[text2] = JsonConvert.DeserializeObject<string[]>(File.ReadAllText(Path.Combine(collectionPresetsPath, text2)), jsonSettings));
}
if (flag)
{
for (int j = 0; j < value.Length; j++)
{
if (!value[j].StartsWith('+'))
{
value[j] = "+" + value[j];
}
}
}
value2.AddRange(value);
}
catch (Exception ex)
{
Debug.LogError((object)("Hornet Audio Editor: " + ex.Message));
}
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('+'));
}
}
}
}
private void Update()
{
while (logBufferQueue.Count > 0 && Time.unscaledTime - logBufferQueue.Peek().time > configLogSpamCooldown.Value)
{
recentLogs.Remove(logBufferQueue.Dequeue().log);
}
}
private void TryLogAudio(string log)
{
if (!recentLogs.Contains(log))
{
((BaseUnityPlugin)this).Logger.LogInfo((object)log);
recentLogs.Add(log);
logBufferQueue.Enqueue((log, Time.unscaledTime));
}
}
}
}