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.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BingBongVoiceLineAPI.Config;
using BingBongVoiceLineAPI.Helpers;
using BingBongVoiceLineAPI.Patches;
using HarmonyLib;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Networking;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("CustomBingBongResponseFramework")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CustomBingBongResponseFramework")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("54d0c49f-026d-49fa-bb01-9b30425dc9b9")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace BingBongVoiceLineAPI
{
[BepInPlugin("MrBytesized.PEAK.BingBongVoiceLineAPI", "Bing Bong Voice Line API", "1.0.0")]
public class BingBongVoiceLineAPI : BaseUnityPlugin
{
private const string mod_guid = "MrBytesized.PEAK.BingBongVoiceLineAPI";
private const string mod_name = "Bing Bong Voice Line API";
private const string mod_version = "1.0.0";
private readonly Harmony harmony = new Harmony("MrBytesized.PEAK.BingBongVoiceLineAPI");
internal static ManualLogSource Log;
private (ConfigEntry<bool> configEntry, Action enablePatch, Action disablePatch, string description)[] _patchArray;
public static BingBongVoiceLineAPI Instance { get; private set; }
private void Awake()
{
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Expected O, but got Unknown
if ((Object)(object)Instance == (Object)null)
{
Instance = this;
}
Configuration.Init(((BaseUnityPlugin)this).Config);
Log = Logger.CreateLogSource("MrBytesized.PEAK.BingBongVoiceLineAPI");
Log.LogInfo((object)"Bing Bong Voice Line API has been activated");
if ((Object)(object)Object.FindFirstObjectByType<CustomSoundManager>() == (Object)null)
{
GameObject val = new GameObject("BingBongVoiceLineAPI_AudioManager");
val.AddComponent<CustomSoundManager>();
Object.DontDestroyOnLoad((Object)val);
}
_patchArray = new(ConfigEntry<bool>, Action, Action, string)[1] { (Configuration.EnableMod, delegate
{
harmony.PatchAll(typeof(GameObjectPatch));
harmony.PatchAll(typeof(UnityObjectPatch));
}, delegate
{
harmony.UnpatchSelf();
}, "Bing Bong Voice Line API") };
(ConfigEntry<bool>, Action, Action, string)[] patchArray = _patchArray;
for (int i = 0; i < patchArray.Length; i++)
{
var (configEntry, enablePatch, disablePatch, description) = patchArray[i];
UpdatePatchFromConfig(configEntry, enablePatch, disablePatch, description);
configEntry.SettingChanged += delegate
{
UpdatePatchFromConfig(configEntry, enablePatch, disablePatch, description);
};
}
}
private void UpdatePatchFromConfig(ConfigEntry<bool> configEntry, Action enablePatch, Action disablePatch, string description)
{
if (configEntry.Value)
{
enablePatch();
Log.LogInfo((object)(description + " patch enabled."));
}
else
{
disablePatch();
Log.LogInfo((object)(description + " patch disabled."));
}
}
}
}
namespace BingBongVoiceLineAPI.Patches
{
internal static class BingBongVoiceLineReplacer
{
public static void TryHandleCreatedObject(Object created)
{
try
{
if (created == (Object)null)
{
BingBongVoiceLineAPI.Log.LogWarning((object)"[BingBongMouthPatch] TryHandleCreatedObject called with null created object.");
return;
}
GameObject val = null;
GameObject val2 = (GameObject)(object)((created is GameObject) ? created : null);
if (val2 != null)
{
val = val2;
}
else
{
Component val3 = (Component)(object)((created is Component) ? created : null);
if (val3 != null)
{
val = val3.gameObject;
}
}
if ((Object)(object)val == (Object)null)
{
BingBongVoiceLineAPI.Log.LogWarning((object)("[BingBongMouthPatch] Created object is not a GameObject or Component. Type=" + ((object)created).GetType().FullName));
return;
}
BingBong val4 = val.GetComponent<BingBong>() ?? val.GetComponentInChildren<BingBong>(true);
Action_AskBingBong val5 = null;
if ((Object)(object)val4 != (Object)null)
{
BingBongVoiceLineAPI.Log.LogInfo((object)("[BingBongMouthPatch] Found BingBong component on '" + ((Object)val).name + "' (type " + ((object)val4).GetType().FullName + ")."));
val5 = ((Component)val4).GetComponent<Action_AskBingBong>() ?? ((Component)val4).GetComponentInChildren<Action_AskBingBong>(true);
}
else
{
val5 = val.GetComponent<Action_AskBingBong>() ?? val.GetComponentInChildren<Action_AskBingBong>(true);
}
if ((Object)(object)val5 != (Object)null)
{
BingBongVoiceLineAPI.Log.LogInfo((object)("[BingBongMouthPatch] Applying custom responses to Action_AskBingBong on '" + ((Object)val).name + "' (instance type " + ((object)val5).GetType().FullName + ")."));
ApplyCustomResponses(val5);
}
else
{
BingBongVoiceLineAPI.Log.LogDebug((object)("[BingBongMouthPatch] No Action_AskBingBong found on '" + ((Object)val).name + "'."));
}
}
catch (Exception arg)
{
BingBongVoiceLineAPI.Log.LogError((object)$"[BingBongMouthPatch] TryHandleCreatedObject exception: {arg}");
}
}
public static void ApplyCustomResponses(Action_AskBingBong latest)
{
//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
//IL_0106: Unknown result type (might be due to invalid IL or missing references)
//IL_0107: Unknown result type (might be due to invalid IL or missing references)
//IL_0111: Expected O, but got Unknown
//IL_0111: Unknown result type (might be due to invalid IL or missing references)
//IL_011e: Expected O, but got Unknown
if ((Object)(object)latest == (Object)null)
{
return;
}
CustomSoundManager instance = CustomSoundManager.Instance;
if ((Object)(object)instance == (Object)null || !instance.IsLoaded)
{
BingBongVoiceLineAPI.Log.LogWarning((object)"CustomSoundManager not ready, skipping custom responses.");
return;
}
List<BingBongResponseConfigEntry> list = ConfigLoader.LoadConfig("response_sound_pack.json");
if (list == null || list.Count == 0)
{
BingBongVoiceLineAPI.Log.LogWarning((object)"No custom Bing Bong responses found in config.");
return;
}
List<BingBongResponse> list2 = new List<BingBongResponse>();
foreach (BingBongResponseConfigEntry item2 in list)
{
AudioClip clip = instance.GetClip(item2.file);
if ((Object)(object)clip == (Object)null)
{
BingBongVoiceLineAPI.Log.LogError((object)("AudioClip not found for " + item2.file));
continue;
}
SFX_Instance val = ScriptableObject.CreateInstance<SFX_Instance>();
val.clips = (AudioClip[])(object)new AudioClip[1] { clip };
string file = item2.file;
string text = (string.IsNullOrEmpty(item2.subtitle) ? "" : item2.subtitle);
LocalizationInjector.AddCustomSubtitle(file, text);
BingBongResponse item = new BingBongResponse
{
subtitleID = file,
sfx = val,
mouthCurve = new AnimationCurve(),
mouthCurveTime = 1f
};
list2.Add(item);
}
if (list2.Count > 0)
{
if (!Configuration.ReplaceBingBongResponses.Value)
{
BingBongResponse[] responses = latest.responses.Concat(list2).ToArray();
latest.responses = responses;
BingBongVoiceLineAPI.Log.LogInfo((object)$"Combined Bing Bong responses. Total responses: {latest.responses.Length}");
}
else
{
latest.responses = list2.ToArray();
BingBongVoiceLineAPI.Log.LogInfo((object)"Replaced Bing Bong responses with custom responses.");
}
}
}
}
[HarmonyPatch(typeof(GameObject))]
internal static class GameObjectPatch
{
[HarmonyPatch("AddComponent", new Type[] { typeof(Type) })]
internal static void Postfix(GameObject __instance, ref Component __result)
{
try
{
BingBongVoiceLineReplacer.TryHandleCreatedObject((Object)(((object)__result) ?? ((object)__instance)));
}
catch (Exception arg)
{
BingBongVoiceLineAPI.Log.LogError((object)$"[GameObjectPatch] AddComponent_Postfix exception: {arg}");
}
}
}
[HarmonyPatch(typeof(Object))]
internal static class UnityObjectPatch
{
[HarmonyPatch(typeof(Object), "Instantiate", new Type[] { typeof(Object) })]
private static void Postfix(Object __result)
{
try
{
BingBongVoiceLineReplacer.TryHandleCreatedObject(__result);
}
catch (Exception arg)
{
BingBongVoiceLineAPI.Log.LogError((object)$"[UnityObjectPatch] Instantiate_Postfix exception: {arg}");
}
}
}
}
namespace BingBongVoiceLineAPI.Helpers
{
public static class AudioLoader
{
[CompilerGenerated]
private sealed class <>c__DisplayClass6_0
{
public StringBuilder stdOutBuilder;
public StringBuilder stdErrBuilder;
internal void <ConvertAndLoadAudio>b__0(object sender, DataReceivedEventArgs args)
{
if (args.Data != null)
{
stdOutBuilder.AppendLine(args.Data);
}
}
internal void <ConvertAndLoadAudio>b__1(object sender, DataReceivedEventArgs args)
{
if (args.Data != null)
{
stdErrBuilder.AppendLine(args.Data);
}
}
}
[CompilerGenerated]
private sealed class <ConvertAndLoadAudio>d__6 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public string sourceFilePath;
public string ffmpegExePath;
public Action<AudioClip> onLoaded;
private <>c__DisplayClass6_0 <>8__1;
private string <outputWavPath>5__2;
private Process <process>5__3;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <ConvertAndLoadAudio>d__6(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>8__1 = null;
<outputWavPath>5__2 = null;
<process>5__3 = null;
<>1__state = -2;
}
private bool MoveNext()
{
string text;
string text2;
switch (<>1__state)
{
default:
return false;
case 0:
{
<>1__state = -1;
string fileHash = GetFileHash(sourceFilePath);
<outputWavPath>5__2 = Path.Combine(audioCachePath, fileHash + ".wav");
if (!File.Exists(<outputWavPath>5__2))
{
<>8__1 = new <>c__DisplayClass6_0();
BingBongVoiceLineAPI.Log.LogInfo((object)("Converting '" + sourceFilePath + "' to WAV..."));
string arguments = "-loglevel error -i \"" + sourceFilePath + "\" -vn -acodec pcm_s16le -ar 44100 -ac 2 \"" + <outputWavPath>5__2 + "\"";
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = ffmpegExePath,
Arguments = arguments,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
<>8__1.stdOutBuilder = new StringBuilder();
<>8__1.stdErrBuilder = new StringBuilder();
<process>5__3 = new Process
{
StartInfo = startInfo
};
<process>5__3.OutputDataReceived += delegate(object sender, DataReceivedEventArgs args)
{
if (args.Data != null)
{
<>8__1.stdOutBuilder.AppendLine(args.Data);
}
};
<process>5__3.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs args)
{
if (args.Data != null)
{
<>8__1.stdErrBuilder.AppendLine(args.Data);
}
};
bool flag = false;
try
{
<process>5__3.Start();
flag = true;
<process>5__3.BeginOutputReadLine();
<process>5__3.BeginErrorReadLine();
}
catch (Exception ex)
{
BingBongVoiceLineAPI.Log.LogError((object)("Exception starting FFmpeg process: " + ex.Message));
onLoaded?.Invoke(null);
return false;
}
if (flag)
{
goto IL_01da;
}
goto IL_02f8;
}
BingBongVoiceLineAPI.Log.LogInfo((object)("Found cached WAV file: " + <outputWavPath>5__2));
goto IL_0322;
}
case 1:
<>1__state = -1;
goto IL_01da;
case 2:
{
<>1__state = -1;
return false;
}
IL_02f8:
<>8__1 = null;
<process>5__3 = null;
goto IL_0322;
IL_0322:
<>2__current = LoadNativeAudio(<outputWavPath>5__2, (AudioType)20, onLoaded);
<>1__state = 2;
return true;
IL_01da:
if (!<process>5__3.HasExited)
{
<>2__current = null;
<>1__state = 1;
return true;
}
text = <>8__1.stdOutBuilder.ToString();
text2 = <>8__1.stdErrBuilder.ToString();
if (<process>5__3.ExitCode != 0)
{
BingBongVoiceLineAPI.Log.LogError((object)$"FFmpeg conversion failed with exit code {<process>5__3.ExitCode}.");
BingBongVoiceLineAPI.Log.LogError((object)("FFmpeg Error: " + text2));
if (!string.IsNullOrWhiteSpace(text))
{
BingBongVoiceLineAPI.Log.LogError((object)("FFmpeg Output: " + text));
}
if (File.Exists(<outputWavPath>5__2))
{
File.Delete(<outputWavPath>5__2);
}
onLoaded?.Invoke(null);
return false;
}
BingBongVoiceLineAPI.Log.LogInfo((object)("Successfully converted file. WAV saved at: " + <outputWavPath>5__2));
if (!string.IsNullOrWhiteSpace(text))
{
BingBongVoiceLineAPI.Log.LogDebug((object)("FFmpeg Output: " + text));
}
if (!string.IsNullOrWhiteSpace(text2))
{
BingBongVoiceLineAPI.Log.LogWarning((object)("FFmpeg Warnings: " + text2));
}
goto IL_02f8;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <LoadAudioClipFromPath>d__3 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public string filePath;
public Action<AudioClip> onLoaded;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <LoadAudioClipFromPath>d__3(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
switch (<>1__state)
{
default:
return false;
case 0:
{
<>1__state = -1;
if (!File.Exists(filePath))
{
BingBongVoiceLineAPI.Log.LogError((object)("Audio file not found at path: " + filePath));
onLoaded?.Invoke(null);
return false;
}
string text = Path.GetExtension(filePath).ToLowerInvariant();
AudioType? nativeAudioType = GetNativeAudioType(text);
if (nativeAudioType.HasValue)
{
<>2__current = LoadNativeAudio(filePath, nativeAudioType.Value, onLoaded);
<>1__state = 1;
return true;
}
if (IsConversionSupported(text))
{
string text2 = FindFFmpeg();
if (string.IsNullOrEmpty(text2))
{
BingBongVoiceLineAPI.Log.LogError((object)("Cannot load '" + text + "' file. ffmpeg.exe was not found."));
onLoaded?.Invoke(null);
return false;
}
<>2__current = ConvertAndLoadAudio(filePath, text2, onLoaded);
<>1__state = 2;
return true;
}
BingBongVoiceLineAPI.Log.LogWarning((object)("Unknown audio extension '" + text + "'. Defaulting to WAV load attempt."));
<>2__current = LoadNativeAudio(filePath, (AudioType)20, onLoaded);
<>1__state = 3;
return true;
}
case 1:
<>1__state = -1;
break;
case 2:
<>1__state = -1;
break;
case 3:
<>1__state = -1;
break;
}
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <LoadNativeAudio>d__4 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public string filePath;
public AudioType audioType;
public Action<AudioClip> onLoaded;
private UnityWebRequest <www>5__2;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <LoadNativeAudio>d__4(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
int num = <>1__state;
if (num == -3 || num == 1)
{
try
{
}
finally
{
<>m__Finally1();
}
}
<www>5__2 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_0091: Invalid comparison between Unknown and I4
try
{
switch (<>1__state)
{
default:
return false;
case 0:
{
<>1__state = -1;
BingBongVoiceLineAPI.Log.LogInfo((object)("Loading native audio from: " + filePath));
string text = "file://" + filePath;
<www>5__2 = UnityWebRequestMultimedia.GetAudioClip(text, audioType);
<>1__state = -3;
<>2__current = <www>5__2.SendWebRequest();
<>1__state = 1;
return true;
}
case 1:
<>1__state = -3;
if ((int)<www>5__2.result == 1)
{
AudioClip content = DownloadHandlerAudioClip.GetContent(<www>5__2);
onLoaded?.Invoke(content);
}
else
{
BingBongVoiceLineAPI.Log.LogError((object)("Failed to load audio: " + <www>5__2.error));
onLoaded?.Invoke(null);
}
<>m__Finally1();
<www>5__2 = null;
return false;
}
}
catch
{
//try-fault
((IDisposable)this).Dispose();
throw;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
private void <>m__Finally1()
{
<>1__state = -1;
if (<www>5__2 != null)
{
((IDisposable)<www>5__2).Dispose();
}
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private static string ffmpegPath = null;
private static bool ffmpegSearched = false;
private static string audioCachePath = Path.Combine(Paths.CachePath, "BingBongAudioCache");
[IteratorStateMachine(typeof(<LoadAudioClipFromPath>d__3))]
public static IEnumerator LoadAudioClipFromPath(string filePath, Action<AudioClip> onLoaded)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <LoadAudioClipFromPath>d__3(0)
{
filePath = filePath,
onLoaded = onLoaded
};
}
[IteratorStateMachine(typeof(<LoadNativeAudio>d__4))]
private static IEnumerator LoadNativeAudio(string filePath, AudioType audioType, Action<AudioClip> onLoaded)
{
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <LoadNativeAudio>d__4(0)
{
filePath = filePath,
audioType = audioType,
onLoaded = onLoaded
};
}
private static string FindFFmpeg()
{
if (ffmpegSearched)
{
return ffmpegPath;
}
ffmpegSearched = true;
try
{
string[] files = Directory.GetFiles(Paths.PluginPath, "ffmpeg.exe", SearchOption.AllDirectories);
if (files.Length == 0)
{
BingBongVoiceLineAPI.Log.LogWarning((object)"ffmpeg.exe not found anywhere in BepInEx plugin path. Non-native audio formats (e.g., .m4a) will not be loaded.");
return null;
}
ffmpegPath = files[0];
BingBongVoiceLineAPI.Log.LogInfo((object)("Found ffmpeg at: " + ffmpegPath));
if (files.Length > 1)
{
BingBongVoiceLineAPI.Log.LogWarning((object)$"Found {files.Length} instances of ffmpeg.exe. Using the first one found.");
}
Directory.CreateDirectory(audioCachePath);
return ffmpegPath;
}
catch (Exception ex)
{
BingBongVoiceLineAPI.Log.LogError((object)("Error while searching for ffmpeg.exe: " + ex.Message));
return null;
}
}
[IteratorStateMachine(typeof(<ConvertAndLoadAudio>d__6))]
private static IEnumerator ConvertAndLoadAudio(string sourceFilePath, string ffmpegExePath, Action<AudioClip> onLoaded)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <ConvertAndLoadAudio>d__6(0)
{
sourceFilePath = sourceFilePath,
ffmpegExePath = ffmpegExePath,
onLoaded = onLoaded
};
}
private static AudioType? GetNativeAudioType(string extension)
{
switch (extension.ToLowerInvariant())
{
case ".wav":
return (AudioType)20;
case ".mp3":
return (AudioType)13;
case ".ogg":
return (AudioType)14;
case ".aif":
case ".aiff":
return (AudioType)2;
case ".xm":
return (AudioType)21;
case ".mod":
return (AudioType)12;
case ".it":
return (AudioType)10;
case ".s3m":
return (AudioType)17;
default:
return null;
}
}
private static bool IsConversionSupported(string extension)
{
if (extension.ToLowerInvariant() == ".m4a")
{
return true;
}
return false;
}
private static string GetFileHash(string filePath)
{
using SHA256 sHA = SHA256.Create();
byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(filePath.ToLowerInvariant()));
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < array.Length; i++)
{
stringBuilder.Append(array[i].ToString("x2"));
}
return stringBuilder.ToString();
}
}
public static class ConfigLoader
{
public static List<BingBongResponseConfigEntry> LoadConfig(string configFileName)
{
List<BingBongResponseConfigEntry> list = new List<BingBongResponseConfigEntry>();
string[] files = Directory.GetFiles(Paths.PluginPath, configFileName, SearchOption.AllDirectories);
if (files.Length == 0)
{
BingBongVoiceLineAPI.Log.LogWarning((object)("No config files named '" + configFileName + "' found in '" + Paths.PluginPath + "'."));
return list;
}
string[] array = files;
foreach (string text in array)
{
try
{
BingBongResponseConfigFile bingBongResponseConfigFile = JsonConvert.DeserializeObject<BingBongResponseConfigFile>(File.ReadAllText(text));
if (bingBongResponseConfigFile?.entries != null)
{
string directoryName = Path.GetDirectoryName(text);
foreach (BingBongResponseConfigEntry entry in bingBongResponseConfigFile.entries)
{
entry.configDirectory = directoryName;
entry.modName = bingBongResponseConfigFile.name;
list.Add(entry);
}
BingBongVoiceLineAPI.Log.LogInfo((object)$"Loaded config for mod '{bingBongResponseConfigFile.name}' from with {bingBongResponseConfigFile.entries.Count} entries.");
}
else
{
BingBongVoiceLineAPI.Log.LogWarning((object)("Config file '" + text + "' is missing entries or mod name."));
}
}
catch (Exception ex)
{
BingBongVoiceLineAPI.Log.LogError((object)("Error loading config file '" + text + "': " + ex.Message));
}
}
list.Sort(delegate(BingBongResponseConfigEntry a, BingBongResponseConfigEntry b)
{
int num = string.Compare(a.modName, b.modName, StringComparison.Ordinal);
if (num != 0)
{
return num;
}
int num2 = string.Compare(a.file, b.file, StringComparison.Ordinal);
return (num2 != 0) ? num2 : string.Compare(a.subtitle, b.subtitle, StringComparison.Ordinal);
});
return list;
}
}
[Serializable]
public class BingBongResponseConfigFile
{
public string name;
public List<BingBongResponseConfigEntry> entries;
}
[Serializable]
public class BingBongResponseConfigEntry
{
public string file;
public string subtitle;
[NonSerialized]
public string configDirectory;
[NonSerialized]
public string modName;
}
public class CustomSoundManager : MonoBehaviour
{
[CompilerGenerated]
private sealed class <>c__DisplayClass11_0
{
public CustomSoundManager <>4__this;
public int filesProcessed;
}
[CompilerGenerated]
private sealed class <>c__DisplayClass11_1
{
public BingBongResponseConfigEntry currentEntry;
public <>c__DisplayClass11_0 CS$<>8__locals1;
internal void <LoadAllClips>b__0(AudioClip clip)
{
try
{
if ((Object)(object)clip != (Object)null)
{
CS$<>8__locals1.<>4__this._audioClips[currentEntry.file] = clip;
BingBongVoiceLineAPI.Log.LogInfo((object)("CustomSoundManager: Loaded " + currentEntry.file));
}
else
{
BingBongVoiceLineAPI.Log.LogError((object)("CustomSoundManager: Failed to load " + currentEntry.file));
}
}
catch (Exception ex)
{
BingBongVoiceLineAPI.Log.LogError((object)("CustomSoundManager: Error in callback for " + currentEntry.file + ": " + ex.Message));
}
finally
{
int filesProcessed = CS$<>8__locals1.filesProcessed;
CS$<>8__locals1.filesProcessed = filesProcessed + 1;
}
}
}
[CompilerGenerated]
private sealed class <LoadAllClips>d__11 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public CustomSoundManager <>4__this;
private <>c__DisplayClass11_0 <>8__1;
private int <totalFilesToLoad>5__2;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <LoadAllClips>d__11(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>8__1 = null;
<>1__state = -2;
}
private bool MoveNext()
{
int num = <>1__state;
CustomSoundManager customSoundManager = <>4__this;
switch (num)
{
default:
return false;
case 0:
{
<>1__state = -1;
<>8__1 = new <>c__DisplayClass11_0();
<>8__1.<>4__this = <>4__this;
List<BingBongResponseConfigEntry> list = ConfigLoader.LoadConfig("response_sound_pack.json");
if (list == null || list.Count == 0)
{
BingBongVoiceLineAPI.Log.LogWarning((object)"CustomSoundManager: No config entries found.");
customSoundManager._isLoaded = true;
return false;
}
<totalFilesToLoad>5__2 = list.Count;
<>8__1.filesProcessed = 0;
BingBongVoiceLineAPI.Log.LogInfo((object)$"CustomSoundManager: Starting to load {<totalFilesToLoad>5__2} audio clips...");
if (<totalFilesToLoad>5__2 == 0)
{
customSoundManager._isLoaded = true;
BingBongVoiceLineAPI.Log.LogInfo((object)"CustomSoundManager: No clips to load.");
return false;
}
foreach (BingBongResponseConfigEntry item in list)
{
<>c__DisplayClass11_1 CS$<>8__locals0 = new <>c__DisplayClass11_1
{
CS$<>8__locals1 = <>8__1,
currentEntry = item
};
string filePath = Path.Combine(CS$<>8__locals0.currentEntry.configDirectory, CS$<>8__locals0.currentEntry.file);
((MonoBehaviour)customSoundManager).StartCoroutine(AudioLoader.LoadAudioClipFromPath(filePath, delegate(AudioClip clip)
{
try
{
if ((Object)(object)clip != (Object)null)
{
CS$<>8__locals0.CS$<>8__locals1.<>4__this._audioClips[CS$<>8__locals0.currentEntry.file] = clip;
BingBongVoiceLineAPI.Log.LogInfo((object)("CustomSoundManager: Loaded " + CS$<>8__locals0.currentEntry.file));
}
else
{
BingBongVoiceLineAPI.Log.LogError((object)("CustomSoundManager: Failed to load " + CS$<>8__locals0.currentEntry.file));
}
}
catch (Exception ex)
{
BingBongVoiceLineAPI.Log.LogError((object)("CustomSoundManager: Error in callback for " + CS$<>8__locals0.currentEntry.file + ": " + ex.Message));
}
finally
{
int filesProcessed = CS$<>8__locals0.CS$<>8__locals1.filesProcessed;
CS$<>8__locals0.CS$<>8__locals1.filesProcessed = filesProcessed + 1;
}
}));
}
break;
}
case 1:
<>1__state = -1;
break;
}
if (<>8__1.filesProcessed < <totalFilesToLoad>5__2)
{
<>2__current = null;
<>1__state = 1;
return true;
}
customSoundManager._isLoaded = true;
BingBongVoiceLineAPI.Log.LogInfo((object)$"CustomSoundManager: Finished loading. {customSoundManager._audioClips.Count} / {<totalFilesToLoad>5__2} clips loaded.");
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private Dictionary<string, AudioClip> _audioClips = new Dictionary<string, AudioClip>();
private bool _isLoaded;
public static CustomSoundManager Instance { get; private set; }
public IReadOnlyDictionary<string, AudioClip> AudioClips => _audioClips;
public bool IsLoaded => _isLoaded;
private void Awake()
{
if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
{
Object.Destroy((Object)(object)((Component)this).gameObject);
return;
}
Instance = this;
Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
((MonoBehaviour)this).StartCoroutine(LoadAllClips());
}
[IteratorStateMachine(typeof(<LoadAllClips>d__11))]
private IEnumerator LoadAllClips()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <LoadAllClips>d__11(0)
{
<>4__this = this
};
}
public AudioClip GetClip(string fileName)
{
_audioClips.TryGetValue(fileName, out var value);
return value;
}
}
public static class LocalizationInjector
{
public static void AddCustomSubtitle(string id, string text)
{
LocalizedText.TryInitTables();
List<string> list = new List<string>();
for (int i = 0; i < 14; i++)
{
list.Add(text);
}
LocalizedText.mainTable[id.ToUpperInvariant()] = list;
BingBongVoiceLineAPI.Log.LogInfo((object)("Added custom subtitle '" + id + "' to localization table."));
}
}
}
namespace BingBongVoiceLineAPI.Config
{
internal class Configuration
{
public static ConfigEntry<bool> EnableMod;
public static ConfigEntry<bool> ReplaceBingBongResponses;
public static void Init(ConfigFile config)
{
EnableMod = config.Bind<bool>("General", "Bing Bong Voice Line API", true, "If enabled, the Bing Bong Voice Line API mod will be active.");
ReplaceBingBongResponses = config.Bind<bool>("General", "Replace Bing Bong Responses", true, "If enabled, custom responses will replace the default Bing Bong responses instead of being added to them.");
}
}
}