Decompiled source of GunSounds v1.0.0

GunSounds.dll

Decompiled a day ago
using System;
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.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ComputerysModdingUtilities;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: StraftatMod(true)]
[assembly: AssemblyCompany("1ntrospective")]
[assembly: AssemblyCopyright("Copyright (c) 1ntrospective")]
[assembly: AssemblyTitle("GunSounds")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace GunSounds;

[BepInPlugin("1ntrospective.straftat.gunsounds", "GunSounds", "2.2.0")]
public class Plugin : BaseUnityPlugin
{
	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<float> Volume;

	internal static ConfigEntry<bool> LogDiscovery;

	internal static string PluginDir;

	internal static string SoundsDir;

	private void Awake()
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Expected O, but got Unknown
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Expected O, but got Unknown
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: Expected O, but got Unknown
		//IL_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the mod on or off.");
		Volume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Volume", 1f, new ConfigDescription("Volume of the custom sounds (1 = original file volume, 2 = twice as loud).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
		LogDiscovery = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogDiscovery", true, "Log each weapon the first time the game finds it.");
		PluginDir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
		SoundsDir = Path.Combine(PluginDir, "Sounds");
		Directory.CreateDirectory(SoundsDir);
		Registry.LoadCustomSounds();
		try
		{
			Harmony val = new Harmony("1ntrospective.straftat.gunsounds");
			MethodInfo methodInfo = AccessTools.Method(typeof(AudioSource), "PlayOneShot", new Type[1] { typeof(AudioClip) }, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(AudioSource), "PlayOneShot", new Type[2]
			{
				typeof(AudioClip),
				typeof(float)
			}, (Type[])null);
			val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Patches), "PrefixOne", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(Patches), "PrefixTwo", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			SceneManager.sceneLoaded += delegate
			{
				Registry.Scan();
			};
			Log.LogInfo((object)("GunSounds by 1ntrospective ready. Sounds folder: " + SoundsDir));
		}
		catch (Exception ex)
		{
			Log.LogError((object)("Could not hook AudioSource.PlayOneShot, mod disabled: " + ex));
		}
	}
}
internal static class Patches
{
	public static void PrefixOne(AudioSource __instance, ref AudioClip clip)
	{
		clip = Registry.Swap(__instance, clip);
	}

	public static void PrefixTwo(AudioSource __instance, ref AudioClip clip, ref float volumeScale)
	{
		clip = Registry.Swap(__instance, clip);
		if ((Object)(object)clip != (Object)null && Registry.IsCustom(clip))
		{
			volumeScale *= Plugin.Volume.Value;
		}
	}
}
internal static class Registry
{
	private class WInfo
	{
		public string[] names;

		public int fireClipId;
	}

	private class Entry
	{
		public string primary;

		public HashSet<string> aliases = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public int fireClipId;
	}

	private static readonly Dictionary<string, AudioClip> custom = new Dictionary<string, AudioClip>(StringComparer.OrdinalIgnoreCase);

	private static readonly HashSet<int> customIds = new HashSet<int>();

	private static readonly Dictionary<int, WInfo> infos = new Dictionary<int, WInfo>();

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

	private static readonly Dictionary<string, Entry> entries = new Dictionary<string, Entry>(StringComparer.OrdinalIgnoreCase);

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

	private static readonly Dictionary<int, AudioClip> pos = new Dictionary<int, AudioClip>();

	private static readonly Dictionary<int, float> neg = new Dictionary<int, float>();

	private static float lastScan = -100f;

	private static bool dirty;

	internal static bool IsCustom(AudioClip c)
	{
		return customIds.Contains(((Object)c).GetInstanceID());
	}

	internal static void LoadCustomSounds()
	{
		string[] source = new string[3] { ".wav", ".ogg", ".mp3" };
		string[] files = Directory.GetFiles(Plugin.SoundsDir);
		foreach (string text in files)
		{
			string text2 = Path.GetExtension(text).ToLowerInvariant();
			if (!source.Contains(text2))
			{
				continue;
			}
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text);
			try
			{
				AudioClip val = LoadClip(text, text2);
				if (!((Object)(object)val == (Object)null))
				{
					((Object)val).name = "custom_" + fileNameWithoutExtension;
					((Object)val).hideFlags = (HideFlags)61;
					custom[fileNameWithoutExtension] = val;
					customIds.Add(((Object)val).GetInstanceID());
					Plugin.Log.LogInfo((object)("Loaded: " + Path.GetFileName(text)));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not load " + text + ": " + ex.Message));
			}
		}
	}

	private static AudioClip LoadClip(string path, string ext)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Invalid comparison between Unknown and I4
		AudioType val = (AudioType)((ext == ".ogg") ? 14 : ((ext == ".mp3") ? 13 : 20));
		UnityWebRequest audioClip = UnityWebRequestMultimedia.GetAudioClip(new Uri(path).AbsoluteUri, val);
		try
		{
			UnityWebRequestAsyncOperation val2 = audioClip.SendWebRequest();
			while (!((AsyncOperation)val2).isDone)
			{
				Thread.Sleep(1);
			}
			if ((int)audioClip.result != 1)
			{
				Plugin.Log.LogError((object)("Error reading " + path + ": " + audioClip.error));
				return null;
			}
			return DownloadHandlerAudioClip.GetContent(audioClip);
		}
		finally
		{
			((IDisposable)audioClip)?.Dispose();
		}
	}

	private static string Clean(string n)
	{
		if (n == null)
		{
			return "";
		}
		n = n.Replace("(Clone)", "").Trim();
		char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
		foreach (char c in invalidFileNameChars)
		{
			n = n.Replace(c.ToString(), "");
		}
		return n;
	}

	private static WInfo GetInfo(Weapon w)
	{
		int instanceID = ((Object)w).GetInstanceID();
		if (infos.TryGetValue(instanceID, out var value))
		{
			return value;
		}
		if (w is MeleeWeapon || (Object)(object)w.fireClip == (Object)null)
		{
			infos[instanceID] = null;
			return null;
		}
		string text = "";
		try
		{
			if ((Object)(object)w.behaviour != (Object)null)
			{
				text = Clean(w.behaviour.weaponName);
			}
		}
		catch
		{
		}
		string text2 = Clean(((Object)((Component)w).gameObject).name);
		string text3 = ((text.Length > 0) ? text : text2);
		value = new WInfo();
		List<string> list = new List<string>();
		list.Add(text3);
		if (text2.Length > 0 && !string.Equals(text2, text3, StringComparison.OrdinalIgnoreCase))
		{
			list.Add(text2);
		}
		value.names = list.ToArray();
		value.fireClipId = ((Object)w.fireClip).GetInstanceID();
		infos[instanceID] = value;
		bool flag = false;
		if (!entries.TryGetValue(text3, out var value2))
		{
			value2 = new Entry
			{
				primary = text3,
				fireClipId = value.fireClipId
			};
			entries[text3] = value2;
			flag = true;
		}
		for (int i = 1; i < value.names.Length; i++)
		{
			value2.aliases.Add(value.names[i]);
		}
		if (!clipKeys.TryGetValue(value.fireClipId, out var value3))
		{
			value3 = new List<string>();
			clipKeys[value.fireClipId] = value3;
		}
		string[] names = value.names;
		foreach (string item in names)
		{
			if (!value3.Contains(item))
			{
				value3.Add(item);
			}
		}
		pos.Remove(value.fireClipId);
		neg.Remove(value.fireClipId);
		dirty = true;
		if (flag && Plugin.LogDiscovery.Value)
		{
			Plugin.Log.LogInfo((object)("[discovered] weapon '" + text3 + "'"));
		}
		return value;
	}

	internal static void Scan()
	{
		try
		{
			Weapon[] array = Resources.FindObjectsOfTypeAll<Weapon>();
			for (int i = 0; i < array.Length; i++)
			{
				GetInfo(array[i]);
			}
			WriteList();
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Scan error: " + ex));
		}
	}

	private static void WriteList()
	{
		if (!dirty)
		{
			return;
		}
		dirty = false;
		try
		{
			List<Entry> list = new List<Entry>();
			Dictionary<int, Entry> dictionary = new Dictionary<int, Entry>();
			foreach (Entry item in entries.Values.OrderBy<Entry, string>((Entry x) => x.primary, StringComparer.OrdinalIgnoreCase))
			{
				if (dictionary.TryGetValue(item.fireClipId, out var value))
				{
					value.aliases.Add(item.primary);
					foreach (string alias in item.aliases)
					{
						value.aliases.Add(alias);
					}
					continue;
				}
				Entry entry = new Entry
				{
					primary = item.primary,
					fireClipId = item.fireClipId
				};
				foreach (string alias2 in item.aliases)
				{
					entry.aliases.Add(alias2);
				}
				list.Add(entry);
				dictionary[item.fireClipId] = entry;
			}
			List<string> list2 = new List<string>();
			list2.Add("GunSounds - weapon list");
			list2.Add("=======================");
			list2.Add("To change a weapon's gun sound, put a .wav / .ogg / .mp3 file in the \"Sounds\" folder");
			list2.Add("named after the weapon (upper/lower case does not matter), e.g.  AK-K.wav");
			list2.Add("Restart the game after adding or changing files.");
			list2.Add("The list grows as the game loads weapons - visit a few maps to fill it.");
			list2.Add("");
			foreach (Entry item2 in list)
			{
				string text = item2.primary;
				if (item2.aliases.Count > 0)
				{
					text = text + "   (also: " + string.Join(", ", item2.aliases.ToArray()) + ")";
				}
				list2.Add(text);
			}
			File.WriteAllLines(Path.Combine(Plugin.PluginDir, "weapons.txt"), list2.ToArray());
		}
		catch
		{
		}
	}

	internal static AudioClip Swap(AudioSource src, AudioClip clip)
	{
		if ((Object)(object)clip == (Object)null || !Plugin.Enabled.Value)
		{
			return clip;
		}
		try
		{
			int instanceID = ((Object)clip).GetInstanceID();
			if (customIds.Contains(instanceID))
			{
				return clip;
			}
			AudioClip value3;
			if ((Object)(object)src != (Object)null)
			{
				int instanceID2 = ((Object)src).GetInstanceID();
				if (!sourceWeapon.TryGetValue(instanceID2, out var value))
				{
					Weapon componentInParent = ((Component)src).GetComponentInParent<Weapon>();
					value = 0;
					if ((Object)(object)componentInParent != (Object)null)
					{
						GetInfo(componentInParent);
						value = ((Object)componentInParent).GetInstanceID();
						WriteList();
					}
					sourceWeapon[instanceID2] = value;
				}
				if (value != 0)
				{
					if (infos.TryGetValue(value, out var value2) && value2 != null && value2.fireClipId == instanceID)
					{
						string[] names = value2.names;
						foreach (string key in names)
						{
							if (custom.TryGetValue(key, out value3))
							{
								return value3;
							}
						}
					}
					return clip;
				}
			}
			if (pos.TryGetValue(instanceID, out value3))
			{
				return value3;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (neg.TryGetValue(instanceID, out var value4) && realtimeSinceStartup - value4 < 5f)
			{
				return clip;
			}
			value3 = LookupByClip(instanceID);
			if ((Object)(object)value3 == (Object)null && realtimeSinceStartup - lastScan > 2f)
			{
				lastScan = realtimeSinceStartup;
				Scan();
				value3 = LookupByClip(instanceID);
			}
			if ((Object)(object)value3 != (Object)null)
			{
				pos[instanceID] = value3;
				return value3;
			}
			neg[instanceID] = realtimeSinceStartup;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Swap error: " + ex));
		}
		return clip;
	}

	private static AudioClip LookupByClip(int clipId)
	{
		if (clipKeys.TryGetValue(clipId, out var value))
		{
			foreach (string item in value)
			{
				if (custom.TryGetValue(item, out var value2))
				{
					return value2;
				}
			}
		}
		return null;
	}
}