Decompiled source of Subtitles v2.1.1

Subtitles.dll

Decompiled a year 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 System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Timers;
using AudibleDistanceLib;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SubtitlesAPI;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Subtitles")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Displays text for in-game sounds to help make the game more accessible for Deaf/HoH gamers.")]
[assembly: AssemblyFileVersion("2.1.1.0")]
[assembly: AssemblyInformationalVersion("2.1.1+4f99db70e06b5ed7de3e4263d547e4e95c0494f8")]
[assembly: AssemblyProduct("Subtitles")]
[assembly: AssemblyTitle("Subtitles")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.1.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 Subtitles
{
	public class Constants
	{
		public static readonly int DefaultExpireSubtitleTimeMs = 5000;

		public static readonly int DefaultVisibleSubtitleLines = 3;

		public static readonly string HtmlLineBreakTag = "<br>";

		public static readonly string PlayerScreenGUIName = "Systems/UI/Canvas/Panel/GameObject/PlayerScreen";
	}
	[BepInPlugin("JustJelly.Subtitles", "Subtitles", "2.1.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private const string pluginGuid = "JustJelly.Subtitles";

		private const string pluginName = "Subtitles";

		private const string pluginVersion = "2.1.1";

		private Harmony harmony;

		public static Plugin Instance;

		public static ManualLogSource ManualLogSource;

		public SubtitleList subtitles = new SubtitleList();

		public ConfigEntry<float> minimumAudibleVolume;

		public ConfigEntry<bool> logSoundNames;

		private void Awake()
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			if (Instance == null)
			{
				Instance = this;
			}
			ManualLogSource = Logger.CreateLogSource("JustJelly.Subtitles");
			ManualLogSource.LogInfo((object)"Subtitles 2.1.1 loaded!");
			minimumAudibleVolume = ((BaseUnityPlugin)this).Config.Bind<float>("\u200bOptions", "MinimumAudibleVolume", 12f, "The minimum volume this mod determines is audible. Scale of 0-100. Any sound heard above this volume will be displayed on subtitles, any sound below will not.");
			logSoundNames = ((BaseUnityPlugin)this).Config.Bind<bool>("Contributors/Developers", "LogSoundNames", false, "Whether the mod should log the names of sounds. Only valuable if trying to add more subtitles / localization.");
			harmony = new Harmony("JustJelly.Subtitles");
			harmony.PatchAll();
		}
	}
	public class SubtitleList : IList<string>, ICollection<string>, IEnumerable<string>, IEnumerable
	{
		private volatile List<Tuple<DateTime, string>> collection = new List<Tuple<DateTime, string>>();

		private readonly Timer timer;

		private readonly TimeSpan expiration;

		public string this[int index]
		{
			get
			{
				return collection[index].Item2;
			}
			set
			{
				collection[index] = new Tuple<DateTime, string>(DateTime.Now, value);
			}
		}

		public int Count => collection.Count;

		public bool IsSynchronized => false;

		public bool IsReadOnly => false;

		public SubtitleList()
		{
			timer = new Timer
			{
				Interval = 1000.0
			};
			timer.Elapsed += RemoveExpiredElements;
			timer.Start();
			expiration = TimeSpan.FromMilliseconds(Constants.DefaultExpireSubtitleTimeMs);
		}

		private void RemoveExpiredElements(object sender, EventArgs e)
		{
			for (int num = collection.Count - 1; num >= 0; num--)
			{
				if (DateTime.Now - collection[num].Item1 >= expiration)
				{
					collection.RemoveAt(num);
				}
			}
		}

		public List<string> TakeLast(int number)
		{
			return (from element in collection
				where DateTime.Now >= element.Item1
				orderby element.Item1
				select element.Item2).TakeLast(number).ToList();
		}

		public IEnumerator<string> GetEnumerator()
		{
			return collection.Select((Tuple<DateTime, string> x) => x.Item2).GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return collection.Select((Tuple<DateTime, string> x) => x.Item2).GetEnumerator();
		}

		public void Add(string item)
		{
			collection.Add(new Tuple<DateTime, string>(DateTime.Now, item));
		}

		public void Add(string item, float seconds)
		{
			collection.Add(new Tuple<DateTime, string>(DateTime.Now.AddSeconds(seconds), item));
		}

		public void CopyTo(string[] array, int index)
		{
			for (int i = 0; i < collection.Count; i++)
			{
				array[i + index] = collection[i].Item2;
			}
		}

		public bool Remove(string item)
		{
			bool result = Contains(item);
			for (int num = collection.Count - 1; num >= 0; num--)
			{
				if (collection[num].Item2 == item)
				{
					collection.RemoveAt(num);
				}
			}
			return result;
		}

		public void RemoveAt(int i)
		{
			collection.RemoveAt(i);
		}

		public bool Contains(string item)
		{
			for (int i = 0; i < collection.Count; i++)
			{
				if (collection[i].Item2 == item)
				{
					return true;
				}
			}
			return false;
		}

		public void Insert(int index, string item)
		{
			collection.Insert(index, new Tuple<DateTime, string>(DateTime.Now, item));
		}

		public int IndexOf(string item)
		{
			for (int i = 0; i < collection.Count; i++)
			{
				if (collection[i].Item2 == item)
				{
					return i;
				}
			}
			return -1;
		}

		public void Clear()
		{
			collection.Clear();
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "Subtitles";

		public const string PLUGIN_NAME = "Subtitles";

		public const string PLUGIN_VERSION = "2.1.1";
	}
}
namespace Subtitles.Patches
{
	[HarmonyPatch(typeof(AudioSource))]
	public class AudioSourcePatch
	{
		[HarmonyPrefix]
		[HarmonyPatch("PlayOneShotHelper", new Type[]
		{
			typeof(AudioSource),
			typeof(AudioClip),
			typeof(float)
		})]
		public static void PlayOneShotHelper_Prefix(AudioSource source, ref AudioClip clip, float volumeScale)
		{
			if (AudibleDistanceLib.IsInWithinAudiableDistance(GameNetworkManager.Instance, source, volumeScale, Plugin.Instance.minimumAudibleVolume.Value))
			{
				AddSubtitle(clip);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("Play", new Type[] { typeof(double) })]
		public static void PlayDelayed_Prefix(AudioSource __instance)
		{
			if (!((Object)(object)__instance.clip == (Object)null) && AudibleDistanceLib.IsInWithinAudiableDistance(GameNetworkManager.Instance, __instance, __instance.volume, Plugin.Instance.minimumAudibleVolume.Value))
			{
				AddSubtitle(__instance.clip);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("Play", new Type[] { })]
		public static void Play_Prefix(AudioSource __instance)
		{
			if (!((Object)(object)__instance.clip == (Object)null) && AudibleDistanceLib.IsInWithinAudiableDistance(GameNetworkManager.Instance, __instance, __instance.volume, Plugin.Instance.minimumAudibleVolume.Value))
			{
				AddSubtitle(__instance.clip);
			}
		}

		private static void AddSubtitle(AudioClip clip)
		{
			if (((clip != null) ? ((Object)clip).name : null) == null)
			{
				return;
			}
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(((Object)clip).name);
			if (SubtitlesAPI.Localization.Translations.TryGetValue(fileNameWithoutExtension, out var value))
			{
				if (Plugin.Instance.logSoundNames.Value)
				{
					Plugin.ManualLogSource.LogInfo((object)("Found translation for " + fileNameWithoutExtension + "!"));
				}
				Plugin.Instance.subtitles.Add(FormatSoundTranslation(value));
				return;
			}
			if (SubtitlesAPI.Localization.DialogueTranslations.TryGetValue(fileNameWithoutExtension, out List<(float, string)> value2))
			{
				if (Plugin.Instance.logSoundNames.Value)
				{
					Plugin.ManualLogSource.LogInfo((object)("Found dialogue translation for " + fileNameWithoutExtension + "!"));
				}
				{
					foreach (var (seconds, translation) in value2)
					{
						Plugin.Instance.subtitles.Add(ForamtDialogueTranslation(translation), seconds);
					}
					return;
				}
			}
			if (Plugin.Instance.logSoundNames.Value)
			{
				Plugin.ManualLogSource.LogInfo((object)("No translation for " + fileNameWithoutExtension + "."));
			}
		}

		private static string FormatSoundTranslation(string translation)
		{
			if (translation.StartsWith("[") && translation.EndsWith("]"))
			{
				return "<color=yellow>" + translation + "</color>";
			}
			return "<color=yellow>[" + translation + "]</color>";
		}

		private static string ForamtDialogueTranslation(string translation)
		{
			if (translation.StartsWith("[") && translation.EndsWith("]"))
			{
				return FormatSoundTranslation(translation);
			}
			return "<color=green>" + translation + "</color>";
		}
	}
	[HarmonyPatch(typeof(HUDManager))]
	public class HUDManagerPatch
	{
		private static TextMeshProUGUI subtitleGUItext;

		[HarmonyPostfix]
		[HarmonyPatch("Awake")]
		private static void Awake_Postfix(ref HUDManager __instance)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("SubtitlesGUI");
			val.AddComponent<RectTransform>();
			TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
			RectTransform rectTransform = ((TMP_Text)val2).rectTransform;
			((Transform)rectTransform).SetParent(GameObject.Find(Constants.PlayerScreenGUIName).transform, false);
			rectTransform.sizeDelta = new Vector2(600f, 200f);
			rectTransform.anchoredPosition = new Vector2(0f, -125f);
			((TMP_Text)val2).alignment = (TextAlignmentOptions)514;
			((TMP_Text)val2).font = ((TMP_Text)__instance.controlTipLines[0]).font;
			((TMP_Text)val2).fontSize = 14f;
			subtitleGUItext = val2;
		}

		[HarmonyPostfix]
		[HarmonyPatch("Update")]
		private static void Update_Postfix()
		{
			((TMP_Text)subtitleGUItext).text = GetLatestSubtitles();
		}

		private static string GetLatestSubtitles()
		{
			StringBuilder stringBuilder = new StringBuilder();
			IList<string> list = Plugin.Instance.subtitles.TakeLast(Constants.DefaultVisibleSubtitleLines).ToList();
			string value = string.Empty;
			foreach (string item in list)
			{
				stringBuilder.Append(value);
				stringBuilder.Append(item);
				value = Constants.HtmlLineBreakTag;
			}
			return stringBuilder.ToString();
		}
	}
}