Please disclose if any significant portion of your mod was created 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 LyricDisplay v1.0.4
LyricDisplay.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; 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 System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("LyricDisplay")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.4.0")] [assembly: AssemblyInformationalVersion("1.0.4")] [assembly: AssemblyProduct("LyricDisplay")] [assembly: AssemblyTitle("LyricDisplay")] [assembly: AssemblyVersion("1.0.4.0")] namespace Moxia.LyricDisplay; public static class BoomboxBatteryFix { public static bool Enabled; private static Type _boomboxType; private static FieldInfo _batteryField; private static FieldInfo _playingField; private static FieldInfo _audioField; private static FieldInfo _chargeField; public static void UpdatePostfix(object __instance) { if (!Enabled || __instance == null) { return; } try { Type type = __instance.GetType(); if (_boomboxType != type || _batteryField == null) { _boomboxType = type; _batteryField = type.GetField("insertedBattery", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _playingField = type.GetField("isPlayingMusic", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _audioField = type.GetField("boomboxAudio", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _chargeField = _batteryField?.FieldType.GetField("charge", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (_batteryField == null || _playingField == null || _audioField == null || _chargeField == null) { return; } object value = _playingField.GetValue(__instance); if (!(value is bool) || !(bool)value) { return; } object value2 = _batteryField.GetValue(__instance); if (value2 != null && _chargeField.GetValue(value2) is float num && !(num < 0.05f)) { object? value3 = _audioField.GetValue(__instance); AudioSource val = (AudioSource)((value3 is AudioSource) ? value3 : null); if (val != null && val.pitch < 1f) { val.pitch = 1f; } } } catch (Exception) { } } } internal static class Diag { public const int Hud = 0; public const int HudSkip = 1; public const int Library = 2; private const int ChannelCount = 3; private const float VerboseHeartbeatSeconds = 30f; private static readonly string[] Prefixes = new string[3] { "[HUD] ", "[HUD] skip: ", string.Empty }; private static readonly string[] LastKeys = new string[3]; private static readonly float[] LastTimes = new float[3]; public static bool Verbose; public static void State(int channel, string key) { if (key != null && !ShouldSkip(channel, key)) { ManualLogSource log = LyricDisplayPlugin.Log; if (log != null) { log.LogInfo((object)(Prefixes[channel] + key)); } } } public static void Message(int channel, string key, string message, bool warning = false) { if (key == null || ShouldSkip(channel, key)) { return; } ManualLogSource log = LyricDisplayPlugin.Log; if (log != null) { if (warning) { log.LogWarning((object)message); } else { log.LogInfo((object)message); } } } private static bool ShouldSkip(int channel, string key) { if (string.Equals(key, LastKeys[channel], StringComparison.Ordinal)) { if (Verbose) { return Time.unscaledTime - LastTimes[channel] < 30f; } return true; } LastKeys[channel] = key; LastTimes[channel] = Time.unscaledTime; return false; } } internal static class FontFixBridge { private static readonly char[] ProbeChars = new char[2] { '中', '乐' }; private static readonly string[] PreferredNameHints = new string[18] { "sourcehan", "source han", "notosans", "noto sans", "noto", "cjk", "yahei", "msyh", "simhei", "simsun", "heiti", "songti", "kaiti", "fangsong", "jhenghei", "unifont", "wenkai", "han" }; private static bool _resolved; private static Font _font; private static string _detail = "not resolved yet"; internal static bool TryGetChineseFont(out Font font, out string detail) { if (!_resolved) { Resolve(); _resolved = true; } font = _font; detail = _detail; return (Object)(object)font != (Object)null; } internal static void Reset() { _resolved = false; _font = null; _detail = "not resolved yet"; } private static void Resolve() { try { Font font2; string token2; if (TryGetTmpSourceFont(out var font, out var token)) { _font = font; _detail = "TMP glyph asset (" + token + ")"; } else if (TryGetLoadedCjkFont(out font2, out token2)) { _font = font2; _detail = "font loaded at runtime (" + token2 + ")"; } else { _font = null; _detail = "no native Chinese glyph source is loaded"; } } catch (Exception ex) { _font = null; _detail = "native glyph lookup failed: " + ex.Message; } } private static bool TryGetTmpSourceFont(out Font font, out string token) { font = null; token = null; string token2; Font val = ReadDefaultTmpSourceFont(out token2); if ((Object)(object)val != (Object)null) { font = val; token = token2; return true; } Type type = FindType("TMPro.TMP_FontAsset"); if (type == null) { return false; } Object[] array; try { array = Resources.FindObjectsOfTypeAll(type); } catch (Exception) { return false; } if (array == null) { return false; } Font val2 = null; string text = null; int num = int.MinValue; foreach (Object val3 in array) { if (val3 == (Object)null) { continue; } Font val4 = ReadFontMember(val3, "sourceFontFile"); if (IsUsableCandidate(val4)) { int num2 = NameScore(((Object)val4).name) + NameScore(val3.name); if (num2 > num) { val2 = val4; text = Describe(val3.name, ((Object)val4).name); num = num2; } } } if ((Object)(object)val2 == (Object)null) { return false; } font = val2; token = text; return true; } private static Font ReadDefaultTmpSourceFont(out string token) { token = null; Type type = FindType("TMPro.TMP_Settings"); if (type == null) { return null; } object staticMember = GetStaticMember(type, "defaultFontAsset"); Object val = (Object)((staticMember is Object) ? staticMember : null); if (val == (Object)null) { object staticMember2 = GetStaticMember(type, "instance"); if (staticMember2 != null) { object member = GetMember(staticMember2, "defaultFontAsset"); val = (Object)((member is Object) ? member : null); if (val == (Object)null) { object member2 = GetMember(staticMember2, "m_defaultFontAsset"); val = (Object)((member2 is Object) ? member2 : null); } } } Font val2 = ReadFontMember(val, "sourceFontFile"); if (!IsUsableCandidate(val2)) { return null; } token = Describe((val == (Object)null) ? null : val.name, ((Object)val2).name); return val2; } private static bool TryGetLoadedCjkFont(out Font font, out string token) { font = null; token = null; Object[] array; try { array = Resources.FindObjectsOfTypeAll(typeof(Font)); } catch (Exception) { return false; } if (array == null) { return false; } Font val = null; int num = int.MinValue; foreach (Object obj in array) { Font val2 = (Font)(object)((obj is Font) ? obj : null); if (IsUsableCandidate(val2)) { int num2 = NameScore(((Object)val2).name); if (num2 > num) { val = val2; num = num2; } } } if ((Object)(object)val == (Object)null) { return false; } font = val; token = Describe(null, ((Object)val).name); return true; } private static bool IsUsableCandidate(Font font) { if ((Object)(object)font == (Object)null) { return false; } try { if (!font.dynamic) { return false; } if (!font.HasCharacter(ProbeChars[0]) || !font.HasCharacter(ProbeChars[1])) { return false; } } catch (Exception) { return false; } return !IsOwnFont(((Object)font).name); } private static bool IsOwnFont(string name) { if (string.IsNullOrEmpty(name)) { return false; } return name.IndexOf("lyric", StringComparison.OrdinalIgnoreCase) >= 0; } private static int NameScore(string name) { if (string.IsNullOrEmpty(name)) { return 0; } string text = name.ToLowerInvariant(); int num = 0; for (int i = 0; i < PreferredNameHints.Length; i++) { if (text.IndexOf(PreferredNameHints[i], StringComparison.Ordinal) >= 0) { num += 2; break; } } for (int j = 0; j < name.Length; j++) { if (name[j] > '⺀') { num++; break; } } return num; } private static string Describe(string assetName, string faceName) { if (string.IsNullOrEmpty(faceName)) { return "unnamed"; } if (string.IsNullOrEmpty(assetName) || string.Equals(assetName, faceName, StringComparison.OrdinalIgnoreCase)) { return "\"" + faceName + "\""; } return "\"" + assetName + "\" -> \"" + faceName + "\""; } private static Font ReadFontMember(object owner, string name) { object member = GetMember(owner, name); return (Font)((member is Font) ? member : null); } private static Type FindType(string fullName) { try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { try { Type type = assemblies[i].GetType(fullName, throwOnError: false); if (type != null) { return type; } } catch (Exception) { } } } catch (Exception) { } return null; } private static object GetStaticMember(Type type, string name) { try { PropertyInfo property = type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property.GetValue(null, null); } FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(null); } } catch (Exception) { } return null; } private static object GetMember(object target, string name) { if (target == null) { return null; } try { Type type = target.GetType(); PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property.GetValue(target, null); } FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(target); } } catch (Exception) { } return null; } } internal static class LethalConfigBridge { private const string ConfigGuid = "ainavt.lc.lethalconfig"; private const string ButtonItemTypeName = "LethalConfig.ConfigItems.GenericButtonConfigItem"; private const string ManagerTypeName = "LethalConfig.LethalConfigManager"; private const string Section = "General"; private const string ButtonName = "Reload"; private const string ButtonText = "Reload"; private const string Description = "Re-read the lyric and the music folders now, inside the running game: songs and .lrc files added while the game is up are indexed without a restart. Safe to press at any time; nothing is written to disk."; private const float GiveUpAfterSeconds = 20f; private static bool _resolved; private static float _firstPollTime = -1f; internal static void Poll(ManualLogSource log) { if (_resolved) { return; } if (!Chainloader.PluginInfos.ContainsKey("ainavt.lc.lethalconfig")) { if (_firstPollTime < 0f) { _firstPollTime = Time.realtimeSinceStartup; } else if (Time.realtimeSinceStartup - _firstPollTime >= 20f) { _resolved = true; log.LogInfo((object)"[Reload] LethalConfig was not found, so no Reload button was added. Reloading the lyric and music folders from inside the game needs it installed."); } return; } _resolved = true; try { Type type = FindType("LethalConfig.ConfigItems.GenericButtonConfigItem"); Type type2 = FindType("LethalConfig.LethalConfigManager"); if (type == null || type2 == null) { log.LogWarning((object)"[Reload] LethalConfig is loaded but does not expose LethalConfig.ConfigItems.GenericButtonConfigItem / LethalConfig.LethalConfigManager; the Reload button was skipped."); return; } ConstructorInfo constructorInfo = FindButtonConstructor(type); bool takesAssembly; MethodInfo methodInfo = FindAddConfigItem(type2, type, out takesAssembly); if (constructorInfo == null || methodInfo == null) { log.LogWarning((object)("[Reload] this LethalConfig version does not offer a button item with the expected signature (found: " + DescribeConstructors(type) + "); the Reload button was skipped.")); return; } object obj = CreateButton(constructorInfo); if (obj == null) { log.LogWarning((object)"[Reload] the button item of this LethalConfig version takes parameters this bridge does not know; the Reload button was skipped."); return; } object[] parameters = ((!takesAssembly) ? new object[1] { obj } : new object[2] { obj, typeof(LethalConfigBridge).Assembly }); object obj2 = methodInfo.Invoke(null, parameters); if (obj2 is bool && !(bool)obj2) { log.LogWarning((object)"[Reload] LethalConfig did not accept the Reload button for the LyricDisplay assembly; the Reload button was not added."); } else { log.LogInfo((object)"[Reload] added the Reload button to the LethalConfig page of this mod."); } } catch (Exception ex) { log.LogWarning((object)("[Reload] the Reload button could not be added: " + ex.Message)); } } private static object CreateButton(ConstructorInfo buttonConstructor) { ParameterInfo[] parameters = buttonConstructor.GetParameters(); object[] array = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { Type parameterType = parameters[i].ParameterType; if (IsCallbackType(parameterType)) { array[i] = CreateCallback(parameterType); if (array[i] == null) { return null; } } else { if (!(parameterType == typeof(string))) { return null; } array[i] = PickString(parameters[i].Name); } } return buttonConstructor.Invoke(array); } private static string PickString(string parameterName) { string text = (parameterName ?? string.Empty).ToLowerInvariant(); if (text.Contains("section") || text.Contains("category") || text.Contains("location")) { return "General"; } if (text.Contains("text")) { return "Reload"; } if (text.Contains("description") || text.Contains("tooltip")) { return "Re-read the lyric and the music folders now, inside the running game: songs and .lrc files added while the game is up are indexed without a restart. Safe to press at any time; nothing is written to disk."; } if (text.Contains("name") || text.Contains("title")) { return "Reload"; } return string.Empty; } private static bool IsCallbackType(Type parameterType) { return typeof(Delegate).IsAssignableFrom(parameterType); } private static Delegate CreateCallback(Type delegateType) { if (delegateType == typeof(Action)) { return new Action(OnReloadClicked); } MethodInfo method = delegateType.GetMethod("Invoke"); if (method == null || method.ReturnType != typeof(void) || method.GetParameters().Length != 0) { return null; } MethodInfo method2 = typeof(LethalConfigBridge).GetMethod("OnReloadClicked", BindingFlags.Static | BindingFlags.NonPublic); if (method2 == null) { return null; } try { return Delegate.CreateDelegate(delegateType, method2); } catch (ArgumentException) { return null; } } private static ConstructorInfo FindButtonConstructor(Type buttonItemType) { ConstructorInfo constructorInfo = null; ConstructorInfo[] constructors = buttonItemType.GetConstructors(); foreach (ConstructorInfo constructorInfo2 in constructors) { ParameterInfo[] parameters = constructorInfo2.GetParameters(); int num = 0; bool flag = true; ParameterInfo[] array = parameters; foreach (ParameterInfo parameterInfo in array) { if (IsCallbackType(parameterInfo.ParameterType)) { num++; } else if (parameterInfo.ParameterType != typeof(string)) { flag = false; break; } } if (flag && num == 1) { if (parameters.Length == 5) { return constructorInfo2; } if (constructorInfo == null) { constructorInfo = constructorInfo2; } } } return constructorInfo; } private static MethodInfo FindAddConfigItem(Type managerType, Type buttonItemType, out bool takesAssembly) { MethodInfo methodInfo = null; MethodInfo[] methods = managerType.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo2 in methods) { if (!(methodInfo2.Name != "AddConfigItem")) { ParameterInfo[] parameters = methodInfo2.GetParameters(); if (parameters.Length == 2 && parameters[0].ParameterType.IsAssignableFrom(buttonItemType) && parameters[1].ParameterType == typeof(Assembly)) { takesAssembly = true; return methodInfo2; } if (parameters.Length == 1 && parameters[0].ParameterType.IsAssignableFrom(buttonItemType) && methodInfo == null) { methodInfo = methodInfo2; } } } takesAssembly = false; return methodInfo; } private static string DescribeConstructors(Type buttonItemType) { ConstructorInfo[] constructors = buttonItemType.GetConstructors(); if (constructors.Length == 0) { return "(none public)"; } string[] array = new string[constructors.Length]; for (int i = 0; i < constructors.Length; i++) { ParameterInfo[] parameters = constructors[i].GetParameters(); string[] array2 = new string[parameters.Length]; for (int j = 0; j < parameters.Length; j++) { array2[j] = parameters[j].ParameterType.Name; } array[i] = "(" + string.Join(", ", array2) + ")"; } return string.Join(" ", array); } private static Type FindType(string fullName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType(fullName, throwOnError: false); if (type != null) { return type; } } return null; } private static void OnReloadClicked() { LyricDisplayPlugin instance = LyricDisplayPlugin.Instance; if (!((Object)(object)instance == (Object)null)) { instance.ReloadIndexes("LethalConfig"); } } } public enum LyricAlign { Left, Center, Right } public sealed class LyricRenderTrack { public string HeaderText; public string CurrentText; public string SecondText; public string CurrentTextRich; internal readonly GradientTextCache Gradient = new GradientTextCache(); } internal sealed class GradientTextCache { internal LyricLine Line; internal Color PlayedColor = Color.clear; internal Color UnplayedColor = Color.clear; internal int[] PlayedCounts = new int[0]; internal readonly StringBuilder Builder = new StringBuilder(512); internal string Text; } public sealed class LyricHudRenderer { private const float PanelMinWidth = 320f; private const float PanelPaddingX = 24f; private const float PanelPaddingY = 20f; private const float LineGap = 6f; private const float HeaderGap = 4f; private const float SourceGap = 10f; private readonly string _fontsFolder; private bool _useSessionGlyphs; private GUIStyle _currentStyle; private GUIStyle _nextStyle; private GUIStyle _headerStyle; private Texture2D _panelTexture; private Font _dynamicFont; private bool _fontReady; private LyricAlign _currentAlign = LyricAlign.Center; private string _bundledFontPath; private static readonly char[] CjkProbeChars = new char[2] { '中', '乐' }; private static readonly string[] CjkFamilyHints = new string[19] { "han", "noto", "yahei", "simsun", "simhei", "msyh", "dengxian", "kaiti", "fangsong", "cjk", "songti", "heiti", "思源", "雅黑", "黑体", "宋体", "楷体", "等线", "仿宋" }; private string _cachedCurrentText; private string _cachedSecondLine; private string _cachedHeaderText; private int _cachedFontSize = -1; private LyricAlign _cachedAlign = LyricAlign.Center; private float _layoutPanelWidth; private float _layoutContentWidth; private float _layoutHeaderHeight; private float _layoutCurrentHeight; private float _layoutNextHeight; private readonly List<string> _multiCacheTexts = new List<string>(); private int _multiCacheCount = -1; private float _multiPanelWidth; private float _multiContentWidth; private float _multiTotalHeight; private float[] _multiHeaderHeights; private float[] _multiCurrentHeights; private readonly GUIContent _measureContent = new GUIContent(); private static Color _cachedPlayedColor = Color.clear; private static string _cachedPlayedHex; private static Color _cachedUnplayedColor = Color.clear; private static string _cachedUnplayedHex; private static readonly GradientTextCache SharedGradientCache = new GradientTextCache(); public bool UseSessionGlyphs { get { return _useSessionGlyphs; } set { if (_useSessionGlyphs != value) { _useSessionGlyphs = value; if (value) { FontFixBridge.Reset(); } ResetResolvedFont(); } } } public LyricHudRenderer(string fontsFolder) : this(fontsFolder, useSessionGlyphs: true) { } public LyricHudRenderer(string fontsFolder, bool useSessionGlyphs) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown _fontsFolder = fontsFolder; _useSessionGlyphs = useSessionGlyphs; } private void ResetResolvedFont() { _fontReady = false; _dynamicFont = null; _currentStyle = null; _nextStyle = null; _headerStyle = null; _cachedFontSize = -1; _multiCacheCount = -1; } public void DrawTracks(IReadOnlyList<LyricRenderTrack> tracks, float verticalOffset, float horizontalOffset, int fontSize, float backgroundAlpha, LyricAlign align, bool showHeader) { if (tracks != null && tracks.Count != 0) { EnsureStyles(fontSize, align); if (tracks.Count == 1) { DrawSingle(tracks[0], verticalOffset, horizontalOffset, backgroundAlpha, showHeader); } else { DrawMulti(tracks, verticalOffset, horizontalOffset, backgroundAlpha, showHeader); } } } private void DrawSingle(LyricRenderTrack track, float verticalOffset, float horizontalOffset, float backgroundAlpha, bool showHeader) { //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) string currentText = track.CurrentText; string secondText = track.SecondText; string text = ((showHeader && !string.IsNullOrEmpty(track.HeaderText)) ? track.HeaderText : null); float num = (float)Screen.height * (1f - verticalOffset); if (LayoutChanged(currentText, secondText, text, _currentStyle.fontSize, _currentAlign)) { ComputeLayout(currentText, secondText, text, _currentStyle.fontSize, _currentAlign); } float layoutPanelWidth = _layoutPanelWidth; float layoutContentWidth = _layoutContentWidth; float layoutCurrentHeight = _layoutCurrentHeight; float layoutNextHeight = _layoutNextHeight; float layoutHeaderHeight = _layoutHeaderHeight; float num2 = layoutHeaderHeight + ((layoutHeaderHeight > 0f) ? 4f : 0f) + layoutCurrentHeight + ((layoutNextHeight > 0f) ? (layoutNextHeight + 6f) : 0f) + 40f; float num3 = (float)Screen.width * horizontalOffset; float num4 = (float)Screen.width * 0.5f - layoutPanelWidth * 0.5f + num3; Rect rect = default(Rect); ((Rect)(ref rect))..ctor(num4, num - num2 * 0.5f, layoutPanelWidth, num2); DrawPanel(rect, backgroundAlpha); float num5 = ((Rect)(ref rect)).y + 20f; if (text != null) { Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(((Rect)(ref rect)).x + 24f, num5, layoutContentWidth, layoutHeaderHeight); DrawOutlined(rect2, text, _headerStyle); num5 += layoutHeaderHeight + 4f; } Rect rect3 = default(Rect); ((Rect)(ref rect3))..ctor(((Rect)(ref rect)).x + 24f, num5, layoutContentWidth, layoutCurrentHeight); DrawOutlined(rect3, track.CurrentTextRich ?? currentText, _currentStyle); if (!string.IsNullOrEmpty(secondText)) { Rect rect4 = default(Rect); ((Rect)(ref rect4))..ctor(((Rect)(ref rect)).x + 24f, ((Rect)(ref rect3)).yMax + 6f, layoutContentWidth, layoutNextHeight); DrawOutlined(rect4, secondText, _nextStyle); } } private void DrawMulti(IReadOnlyList<LyricRenderTrack> tracks, float verticalOffset, float horizontalOffset, float backgroundAlpha, bool showHeader) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) float num = (float)Screen.height * (1f - verticalOffset); if (MultiLayoutChanged(tracks, showHeader)) { ComputeMultiLayout(tracks, showHeader); } float multiPanelWidth = _multiPanelWidth; float multiContentWidth = _multiContentWidth; float multiTotalHeight = _multiTotalHeight; float[] multiHeaderHeights = _multiHeaderHeights; float[] multiCurrentHeights = _multiCurrentHeights; float num2 = (float)Screen.width * horizontalOffset; float num3 = (float)Screen.width * 0.5f - multiPanelWidth * 0.5f + num2; Rect rect = default(Rect); ((Rect)(ref rect))..ctor(num3, num - multiTotalHeight * 0.5f, multiPanelWidth, multiTotalHeight); DrawPanel(rect, backgroundAlpha); float num4 = ((Rect)(ref rect)).y + 20f; Rect rect2 = default(Rect); Rect rect3 = default(Rect); for (int i = 0; i < tracks.Count; i++) { string text = ((showHeader && !string.IsNullOrEmpty(tracks[i].HeaderText)) ? tracks[i].HeaderText : null); if (text != null) { ((Rect)(ref rect2))..ctor(((Rect)(ref rect)).x + 24f, num4, multiContentWidth, multiHeaderHeights[i]); DrawOutlined(rect2, text, _headerStyle); num4 += multiHeaderHeights[i] + 4f; } ((Rect)(ref rect3))..ctor(((Rect)(ref rect)).x + 24f, num4, multiContentWidth, multiCurrentHeights[i]); DrawOutlined(rect3, tracks[i].CurrentTextRich ?? tracks[i].CurrentText, _currentStyle); num4 += multiCurrentHeights[i] + ((i < tracks.Count - 1) ? 10f : 0f); } } private bool MultiLayoutChanged(IReadOnlyList<LyricRenderTrack> tracks, bool showHeader) { int num = 0; for (int i = 0; i < tracks.Count; i++) { num += ((showHeader && !string.IsNullOrEmpty(tracks[i].HeaderText)) ? 1 : 0) + 1; } if (_multiCacheCount != num) { return true; } int num2 = 0; for (int j = 0; j < tracks.Count; j++) { string text = ((showHeader && !string.IsNullOrEmpty(tracks[j].HeaderText)) ? tracks[j].HeaderText : null); if (text != null && !string.Equals(_multiCacheTexts[num2++], text, StringComparison.Ordinal)) { return true; } if (!string.Equals(_multiCacheTexts[num2++], tracks[j].CurrentText, StringComparison.Ordinal)) { return true; } } return false; } private void ComputeMultiLayout(IReadOnlyList<LyricRenderTrack> tracks, bool showHeader) { //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) _multiCacheTexts.Clear(); _multiCacheCount = 0; _multiPanelWidth = 320f; for (int i = 0; i < tracks.Count; i++) { string text = ((showHeader && !string.IsNullOrEmpty(tracks[i].HeaderText)) ? tracks[i].HeaderText : null); if (text != null) { _multiCacheTexts.Add(text); _multiCacheCount++; float x = _headerStyle.CalcSize(MeasureContent(text)).x; _multiPanelWidth = Mathf.Max(_multiPanelWidth, x + 48f); } _multiCacheTexts.Add(tracks[i].CurrentText); _multiCacheCount++; float x2 = _currentStyle.CalcSize(MeasureContent(tracks[i].CurrentText)).x; _multiPanelWidth = Mathf.Max(_multiPanelWidth, x2 + 48f); } _multiContentWidth = _multiPanelWidth - 48f; if (_multiHeaderHeights == null || _multiHeaderHeights.Length != tracks.Count) { _multiHeaderHeights = new float[tracks.Count]; _multiCurrentHeights = new float[tracks.Count]; } _multiTotalHeight = 40f; for (int j = 0; j < tracks.Count; j++) { string text2 = ((showHeader && !string.IsNullOrEmpty(tracks[j].HeaderText)) ? tracks[j].HeaderText : null); float num = ((text2 != null) ? _headerStyle.CalcHeight(MeasureContent(text2), _multiContentWidth) : 0f); float num2 = _currentStyle.CalcHeight(MeasureContent(tracks[j].CurrentText), _multiContentWidth); _multiHeaderHeights[j] = num; _multiCurrentHeights[j] = num2; _multiTotalHeight += num + ((num > 0f) ? 4f : 0f) + num2; if (j < tracks.Count - 1) { _multiTotalHeight += 10f; } } } public static string ResolveSecondLine(LyricLine current, LyricLine next) { if (!string.IsNullOrWhiteSpace(current.Translation)) { return current.Translation; } if (next != null && !string.IsNullOrWhiteSpace(next.Text)) { return next.Text; } return null; } public static string BuildGradientText(LyricLine line, double playbackSeconds, Color playedColor, Color unplayedColor) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) return BuildGradientText(line, playbackSeconds, playedColor, unplayedColor, SharedGradientCache); } internal static string BuildGradientText(LyricLine line, double playbackSeconds, Color playedColor, Color unplayedColor, GradientTextCache cache) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Unknown result type (might be due to invalid IL or missing references) //IL_0396: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_039e: Unknown result type (might be due to invalid IL or missing references) if (cache == null) { cache = SharedGradientCache; } if (line == null || line.Words == null || line.Words.Count == 0) { return null; } double num = (playbackSeconds - line.TimeSeconds) * 1000.0; if (num < 0.0) { num = 0.0; } if (_cachedPlayedColor != playedColor) { _cachedPlayedColor = playedColor; _cachedPlayedHex = ColorUtility.ToHtmlStringRGB(playedColor); } if (_cachedUnplayedColor != unplayedColor) { _cachedUnplayedColor = unplayedColor; _cachedUnplayedHex = ColorUtility.ToHtmlStringRGB(unplayedColor); } string cachedPlayedHex = _cachedPlayedHex; string cachedUnplayedHex = _cachedUnplayedHex; int count = line.Words.Count; if (cache.PlayedCounts.Length < count) { cache.PlayedCounts = new int[count]; } bool flag = cache.Line == line && cache.PlayedColor == playedColor && cache.UnplayedColor == unplayedColor; for (int i = 0; i < count; i++) { LyricWord lyricWord = line.Words[i]; int num2; if (string.IsNullOrEmpty(lyricWord.Text)) { num2 = 0; } else if (num >= lyricWord.EndMs) { num2 = lyricWord.Text.Length; } else if (num < lyricWord.StartMs) { num2 = 0; } else { double num3 = (num - lyricWord.StartMs) / Math.Max(1.0, lyricWord.DurationMs); num3 = Mathf.Clamp01((float)num3); num2 = (int)Math.Ceiling((double)lyricWord.Text.Length * num3); } if (flag && cache.PlayedCounts[i] != num2) { flag = false; } cache.PlayedCounts[i] = num2; } if (flag && cache.Text != null) { return cache.Text; } StringBuilder builder = cache.Builder; builder.Length = 0; bool flag2 = true; char c = '\0'; for (int j = 0; j < line.Words.Count; j++) { LyricWord lyricWord2 = line.Words[j]; if (string.IsNullOrEmpty(lyricWord2.Text)) { continue; } if (!flag2) { char c2 = lyricWord2.Text[0]; bool num4 = c != 0 && LyricParser.IsAsciiWordChar(c) && !LyricParser.IsCjkChar(c); bool flag3 = LyricParser.IsAsciiWordChar(c2) && !LyricParser.IsCjkChar(c2); bool flag4 = c != 0 && LyricParser.IsAsciiPunctuation(c); if ((num4 && flag3) || (flag4 && flag3)) { builder.Append(' '); } } flag2 = false; string text = lyricWord2.Text; int num5 = cache.PlayedCounts[j]; if (num5 <= 0) { builder.Append("<color=#").Append(cachedUnplayedHex).Append('>') .Append(text) .Append("</color>"); } else if (num5 >= text.Length) { builder.Append("<color=#").Append(cachedPlayedHex).Append('>') .Append(text) .Append("</color>"); } else { builder.Append("<color=#").Append(cachedPlayedHex).Append('>'); builder.Append(text, 0, num5); builder.Append("</color>"); builder.Append("<color=#").Append(cachedUnplayedHex).Append('>'); builder.Append(text, num5, text.Length - num5); builder.Append("</color>"); } c = text[text.Length - 1]; } cache.Line = line; cache.PlayedColor = playedColor; cache.UnplayedColor = unplayedColor; cache.Text = ((builder.Length > 0) ? builder.ToString() : null); return cache.Text; } private GUIContent MeasureContent(string text) { _measureContent.text = text; return _measureContent; } private bool LayoutChanged(string currentText, string secondLine, string headerText, int fontSize, LyricAlign align) { if (_cachedFontSize == fontSize && _cachedAlign == align && string.Equals(_cachedCurrentText, currentText, StringComparison.Ordinal) && string.Equals(_cachedSecondLine, secondLine, StringComparison.Ordinal)) { return !string.Equals(_cachedHeaderText, headerText, StringComparison.Ordinal); } return true; } private void ComputeLayout(string currentText, string secondLine, string headerText, int fontSize, LyricAlign align) { //IL_0032: 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) //IL_007b: Unknown result type (might be due to invalid IL or missing references) _cachedCurrentText = currentText; _cachedSecondLine = secondLine; _cachedHeaderText = headerText; _cachedFontSize = fontSize; _cachedAlign = align; float x = _currentStyle.CalcSize(MeasureContent(currentText)).x; float num = ((!string.IsNullOrEmpty(secondLine)) ? _nextStyle.CalcSize(MeasureContent(secondLine)).x : 0f); float num2 = ((headerText != null) ? _headerStyle.CalcSize(MeasureContent(headerText)).x : 0f); _layoutPanelWidth = Mathf.Max(320f, Mathf.Max(x, Mathf.Max(num, num2)) + 48f); _layoutContentWidth = _layoutPanelWidth - 48f; _layoutCurrentHeight = _currentStyle.CalcHeight(MeasureContent(currentText), _layoutContentWidth); _layoutNextHeight = ((!string.IsNullOrEmpty(secondLine)) ? _nextStyle.CalcHeight(MeasureContent(secondLine), _layoutContentWidth) : 0f); _layoutHeaderHeight = ((headerText != null) ? _headerStyle.CalcHeight(MeasureContent(headerText), _layoutContentWidth) : 0f); } private void EnsureStyles(int fontSize, LyricAlign align) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected O, but got Unknown //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Expected O, but got Unknown //IL_0184: Unknown result type (might be due to invalid IL or missing references) if (_currentStyle == null || _currentStyle.fontSize != fontSize || _currentAlign != align) { EnsureFont(); _currentAlign = align; TextAnchor alignment = (TextAnchor)(align switch { LyricAlign.Right => 5, LyricAlign.Left => 3, _ => 4, }); _currentStyle = new GUIStyle(GUI.skin.label) { fontSize = fontSize, fontStyle = (FontStyle)0, alignment = alignment, wordWrap = true, richText = true }; _currentStyle.normal.textColor = Color.white; if ((Object)(object)_dynamicFont != (Object)null) { _currentStyle.font = _dynamicFont; } _nextStyle = new GUIStyle(GUI.skin.label) { fontSize = Mathf.Max(10, fontSize - 6), fontStyle = (FontStyle)0, alignment = alignment, wordWrap = true }; _nextStyle.normal.textColor = new Color(1f, 1f, 1f, 0.65f); if ((Object)(object)_dynamicFont != (Object)null) { _nextStyle.font = _dynamicFont; } _headerStyle = new GUIStyle(GUI.skin.label) { fontSize = Mathf.Max(9, fontSize - 10), fontStyle = (FontStyle)0, alignment = alignment, wordWrap = true }; _headerStyle.normal.textColor = new Color(1f, 1f, 1f, 0.45f); if ((Object)(object)_dynamicFont != (Object)null) { _headerStyle.font = _dynamicFont; } } } private void EnsureFont() { if (_fontReady) { return; } _fontReady = true; if (_useSessionGlyphs && FontFixBridge.TryGetChineseFont(out var font, out var detail)) { _dynamicFont = font; ManualLogSource log = LyricDisplayPlugin.Log; if (log != null) { log.LogInfo((object)("[HUD] lyric font = native session glyphs " + detail + "; bundled/system font lookup skipped")); } return; } string detail2; Font val = LoadBundledFont(out detail2); if (IsCjkFont(val)) { _dynamicFont = val; ManualLogSource log2 = LyricDisplayPlugin.Log; if (log2 != null) { log2.LogInfo((object)("[HUD] lyric font = bundled font (" + detail2 + ")")); } return; } string[] array = new string[13] { "Source Han Sans SC", "Source Han Sans SC VF", "SourceHanSansSC-VF", "SourceHanSansSC", "Source Han Sans CN", "Source Han Sans", "Noto Sans SC", "Noto Sans CJK SC", "Microsoft YaHei", "Microsoft YaHei UI", "SimHei", "SimSun", "DengXian" }; string[] array2 = array; foreach (string text in array2) { Font val2 = TryCreateDynamicFont(text); if (IsCjkFont(val2)) { _dynamicFont = val2; ManualLogSource log3 = LyricDisplayPlugin.Log; if (log3 != null) { log3.LogInfo((object)("[HUD] lyric font = system family " + text)); } return; } } string matched; Font val3 = TryFindInstalledCjkFamily(out matched); if (IsCjkFont(val3)) { _dynamicFont = val3; ManualLogSource log4 = LyricDisplayPlugin.Log; if (log4 != null) { log4.LogInfo((object)("[HUD] lyric font = scanned system family " + matched)); } return; } array2 = array; foreach (string text2 in array2) { Font val4 = TryCreateDynamicFont(text2); if ((Object)(object)val4 != (Object)null) { _dynamicFont = val4; ManualLogSource log5 = LyricDisplayPlugin.Log; if (log5 != null) { log5.LogWarning((object)("[HUD] no CJK-capable font found; Chinese lyrics may render as boxes (bundled: " + detail2 + ", fallback family: " + text2 + ", scanned: " + matched + ")")); } return; } } ManualLogSource log6 = LyricDisplayPlugin.Log; if (log6 != null) { log6.LogWarning((object)("[HUD] no CJK-capable font found; Chinese lyrics may render as boxes (bundled: " + detail2 + ", scanned: " + matched + ")")); } } private static Font TryCreateDynamicFont(string family) { try { return Font.CreateDynamicFontFromOSFont(family, 24); } catch (Exception) { return null; } } private static bool IsCjkFont(Font font) { if ((Object)(object)font == (Object)null || !font.dynamic) { return false; } try { char[] cjkProbeChars = CjkProbeChars; foreach (char c in cjkProbeChars) { if (!font.HasCharacter(c)) { return false; } } } catch (Exception) { return false; } return true; } private static Font TryFindInstalledCjkFamily(out string matched) { matched = "none"; try { List<string> list = new List<string>(); Font val = null; string[] oSInstalledFontNames = Font.GetOSInstalledFontNames(); foreach (string text in oSInstalledFontNames) { if (!LooksLikeCjkFamily(text)) { continue; } Font val2 = TryCreateDynamicFont(text); if (IsCjkFont(val2)) { if ((Object)(object)val == (Object)null) { val = val2; list.Add(text); } else if (list.Count < 5) { list.Add(text); } } } if (list.Count > 0) { matched = string.Join(", ", list.ToArray()); } return val; } catch (Exception) { return null; } } private static bool LooksLikeCjkFamily(string family) { if (string.IsNullOrEmpty(family)) { return false; } string text = family.ToLowerInvariant(); string[] cjkFamilyHints = CjkFamilyHints; foreach (string value in cjkFamilyHints) { if (text.Contains(value)) { return true; } } return false; } private Font LoadBundledFont(out string detail) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown detail = "no Fonts folder"; if (string.IsNullOrEmpty(_fontsFolder) || !Directory.Exists(_fontsFolder)) { return null; } try { List<string> list = new List<string>(); list.AddRange(Directory.GetFiles(_fontsFolder, "*.otf")); list.AddRange(Directory.GetFiles(_fontsFolder, "*.ttf")); list.Sort(StringComparer.OrdinalIgnoreCase); if (list.Count == 0) { detail = "no otf/ttf in the Fonts folder"; return null; } foreach (string item in list) { string fileName = Path.GetFileName(item); try { Font val = new Font(item); if (IsCjkFont(val)) { _bundledFontPath = item; detail = "loaded by path: " + fileName; return val; } } catch (Exception ex) { detail = fileName + " failed to load by path (" + ex.GetType().Name + ")"; } if (!NativeFontRegistrar.Register(item)) { detail = fileName + " GDI registration failed"; continue; } foreach (string item2 in DeriveFamilyNames(fileName)) { Font val2 = TryCreateDynamicFont(item2); if (IsCjkFont(val2)) { _bundledFontPath = item; detail = "loaded as family \"" + item2 + "\" after GDI registration (" + fileName + ")"; return val2; } } detail = fileName + " registered, but no family name matched"; } } catch (Exception ex2) { detail = "failed to scan the Fonts folder: " + ex2.Message; } return null; } private static IEnumerable<string> DeriveFamilyNames(string fileName) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase); List<string> candidates = new List<string>(); Add(fileNameWithoutExtension); Add(SplitCamelCase(fileNameWithoutExtension)); int num = fileNameWithoutExtension.IndexOfAny(new char[3] { '-', '_', ' ' }); if (num > 0) { string value = fileNameWithoutExtension.Substring(0, num); Add(value); Add(SplitCamelCase(value)); } return candidates; void Add(string text) { if (!string.IsNullOrEmpty(text) && seen.Add(text)) { candidates.Add(text); } } } private static string SplitCamelCase(string value) { if (string.IsNullOrEmpty(value)) { return value; } StringBuilder stringBuilder = new StringBuilder(value.Length + 4); for (int i = 0; i < value.Length; i++) { char c = value[i]; if (i > 0 && char.IsUpper(c) && !char.IsUpper(value[i - 1])) { stringBuilder.Append(' '); } stringBuilder.Append(c); } return stringBuilder.ToString(); } private void DrawOutlined(Rect rect, string text, GUIStyle style) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; GUI.color = Color.black; GUI.Label(new Rect(((Rect)(ref rect)).x + 1f, ((Rect)(ref rect)).y + 1f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), text, style); GUI.color = color; GUI.Label(rect, text, style); } private void DrawPanel(Rect rect, float alpha) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (!(alpha <= 0f)) { if ((Object)(object)_panelTexture == (Object)null) { _panelTexture = new Texture2D(1, 1); _panelTexture.SetPixel(0, 0, Color.white); _panelTexture.Apply(); } Color color = GUI.color; GUI.color = new Color(0f, 0f, 0f, Mathf.Clamp01(alpha)); GUI.DrawTexture(rect, (Texture)(object)_panelTexture); GUI.color = color; } } } internal static class LyriclibBridge { private const string LibraryGuid = "Lyriclib"; private const string ApiTypeName = "Lyriclib.LyriclibApi"; private const float GiveUpAfterSeconds = 10f; private static bool _resolved; private static float _firstPollTime = -1f; internal static void Poll(ManualLogSource log) { if (!_resolved) { if (Chainloader.PluginInfos.TryGetValue("Lyriclib", out var value)) { _resolved = true; log.LogInfo((object)("[FLAC] playback is provided by Lyriclib " + value.Metadata.Version?.ToString() + "; LyricDisplay no longer decodes flac itself." + DescribeSwitch())); } else if (_firstPollTime < 0f) { _firstPollTime = Time.realtimeSinceStartup; } else if (Time.realtimeSinceStartup - _firstPollTime >= 10f) { _resolved = true; log.LogWarning((object)"[FLAC] Lyriclib was not found, so the .flac tracks in the CustomBoomboxMusic folders will not play. Install BiliBiliMOXL-Lyriclib to bring flac playback back; every other format is loaded by CustomBoomboxMusic as before."); } } } private static string DescribeSwitch() { try { Type type = null; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { type = assemblies[i].GetType("Lyriclib.LyriclibApi", throwOnError: false); if (type != null) { break; } } if (type == null) { return string.Empty; } PropertyInfo property = type.GetProperty("IsFlacSupportEnabled", BindingFlags.Static | BindingFlags.Public); if (property == null || property.PropertyType != typeof(bool)) { return string.Empty; } return ((bool)property.GetValue(null, null)) ? " Its flac support is on." : " Its flac support is switched off in Lyriclib.cfg, so flac tracks stay silent."; } catch { return string.Empty; } } } public sealed class LyricLibrary { private readonly string _folder; private readonly Dictionary<string, List<LyricLine>> _index = new Dictionary<string, List<LyricLine>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, List<LyricLine>> _externalIndex = new Dictionary<string, List<LyricLine>>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> _scannedExternalFolders = new HashSet<string>(StringComparer.OrdinalIgnoreCase); public string Folder => _folder; public int Count => _index.Count; public int ExternalCount => _externalIndex.Count; public LyricLibrary(string folder) { _folder = folder; Reload(); } public void Reload() { _index.Clear(); ScanFolderInto(_index, _folder); } public void SetExternalFolders(IEnumerable<string> folders) { _externalIndex.Clear(); _scannedExternalFolders.Clear(); if (folders == null) { return; } foreach (string folder in folders) { if (!string.IsNullOrEmpty(folder)) { string item; try { item = Path.GetFullPath(folder); } catch (Exception) { item = folder; } if (_scannedExternalFolders.Add(item)) { ScanFolderInto(_externalIndex, folder); } } } } public void AddPluginLyricFolders(string pluginsRoot, params string[] folderTokens) { if (string.IsNullOrEmpty(pluginsRoot) || !Directory.Exists(pluginsRoot) || folderTokens == null) { return; } string text = null; try { text = Path.GetFullPath(_folder); } catch (Exception) { } try { string[] directories = Directory.GetDirectories(pluginsRoot, "*", SearchOption.AllDirectories); foreach (string text2 in directories) { string fileName = Path.GetFileName(text2); if (string.IsNullOrEmpty(fileName)) { continue; } bool flag = false; foreach (string text3 in folderTokens) { if (fileName.IndexOf(text3, StringComparison.OrdinalIgnoreCase) >= 0) { flag = true; break; } if (fileName.Equals("Lyric", StringComparison.OrdinalIgnoreCase) && text3.Equals("Lyrics", StringComparison.OrdinalIgnoreCase)) { flag = true; break; } } if (flag) { string text4; try { text4 = Path.GetFullPath(text2); } catch (Exception) { text4 = text2; } if ((text == null || !string.Equals(text4, text, StringComparison.OrdinalIgnoreCase)) && _scannedExternalFolders.Add(text4)) { ScanFolderInto(_externalIndex, text2); } } } } catch (Exception ex3) { ManualLogSource log = LyricDisplayPlugin.Log; if (log != null) { log.LogWarning((object)("Failed to scan plugins folder for lyric folders: " + ex3.Message)); } } } public LyricTimeline Load(string songName, string fileBaseName = null) { LyricTimeline lyricTimeline = TryLoadFrom(_externalIndex, songName); if (lyricTimeline != null) { return lyricTimeline; } lyricTimeline = TryLoadFrom(_externalIndex, fileBaseName); if (lyricTimeline != null) { return lyricTimeline; } lyricTimeline = TryLoadFrom(_index, songName); if (lyricTimeline != null) { return lyricTimeline; } return TryLoadFrom(_index, fileBaseName); } private static LyricTimeline TryLoadFrom(Dictionary<string, List<LyricLine>> index, string key) { if (string.IsNullOrEmpty(key)) { return null; } if (index.TryGetValue(key, out var value)) { return new LyricTimeline(value); } return null; } private void ScanFolderInto(Dictionary<string, List<LyricLine>> target, string folder) { if (!Directory.Exists(folder)) { return; } Stack<string> stack = new Stack<string>(); stack.Push(folder); while (stack.Count > 0) { string text = stack.Pop(); try { string[] directories = Directory.GetDirectories(text); foreach (string text2 in directories) { string fileName = Path.GetFileName(text2); if (fileName.IndexOf("backup", StringComparison.OrdinalIgnoreCase) < 0 && fileName.IndexOf("bak", StringComparison.OrdinalIgnoreCase) < 0 && fileName.IndexOf("old", StringComparison.OrdinalIgnoreCase) < 0) { stack.Push(text2); } } directories = Directory.GetFiles(text, "*.lrc"); foreach (string text3 in directories) { try { List<LyricLine> list = LyricParser.ParseFile(text3); if (list.Count != 0) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text3); if (!string.IsNullOrEmpty(fileNameWithoutExtension) && (!target.TryGetValue(fileNameWithoutExtension, out var value) || !HasWordTags(value) || HasWordTags(list))) { target[fileNameWithoutExtension] = list; } } } catch (Exception ex) { ManualLogSource log = LyricDisplayPlugin.Log; if (log != null) { log.LogWarning((object)("Failed to parse lyric file " + text3 + ": " + ex.Message)); } } } } catch (Exception ex2) { ManualLogSource log2 = LyricDisplayPlugin.Log; if (log2 != null) { log2.LogWarning((object)("Failed to scan lyric folder " + text + ": " + ex2.Message)); } } } } private static bool HasWordTags(List<LyricLine> lines) { for (int i = 0; i < lines.Count; i++) { IReadOnlyList<LyricWord> words = lines[i].Words; if (words != null && words.Count > 0) { return true; } } return false; } } public sealed class LyricWord { public double StartMs { get; } public double DurationMs { get; } public string Text { get; } public double EndMs => StartMs + DurationMs; public LyricWord(double startMs, double durationMs, string text) { StartMs = startMs; DurationMs = durationMs; Text = text ?? string.Empty; } } public sealed class LyricLine { public double TimeSeconds { get; } public string Text { get; } public string Translation { get; } public IReadOnlyList<LyricWord> Words { get; } public LyricLine(double timeSeconds, string text, string translation = null, IReadOnlyList<LyricWord> words = null) { TimeSeconds = timeSeconds; Text = text ?? string.Empty; Translation = translation; Words = words; } } public static class LyricParser { private static readonly Regex TimestampRegex = new Regex("\\[(\\d{1,3}):(\\d{1,2})(?:[.:](\\d{1,3}))?\\]", RegexOptions.Compiled | RegexOptions.CultureInvariant); private static readonly Regex OffsetRegex = new Regex("^\\[offset:\\s*([+-]?\\d+)\\]$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant); private static readonly Regex WordTagRegex = new Regex("<\\d+,\\d+>", RegexOptions.Compiled | RegexOptions.CultureInvariant); private const char WordTagPlaceholder = '\ue000'; private static readonly Regex WordTagCaptureRegex = new Regex("<(\\d+),(\\d+)>([^<]*)", RegexOptions.Compiled | RegexOptions.CultureInvariant); private static readonly Regex WhitespaceRegex = new Regex("\\s+", RegexOptions.Compiled | RegexOptions.CultureInvariant); private static readonly Regex SpaceBeforePunctuationRegex = new Regex("\\s+([,.;:!?])", RegexOptions.Compiled | RegexOptions.CultureInvariant); public static List<LyricLine> Parse(string text) { List<LyricLine> list = new List<LyricLine>(); if (string.IsNullOrEmpty(text)) { return list; } double num = 0.0; string[] array = text.Split('\n'); for (int i = 0; i < array.Length; i++) { string text2 = array[i].TrimEnd('\r'); List<double> list2 = new List<double>(); int num2 = 0; bool flag = true; Match match = TimestampRegex.Match(text2); while (match.Success) { if (match.Index != num2) { flag = false; break; } double num3 = double.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture); double num4 = double.Parse(match.Groups[2].Value, CultureInfo.InvariantCulture); double num5 = 0.0; if (match.Groups[3].Success) { string value = match.Groups[3].Value; num5 = double.Parse(value, CultureInfo.InvariantCulture) / Math.Pow(10.0, value.Length); } list2.Add(num3 * 60.0 + num4 + num5); num2 = match.Index + match.Length; match = match.NextMatch(); } if (!flag || list2.Count == 0) { Match match2 = OffsetRegex.Match(text2); if (match2.Success) { num = double.Parse(match2.Groups[1].Value, CultureInfo.InvariantCulture) / 1000.0; } continue; } string segment = text2.Substring(num2).Trim(); string text3 = RestoreDisplayText(segment); IReadOnlyList<LyricWord> words = ParseWords(segment); foreach (double item in list2) { list.Add(new LyricLine(item + num, text3, null, words)); } } list = list.OrderBy((LyricLine l) => l.TimeSeconds).ToList(); return MergeTranslations(list); } private static string RestoreDisplayText(string segment) { string input = WordTagRegex.Replace(segment, new string('\ue000', 1)); input = WhitespaceRegex.Replace(input, " ").Trim(); input = SpaceBeforePunctuationRegex.Replace(input, "$1"); if (input.IndexOf('\ue000') < 0) { return input; } StringBuilder stringBuilder = new StringBuilder(input.Length + 8); for (int i = 0; i < input.Length; i++) { char c = input[i]; if (c != '\ue000') { stringBuilder.Append(c); continue; } char c2 = FindNeighbor(input, i, -1); char c3 = FindNeighbor(input, i, 1); if (c2 != 0 && c3 != 0) { bool num = IsAsciiWordChar(c2) && !IsCjkChar(c2); bool flag = IsAsciiWordChar(c3) && !IsCjkChar(c3); bool flag2 = IsAsciiPunctuation(c2); if ((num && flag) || (flag2 && flag)) { stringBuilder.Append(' '); } } } return stringBuilder.ToString(); } private static char FindNeighbor(string text, int index, int step) { for (int i = index + step; i >= 0 && i < text.Length; i += step) { char c = text[i]; if (c != '\ue000') { if (char.IsWhiteSpace(c)) { return '\0'; } return c; } } return '\0'; } internal static bool IsCjkChar(char c) { if ((c < '\u3040' || c > 'ヿ') && (c < '㐀' || c > '鿿')) { if (c >= '가') { return c <= '\ud7af'; } return false; } return true; } internal static bool IsAsciiWordChar(char c) { if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c > '9') && c != '\'') { return c == '-'; } return true; } internal static bool IsAsciiPunctuation(char c) { switch (c) { case '!': case ',': case '.': case ':': case ';': case '?': return true; default: return false; } } private static IReadOnlyList<LyricWord> ParseWords(string segment) { List<LyricWord> list = null; Match match = WordTagCaptureRegex.Match(segment); while (match.Success) { double startMs = double.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture); double durationMs = double.Parse(match.Groups[2].Value, CultureInfo.InvariantCulture); string text = match.Groups[3].Value.Trim(); if (text.Length > 0) { if (list == null) { list = new List<LyricWord>(); } list.Add(new LyricWord(startMs, durationMs, text)); } match = match.NextMatch(); } if (list == null || list.Count <= 0) { return null; } return list; } private static List<LyricLine> MergeTranslations(List<LyricLine> lines) { List<LyricLine> list = new List<LyricLine>(lines.Count); int num = 0; while (num < lines.Count) { LyricLine lyricLine = lines[num]; string text = null; int i; for (i = num + 1; i < lines.Count && Math.Abs(lines[i].TimeSeconds - lyricLine.TimeSeconds) < 0.001; i++) { text = lines[i].Text; } if (text != null && string.Equals(text.Trim(), lyricLine.Text.Trim(), StringComparison.Ordinal)) { text = null; } list.Add(new LyricLine(lyricLine.TimeSeconds, lyricLine.Text, text, lyricLine.Words)); num = i; } return list; } public static List<LyricLine> ParseFile(string path) { return Parse(ReadFileSmart(path)); } private static string ReadFileSmart(string path) { try { return File.ReadAllText(path, Encoding.UTF8); } catch (Exception) { } try { return File.ReadAllText(path, Encoding.Default); } catch (Exception) { return string.Empty; } } } internal static class LyricRichText { private const string HeaderColorHex = "FFFFFF73"; private const string NextColorHex = "FFFFFFA6"; public static string Compose(StringBuilder builder, IReadOnlyList<LyricRenderTrack> tracks, int fontSize, bool showHeader) { return Compose(builder, tracks, fontSize, showHeader, null); } public static string Compose(StringBuilder builder, IReadOnlyList<LyricRenderTrack> tracks, int fontSize, bool showHeader, RichTextCache cache) { if (builder == null || tracks == null || tracks.Count == 0) { cache?.Invalidate(); return null; } if (cache != null && cache.Matches(tracks, fontSize, showHeader)) { return cache.Result; } builder.Length = 0; int value = Mathf.Max(9, fontSize - 10); int value2 = Mathf.Max(10, fontSize - 6); for (int i = 0; i < tracks.Count; i++) { LyricRenderTrack lyricRenderTrack = tracks[i]; if (lyricRenderTrack != null) { if (builder.Length > 0) { builder.Append('\n'); } string value3 = (showHeader ? lyricRenderTrack.HeaderText : null); if (!string.IsNullOrEmpty(value3)) { builder.Append("<size=").Append(value).Append("><color=#") .Append("FFFFFF73") .Append('>') .Append(value3) .Append("</color></size>\n"); } builder.Append((!string.IsNullOrEmpty(lyricRenderTrack.CurrentTextRich)) ? lyricRenderTrack.CurrentTextRich : lyricRenderTrack.CurrentText); if (tracks.Count == 1 && !string.IsNullOrEmpty(lyricRenderTrack.SecondText)) { builder.Append("\n<size=").Append(value2).Append("><color=#") .Append("FFFFFFA6") .Append('>') .Append(lyricRenderTrack.SecondText) .Append("</color></size>"); } } } string result = ((builder.Length == 0) ? null : builder.ToString()); cache?.Store(tracks, fontSize, showHeader, result); return result; } } internal sealed class RichTextCache { private const int RefsPerTrack = 3; private string[] _refs = new string[6]; private int _refCount; private int _fontSize = int.MinValue; private bool _showHeader; private bool _valid; private string[] _glyphRefs = new string[6]; private int _glyphCount; private int _glyphFontSize = int.MinValue; private bool _glyphShowHeader; private bool _glyphValid; public string Result; public bool GlyphsUnchanged { get; private set; } public bool Matches(IReadOnlyList<LyricRenderTrack> tracks, int fontSize, bool showHeader) { if (!_valid || _fontSize != fontSize || _showHeader != showHeader) { return false; } int num = tracks.Count * 3; if (num != _refCount) { return false; } for (int i = 0; i < num; i++) { if ((object)_refs[i] != Input(tracks, i)) { return false; } } return true; } public void Store(IReadOnlyList<LyricRenderTrack> tracks, int fontSize, bool showHeader, string result) { int num = tracks.Count * 3; if (_refs.Length < num) { _refs = new string[num]; } for (int i = 0; i < num; i++) { _refs[i] = Input(tracks, i); } _refCount = num; _fontSize = fontSize; _showHeader = showHeader; _valid = true; Result = result; GlyphsUnchanged = GlyphMatches(tracks, fontSize, showHeader); if (_glyphRefs.Length < num) { _glyphRefs = new string[num]; } for (int j = 0; j < num; j++) { _glyphRefs[j] = Input(tracks, j, plainLine: true); } _glyphCount = num; _glyphFontSize = fontSize; _glyphShowHeader = showHeader; _glyphValid = true; } public void Invalidate() { _valid = false; _refCount = 0; Result = null; _glyphValid = false; _glyphCount = 0; GlyphsUnchanged = false; } private bool GlyphMatches(IReadOnlyList<LyricRenderTrack> tracks, int fontSize, bool showHeader) { if (!_glyphValid || _glyphFontSize != fontSize || _glyphShowHeader != showHeader) { return false; } int num = tracks.Count * 3; if (num != _glyphCount) { return false; } for (int i = 0; i < num; i++) { if ((object)_glyphRefs[i] != Input(tracks, i, plainLine: true)) { return false; } } return true; } private static string Input(IReadOnlyList<LyricRenderTrack> tracks, int index, bool plainLine = false) { LyricRenderTrack lyricRenderTrack = tracks[index / 3]; if (lyricRenderTrack == null) { return null; } switch (index % 3) { case 0: return lyricRenderTrack.HeaderText; case 1: if (plainLine) { return lyricRenderTrack.CurrentText; } if (string.IsNullOrEmpty(lyricRenderTrack.CurrentTextRich)) { return lyricRenderTrack.CurrentText; } return lyricRenderTrack.CurrentTextRich; default: return lyricRenderTrack.SecondText; } } } public sealed class LyricTimeline { private readonly List<LyricLine> _lines; private int _index = -1; public bool IsLoaded => _lines.Count > 0; public int Count => _lines.Count; public double Duration { get { if (_lines.Count <= 0) { return 0.0; } return _lines[_lines.Count - 1].TimeSeconds; } } public LyricLine Current { get { if (!IsValidIndex(_index)) { return null; } return _lines[_index]; } } public LyricLine Next { get { if (!IsValidIndex(_index + 1)) { return null; } return _lines[_index + 1]; } } public LyricTimeline(List<LyricLine> lines) { _lines = lines ?? new List<LyricLine>(); } public void Reset() { _index = -1; } public bool Advance(double timeSeconds) { if (_index < 0) { _index = LowerBound(timeSeconds); return _index >= 0; } if (timeSeconds < _lines[_index].TimeSeconds) { int num = LowerBound(timeSeconds); if (num == _index) { return false; } _index = num; return true; } int i; for (i = _index + 1; i < _lines.Count && _lines[i].TimeSeconds <= timeSeconds; i++) { } int num2 = i - 1; if (num2 == _index) { return false; } _index = num2; return true; } private bool IsValidIndex(int index) { if (index >= 0) { return index < _lines.Count; } return false; } private int LowerBound(double t) { int num = 0; int num2 = _lines.Count - 1; int result = -1; while (num <= num2) { int num3 = (num + num2) / 2; if (_lines[num3].TimeSeconds <= t) { result = num3; num = num3 + 1; } else { num2 = num3 - 1; } } return result; } } internal static class NativeFontRegistrar { private const uint FR_PRIVATE = 16u; private const uint WM_FONTCHANGE = 29u; private const uint SMTO_ABORTIFHUNG = 2u; private static readonly IntPtr HWND_BROADCAST = new IntPtr(65535); private static readonly HashSet<string> Registered = new HashSet<string>(StringComparer.OrdinalIgnoreCase); [DllImport("gdi32.dll", CharSet = CharSet.Unicode, EntryPoint = "AddFontResourceExW", SetLastError = true)] private static extern int AddFontResourceEx(string fileName, uint flags, IntPtr reserved); [DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageTimeoutW", SetLastError = true)] private static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, uint flags, uint timeout, out IntPtr result); public static bool Register(string path) { if (string.IsNullOrEmpty(path)) { return false; } lock (Registered) { if (Registered.Contains(path)) { return true; } try { int num = AddFontResourceEx(path, 16u, IntPtr.Zero); SendMessageTimeout(HWND_BROADCAST, 29u, IntPtr.Zero, IntPtr.Zero, 2u, 1000u, out var _); if (num <= 0) { ManualLogSource log = LyricDisplayPlugin.Log; if (log != null) { log.LogWarning((object)("[HUD] AddFontResourceEx rejected \"" + path + "\"")); } return false; } Registered.Add(path); return true; } catch (Exception ex) { ManualLogSource log2 = LyricDisplayPlugin.Log; if (log2 != null) { log2.LogWarning((object)("[HUD] AddFontResourceEx failed for \"" + path + "\": " + ex.Message)); } return false; } } } } internal sealed class NativeLyricText { private const string RootName = "MOXL_LyricDisplay_NativeLyric"; private const string TextName = "Lyrics"; private const float MaxWidthRatio = 0.85f; private const float MinWidthRatio = 0.25f; private const float ReferenceFontSize = 22f; private TextMeshProUGUI _counter; private RectTransform _hostRect; private RectTransform _rootRect; private TextMeshProUGUI _text; private TMP_FontAsset _fontAsset; private bool _fontResolved; private bool _useSessionGlyphs = true; private readonly StringBuilder _builder = new StringBuilder(512); private readonly RichTextCache _richCache = new RichTextCache(); private string _cachedRich; private float _cachedFontSize = -1f; private float _cachedWidth = -1f; private LyricAlign _cachedAlign = (LyricAlign)(-1); private Vector2 _cachedPosition; private bool _hasCachedPosition; public bool UseSessionGlyphs { get { return _useSessionGlyphs; } set { if (_useSessionGlyphs != value) { _useSessionGlyphs = value; _fontResolved = false; _fontAsset = null; } } } public bool IsAttached { get { if ((Object)(object)_rootRect != (Object)null) { return (Object)(object)_text != (Object)null; } return false; } } public static void HudStartPostfix(HUDManager __instance) { try { LyricDisplayPlugin.Instance?.OnHudManagerStarted(__instance); } catch (Exception arg) { ManualLogSource log = LyricDisplayPlugin.Log; if (log != null) { log.LogError((object)$"[HUD][Native] attaching the lyric text failed: {arg}"); } } } public void Attach(HUDManager hud) { //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) DestroyRoot(); _counter = (((Object)(object)hud != (Object)null) ? hud.weightCounter : null); _fontResolved = false; _fontAsset = null; _cachedRich = null; _cachedFontSize = -1f; _cachedWidth = -1f; _cachedAlign = (LyricAlign)(-1); _richCache.Invalidate(); _hasCachedPosition = false; if ((Object)(object)hud == (Object)null) { ManualLogSource log = LyricDisplayPlugin.Log; if (log != null) { log.LogWarning((object)"[HUD][Native] HUDManager is null, nothing to attach to"); } return; } if ((Object)(object)_counter == (Object)null) { ManualLogSource log2 = LyricDisplayPlugin.Log; if (log2 != null) { log2.LogWarning((object)"[HUD][Native] weightCounter is null, nothing to attach to"); } return; } Canvas canvas = ((Graphic)_counter).canvas; if ((Object)(object)canvas == (Object)null) { ManualLogSource log3 = LyricDisplayPlugin.Log; if (log3 != null) { log3.LogWarning((object)"[HUD][Native] the HUD canvas was not found, nothing to attach to"); } return; } Transform transform = ((Component)canvas).transform; _hostRect = (RectTransform)(object)((transform is RectTransform) ? transform : null); try { _rootRect = BuildHierarchy(transform); } catch (Exception arg) { ManualLogSource log4 = LyricDisplayPlugin.Log; if (log4 != null) { log4.LogError((object)$"[HUD][Native] lyric text creation failed: {arg}"); } _rootRect = null; _text = null; } if ((Object)(object)_rootRect != (Object)null) { ManualLogSource log5 = LyricDisplayPlugin.Log; if (log5 != null) { log5.LogInfo((object)("[HUD][Native] lyric text attached to the game HUD: host=\"" + ((Object)transform).name + "\" hostScale=" + ((Transform)_rootRect).lossyScale.y + " weightCounterFontSize=" + ((TMP_Text)_counter).fontSize + " weightCounterScale=" + ((Transform)((TMP_Text)_counter).rectTransform).lossyScale.y + " layer=" + ((Component)transform).gameObject.layer)); } } } private RectTransform BuildHierarchy(Transform host) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) RectTransform component = new GameObject("MOXL_LyricDisplay_NativeLyric", new Type[1] { typeof(RectTransform) }) { layer = ((Component)host).gameObject.layer }.GetComponent<RectTransform>(); ((Transform)component).SetParent(host, false); component.anchorMin = new Vector2(0.5f, 0.5f); component.anchorMax = new Vector2(0.5f, 0.5f); component.pivot = new Vector2(0.5f, 0.5f); ((Transform)component).localScale = Vector3.one; ((Transform)component).localRotation = Quaternion.identity; component.anchoredPosition = Vector2.zero; component.sizeDelta = new Vector2(320f, 60f); GameObject val = new GameObject("Lyrics", new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); val.layer = ((Component)host).gameObject.layer; val.transform.SetParent((Transform)(object)component, false); RectTransform component2 = val.GetComponent<RectTransform>(); component2.anchorMin = Vector2.zero; component2.anchorMax = Vector2.one; component2.pivot = new Vector2(0.5f, 0.5f); component2.offsetMin = Vector2.zero; component2.offsetMax = Vector2.zero; _text = val.GetComponent<TextMeshProUGUI>(); ((Graphic)_text).raycastTarget = false; ((TMP_Text)_text).richText = true; ((Graphic)_text).color = Color.white; ((TMP_Text)_text).overflowMode = (TextOverflowModes)0; ((TMP_Text)_text).enableWordWrapping = true; ((TMP_Text)_text).alignment = (TextAlignmentOptions)514; ApplyOutline(_text); return component; } private static void ApplyOutline(TextMeshProUGUI text) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) try { Material fontMaterial = ((TMP_Text)text).fontMaterial; if (!((Object)(object)fontMaterial == (Object)null)) { fontMaterial.EnableKeyword("OUTLINE_ON"); fontMaterial.SetColor(ShaderUtilities.ID_OutlineColor, new Color(0f, 0f, 0f, 0.85f)); fontMaterial.SetFloat(ShaderUtilities.ID_OutlineWidth, 0.15f); } } catch (Exception) { } } public bool Render(IReadOnlyList<LyricRenderTrack> tracks, float verticalOffset, float horizontalOffset, int fontSize, LyricAlign align, bool showHeader) { //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) if (!IsAttached) { return false; } if (!EnsureFont()) { return false; } string text = LyricRichText.Compose(_builder, tracks, fontSize, showHeader, _richCache); if (string.IsNullOrEmpty(text)) { Hide(); return true; } if (!TryGetHostSize(out var width, out var height)) { return false; } if (!((Component)_rootRect).gameObject.activeSelf) { ((Component)_rootRect).gameObject.SetActive(true); } if ((Object)(object)((TMP_Text)_text).font != (Object)(object)_fontAsset) { ((TMP_Text)_text).font = _fontAsset; } float num = Mathf.Max(6f, (float)fontSize * ResolveFontScale()); int num2; if (Mathf.Approximately(_cachedFontSize, num)) { num2 = ((_cachedAlign != align) ? 1 : 0); if (num2 == 0) { goto IL_010d; } } else { num2 = 1; } _cachedFontSize = num; _cachedAlign = align; ((TMP_Text)_text).fontSize = num; ((TMP_Text)_text).alignment = (TextAlignmentOptions)(align switch { LyricAlign.Right => 516, LyricAlign.Left => 513, _ => 514, }); goto IL_010d; IL_010d: bool flag = (object)_cachedRich != text; if (flag) { ((TMP_Text)_text).text = text; _cachedRich = text; } if (num2 != 0 || !Mathf.Approximately(_cachedWidth, width) || (flag && !_richCache.GlyphsUnchanged)) { RebuildLayout(width, text); } Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(width * horizontalOffset, height * verticalOffset - height * 0.5f); if (!_hasCachedPosition || !Mathf.Approximately(_cachedPosition.x, val.x) || !Mathf.Approximately(_cachedPosition.y, val.y)) { _hasCachedPosition = true; _cachedPosition = val; _rootRect.anchoredPosition = val; } return true; } public void Hide() { if ((Object)(object)_rootRect != (Object)null && ((Component)_rootRect).gameObject.activeSelf) { ((Component)_rootRect).gameObject.SetActive(false); } } private void RebuildLayout(float hostWidth, string rich) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) _cachedWidth = hostWidth; if (!((Object)(object)_rootRect == (Object)null) && !((Object)(object)_text == (Object)null)) { float num = hostWidth * 0.85f; float num2 = Mathf.Min(hostWidth * 0.25f, num); float num3 = Mathf.Clamp(((TMP_Text)_text).GetPreferredValues(rich, 0f, 0f).x, num2, num); float num4 = Mathf.Max(((TMP_Text)_text).fontSize, ((TMP_Text)_text).GetPreferredValues(rich, num3, 0f).y); _rootRect.sizeDelta = new Vector2(num3, num4); } } private bool TryGetHostSize(out float width, out float height) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) width = 0f; height = 0f; if ((Object)(object)_hostRect != (Object)null) { Rect rect = _hostRect.rect; width = ((Rect)(ref rect)).width; height = ((Rect)(ref rect)).height; } if (width <= 1f || height <= 1f) { Canvas val = (((Object)(object)_counter != (Object)null) ? ((Graphic)_counter).canvas : null); if ((Object)(object)val != (Object)null) { Rect pixelRect = val.pixelRect; width = ((Rect)(ref pixelRect)).width; pixelRect = val.pixelRect; height = ((Rect)(ref pixelRect)).height; } } if (width <= 1f || height <= 1f) { width = Screen.width; height = Screen.height; } if (width > 1f) { return height > 1f; } return false; } private float ResolveFontScale() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) float num = (((Object)(object)_counter != (Object)null) ? ((TMP_Text)_counter).fontSize : 22f); if (num <= 0.01f) { num = 22f; } float num2 = 1f; if ((Object)(object)_counter != (Object)null && (Object)(object)_rootRect != (Object)null) { float y = ((Transform)((TMP_Text)_counter).rectTransform).lossyScale.y; float y2 = ((Transform)_rootRect).lossyScale.y; if (y2 > 0.0001f && y > 0.0001f) { num2 = y / y2; } } return num / 22f * num2; } private bool EnsureFont() { if (_fontResolved) { return (Object)(object)_fontAsset != (Object)null; } _fontResolved = true; TMP_FontAsset preferred = (((Object)(object)_counter != (Object)null) ? ((TMP_Text)_counter).font : null); _fontAsset = TmpGlyphSource.Resolve(preferred, _useSessionGlyphs, out var detail); if ((Object)(object)_fontAsset != (Object)null) { ManualLogSource log = LyricDisplayPlugin.Log; if (log != null) { log.LogInfo((object)("[HUD][Native] lyric font = " + detail)); } return true; } ManualLogSource log2 = LyricDisplayPlugin.Log; if (log2 != null) { log2.LogWarning((object)("[HUD][Native] no usable TextMeshPro font (" + detail + "); the native HUD text will not be used")); } return false; } private void DestroyRoot() { if ((Object)(object)_rootRect != (Object)null) { Object.Destroy((Object)(object)((Component)_rootRect).gameObject); } _rootRect = null; _text = null; _hostRect = null; _cachedRich = null; _cachedFontSize = -1f; _cachedWidth = -1f; _cachedAlign = (LyricAlign)(-1); _richCache.Invalidate(); _hasCachedPosition = false; } } internal static class PluginInfo { public const string PLUGIN_GUID = "LyricDisplay"; public const string PLUGIN_NAME = "LyricDisplay"; public const string PLUGIN_VERSION = "1.0.4"; } [BepInPlugin("LyricDisplay", "LyricDisplay", "1.0.4")] public sealed class LyricDisplayPlugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static LyricDisplayPlugin Instance; private ConfigEntry<bool> _enabled; private ConfigEntry<string> _toggleKey; private ConfigEntry<float> _verticalOffset; private ConfigEntry<float> _horizontalOffset; private ConfigEntry<int> _fontSize; private ConfigEntry<bool> _showNextLine; private ConfigEntry<float> _backgroundAlpha; private ConfigEntry<float> _holdAfterEnd; private ConfigEntry<LyricAlign> _textAlign; private ConfigEntry<bool> _showHeader; private ConfigEntry<bool> _showDetails; private ConfigEntry<bool> _showTitle; private ConfigEntry<bool> _showArtist; private ConfigEntry<bool> _showAlbum; private ConfigEntry<bool> _showOtherInfo; private ConfigEntry<bool> _showInSpectate; private ConfigEntry<float> _maxAudioDistance; private ConfigEntry<int> _maxSimultaneousSources; private ConfigEntry<bool> _gradientEnabled; private ConfigEntry<string> _playedColor; private ConfigEntry<string> _unplayedColor; private ConfigEntry<bool> _fixLowBatteryPitch; private ConfigEntry<bool> _verboseLogging; private ConfigEntry<bool> _useNativeHudLyrics; private ConfigEntry<bool> _showUiLyrics; private LyricLibrary _library; private SongTracker _tracker; private LyricHudRenderer _renderer; private TmpLyricOverlay _tmpOverlay; private bool _tmpOverlayFailed; private NativeLyricText _nativeLyric; private bool _nativeLyricFailed; private readonly Dictionary<int, LyricTimeline> _timelineBySource = new Dictionary<int, LyricTimeline>(); private readonly Dictionary<int, int> _timelineVersionBySource = new Dictionary<int, int>(); private readonly HashSet<int> _activeSourceIds = new HashSet<int>(); private readonly List<int> _staleSourceIds = new List<int>(); private static readonly List<ActiveTrackState> EmptyTracks = new List<ActiveTrackState>(0); private List<ActiveTrackState> _activeTracks = EmptyTracks; private const int IdlePollInterval = 6; private int _idlePollCountdown; private readonly List<LyricRenderTrack> _renderTracks = new List<LyricRenderTrack>(); private string _cachedSkipReason; private bool _displayToggled = true; private Vector3 _playerPosition; private static Camera _mainCamera; private static Transform _mainCameraTransform; private bool _configDirty = true; private float _lastConfigRefresh = float.MinValue; private bool _cfgEnabled = true; private bool _cfgShowHeader = true; private bool _cfgShowDetails = true; private bool _cfgShowTitle = true; private bool _cfgShowArtist = true; private bool _cfgShowAlbum = true; private bool _cfgShowOtherInfo = true; private bool _cfgShowNextLine = true; private float _cfgVerticalOffset = 0.26f; private float _cfgHorizontalOffset; private int _cfgFontSize = 22; private float _cfgBackgroundAlpha = 0.55f; private LyricAlign _cfgTextAlign = LyricAlign.Center; private bool _cfgGradientEnabled = true; private bool _cfgUseNativeHudLyrics = true; private bool _cfgShowUiLyrics; private bool _cfgShowInSpectate = true; private float _cfgMaxAudioDistance = 35f; private int _cfgMaxSimultaneousSources = 3; private float _cfgHoldAfterEnd = 4f; private Color _cfgPlayedColor = Color.red; private Color _cfgUnplayedColor = Color.white; private string _lastPlayedColorStr; private string _lastUnplayedColorStr; private KeyCode _toggleKeyCode = (KeyCode)279; private bool _toggleKeyParsed; private ConfigEntry<bool> _preloadMusicFolders; private const float PreloadRetryInterval = 2f; private const float PreloadGiveUpAfterSeconds = 30f; private float _preloadFirstTryTime = -1f; private float _lastPreloadTry = float.MinValue; private bool _preloadDone; private static Type _startOfRoundType; private static PropertyInfo _instanceProperty; private static PropertyInfo _localPlayerProperty; private static PropertyInfo _isDeadProperty; private static FieldInfo _spectateUiField; private static bool _spectateReflectFailed; private static PropertyInfo _playerTransformProperty; private bool _diagPlaying; private bool _diagInitialized; private bool _diagGradientLogged; private bool _diagNoWordTagsLogged; private static bool _sceneCacheValid; private static bool _sceneIsGame; private static string _gameSceneSkipReason; private void Awake() { //IL_046c: Unknown result type (might be due to invalid IL or missing references) //IL_0472: Expected O, but got Unknown //IL_04b3: Unknown result type (might be due to invalid IL or missing references) //IL_04c0: Expected O, but got Unknown //IL_051a: Unknown result type (might be due to invalid IL or missing references) //IL_0527: Expected O, but got Unknown //IL_05a3: Unknown result type (might be due to invalid IL or missing references) //IL_05b0: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; _enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch of LyricDisplay. True = the mod loads and runs. False = the mod stays completely inactive: none of its Harmony hooks are applied, no lyrics are tracked and nothing is drawn, so it does not take part in the game until it is re-enabled and the game is restarted."); _toggleKey = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ToggleDisplayKey", "End", "Key to show/hide the HUD. Unity KeyCode name, e.g. F9 or End."); _verticalOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "VerticalOffset", 0.26f, "Height of the lyrics from the bottom of the screen (0-1). 0.13 sits right above the inventory bar."); _horizontalOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "HorizontalOffset", 0f, "Shift the panel left/right as a fraction of screen width. Negative = left, positive = right."); _fontSize = ((BaseUnityPlugin)this).Config.Bind<int>("Display", "FontSize", 22, "Font size of the lyrics."); _showNextLine = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowNextLine", true, "Show the next lyric line below the current one."); _backgroundAlpha = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "BackgroundAlpha", 0.55f, "Background darkness (0 = transparent, 1 = solid)."); _holdAfterEnd = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "HoldAfterEnd", 4f, "How long the last line stays after the music stops (seconds)."); _textAlign = ((BaseUnityPlugin)this).Config.Bind<LyricAlign>("Display", "TextAlign", LyricAlign.Center, "Align text inside the panel: Left, Center or Right."); _showHeader = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowHeader", true, "Show the song details above the lyrics. Kept for compatibility and equivalent to the master switch of the 'TrackDetails' section; that section's switches can turn the parts off individually."); _showDetails = ((BaseUnityPlugin)this).Config.Bind<bool>("TrackDetails", "Enabled", true, "Master switch of the song-details line above the lyrics. Off hides the line entirely, no matter what the switches below say. Works with every renderer (native HUD, UI overlay, IMGUI)."); _showTitle = ((BaseUnityPlugin)this).Config.Bind<bool>("TrackDetails", "ShowTitle", true, "Show the track title in the song-details line."); _showArtist = ((BaseUnityPlugin)this).Config.Bind<bool>("TrackDetails", "ShowArtist", true, "Show the artist in the song-details line."); _showAlbum = ((BaseUnityPlugin)this).Config.Bind<bool>("TrackDetails", "ShowAlbum", true, "Show the album in the song-details line."); _showOtherInfo = ((BaseUnityPlugin)this).Config.Bind<bool>("TrackDetails", "ShowOtherInfo", true, "Show extra tag information - comment, genre, track number, year - in the song-details line, joined after the album."); _showInSpectate = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowInSpectate", true, "Show lyrics while spectating after death. Set false to hide them."); _maxAudioDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Distance", "MaxAudioDistance", 35f, "Max distance to show a boombox's lyrics. Farther boomboxes are ignored."); _maxSimultaneousSources = ((BaseUnityPlugin)this).Config.Bind<int>("Distance", "MaxSimultaneousSources", 3, "Max number of boomboxes shown at once (closest first)."); _gradientEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Gradient", "GradientEnabled", true, "Karaoke gradient: played words are tinted, upcoming words stay the unplayed color."); _playedColor = ((BaseUnityPlugin)this).Config.Bind<string>("Gradient", "PlayedColor", "FF0000", "Color of already-played words (hex RGB, e.g. FF0000)."); _unplayedColor = ((BaseUnityPlugin)this).Config.Bind<string>("Gradient", "UnplayedColor", "FFFFFF", "Color of not-yet-played words (hex RGB, e.g. FFFFFF)."); _fixLowBatteryPitch = ((BaseUnityPlugin)this).Config.Bind<bool>("Fixes", "FixLowBatteryPitchAfterRecharge", false, "After a low battery recharges above 5%, restore the boombox pitch to normal. Vanilla keeps the lowered pitch until the music stops."); _verboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "VerboseLogging", false, "Re-print the low-level tracking diagnostics even while nothing changes (at most one line per topic every 30s). Enable only while troubleshooting."); _useNativeHudLyrics = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "UseNativeHudLyrics", true, "Draw the lyrics with the game's own HUD and the Chinese glyphs the session already has loaded: a TextMeshPro label inside the HUD hierarchy the game builds, drawn with the HUD's own font asset, so installed Chinese localization / font packs are picked up automatically. Off: the bundled / system font and this mod's own renderers instead. This is the default renderer."); _showUiLyrics = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "UseUiLyrics", false, "Draw the lyrics on this mod's own screen space overlay canvas with a TextMeshPro label (the UI version). Off by default, because the game's own HUD text is the default renderer. Enable this to draw with the UI label instead, for example while the native HUD text is switched off or unavailable. With both this and the native HUD text switched off nothing is drawn at all; when an enabled renderer fails, the IMGUI overlay is used as the last resort so the lyrics stay visible."); _preloadMusicFolders = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PreloadMusicFolders", false, "[Warning] Do not use this feature unless you know what you are doing. Preload the music folders before the mode is chosen: with this on, the music folders are read while the main menu is still up - before the player picks the online or the LAN mode - instead of waiting for the first boombox to play. Meant only for troubleshooting folder / index problems, because it loads the folders earlier than the game would and costs loading time and memory for a session that may never touch a boombox. Every session with this switch on logs the warning above as well."); if (!_enabled.Value) { Log.LogInfo((object)"LyricDisplay is disabled in the config; the mod stays inactive (no hooks, no lyric tracking, no HUD text)."); return; } string text = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "."; _library = new LyricLibrary(ResolveLyricsFolder(text)); _tracker = new SongTracker(); _renderer = new LyricHudRenderer(Path.Combine(text, "Fonts"), _useNativeHudLyrics.Value); _tmpOverlay = new TmpLyricOverlay(); _tmpOverlay.UseSessionGlyphs = _useNativeHudLyrics.Value; _nativeLyric = new NativeLyricText(); _nativeLyric.UseSessionGlyphs = _useNativeHudLyrics.Value; _library.AddPluginLyricFolders(Paths.PluginPath, "CustomBoomboxMusic", "Lyrics"); Harmony val = new Harmony("LyricDisplay"); Type type = AccessTools.TypeByName("BoomboxItem"); MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "StartMusic", (Type[])null, (Type[])null) : null); if (methodInfo != null) { val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(SongTracker), "StartMusicPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Hooked BoomboxItem.StartMusic for lyric tracking"); } else { Log.LogWarning((object)"BoomboxItem.StartMusic not found - lyric tracking unavailable"); } MethodInfo methodInfo2 = ((type != null) ? AccessTools.Method(type, "Update", (Type[])null, (Type[])null) : null); if (methodInfo2 != null) { val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(BoomboxBatteryFix), "UpdatePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Hooked BoomboxItem.Update for low-battery pitch recovery fix"); } else { Log.LogWarning((object)"BoomboxItem.Update not found - low-battery pitch recovery unavailable"); } Type type2 = AccessTools.TypeByName("HUDManager"); MethodInfo methodInfo3 = ((type2 != null) ? AccessTools.Method(type2, "Start", (Type[])null, (Type[])null) : null); MethodInfo methodInfo4 = AccessTools.Method(typeof(NativeLyricText), "HudStartPostfix", (Type[])null, (Type[])null); if (methodInfo3 != null && methodInfo4 != null) { val.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(methodInfo4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Hooked HUDManager.Start for the native HUD lyric text"); } else { Log.LogWarning((object)"HUDManager.Start not found - the native HUD lyric text is attached lazily instead"); } Log.LogInfo((object)string.Format("LyricDisplay v{0} loaded; {1} lyric file(s) indexed from {2} (plus {3} from CustomBoomboxMusic / lyric folders under the plugins root)", "1.0.4", _library.Count, _library.Folder, _library.ExternalCount)); if (_library.Count + _library.ExternalCount == 0) { Log.LogWarning((object)("No .lrc lyric file was found. Put the lyric files in '" + Path.Combine(text, "Lyrics") + "' (a folder named 'Lyric' works too), or next to the music inside a 'CustomBoomboxMusic' folder, and name every file exactly like its audio track.")); } _toggleKey.SettingChanged += delegate { ParseToggleKey(); }; _fixLowBatteryPitch.SettingChanged += delegate { BoomboxBatteryFix.Enabled = _fixLowBatteryPitch.Value; }; BoomboxBatteryFix.Enabled = _fixLowBatteryPitch.Value; ParseToggleKey(); SceneManager.activeSceneChanged += delegate { _sceneCacheValid = false; _gameSceneSkipReason = null; }; _verboseLogging.SettingChanged += delegate { Diag.Verbose = _verboseLogging.Value; }; Diag.Verbose = _verboseLogging.Value; _preloadMusicFolders.SettingChanged += delegate { _preloadFirstTryTime = -1f; _lastPreloadTry = float.MinValue; _preloadDone = false; if (_preloadMusicFolders.Value) { LogPreloadWarning(); } }; if (_preloadMusicFolders.Value) { LogPreloadWarning(); } WatchConfig<bool>(_enabled); WatchConfig<string>(_toggleKey); WatchConfig<float>(_verticalOffset); WatchConfig<float>(_horizontalOffset); WatchConfig<int>(_fontSize); WatchConfig<bool>(_showNextLine); WatchConfig<float>(_backgroundAlpha); WatchConfig<float>(_holdAfterEnd); WatchConfig<LyricAlign>(_textAlign); WatchConfig<bool>(_showHeader); WatchConfig<bool>(_showInSpectate); WatchConfig<float>(_maxAudioDistance); WatchConfig<int>(_maxSimultaneousSources); WatchConfig<bool>(_gradientEnabled); WatchConfig<string>(_playedColor); WatchConfig<string>(_unplayedColor); WatchConfig<bool>(_fixLowBatteryPitch); WatchConfig<bool>(_verboseLogging); WatchConfig<bool>(_useNativeHudLyrics); WatchConfig<bool>(_showUiLyrics); } private void WatchConfig<T>(ConfigEntry<T> entry) { entry.SettingChanged += delegate { _configDirty = true; }; } private void Update() { //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_03e0: Unknown result type (might be due to invalid IL or missing references) //IL_03e6: Unknown result type (might be due to invalid IL or missing references) //IL_045d: Unknown result type (might be due to invalid IL or missing references) LyriclibBridge.Poll(Log); if (_library != null) { PumpPreload(); LethalConfigBridge.Poll(Log); } if (_configDirty || Time.unscaledTime - _lastConfigRefresh > 2f) { RefreshConfigCache(); } if (!_cfgEnabled) { HideNativeOverlay(); return; } EnsureNativeLyricAttached(); string text = (_displayToggled ? null : "display toggled off"); if (text == null) { text = GameSceneSkipReason(); } if (text == null && !_cfgShowInSpectate && IsSpectating()) { text = "spectating (player dead)"; }