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;
}
}