Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CreepySoundPack v2.3.3
CustomSounds.dll
Decompiled a year agousing 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.Text; using BepInEx; using BepInEx.Logging; using CustomSounds.Networking; using CustomSounds.Patches; using HarmonyLib; using LCSoundTool; using TMPro; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("CustomSounds")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("CustomSounds")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("9e086160-a7fd-4721-ba09-3e8534cb7011")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] internal class <Module> { static <Module>() { } } namespace CustomSounds { [BepInPlugin("CustomSounds", "Custom Sounds", "2.3.2")] public class Plugin : BaseUnityPlugin { public struct SoundData { public string SoundName; public float? RandomPercentage; public string CustomName; public string FilePath; public string FileExtension; public string PackName; public string AudioSource; public string DirectoryPath; public string RelativeDirectoryPath; } public class FolderTree { public Dictionary<string, FolderTree> SubFolders { get; set; } public List<SoundData> Files { get; set; } public FolderTree() { SubFolders = new Dictionary<string, FolderTree>(); Files = new List<SoundData>(); } } public static class SoundDataProcessor { public static FolderTree BuildFolderTree(List<SoundData> soundDataList) { FolderTree folderTree = new FolderTree(); foreach (SoundData soundData in soundDataList) { string relativeDirectoryPath = soundData.RelativeDirectoryPath; string[] array = relativeDirectoryPath.Split(Path.DirectorySeparatorChar, '\u0001'); FolderTree folderTree2 = folderTree; string[] array2 = array; foreach (string key in array2) { if (!folderTree2.SubFolders.ContainsKey(key)) { folderTree2.SubFolders[key] = new FolderTree(); } folderTree2 = folderTree2.SubFolders[key]; } folderTree2.Files.Add(soundData); } return folderTree; } public static string DisplayTree(bool isListing, FolderTree tree, int indent = 0, bool isRoot = true, int soundCount = 0) { StringBuilder stringBuilder = new StringBuilder(); if (isRoot) { soundCount = CountSounds(tree); string text = (isListing ? "Listing all currently loaded custom sounds:" : "Customsounds reloaded."); stringBuilder.AppendLine(text + $" ({soundCount} sounds)"); } foreach (KeyValuePair<string, FolderTree> subFolder in tree.SubFolders) { if (isRoot) { stringBuilder.Append("\n"); } string text2 = subFolder.Key; if (text2.EndsWith("-AS")) { text2 = subFolder.Key.Substring(0, subFolder.Key.Length - 3) + " (AudioSource)"; } stringBuilder.AppendLine(new string(' ', indent * 2) + ((indent > 0) ? "∟ " : "") + text2 + " :"); stringBuilder.Append(DisplayTree(isListing, subFolder.Value, indent + 1, isRoot: false)); } foreach (SoundData file in tree.Files) { string text3 = ((!file.RandomPercentage.HasValue) ? "" : $" (Random: {file.RandomPercentage * 100f}%)"); string text4 = ((file.CustomName == "") ? "" : (" [" + file.CustomName + "]")); stringBuilder.AppendLine(new string(' ', indent * 2) + "- " + file.SoundName + text3 + text4 + " [" + file.FileExtension.ToUpper() + "]"); } return stringBuilder.ToString(); } private static int CountSounds(FolderTree tree) { int num = tree.Files.Count; foreach (KeyValuePair<string, FolderTree> subFolder in tree.SubFolders) { num += CountSounds(subFolder.Value); } return num; } } private const string PLUGIN_GUID = "CustomSounds"; private const string PLUGIN_NAME = "Custom Sounds"; private const string PLUGIN_VERSION = "2.3.2"; public static Plugin Instance; internal ManualLogSource logger; private Harmony harmony; public HashSet<string> currentSounds = new HashSet<string>(); public HashSet<string> oldSounds = new HashSet<string>(); public HashSet<string> modifiedSounds = new HashSet<string>(); public Dictionary<string, string> soundHashes = new Dictionary<string, string>(); public Dictionary<string, string> soundPacks = new Dictionary<string, string>(); public static bool hasAcceptedSync = false; public static List<SoundData> soundDataList = new List<SoundData>(); public static bool Initialized { get; private set; } private void Awake() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown if (!((Object)(object)Instance == (Object)null)) { return; } Instance = this; logger = Logger.CreateLogSource("CustomSounds"); harmony = new Harmony("CustomSounds"); harmony.PatchAll(typeof(TerminalParsePlayerSentencePatch)); modifiedSounds = new HashSet<string>(); string customSoundsFolderPath = GetCustomSoundsFolderPath(); if (!Directory.Exists(customSoundsFolderPath)) { logger.LogInfo((object)"\"CustomSounds\" folder not found. Creating it now."); string path = Path.Combine(customSoundsFolderPath, "YourOwnSoundPack"); Directory.CreateDirectory(path); string contents = "If you're interested in creating your own sound pack, please refer to the 'For Sound Packs Creator' section on the CustomSounds Thunderstore page. If you simply wish to replace a few sounds on your own, you can drop the desired sounds into the 'YourOwnSoundPack' folder."; File.WriteAllText(Path.Combine(customSoundsFolderPath, "READ-ME-PLEASE.txt"), contents); } string path2 = Path.Combine(Paths.BepInExConfigPath); try { List<string> list = File.ReadAllLines(path2).ToList(); int num = list.FindIndex((string line) => line.StartsWith("HideManagerGameObject")); if (num != -1 && list[num].Contains("false")) { logger.LogInfo((object)"\"HideManagerGameObject\" value not correctly set. Fixing it now."); list[num] = "HideManagerGameObject = true"; File.WriteAllLines(path2, list); harmony.PatchAll(typeof(MenuPatcher)); } else if (num != -1) { logger.LogInfo((object)"\"HideManagerGameObject\" is correctly set to true."); } } catch (Exception ex) { logger.LogError((object)("Error modifying config file: " + ex.Message)); } Type[] types = Assembly.GetExecutingAssembly().GetTypes(); Type[] array = types; foreach (Type type in array) { MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic); MethodInfo[] array2 = methods; foreach (MethodInfo methodInfo in array2) { object[] customAttributes = methodInfo.GetCustomAttributes(typeof(RuntimeInitializeOnLoadMethodAttribute), inherit: false); if (customAttributes.Length != 0) { methodInfo.Invoke(null, null); } } } logger.LogInfo((object)"Plugin CustomSounds is loaded!"); } internal void Start() { Initialize(); } internal void OnDestroy() { Initialize(); } internal void Initialize() { if (!Initialized) { Initialized = true; ReloadSounds(); } } private void OnApplicationQuit() { } public GameObject LoadNetworkPrefabFromEmbeddedResource() { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string name = "CustomSounds.Bundle.audionetworkhandler"; using Stream stream = executingAssembly.GetManifestResourceStream(name); if (stream == null) { Debug.LogError((object)"Asset bundle not found in embedded resources."); return null; } byte[] array = new byte[stream.Length]; stream.Read(array, 0, array.Length); AssetBundle val = AssetBundle.LoadFromMemory(array); if ((Object)(object)val == (Object)null) { Debug.LogError((object)"Failed to load AssetBundle from memory."); return null; } return val.LoadAsset<GameObject>("audionetworkhandler"); } public string GetCustomSoundsFolderPath() { return Path.Combine(Paths.PluginPath, "CustomSounds"); } public void RevertSounds() { if (currentSounds == null || currentSounds.Count == 0) { logger.LogInfo((object)"No sounds to revert."); return; } HashSet<string> hashSet = new HashSet<string>(); foreach (string currentSound in currentSounds) { string text = currentSound; if (currentSound.Contains("-")) { text = currentSound.Substring(0, currentSound.IndexOf("-")); } if (!hashSet.Contains(text)) { logger.LogInfo((object)(text + " restored.")); SoundTool.RestoreAudioClip(text); hashSet.Add(text); } } logger.LogInfo((object)"Original game sounds restored."); } public void ReloadSounds() { oldSounds = new HashSet<string>(currentSounds); currentSounds.Clear(); modifiedSounds.Clear(); soundDataList.Clear(); string directoryName = Path.GetDirectoryName(Paths.PluginPath); ProcessDirectory(directoryName); } private string GetRelativePathToCustomSounds(string filePath) { Debug.Log((object)("FilePath: " + filePath)); string[] array = filePath.Split(new char[1] { Path.DirectorySeparatorChar }); int num = Array.IndexOf(array, "CustomSounds"); if (num == -1 || num == array.Length - 1) { return ""; } string[] paths = array.Skip(num + 1).ToArray(); return Path.Combine(paths); } private void ProcessDirectory(string directoryPath) { string[] directories = Directory.GetDirectories(directoryPath, "*", SearchOption.AllDirectories); foreach (string text in directories) { string fileName = Path.GetFileName(text); ProcessSoundFiles(text, fileName); } } private void ProcessSoundFiles(string directoryPath, string packName) { string[] array = new string[3] { "*.wav", "*.ogg", "*.mp3" }; string[] array2 = array; foreach (string searchPattern in array2) { string[] files = Directory.GetFiles(directoryPath, searchPattern); foreach (string text in files) { if (text.Contains("CustomSounds")) { ProcessSingleFile(text, packName); } } } } private void ProcessSingleFile(string file, string packName) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); (string soundName, float? percentage, string customName) tuple = ParseSoundFileName(fileNameWithoutExtension); string item = tuple.soundName; float? item2 = tuple.percentage; string item3 = tuple.customName; string fileExtension = Path.GetExtension(file).TrimStart(new char[1] { '.' }).ToLower(); string relativePathToCustomSounds = GetRelativePathToCustomSounds(file); string fileName = Path.GetFileName(Path.GetDirectoryName(file)); string text = (fileName.EndsWith("-AS") ? fileName.Substring(0, fileName.Length - 3) : null); SoundData soundData = default(SoundData); soundData.SoundName = item; soundData.RandomPercentage = item2; soundData.CustomName = item3; soundData.FilePath = file; soundData.FileExtension = fileExtension; soundData.PackName = packName; soundData.AudioSource = text; soundData.DirectoryPath = Path.GetDirectoryName(file); soundData.RelativeDirectoryPath = Path.GetDirectoryName(relativePathToCustomSounds); SoundData item4 = soundData; soundDataList.Add(item4); string text2 = "Sound replaced: " + item; if (item4.RandomPercentage.HasValue) { text2 += $" (Percentage = {item4.RandomPercentage.Value * 100f}%)"; } if (!string.IsNullOrEmpty(item4.CustomName)) { text2 = text2 + " (Custom Name = " + item4.CustomName + ")"; } text2 = text2 + " (File Extension = " + item4.FileExtension + ")"; Debug.Log((object)text2); AudioClip audioClip = SoundTool.GetAudioClip(Path.GetDirectoryName(file), file); ((Object)audioClip).name = fileNameWithoutExtension; currentSounds.Add(item4.SoundName); if (item4.RandomPercentage.HasValue) { if (item4.AudioSource != null) { SoundTool.ReplaceAudioClip(item4.SoundName, audioClip, item4.RandomPercentage.Value, item4.AudioSource); } else { SoundTool.ReplaceAudioClip(item4.SoundName, audioClip, item4.RandomPercentage.Value); } } else if (text != null) { SoundTool.ReplaceAudioClip(item4.SoundName, audioClip, item4.AudioSource); } else { SoundTool.ReplaceAudioClip(item4.SoundName, audioClip); } } private (string soundName, float? percentage, string customName) ParseSoundFileName(string fullSoundName) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullSoundName); string[] array = fileNameWithoutExtension.Split(new char[1] { '-' }); string s = array[^1]; if (int.TryParse(s, out var result)) { string item = array[0]; string item2 = string.Join(" ", array.Skip(1).Take(array.Length - 2)); float value = (float)result / 100f; return (item, value, item2); } return (array[0], null, string.Join(" ", array.Skip(1))); } } } namespace CustomSounds.Patches { [HarmonyPatch] public class NetworkObjectManager { private static GameObject networkPrefab; private static GameObject networkHandlerHost; [HarmonyPatch(typeof(GameNetworkManager), "Start")] [HarmonyPrefix] public static void Init() { if (!((Object)(object)networkPrefab != (Object)null)) { networkPrefab = Plugin.Instance.LoadNetworkPrefabFromEmbeddedResource(); networkPrefab.AddComponent<AudioNetworkHandler>(); NetworkManager.Singleton.AddNetworkPrefab(networkPrefab); Plugin.Instance.logger.LogInfo((object)"Created AudioNetworkHandler prefab"); } } [HarmonyPostfix] [HarmonyPatch(typeof(StartOfRound), "Awake")] private static void SpawnNetworkHandler() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) try { if (NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsServer) { Plugin.Instance.logger.LogInfo((object)"Spawning network handler"); networkHandlerHost = Object.Instantiate<GameObject>(networkPrefab, Vector3.zero, Quaternion.identity); if (networkHandlerHost.GetComponent<NetworkObject>().IsSpawned) { Debug.Log((object)"NetworkObject is spawned and active."); } else { Debug.Log((object)"Failed to spawn NetworkObject."); } networkHandlerHost.GetComponent<NetworkObject>().Spawn(true); if ((Object)(object)AudioNetworkHandler.Instance != (Object)null) { Debug.Log((object)"Successfully accessed AudioNetworkHandler instance."); } else { Debug.Log((object)"AudioNetworkHandler instance is null."); } } } catch { Plugin.Instance.logger.LogError((object)"Failed to spawned network handler"); } } [HarmonyPostfix] [HarmonyPatch(typeof(GameNetworkManager), "StartDisconnect")] private static void DestroyNetworkHandler() { try { if (NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsServer) { Plugin.Instance.logger.LogInfo((object)"Destroying network handler"); Object.Destroy((Object)(object)networkHandlerHost); networkHandlerHost = null; } } catch { Plugin.Instance.logger.LogError((object)"Failed to destroy network handler"); } } } [HarmonyPatch(typeof(MenuManager))] internal class MenuPatcher { [HarmonyPatch("Start")] [HarmonyPostfix] public static void StartPostFix(MenuManager __instance) { ((MonoBehaviour)__instance).StartCoroutine(DelayedMainMenuModification()); } private static IEnumerator DelayedMainMenuModification() { yield return (object)new WaitForSeconds(0f); ChangeHostButtonFromMainMenu(); } private static void ChangeHostButtonFromMainMenu() { Debug.Log((object)"Attempting to add a description to the Host button..."); GameObject val = GameObject.Find("MenuContainer"); Transform val2 = ((val != null) ? val.transform.Find("MainButtons") : null); object obj; if (val2 == null) { obj = null; } else { Transform obj2 = val2.Find("HostButton"); obj = ((obj2 != null) ? ((Component)obj2).gameObject : null); } GameObject val3 = (GameObject)obj; if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null) && !((Object)(object)val3 == (Object)null)) { string text = ((TMP_Text)val3.GetComponentInChildren<TextMeshProUGUI>()).text; ((TMP_Text)val3.GetComponentInChildren<TextMeshProUGUI>()).text = "<line-height=0.28em>" + text + "\n<size=8><color=#B0B0B0> Please restart the game for <color=#E6B800>CustomSounds</color> to work properly!</color></size></line-height>"; } } } [HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")] public static class TerminalParsePlayerSentencePatch { public static bool Prefix(Terminal __instance, ref TerminalNode __result) { string[] array = __instance.screenText.text.Split(new char[1] { '\n' }); if (array.Length == 0) { return true; } string[] array2 = array.Last().Trim().ToLower() .Split(new char[1] { ' ' }); if (array2.Length == 0 || (array2[0] != "customsounds" && array2[0] != "cs")) { return true; } Plugin.Instance.logger.LogInfo((object)("Received terminal command: " + string.Join(" ", array2))); if (array2.Length > 1 && (array2[0] == "customsounds" || array2[0] == "cs")) { switch (array2[1]) { case "reload": case "rl": Plugin.Instance.RevertSounds(); Plugin.Instance.ReloadSounds(); __result = CreateTerminalNode(Plugin.SoundDataProcessor.DisplayTree(isListing: false, Plugin.SoundDataProcessor.BuildFolderTree(Plugin.soundDataList))); return false; case "revert": case "rv": Plugin.Instance.RevertSounds(); __result = CreateTerminalNode("Game sounds reverted to original.\n\n"); return false; case "list": case "l": __result = CreateTerminalNode(Plugin.SoundDataProcessor.DisplayTree(isListing: true, Plugin.SoundDataProcessor.BuildFolderTree(Plugin.soundDataList))); return false; case "help": case "h": if (NetworkManager.Singleton.IsHost) { __result = CreateTerminalNode("CustomSounds commands \n(Can also be used with 'CS' as an alias).\n\n>CUSTOMSOUNDS LIST/L\nTo display all currently loaded sounds\n\n>CUSTOMSOUNDS RELOAD/RL\nTo reload and apply sounds from the 'CustomSounds' folder and its subfolders.\n\n>CUSTOMSOUNDS REVERT/RV\nTo unload all custom sounds and restore original game sounds\n\n"); } else { __result = CreateTerminalNode("CustomSounds commands \n(Can also be used with 'CS' as an alias).\n\n>CUSTOMSOUNDS LIST/L\nTo display all currently loaded sounds\n\n>CUSTOMSOUNDS RELOAD/RL\nTo reload and apply sounds from the 'CustomSounds' folder and its subfolders.\n\n>CUSTOMSOUNDS REVERT/RV\nTo unload all custom sounds and restore original game sounds\n\n"); } return false; case "sync": case "s": __result = CreateTerminalNode("/!\\ ERROR /!\\ \nThis command is not supported in this version of CustomSounds\n\n"); return false; case "unsync": case "u": __result = CreateTerminalNode("/!\\ ERROR /!\\ \nThis command is not supported in this version of CustomSounds\n\n"); return false; case "fu": case "force-unsync": __result = CreateTerminalNode("/!\\ ERROR /!\\ \nThis command is not supported in this version of CustomSounds\n\n"); return false; default: __result = CreateTerminalNode("Unknown customsounds command.\n\n"); return false; } } return true; } private static TerminalNode CreateTerminalNode(string message) { TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>(); val.displayText = message; val.clearPreviousText = true; return val; } } } namespace CustomSounds.Networking { public class AudioNetworkHandler : NetworkBehaviour { public static AudioNetworkHandler Instance { get; private set; } protected override void __initializeVariables() { ((NetworkBehaviour)this).__initializeVariables(); } protected internal override string __getTypeName() { return "AudioNetworkHandler"; } } }