Decompiled source of RedactedLootBug v1.0.2

BepInEx/plugins/RedactedLootBug/RedactedLootBug.dll

Decompiled 4 hours ago
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 BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyVersion("1.0.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 RedactedLootBug
{
	internal sealed class PcmWaveData
	{
		public readonly float[] Samples;

		public readonly int Channels;

		public readonly int SampleRate;

		public readonly int Frames;

		internal PcmWaveData(float[] samples, int channels, int sampleRate, int frames)
		{
			Samples = samples;
			Channels = channels;
			SampleRate = sampleRate;
			Frames = frames;
		}
	}
	internal static class PcmWaveReader
	{
		internal static PcmWaveData Read(byte[] bytes)
		{
			if (bytes == null)
			{
				throw new ArgumentNullException("bytes");
			}
			if (bytes.Length < 12 || !HasId(bytes, 0, "RIFF") || !HasId(bytes, 8, "WAVE"))
			{
				throw Invalid("Expected a RIFF/WAVE file.");
			}
			long num = 8L + (long)U32(bytes, 4);
			if (num < 12 || num > bytes.Length)
			{
				throw Invalid("The RIFF length exceeds the available file data.");
			}
			int num2 = 0;
			int sampleRate = 0;
			int num3 = 0;
			int num4 = -1;
			int num5 = 0;
			bool flag = false;
			long num6 = 12L;
			while (num6 < num)
			{
				if (num - num6 < 8)
				{
					throw Invalid("A WAV chunk header is truncated.");
				}
				int num7 = (int)num6;
				uint num8 = U32(bytes, num7 + 4);
				long num9 = num6 + 8;
				long num10 = num9 + num8;
				long num11 = num10 + (num8 & 1);
				if (num10 > num || num11 > num)
				{
					throw Invalid("A WAV chunk or its padding exceeds the RIFF bounds.");
				}
				if (HasId(bytes, num7, "fmt "))
				{
					if (flag)
					{
						throw Invalid("Multiple WAV format chunks are not supported.");
					}
					if (num8 < 16)
					{
						throw Invalid("The WAV format chunk is truncated.");
					}
					int num12 = (int)num9;
					int num13 = U16(bytes, num12);
					num2 = U16(bytes, num12 + 2);
					uint num14 = U32(bytes, num12 + 4);
					uint num15 = U32(bytes, num12 + 8);
					num3 = U16(bytes, num12 + 12);
					int num16 = U16(bytes, num12 + 14);
					if (num13 != 1 || num16 != 16)
					{
						throw Invalid("Only uncompressed signed 16-bit PCM WAV audio is supported.");
					}
					if (num2 == 0 || num14 == 0 || num14 > int.MaxValue)
					{
						throw Invalid("The WAV channel count or sample rate is invalid.");
					}
					if (num3 != num2 * 2 || num14 * num3 != num15)
					{
						throw Invalid("The WAV block alignment or byte rate is invalid.");
					}
					sampleRate = (int)num14;
					flag = true;
				}
				else if (HasId(bytes, num7, "data"))
				{
					if (num4 >= 0)
					{
						throw Invalid("Multiple WAV data chunks are not supported.");
					}
					num4 = (int)num9;
					num5 = (int)num8;
				}
				num6 = num11;
			}
			if (!flag || num4 < 0)
			{
				throw Invalid("The WAV requires format and data chunks.");
			}
			if (num5 == 0 || num5 % num3 != 0)
			{
				throw Invalid("The WAV data is empty or does not contain complete sample frames.");
			}
			float[] array = new float[num5 / 2];
			int num17 = 0;
			int num18 = num4;
			while (num17 < array.Length)
			{
				short num19 = (short)U16(bytes, num18);
				array[num17] = (float)num19 / 32768f;
				num17++;
				num18 += 2;
			}
			return new PcmWaveData(array, num2, sampleRate, num5 / num3);
		}

		private static bool HasId(byte[] bytes, int offset, string id)
		{
			if (bytes[offset] == id[0] && bytes[offset + 1] == id[1] && bytes[offset + 2] == id[2])
			{
				return bytes[offset + 3] == id[3];
			}
			return false;
		}

		private static int U16(byte[] bytes, int offset)
		{
			return bytes[offset] | (bytes[offset + 1] << 8);
		}

		private static uint U32(byte[] bytes, int offset)
		{
			return (uint)(bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24));
		}

		private static InvalidDataException Invalid(string message)
		{
			return new InvalidDataException(message);
		}
	}
	[BepInPlugin("madjack.redactedlootbug", "Redacted Loot Bug", "1.0.2")]
	public sealed class RedactedLootBugPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "madjack.redactedlootbug";

		private static ManualLogSource _log;

		private readonly List<AudioClip> _ownedClips = new List<AudioClip>();

		private Harmony _harmony;

		private Type _bugType;

		private static FieldInfo _chitterField;

		private MethodInfo _startMethod;

		private MethodInfo _detectMethod;

		private static AudioClip[] _chatterClips = Array.Empty<AudioClip>();

		private static bool _reportedApplyError;

		private static bool _reportedApplySuccess;

		public void Awake()
		{
			_log = ((BaseUnityPlugin)this).Logger;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Initializing Redacted Loot Bug 1.0.2.");
			try
			{
				_bugType = AccessTools.TypeByName("HoarderBugAI");
				_chitterField = ((_bugType == null) ? null : AccessTools.Field(_bugType, "chitterSFX"));
				_startMethod = ((_bugType == null) ? null : AccessTools.DeclaredMethod(_bugType, "Start", Type.EmptyTypes, (Type[])null));
				_detectMethod = ((_bugType == null) ? null : AccessTools.DeclaredMethod(_bugType, "DetectAndLookAtPlayers", Type.EmptyTypes, (Type[])null));
				if (_chitterField == null || _chitterField.FieldType != typeof(AudioClip[]) || _startMethod == null || _detectMethod == null)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)"Hoarding Bug chatter methods/field were not found. Sound replacement is disabled; check game compatibility.");
					return;
				}
				string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "Audio", "Chatter");
				Directory.CreateDirectory(text);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Reading chatter from: " + text));
				string[] source = Directory.GetFiles(text).OrderBy<string, string>((string path) => path, StringComparer.OrdinalIgnoreCase).ToArray();
				foreach (string item in source.Where(IsWave))
				{
					LoadPcmWave(item);
				}
				PublishClips();
				string[] array = source.Where((string path) => string.Equals(Path.GetExtension(path), ".ogg", StringComparison.OrdinalIgnoreCase)).ToArray();
				if (array.Length != 0)
				{
					((MonoBehaviour)this).StartCoroutine(LoadOgg(array));
				}
				else if (_chatterClips.Length == 0)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"No valid chatter clips found. Original game chatter is preserved.");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not initialize Redacted Loot Bug: " + ex));
			}
		}

		private static bool IsWave(string path)
		{
			string extension = Path.GetExtension(path);
			if (!string.Equals(extension, ".wav", StringComparison.OrdinalIgnoreCase))
			{
				return string.Equals(extension, ".wave", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private void LoadPcmWave(string path)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Invalid comparison between Unknown and I4
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			AudioClip val = null;
			try
			{
				PcmWaveData pcmWaveData = PcmWaveReader.Read(File.ReadAllBytes(path));
				val = AudioClip.Create("RedactedLootBug_" + Path.GetFileNameWithoutExtension(path), pcmWaveData.Frames, pcmWaveData.Channels, pcmWaveData.SampleRate, false);
				if ((Object)(object)val == (Object)null || !val.SetData(pcmWaveData.Samples, 0))
				{
					throw new InvalidDataException("Unity could not populate the audio clip.");
				}
				if ((int)val.loadState != 2)
				{
					throw new InvalidDataException("Unity audio clip is not ready: " + ((object)val.loadState/*cast due to .constrained prefix*/).ToString());
				}
				AudioClip obj = val;
				((Object)obj).hideFlags = (HideFlags)(((Object)obj).hideFlags | 0x20);
				_ownedClips.Add(val);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Decoded " + Path.GetFileName(path) + ": " + pcmWaveData.Frames + " frames, " + pcmWaveData.Channels + " channels, " + pcmWaveData.SampleRate + " Hz, state=" + ((object)val.loadState/*cast due to .constrained prefix*/).ToString() + "."));
			}
			catch (Exception ex)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not load " + Path.GetFileName(path) + ": " + ex));
			}
		}

		private IEnumerator LoadOgg(string[] files)
		{
			foreach (string path in files)
			{
				UnityWebRequest request = null;
				UnityWebRequestAsyncOperation val;
				try
				{
					request = UnityWebRequestMultimedia.GetAudioClip(new Uri(Path.GetFullPath(path)).AbsoluteUri, (AudioType)14);
					request.timeout = 30;
					val = request.SendWebRequest();
				}
				catch (Exception ex)
				{
					if (request != null)
					{
						request.Dispose();
					}
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not open " + Path.GetFileName(path) + ": " + ex.Message));
					continue;
				}
				UnityWebRequest val2 = request;
				try
				{
					yield return val;
					if ((int)request.result != 1)
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not load " + Path.GetFileName(path) + ": " + request.error));
						continue;
					}
					AudioClip content;
					try
					{
						content = DownloadHandlerAudioClip.GetContent(request);
					}
					catch (Exception ex2)
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not decode " + Path.GetFileName(path) + ": " + ex2.Message));
						goto end_IL_00ea;
					}
					if ((Object)(object)content == (Object)null || content.samples <= 0 || (int)content.loadState != 2)
					{
						if ((Object)(object)content != (Object)null)
						{
							Object.Destroy((Object)(object)content);
						}
						((BaseUnityPlugin)this).Logger.LogWarning((object)("Skipping empty or unready OGG clip: " + Path.GetFileName(path)));
					}
					else
					{
						((Object)content).name = "RedactedLootBug_" + Path.GetFileNameWithoutExtension(path);
						((Object)content).hideFlags = (HideFlags)(((Object)content).hideFlags | 0x20);
						_ownedClips.Add(content);
					}
					end_IL_00ea:;
				}
				finally
				{
					((IDisposable)val2)?.Dispose();
				}
			}
			PublishClips();
			if (_chatterClips.Length == 0)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"No valid chatter clips found. Original game chatter is preserved.");
			}
		}

		private void PublishClips()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			if (_ownedClips.Count != 0)
			{
				_chatterClips = _ownedClips.ToArray();
				if (_harmony == null)
				{
					_harmony = new Harmony("madjack.redactedlootbug");
					HarmonyMethod val = new HarmonyMethod(typeof(RedactedLootBugPlugin), "HoarderStartPrefix", (Type[])null);
					val.priority = 800;
					_harmony.Patch((MethodBase)_startMethod, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					HarmonyMethod val2 = new HarmonyMethod(typeof(RedactedLootBugPlugin), "HoarderDetectPrefix", (Type[])null);
					val2.priority = 0;
					_harmony.Patch((MethodBase)_detectMethod, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Patched " + _bugType.Assembly.GetName().Name + ": HoarderBugAI.Start and DetectAndLookAtPlayers."));
				}
				Object[] array = Object.FindObjectsOfType(_bugType);
				for (int i = 0; i < array.Length; i++)
				{
					ApplyChatter(array[i], "existing bug scan");
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Loaded " + _chatterClips.Length + " chatter clip(s). Only normal Hoarding Bug chatter is replaced."));
			}
		}

		private static void HoarderStartPrefix(object __instance)
		{
			ApplyChatter(__instance, "Start prefix");
		}

		private static void HoarderDetectPrefix(object __instance)
		{
			ApplyChatter(__instance, "chatter detection prefix");
		}

		private static void ApplyChatter(object bug, string reason)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			if (bug == null || _chatterClips.Length == 0)
			{
				return;
			}
			try
			{
				if (_chitterField.GetValue(bug) != _chatterClips)
				{
					_chitterField.SetValue(bug, _chatterClips);
				}
				if (!_reportedApplySuccess)
				{
					_reportedApplySuccess = true;
					_log.LogInfo((object)("Applied custom chatter to a Hoarding Bug (" + reason + "); first clip=" + ((Object)_chatterClips[0]).name + ", state=" + ((object)_chatterClips[0].loadState/*cast due to .constrained prefix*/).ToString() + "."));
				}
			}
			catch (Exception ex)
			{
				if (!_reportedApplyError)
				{
					_reportedApplyError = true;
					_log.LogError((object)("Could not replace Hoarding Bug chatter: " + ex));
				}
			}
		}
	}
}