using System; using System.CodeDom.Compiler; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.Common; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Logging; using GameConsole; using HarmonyLib; using Microsoft.CodeAnalysis; using Microsoft.Data.Sqlite; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using PluginConfig.API; using PluginConfig.API.Decorators; using PluginConfig.API.Fields; using SQLitePCL; using TMPro; using ULTRASTATS.Capture; using ULTRASTATS.Core; using ULTRASTATS.Legacy; using ULTRASTATS.Patches; using ULTRASTATS.Queries; using ULTRASTATS.Storage; using ULTRASTATS.UI; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; 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("ULTRASTATS")] [assembly: AssemblyConfiguration("Package")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+5ea8e60157e0ecbfb2ce3cec5474b8c8a6684e42")] [assembly: AssemblyProduct("ULTRASTATS")] [assembly: AssemblyTitle("ULTRASTATS")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ULTRASTATS { internal static class BepInExLogs_US { private static bool DebugEnabled => Plugin.DebugLoggingEnabled; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ModLoaded() { Plugin.Log.LogInfo((object)"[ULTRASTATS] : Mod loaded"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Queue(string message) { Plugin.Log.LogInfo((object)("[Queue] : " + message)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void QueueCleared() { Queue("Queue has been cleared!"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void QueueAppended(long id, string path) { Queue($"Run {id} has been appended to {path}"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Debug(string message) { if (DebugEnabled) { Plugin.Log.LogInfo((object)("[Debug] : " + message)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Debug(Func messageFactory) { if (DebugEnabled) { Plugin.Log.LogInfo((object)("[Debug] : " + messageFactory())); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Warn(string message) { Plugin.Log.LogWarning((object)("[ULTRASTATS] : " + message)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Error(string message) { Plugin.Log.LogError((object)("[ULTRASTATS] : " + message)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Error(string context, Exception ex) { Plugin.Log.LogError((object)$"[ULTRASTATS] : {context}: {ex}"); } } [BepInProcess("ULTRAKILL.exe")] [BepInPlugin("atom.ultrastats", "ULTRASTATS", "0.1.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { internal enum MainMenuButtonCornerOption { BottomRight, BottomLeft, TopRight, TopLeft } internal enum DefaultMainMenuTabOption { Info, Stats, Plots } public const string ModGuid = "atom.ultrastats"; public const string ModName = "ULTRASTATS"; public const string ModVer = "0.1.3"; private Harmony? _harmony; private bool _lifecycleHooksRegistered; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static UltraStatsConfig Config { get; private set; } internal static string DataFolderPath => UltraStatsPaths.DataFolderPath; internal static string DataFolderParentPath => UltraStatsPaths.DataFolderParentPath; internal static bool DebugLoggingEnabled => Config?.EnableDebugLogging ?? false; internal static bool EndscreenDiscardEnabled => Config?.EnableEndscreenDiscard ?? false; internal static KeyCode EndscreenDiscardKey => (KeyCode)(((??)Config?.DiscardPendingRunKey) ?? 127); internal static bool CampaignLoggingEnabled => Config?.EnableCampaignLogging ?? true; internal static bool CybergrindLoggingEnabled => Config?.EnableCybergrindLogging ?? true; internal static bool CustomLevelLoggingEnabled { get { if (AngryLevelLoaderInstalled) { return Config?.EnableCustomLevelLogging ?? true; } return false; } } internal static bool AngryLevelLoaderInstalled => CustomLevelMetadataResolver.AngryAvailable; internal static MainMenuButtonCornerOption MainMenuButtonCorner => (MainMenuButtonCornerOption)(Config?.MainMenuButtonCorner ?? ULTRASTATS.Core.MainMenuButtonCorner.BottomRight); internal static DefaultMainMenuTabOption DefaultMainMenuTab => (DefaultMainMenuTabOption)(Config?.DefaultTab ?? MainMenuDefaultTab.Stats); internal static RunFilterSortDirection NewColumnFirstClickSortDirection => Config?.NewColumnFirstClickSortDirection ?? RunFilterSortDirection.Ascending; internal static AngryBundleSortMode AngryBundleSortMode => Config?.AngryBundleSortMode ?? AngryBundleSortMode.Alphabetical; internal static bool ShowLegacyTransitionNotice => Config?.ShowLegacyTransitionNotice ?? true; internal static bool HasSeenFreshInstallWelcome => File.Exists(UltraStatsPaths.FreshInstallWelcomeSeenMarkerPath); private void Awake() { //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Config = new UltraStatsConfig(); Config.BuildUI(); UltraStatsStartupNoticeController.CaptureStartupState(); BepInExLogs_US.ModLoaded(); BepInExLogs_US.Debug(() => "Data folder parent = " + DataFolderParentPath); BepInExLogs_US.Debug(() => "Data folder = " + DataFolderPath); BepInExLogs_US.Debug(() => $"Campaign logging enabled = {CampaignLoggingEnabled}"); BepInExLogs_US.Debug(() => $"Cybergrind logging enabled = {CybergrindLoggingEnabled}"); BepInExLogs_US.Debug(() => $"Custom level logging enabled = {CustomLevelLoggingEnabled}"); BepInExLogs_US.Debug(() => $"Angry Level Loader available = {AngryLevelLoaderInstalled}"); BepInExLogs_US.Debug(() => $"Main menu button corner = {MainMenuButtonCorner}"); BepInExLogs_US.Debug(() => $"Main menu default tab = {DefaultMainMenuTab}"); BepInExLogs_US.Debug(() => $"New column first-click sort = {NewColumnFirstClickSortDirection}"); BepInExLogs_US.Debug(() => $"Angry bundle sort mode = {AngryBundleSortMode}"); BepInExLogs_US.Debug(() => $"Show legacy transition notice = {ShowLegacyTransitionNotice}"); BepInExLogs_US.Debug(() => $"Has seen fresh install welcome = {HasSeenFreshInstallWelcome}"); BepInExLogs_US.Debug(() => (!Chainloader.PluginInfos.ContainsKey("com.eternalUnion.angryLevelLoader")) ? "Angry Level Loader is not loaded." : "Angry Level Loader is loaded."); if (!AngryLevelLoaderInstalled) { BepInExLogs_US.Warn("Angry Level Loader not found. Custom level logging is disabled."); } try { UltraStatsBootstrap.Initialize(); } catch (Exception ex) { BepInExLogs_US.Error("SQLite bootstrap failed", ex); return; } Application.quitting += OnAppQuitting; SceneManager.activeSceneChanged += OnActiveSceneChanged; _lifecycleHooksRegistered = true; _harmony = new Harmony("atom.ultrastats"); _harmony.PatchAll(); MainMenuButton_US.Init(); BepInExLogs_US.Debug("Harmony patches applied."); } private void OnDestroy() { try { if (_lifecycleHooksRegistered) { SceneManager.activeSceneChanged -= OnActiveSceneChanged; Application.quitting -= OnAppQuitting; _lifecycleHooksRegistered = false; } MainMenuButton_US.Shutdown(); FlushPendingRuns("Plugin.OnDestroy"); UltraStatsBootstrap.Shutdown(); } finally { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } private static void OnAppQuitting() { FlushPendingRuns("Application.quitting"); } private static void OnActiveSceneChanged(Scene oldScene, Scene newScene) { FlushPendingRuns("SceneManager.activeSceneChanged"); } private static void FlushPendingRuns(string reason) { UltraStatsBootstrap.TryCommitActiveRun(reason); UltraStatsBootstrap.FlushPendingRuns(reason); } internal static void MarkFreshInstallWelcomeSeen() { if (HasSeenFreshInstallWelcome) { return; } try { Directory.CreateDirectory(UltraStatsPaths.InternalStateDirectory); File.WriteAllText(UltraStatsPaths.FreshInstallWelcomeSeenMarkerPath, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString()); } catch (Exception ex) { Log.LogWarning((object)("[ULTRASTATS] Could not persist fresh install welcome state: " + ex.Message)); } } } internal readonly struct FilterCycleLabelContent { public string Text { get; } public bool IsRichText { get; } public Color? Color { get; } private FilterCycleLabelContent(string text, bool isRichText, Color? color) { Text = text ?? string.Empty; IsRichText = isRichText; Color = color; } public static FilterCycleLabelContent Plain(string text, Color? color = null) { return new FilterCycleLabelContent(text, isRichText: false, color); } public static FilterCycleLabelContent Rich(string richText) { return new FilterCycleLabelContent(richText, isRichText: true, null); } public string ToDisplayText() { if (string.IsNullOrEmpty(Text)) { return string.Empty; } if (IsRichText) { return Text; } return UltraStatsRichCycleButton.WrapColor(UltraStatsRichCycleButton.EscapeRichText(Text), Color); } } internal static class FilterBooleanLabelPresets { public static FilterCycleLabelContent CheatsUsed => FilterCycleLabelContent.Rich("CHEATS USED"); public static FilterCycleLabelContent MajorAssists => FilterCycleLabelContent.Rich("MAJOR ASSISTS"); public static FilterCycleLabelContent Challenge => FilterCycleLabelContent.Rich("CHALLENGE"); public static FilterCycleLabelContent NoDamage => FilterCycleLabelContent.Rich("NO DAMAGE"); public static FilterCycleLabelContent StrayModeSaveFile => FilterRichTextLabels.StrayModeSaveFile; public static FilterCycleLabelContent MasqueradeDivinitySaveFile => FilterRichTextLabels.MasqueradeDivinitySaveFile; public static FilterCycleLabelContent FentoKill => FilterCycleLabelContent.Rich("FENTOKILL"); public static FilterCycleLabelContent BillionNemesis => FilterCycleLabelContent.Rich("BILLION NEMESIS"); public static FilterCycleLabelContent ForceRadiance => FilterCycleLabelContent.Rich(FlagRichTextFormatter.FormatForceRadianceText("FORCERADIANCE")); public static FilterCycleLabelContent ForceSand => FilterCycleLabelContent.Rich(FlagRichTextFormatter.FormatForceSandText("FORCESAND")); } internal readonly struct FilterCycleDisplayState { public string ValueText { get; } public Color? ValueColor { get; } public FilterCycleLabelContent Label { get; } public FilterCycleDisplayState(string valueText, Color? valueColor, FilterCycleLabelContent label) { ValueText = valueText ?? string.Empty; ValueColor = valueColor; Label = label; } } internal static class FilterCycleButtons { private const string NeutralTriStateSymbol = "*"; private static readonly Color DefaultIncludeColor = new Color(0.33f, 0.9f, 0.33f, 1f); private static readonly Color DefaultExcludeColor = new Color(0.92f, 0.24f, 0.24f, 1f); public static UltraStatsFilterTriStateButton CreateTriStateButton(RectTransform parent, Transform styleRoot, string name, FilterCycleLabelContent label, Color? neutralValueColor = null, Color? includeValueColor = null, Color? excludeValueColor = null, float height = 34f) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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) UltraStatsRichCycleButton ultraStatsRichCycleButton = CreateSequenceButton(parent, styleRoot, name, new FilterCycleDisplayState[3] { new FilterCycleDisplayState("*", neutralValueColor, label), new FilterCycleDisplayState("+", (Color)(((??)includeValueColor) ?? DefaultIncludeColor), label), new FilterCycleDisplayState("-", (Color)(((??)excludeValueColor) ?? DefaultExcludeColor), label) }, height); UltraStatsFilterTriStateButton ultraStatsFilterTriStateButton = ((Component)ultraStatsRichCycleButton).gameObject.AddComponent(); ultraStatsFilterTriStateButton.Initialize(ultraStatsRichCycleButton); return ultraStatsFilterTriStateButton; } public static UltraStatsRichCycleButton CreateSequenceButton(RectTransform parent, Transform styleRoot, string name, FilterCycleDisplayState[] states, float height = 34f) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)parent == (Object)null) { throw new ArgumentNullException("parent"); } if ((Object)(object)styleRoot == (Object)null) { throw new ArgumentNullException("styleRoot"); } if (states == null || states.Length == 0) { throw new ArgumentException("At least one cycle state is required.", "states"); } UltraStatsVanillaButtonState state; Button val = MainMenuButton_US.CreateVanillaStyledButton((Transform)(object)parent, styleRoot, name, new Vector2(220f, height), UltraStatsRichCycleButton.BuildButtonText(states[0]), null, out state); LayoutElement obj = ((Component)val).gameObject.GetComponent() ?? ((Component)val).gameObject.AddComponent(); obj.preferredHeight = height; obj.minHeight = height; obj.preferredWidth = 0f; obj.minWidth = 0f; obj.flexibleWidth = 1f; ConfigureButtonLabelVisuals(val); UltraStatsRichCycleButton ultraStatsRichCycleButton = ((Component)val).gameObject.AddComponent(); ultraStatsRichCycleButton.Initialize(val, states, 0); return ultraStatsRichCycleButton; } private static void ConfigureButtonLabelVisuals(Button button) { TMP_Text[] componentsInChildren = ((Component)button).GetComponentsInChildren(true); foreach (TMP_Text obj in componentsInChildren) { obj.richText = true; obj.enableAutoSizing = false; obj.fontSize = 16f; obj.fontStyle = (FontStyles)1; obj.alignment = (TextAlignmentOptions)514; obj.enableWordWrapping = false; obj.overflowMode = (TextOverflowModes)0; } Text[] componentsInChildren2 = ((Component)button).GetComponentsInChildren(true); foreach (Text obj2 in componentsInChildren2) { obj2.supportRichText = true; obj2.resizeTextForBestFit = false; obj2.fontSize = 16; obj2.fontStyle = (FontStyle)1; obj2.alignment = (TextAnchor)4; obj2.horizontalOverflow = (HorizontalWrapMode)1; } } } internal sealed class UltraStatsRichCycleButton : MonoBehaviour { private Button _button; private FilterCycleDisplayState[] _states = Array.Empty(); private TMP_Text[] _tmpTexts = Array.Empty(); private Text[] _legacyTexts = Array.Empty(); private int _stateIndex; public int StateIndex => _stateIndex; public event Action? OnStateIndexChanged; public void Initialize(Button button, FilterCycleDisplayState[] states, int initialStateIndex) { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown if ((Object)(object)button == (Object)null) { throw new ArgumentNullException("button"); } if (states == null || states.Length == 0) { throw new ArgumentException("At least one cycle state is required.", "states"); } _button = button; _states = new FilterCycleDisplayState[states.Length]; Array.Copy(states, _states, states.Length); _tmpTexts = ((Component)button).GetComponentsInChildren(true); _legacyTexts = ((Component)button).GetComponentsInChildren(true); _stateIndex = Mathf.Clamp(initialStateIndex, 0, _states.Length - 1); TMP_Text[] tmpTexts = _tmpTexts; for (int i = 0; i < tmpTexts.Length; i++) { tmpTexts[i].richText = true; } Text[] legacyTexts = _legacyTexts; for (int i = 0; i < legacyTexts.Length; i++) { legacyTexts[i].supportRichText = true; } ((UnityEvent)_button.onClick).AddListener(new UnityAction(AdvanceState)); RefreshLabel(); } public void SetStateIndexWithoutNotify(int index) { if (_states.Length != 0) { _stateIndex = Mathf.Clamp(index, 0, _states.Length - 1); RefreshLabel(); } } private void OnDestroy() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if ((Object)(object)_button != (Object)null) { ((UnityEvent)_button.onClick).RemoveListener(new UnityAction(AdvanceState)); } } private void AdvanceState() { if (_states.Length != 0) { _stateIndex = (_stateIndex + 1) % _states.Length; RefreshLabel(); this.OnStateIndexChanged?.Invoke(_stateIndex); } } private void RefreshLabel() { if (_states.Length != 0) { string text = BuildButtonText(_states[_stateIndex]); TMP_Text[] tmpTexts = _tmpTexts; for (int i = 0; i < tmpTexts.Length; i++) { tmpTexts[i].text = text; } Text[] legacyTexts = _legacyTexts; for (int i = 0; i < legacyTexts.Length; i++) { legacyTexts[i].text = text; } } } public static string BuildButtonText(FilterCycleDisplayState state) { string text = WrapColor(EscapeRichText(state.ValueText), state.ValueColor); string text2 = state.Label.ToDisplayText(); if (!string.IsNullOrEmpty(text2)) { return text + " " + text2; } return text; } internal static string WrapColor(string value, Color? color) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(value) || !color.HasValue) { return value; } return "" + value + ""; } internal static string EscapeRichText(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } return value.Replace("&", "&").Replace("<", "<").Replace(">", ">"); } } internal sealed class UltraStatsFilterTriStateButton : MonoBehaviour { private UltraStatsRichCycleButton _cycleButton; public RunFilterTriState Value => (RunFilterTriState)Mathf.Clamp(_cycleButton?.StateIndex ?? 0, 0, 2); public event Action? OnValueChanged; public void Initialize(UltraStatsRichCycleButton cycleButton) { _cycleButton = cycleButton ?? throw new ArgumentNullException("cycleButton"); _cycleButton.OnStateIndexChanged += HandleCycleStateChanged; } public void SetValueWithoutNotify(RunFilterTriState value) { _cycleButton?.SetStateIndexWithoutNotify(Mathf.Clamp((int)value, 0, 2)); } private void OnDestroy() { if ((Object)(object)_cycleButton != (Object)null) { _cycleButton.OnStateIndexChanged -= HandleCycleStateChanged; } } private void HandleCycleStateChanged(int stateIndex) { this.OnValueChanged?.Invoke((RunFilterTriState)Mathf.Clamp(stateIndex, 0, 2)); } } internal static class FilterRichTextLabels { public static FilterCycleLabelContent StrayModeSaveFile => FilterCycleLabelContent.Rich("STRAYMODE"); public static FilterCycleLabelContent MasqueradeDivinitySaveFile => FilterCycleLabelContent.Rich("MASQUERADE DIVINITY"); public static FilterCycleLabelContent GetSaveFileLabel(int saveSlot) { return saveSlot switch { 1015064007 => StrayModeSaveFile, 11 => MasqueradeDivinitySaveFile, _ => FilterCycleLabelContent.Plain($"Save {saveSlot}"), }; } public static string GetSaveFileDisplayText(int saveSlot) { return GetSaveFileLabel(saveSlot).ToDisplayText(); } } internal static class FilterWindowLayout { public const float ColumnStackSpacing = 5f; public const float ControlRowSpacing = 10f; public const float CompactRowSpacing = 6f; public const float CompactSectionGapHeight = 6f; public const float SectionGapHeight = 10f; public const float LowerSectionSpacing = 6f; public const float BooleanSectionSpacing = 8f; public const float DividerThickness = 1f; public const float DividerTopPadding = 0f; public const float DividerBottomPadding = 0f; public const float LowerSectionDividerTopPadding = 11f; public const float LowerSectionDividerBottomPadding = 10f; public const float CenterDividerWidth = 2f; public const float SectionLabelHeight = 15f; public const float InlineRowHeight = 30f; public const float InlineLabelWidth = 120f; public const float TriStateButtonHeight = 30f; public static RectTransform CreateVerticalSection(Transform parent, string name, float spacing = 6f) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[4] { typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter), typeof(LayoutElement) }); val.transform.SetParent(parent, false); LayoutElement component = val.GetComponent(); component.minHeight = 0f; component.flexibleHeight = 0f; VerticalLayoutGroup component2 = val.GetComponent(); ((LayoutGroup)component2).padding = new RectOffset(0, 0, 0, 0); ((HorizontalOrVerticalLayoutGroup)component2).spacing = spacing; ((LayoutGroup)component2).childAlignment = (TextAnchor)0; ((HorizontalOrVerticalLayoutGroup)component2).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component2).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false; ContentSizeFitter component3 = val.GetComponent(); component3.horizontalFit = (FitMode)0; component3.verticalFit = (FitMode)2; return val.GetComponent(); } public static GameObject CreateSpacer(Transform parent, string name, float height) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(LayoutElement) }); val.transform.SetParent(parent, false); LayoutElement component = val.GetComponent(); component.preferredHeight = height; component.minHeight = height; component.flexibleHeight = 0f; return val; } public static RectTransform CreateDivider(Transform parent, string name, Color color, float topPadding = 0f, float bottomPadding = 0f, float thickness = 1f) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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_0142: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(LayoutElement) }); val.transform.SetParent(parent, false); LayoutElement component = val.GetComponent(); float num = Mathf.Max(1f, thickness); float minHeight = (component.preferredHeight = Mathf.Max(0f, topPadding) + num + Mathf.Max(0f, bottomPadding)); component.minHeight = minHeight; component.flexibleHeight = 0f; GameObject val2 = new GameObject(name + "Line", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image) }); val2.transform.SetParent(val.transform, false); Image component2 = val2.GetComponent(); ((Graphic)component2).color = color; ((Graphic)component2).raycastTarget = false; RectTransform component3 = val2.GetComponent(); component3.anchorMin = new Vector2(0f, 1f); component3.anchorMax = new Vector2(1f, 1f); component3.pivot = new Vector2(0.5f, 1f); component3.sizeDelta = new Vector2(0f, num); component3.anchoredPosition = new Vector2(0f, 0f - Mathf.Max(0f, topPadding)); return val.GetComponent(); } public static TextMeshProUGUI CreateTextLabel(Transform parent, Transform styleRoot, string name, string text, float fontSize, TextAlignmentOptions alignment, float preferredHeight, float preferredWidth = -1f) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(TextMeshProUGUI), typeof(LayoutElement) }); val.transform.SetParent(parent, false); LayoutElement component = val.GetComponent(); if (preferredHeight > 0f) { component.preferredHeight = preferredHeight; component.minHeight = preferredHeight; } if (preferredWidth >= 0f) { component.preferredWidth = preferredWidth; component.minWidth = preferredWidth; component.flexibleWidth = 0f; } TextMeshProUGUI component2 = val.GetComponent(); ((TMP_Text)component2).richText = true; MainMenuButton_US.ApplyPanelTextStyle(styleRoot, component2); ((TMP_Text)component2).text = text ?? string.Empty; ((TMP_Text)component2).fontSize = fontSize; ((Graphic)component2).color = Color.white; ((TMP_Text)component2).alignment = alignment; ((TMP_Text)component2).enableWordWrapping = false; ((TMP_Text)component2).overflowMode = (TextOverflowModes)1; ((Graphic)component2).raycastTarget = false; return component2; } } internal static class FlagRichTextFormatter { private static readonly string[] ForceRadiancePalette = new string[13] { "#48ffff", "#c7f797", "#ffff7f", "#ffbf50", "#ffafbf", "#ff7087", "#9f70c7", "#6087cf", "#48ffff", "#c7f797", "#ffff7f", "#ffbf50", "#ffafbf" }; private static readonly string[] ForceSandPalette = new string[9] { "#E0AD6D", "#E29D6A", "#E49067", "#DD7A60", "#DA685B", "#EA655E", "#EB585A", "#EA4D58", "#EE4858" }; public static string FormatForceRadianceText(string text) { return ApplyPalette(text, ForceRadiancePalette); } public static string FormatForceSandText(string text) { return ApplyPalette(text, ForceSandPalette); } public static string FormatRunDateByFlags(string dateText, long flags) { return FormatRunDateByFlags(dateText, FlagEncoding.Has(flags, 16L), FlagEncoding.Has(flags, 32L)); } public static string FormatRunDateByFlags(string dateText, bool forceRadianceEnabled, bool forceSandEnabled) { if (string.IsNullOrEmpty(dateText) || dateText == "-") { return dateText ?? string.Empty; } if (forceRadianceEnabled && forceSandEnabled) { int num = (dateText.Length + 1) / 2; return ApplyPalette(dateText.Substring(0, num), ForceRadiancePalette) + ApplyPalette(dateText.Substring(num), ForceSandPalette); } if (forceRadianceEnabled) { return ApplyPalette(dateText, ForceRadiancePalette); } if (forceSandEnabled) { return ApplyPalette(dateText, ForceSandPalette); } return dateText; } public static string FormatNoHitDeathsText(string deathsText, long flags) { if (!FlagEncoding.Has(flags, 8L) || string.IsNullOrEmpty(deathsText) || deathsText == "-") { return deathsText ?? string.Empty; } return WrapColor(deathsText, "orange"); } private static string ApplyPalette(string text, string[] palette) { if (string.IsNullOrEmpty(text)) { return string.Empty; } if (palette == null || palette.Length == 0) { return UltraStatsRichCycleButton.EscapeRichText(text); } StringBuilder stringBuilder = new StringBuilder(text.Length * 24); for (int i = 0; i < text.Length; i++) { stringBuilder.Append("'); stringBuilder.Append(UltraStatsRichCycleButton.EscapeRichText(text[i].ToString())); stringBuilder.Append(""); } return stringBuilder.ToString(); } private static string WrapColor(string text, string colorToken) { if (string.IsNullOrEmpty(text)) { return string.Empty; } return "" + UltraStatsRichCycleButton.EscapeRichText(text) + ""; } } internal enum StartupNoticeType { None, FreshInstallWelcome, LegacyTransition } internal static class UltraStatsStartupNoticeController { private readonly struct StartupNoticeCopy { public string Title { get; } public string Body { get; } public StartupNoticeCopy(string title, string body) { Title = title; Body = body; } } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Func <>9__17_0; public static UnityAction <>9__20_0; internal string b__17_0() { return $"Startup notices captured: hasDbAtStartup={_hadDatabaseAtStartup}, hasLegacyAtStartup={_hadLegacyDataAtStartup}"; } internal void b__20_0() { } } private const string OverlayObjectName = "ULTRASTATS_StartupNoticeOverlay"; private const float WindowWidth = 760f; private const float WindowHeight = 344f; private const float WindowInset = 18f; private const float TitleTopInset = -44f; private const float TitleBottomInset = -12f; private const float DividerTopInset = -54f; private const float DividerBottomInset = -52f; private const float BodyTopInset = -66f; private const float BodyBottomInset = 18f; private const float CloseButtonTopOffset = 18f; private static readonly StartupNoticeCopy FreshInstallCopy = new StartupNoticeCopy("ULTRASTATS NOTICE", "Dear User,\nWelcome to ULTRASTATS, the stattracker for ULTRAKILL. If you have any issues with ULTRASTATS please DM me on discord (atomsmasher_1586).\nThis mod is currently still early in development with much more planned to come so stay tuned! To see your saved runs click on the button in the bottom right (you can change where the button appears in Plugin Configurator).\n\nP.S. If you see this and are an old user, it's because ULTRASTATS can't find your run database."); private static readonly StartupNoticeCopy LegacyTransitionCopy = new StartupNoticeCopy("ULTRASTATS NOTICE", "Dear User,\nWelcome to ULTRASTATS v0.1.2!\nA lot has changed with this update, mainly switching the way ULTRASTATS stores data. Now your runs are all stored in a database file (ultrastats.db) instead of in .jsonl files. If everything looks correct in the stats chart then you are free to delete the old (now unused) Difficulty_ folders in your ULTRASTATS data folder.\n\nTHIS MESSAGE WILL REAPPEAR UNTIL THOSE OLD FOLDERS ARE DELETED OR STARTUP NOTICES ARE DISABLED IN PLUGIN CONFIGURATOR.\n\nIf something goes wrong with conversion from .jsonl to .db delete ultrastats.db to reattempt conversion. If that still doesn't work DM me on discord (atomsmasher_1586)."); private static bool _startupStateCaptured; private static bool _hadDatabaseAtStartup; private static bool _hadLegacyDataAtStartup; private static bool _noticeHandledThisSession; public static void CaptureStartupState() { if (!_startupStateCaptured) { _hadDatabaseAtStartup = File.Exists(UltraStatsPaths.DatabasePath); _hadLegacyDataAtStartup = LegacyBootstrapImporter.HasLegacyData(UltraStatsPaths.LegacyJsonlRoot); _startupStateCaptured = true; BepInExLogs_US.Debug(() => $"Startup notices captured: hasDbAtStartup={_hadDatabaseAtStartup}, hasLegacyAtStartup={_hadLegacyDataAtStartup}"); } } public static void TryShowIfNeeded(Transform mainMenu) { if ((Object)(object)mainMenu == (Object)null || _noticeHandledThisSession) { return; } CaptureStartupState(); _noticeHandledThisSession = true; if (!((Object)(object)mainMenu.Find("ULTRASTATS_StartupNoticeOverlay") != (Object)null)) { StartupNoticeType startupNoticeType = DecideNoticeType(); if (startupNoticeType != 0) { BuildNoticeOverlay(mainMenu, startupNoticeType); } } } private static StartupNoticeType DecideNoticeType() { bool flag = File.Exists(UltraStatsPaths.DatabasePath); if (_hadLegacyDataAtStartup && flag && Plugin.ShowLegacyTransitionNotice) { return StartupNoticeType.LegacyTransition; } if (!_hadDatabaseAtStartup && !_hadLegacyDataAtStartup && !Plugin.HasSeenFreshInstallWelcome) { return StartupNoticeType.FreshInstallWelcome; } return StartupNoticeType.None; } private static void BuildNoticeOverlay(Transform mainMenu, StartupNoticeType noticeType) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown //IL_0132: 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_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_0379: Unknown result type (might be due to invalid IL or missing references) //IL_038e: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03b7: Unknown result type (might be due to invalid IL or missing references) //IL_03da: Unknown result type (might be due to invalid IL or missing references) //IL_0406: Unknown result type (might be due to invalid IL or missing references) //IL_041b: Unknown result type (might be due to invalid IL or missing references) //IL_0430: Unknown result type (might be due to invalid IL or missing references) //IL_0444: Unknown result type (might be due to invalid IL or missing references) //IL_045a: Unknown result type (might be due to invalid IL or missing references) //IL_0464: Expected O, but got Unknown //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Expected O, but got Unknown GameObject val = new GameObject("ULTRASTATS_StartupNoticeOverlay", new Type[7] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button), typeof(Canvas), typeof(GraphicRaycaster), typeof(UltraStatsStartupNoticeModal) }); val.transform.SetParent(mainMenu, false); RectTransform component = val.GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; Canvas component2 = val.GetComponent(); component2.overrideSorting = true; component2.sortingOrder = 950; Image component3 = val.GetComponent(); ((Graphic)component3).color = new Color(0f, 0f, 0f, 0.66f); ((Graphic)component3).raycastTarget = true; UltraStatsStartupNoticeModal component4 = val.GetComponent(); component4.Initialize(noticeType); Button component5 = val.GetComponent