Decompiled source of DealerCustomerPreferences v1.0.0

DealerCustomerPreferences.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using DealerCustomerPreferences;
using DealerCustomerPreferences.Config;
using DealerCustomerPreferences.Integrations.Interop;
using DealerCustomerPreferences.Integrations.Tooltips;
using HarmonyLib;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using S1API.Logging;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Core), "Dealer Customer Preferences", "1.0.0", "Riccaforte", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: MelonAuthorColor(1, 68, 2, 152)]
[assembly: MelonColor(1, 68, 2, 152)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DealerCustomerPreferences")]
[assembly: AssemblyConfiguration("CrossCompat")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+c009ad7123b5c0e7913ea8348486801dd78b1656")]
[assembly: AssemblyProduct("DealerCustomerPreferences")]
[assembly: AssemblyTitle("DealerCustomerPreferences")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.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 DealerCustomerPreferences
{
	public class Core : MelonMod
	{
		private static ModPreferences? _preferences;

		private PreferencesFileWatcher? _preferencesWatcher;

		public readonly Log _logger = new Log("DealerCustomerPreferences");

		public static Core? Instance { get; private set; }

		public static bool IsModEnabled => _preferences?.IsEnabled ?? true;

		public static int TooltipFontSize => _preferences?.TooltipFontSize ?? 14;

		public override void OnInitializeMelon()
		{
			Instance = this;
			_preferences = new ModPreferences();
			_preferencesWatcher = new PreferencesFileWatcher(_logger);
			_logger.Msg("Mod initialized and config loaded.");
		}

		public override void OnUpdate()
		{
			PreferencesFileWatcher? preferencesWatcher = _preferencesWatcher;
			if (preferencesWatcher != null && preferencesWatcher.TryConsumeScheduledReload())
			{
				ReloadPreferencesFromDisk();
			}
		}

		public override void OnApplicationQuit()
		{
			_preferencesWatcher?.Dispose();
			_preferencesWatcher = null;
			_preferences = null;
			Instance = null;
		}

		private void ReloadPreferencesFromDisk()
		{
			string text = _preferencesWatcher?.FilePath;
			if (string.IsNullOrWhiteSpace(text))
			{
				return;
			}
			if (!PreferencesFileParser.TryReadSnapshot(text, out PreferencesSnapshot snapshot))
			{
				_preferencesWatcher?.ScheduleReload();
				return;
			}
			ModPreferences? preferences = _preferences;
			if (preferences != null && preferences.ApplySnapshot(snapshot))
			{
				_logger.Msg("Reloaded preferences from MelonPreferences.cfg.");
			}
		}
	}
}
namespace DealerCustomerPreferences.Utils
{
	public static class Constants
	{
		public static class Game
		{
			public const string GAME_STUDIO = "TVGS";

			public const string GAME_NAME = "Schedule I";
		}

		public const string MOD_NAME = "Dealer Customer Preferences";

		public const string MOD_VERSION = "1.0.0";

		public const string MOD_AUTHOR = "Riccaforte";

		public const string PREFERENCES_CATEGORY = "Dealer Customer Preferences";
	}
}
namespace DealerCustomerPreferences.Integrations
{
	public static class HarmonyPatches
	{
		[HarmonyPatch]
		private static class DealerManagementAppSetDisplayedDealerPatch
		{
			[HarmonyPrepare]
			private static bool Prepare()
			{
				return CanPatch(GameTypeNames.DealerManagementApp, "SetDisplayedDealer");
			}

			[HarmonyTargetMethod]
			private static MethodBase TargetMethod()
			{
				return GetRequiredPatchMethod(GameTypeNames.DealerManagementApp, "SetDisplayedDealer");
			}

			[HarmonyPostfix]
			private static void Postfix(object __instance, object dealer)
			{
				if (!Core.IsModEnabled)
				{
					return;
				}
				try
				{
					CustomerTooltips.ApplyDealerTooltips(__instance, dealer);
				}
				catch (Exception arg)
				{
					MelonLogger.Error($"Failed to update dealer customer preference tooltips: {arg}");
				}
			}
		}

		[HarmonyPatch]
		private static class TooltipManagerCheckForTooltipHoverPatch
		{
			[HarmonyPrepare]
			private static bool Prepare()
			{
				return CanPatch(GameTypeNames.TooltipManager, "CheckForTooltipHover");
			}

			[HarmonyTargetMethod]
			private static MethodBase TargetMethod()
			{
				return GetRequiredPatchMethod(GameTypeNames.TooltipManager, "CheckForTooltipHover");
			}

			[HarmonyPostfix]
			private static void Postfix(object __instance)
			{
				if (!Core.IsModEnabled)
				{
					return;
				}
				try
				{
					CustomerTooltips.ShowCustomerNameTooltip(__instance);
				}
				catch (Exception arg)
				{
					MelonLogger.Error($"Failed to show dealer customer name tooltip: {arg}");
				}
			}
		}

		[HarmonyPatch]
		private static class CustomerSelectorCreateEntryPatch
		{
			[HarmonyPrepare]
			private static bool Prepare()
			{
				return CanPatch(GameTypeNames.CustomerSelector, "CreateEntry");
			}

			[HarmonyTargetMethod]
			private static MethodBase TargetMethod()
			{
				return GetRequiredPatchMethod(GameTypeNames.CustomerSelector, "CreateEntry");
			}

			[HarmonyPrefix]
			private static void Prefix(object __instance, out int __state)
			{
				__state = 0;
				if (Core.IsModEnabled)
				{
					__state = CustomerTooltips.GetCustomerSelectorEntryCount(__instance);
				}
			}

			[HarmonyPostfix]
			private static void Postfix(object __instance, object __0, int __state)
			{
				if (!Core.IsModEnabled)
				{
					return;
				}
				try
				{
					CustomerTooltips.CaptureCustomerSelectorRoot(__instance);
					CustomerTooltips.BindCreatedCustomerSelectorEntry(__instance, __0, __state);
				}
				catch (Exception arg)
				{
					MelonLogger.Error($"Failed to update customer selector preference tooltips: {arg}");
				}
			}
		}

		[HarmonyPatch]
		private static class CustomerSelectorOpenPatch
		{
			[HarmonyPrepare]
			private static bool Prepare()
			{
				return CanPatch(GameTypeNames.CustomerSelector, "Open");
			}

			[HarmonyTargetMethod]
			private static MethodBase TargetMethod()
			{
				return GetRequiredPatchMethod(GameTypeNames.CustomerSelector, "Open");
			}

			[HarmonyPostfix]
			private static void Postfix(object __instance)
			{
				if (!Core.IsModEnabled)
				{
					return;
				}
				try
				{
					CustomerTooltips.CaptureCustomerSelectorRoot(__instance);
					CustomerTooltips.RefreshCustomerSelectorVisibility(__instance);
				}
				catch (Exception arg)
				{
					MelonLogger.Error($"Failed to refresh customer selector preference tooltips: {arg}");
				}
			}
		}

		[HarmonyPatch]
		private static class CustomerSelectorAwakePatch
		{
			[HarmonyPrepare]
			private static bool Prepare()
			{
				return CanPatch(GameTypeNames.CustomerSelector, "Awake");
			}

			[HarmonyTargetMethod]
			private static MethodBase TargetMethod()
			{
				return GetRequiredPatchMethod(GameTypeNames.CustomerSelector, "Awake");
			}

			[HarmonyPostfix]
			private static void Postfix(object __instance)
			{
				CustomerTooltips.MarkCustomerSelectorInitialized(__instance);
			}
		}

		[HarmonyPatch]
		private static class CustomerSelectorClosePatch
		{
			[HarmonyPrepare]
			private static bool Prepare()
			{
				return CanPatch(GameTypeNames.CustomerSelector, "Close");
			}

			[HarmonyTargetMethod]
			private static MethodBase TargetMethod()
			{
				return GetRequiredPatchMethod(GameTypeNames.CustomerSelector, "Close");
			}

			[HarmonyPrefix]
			private static bool Prefix()
			{
				if (!Core.IsModEnabled)
				{
					return true;
				}
				return CustomerTooltips.ShouldAllowCustomerSelectorClose();
			}
		}

		[HarmonyPatch]
		private static class DealerManagementAppAssignCustomerPatch
		{
			[HarmonyPrepare]
			private static bool Prepare()
			{
				return CanPatch(GameTypeNames.DealerManagementApp, "AssignCustomer");
			}

			[HarmonyTargetMethod]
			private static MethodBase TargetMethod()
			{
				return GetRequiredPatchMethod(GameTypeNames.DealerManagementApp, "AssignCustomer");
			}

			[HarmonyPrefix]
			private static void Prefix(object __instance)
			{
				if (!Core.IsModEnabled)
				{
					return;
				}
				try
				{
					CustomerTooltips.PrepareCustomerSelectorFirstOpen(__instance);
				}
				catch (Exception arg)
				{
					MelonLogger.Error($"Failed to prepare customer selector first-open state: {arg}");
				}
			}

			[HarmonyPostfix]
			private static void Postfix(object __instance)
			{
				if (!Core.IsModEnabled)
				{
					return;
				}
				try
				{
					CustomerTooltips.RecoverCustomerSelectorFirstOpen(__instance);
				}
				catch (Exception arg)
				{
					MelonLogger.Error($"Failed to recover customer selector first open state: {arg}");
				}
			}
		}

		private static readonly CustomerTooltipController CustomerTooltips = new CustomerTooltipController();

		private static bool CanPatch(string[] typeNames, string methodName)
		{
			Type type = RuntimeInterop.ResolveGameType(typeNames);
			return type != null && AccessTools.Method(type, methodName, (Type[])null, (Type[])null) != null;
		}

		private static MethodBase GetRequiredPatchMethod(string[] typeNames, string methodName)
		{
			Type type = RuntimeInterop.ResolveGameType(typeNames);
			MethodBase methodBase = ((type == null) ? null : AccessTools.Method(type, methodName, (Type[])null, (Type[])null));
			if (methodBase != null)
			{
				return methodBase;
			}
			throw new MissingMethodException("Could not resolve Harmony patch target '" + methodName + "'.");
		}
	}
}
namespace DealerCustomerPreferences.Integrations.Tooltips
{
	internal sealed class CustomerTooltipController
	{
		private enum TooltipBindingContext
		{
			DealerList,
			CustomerSelector
		}

		private sealed class CustomerNameTooltipBinding
		{
			public RectTransform LabelOriginRect { get; }

			public string Text { get; }

			public Vector2 LabelOffset { get; }

			public TooltipBindingContext Context { get; }

			public CustomerNameTooltipBinding(RectTransform labelOriginRect, string text, Vector2 labelOffset, TooltipBindingContext context)
			{
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				LabelOriginRect = labelOriginRect;
				Text = text;
				LabelOffset = labelOffset;
				Context = context;
			}
		}

		private readonly Dictionary<int, CustomerNameTooltipBinding> _customerNameTooltips = new Dictionary<int, CustomerNameTooltipBinding>();

		private GameObject? _customerSelectorRootObject;

		private bool _customerSelectorInitialized;

		private bool _skipNextCustomerSelectorClose;

		private bool _recoverNextCustomerSelectorOpen;

		public void ApplyDealerTooltips(object appInstance, object dealer)
		{
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			IReadOnlyList<object> readOnlyList = RuntimeInterop.MaterializeObjects(RuntimeInterop.GetMemberValue(appInstance, "CustomerEntries"));
			IReadOnlyList<object> readOnlyList2 = RuntimeInterop.MaterializeObjects(RuntimeInterop.GetMemberValue(dealer, "AssignedCustomers"));
			for (int i = 0; i < readOnlyList.Count; i++)
			{
				object obj = readOnlyList[i];
				RectTransform val = (RectTransform)((obj is RectTransform) ? obj : null);
				if (val == null)
				{
					continue;
				}
				Transform val2 = ((Transform)val).Find("Name");
				if (val2 == null)
				{
					continue;
				}
				Graphic component = ((Component)val2).GetComponent<Graphic>();
				if ((Object)(object)component != (Object)null)
				{
					component.raycastTarget = true;
				}
				int instanceID = ((Object)((Component)val2).gameObject).GetInstanceID();
				if (i >= readOnlyList2.Count)
				{
					_customerNameTooltips.Remove(instanceID);
					continue;
				}
				RectTransform component2 = ((Component)val2).GetComponent<RectTransform>();
				if ((Object)(object)component2 == (Object)null)
				{
					_customerNameTooltips.Remove(instanceID);
					continue;
				}
				object obj2 = readOnlyList2[i];
				if (obj2 == null)
				{
					_customerNameTooltips.Remove(instanceID);
				}
				else
				{
					SetTooltipBinding(val2, component2, obj2, GetTooltipOffset(), TooltipBindingContext.DealerList);
				}
			}
		}

		public void CaptureCustomerSelectorRoot(object customerSelector)
		{
			object? memberValue = RuntimeInterop.GetMemberValue(customerSelector, "gameObject");
			GameObject val = (GameObject)((memberValue is GameObject) ? memberValue : null);
			if (val != null)
			{
				_customerSelectorRootObject = val;
			}
		}

		public void MarkCustomerSelectorInitialized(object customerSelector)
		{
			CaptureCustomerSelectorRoot(customerSelector);
			_customerSelectorInitialized = true;
		}

		public void RefreshCustomerSelectorVisibility(object customerSelector)
		{
			IReadOnlyList<object> readOnlyList = RuntimeInterop.MaterializeObjects(RuntimeInterop.GetMemberValue(customerSelector, "customerEntries"));
			if (readOnlyList.Count == 0)
			{
				return;
			}
			object memberValue = RuntimeInterop.GetMemberValue(customerSelector, "entryToCustomer");
			if (memberValue == null)
			{
				return;
			}
			Type type = RuntimeInterop.ResolveGameType(GameTypeNames.Dealer);
			if (type == null)
			{
				return;
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (object item in RuntimeInterop.MaterializeObjects(RuntimeInterop.GetStaticMemberValue(type, "AllPlayerDealers")))
			{
				if (item == null)
				{
					continue;
				}
				foreach (object item2 in RuntimeInterop.MaterializeObjects(RuntimeInterop.GetMemberValue(item, "AssignedCustomers")))
				{
					string text = RuntimeInterop.GetMemberValue(RuntimeInterop.GetMemberValue(item2, "NPC"), "ID")?.ToString();
					if (!string.IsNullOrWhiteSpace(text))
					{
						hashSet.Add(text);
					}
				}
			}
			foreach (object item3 in readOnlyList)
			{
				RectTransform val = (RectTransform)((item3 is RectTransform) ? item3 : null);
				if (val != null)
				{
					object instance = RuntimeInterop.TryGetMappedValue(memberValue, val);
					string text2 = RuntimeInterop.GetMemberValue(RuntimeInterop.GetMemberValue(instance, "NPC"), "ID")?.ToString();
					if (!string.IsNullOrWhiteSpace(text2))
					{
						((Component)val).gameObject.SetActive(!hashSet.Contains(text2));
					}
				}
			}
		}

		public int GetCustomerSelectorEntryCount(object customerSelector)
		{
			return RuntimeInterop.MaterializeObjects(RuntimeInterop.GetMemberValue(customerSelector, "customerEntries")).Count;
		}

		public void BindCreatedCustomerSelectorEntry(object customerSelector, object? customer, int previousEntryCount)
		{
			if (customer == null)
			{
				return;
			}
			IReadOnlyList<object> readOnlyList = RuntimeInterop.MaterializeObjects(RuntimeInterop.GetMemberValue(customerSelector, "customerEntries"));
			if (readOnlyList.Count > previousEntryCount)
			{
				object obj = readOnlyList[readOnlyList.Count - 1];
				RectTransform val = (RectTransform)((obj is RectTransform) ? obj : null);
				if (val != null)
				{
					BindCustomerSelectorEntry(val, customer);
				}
			}
		}

		public bool ShouldAllowCustomerSelectorClose()
		{
			if (!RuntimeInterop.IsIl2CppRuntime())
			{
				return true;
			}
			if (_skipNextCustomerSelectorClose)
			{
				_skipNextCustomerSelectorClose = false;
				return false;
			}
			return true;
		}

		public void PrepareCustomerSelectorFirstOpen(object appInstance)
		{
			if (!RuntimeInterop.IsIl2CppRuntime())
			{
				return;
			}
			object memberValue = RuntimeInterop.GetMemberValue(appInstance, "CustomerSelector");
			if (memberValue != null)
			{
				CaptureCustomerSelectorRoot(memberValue);
				if (!_customerSelectorInitialized)
				{
					_skipNextCustomerSelectorClose = true;
					_recoverNextCustomerSelectorOpen = true;
				}
			}
		}

		public void RecoverCustomerSelectorFirstOpen(object appInstance)
		{
			if (!RuntimeInterop.IsIl2CppRuntime() || !_recoverNextCustomerSelectorOpen)
			{
				return;
			}
			try
			{
				object memberValue = RuntimeInterop.GetMemberValue(appInstance, "CustomerSelector");
				if (memberValue != null)
				{
					CaptureCustomerSelectorRoot(memberValue);
					if ((Object)(object)_customerSelectorRootObject != (Object)null && !_customerSelectorRootObject.activeSelf)
					{
						AccessTools.Method(memberValue.GetType(), "Open", (Type[])null, (Type[])null)?.Invoke(memberValue, Array.Empty<object>());
					}
				}
			}
			finally
			{
				_recoverNextCustomerSelectorOpen = false;
			}
		}

		public void ShowCustomerNameTooltip(object tooltipManager)
		{
			if (RuntimeInterop.GetMemberValue(tooltipManager, "tooltipShownThisFrame") is int num && num != 0)
			{
				return;
			}
			IReadOnlyList<object> readOnlyList = RuntimeInterop.MaterializeObjects(RuntimeInterop.GetMemberValue(tooltipManager, "rayResults"));
			if (readOnlyList.Count == 0)
			{
				return;
			}
			if (IsCustomerSelectorVisible())
			{
				CustomerNameTooltipBinding customerNameTooltipBinding = FindTooltipBinding(readOnlyList, TooltipBindingContext.CustomerSelector);
				if (customerNameTooltipBinding != null)
				{
					ShowTooltip(tooltipManager, customerNameTooltipBinding);
				}
			}
			else
			{
				CustomerNameTooltipBinding customerNameTooltipBinding2 = FindTooltipBinding(readOnlyList);
				if (customerNameTooltipBinding2 != null)
				{
					ShowTooltip(tooltipManager, customerNameTooltipBinding2);
				}
			}
		}

		private void BindCustomerSelectorEntry(RectTransform entryTransform, object customer)
		{
			//IL_006c: 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)
			SetRaycastTargets((Transform)(object)entryTransform, raycastTarget: true);
			Transform val = ((Transform)entryTransform).Find("Name");
			RectTransform labelOriginRect = ((val != null) ? ((Component)val).GetComponent<RectTransform>() : null) ?? entryTransform;
			Graphic val2 = ((val != null) ? ((Component)val).GetComponent<Graphic>() : null);
			if ((Object)(object)val2 != (Object)null)
			{
				val2.raycastTarget = true;
			}
			if ((Object)(object)val != (Object)null)
			{
				SetTooltipBinding(val, labelOriginRect, customer, GetTooltipOffset(), TooltipBindingContext.CustomerSelector);
			}
			SetTooltipBinding((Transform)(object)entryTransform, labelOriginRect, customer, GetTooltipOffset(), TooltipBindingContext.CustomerSelector);
		}

		private void ShowTooltip(object tooltipManager, CustomerNameTooltipBinding binding)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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)
			MethodInfo methodInfo = AccessTools.Method(tooltipManager.GetType(), "ShowTooltip", new Type[3]
			{
				typeof(string),
				typeof(Vector2),
				typeof(bool)
			}, (Type[])null);
			if (!(methodInfo == null))
			{
				EnsureTooltipStyle(tooltipManager);
				Vector2 val = GetMousePosition() + binding.LabelOffset;
				methodInfo.Invoke(tooltipManager, new object[3] { binding.Text, val, false });
			}
		}

		private bool IsCustomerSelectorVisible()
		{
			return (Object)(object)_customerSelectorRootObject != (Object)null && _customerSelectorRootObject.activeInHierarchy;
		}

		private CustomerNameTooltipBinding? FindTooltipBinding(IReadOnlyList<object?> rayResults, TooltipBindingContext? requiredContext = null)
		{
			for (int i = 0; i < rayResults.Count; i++)
			{
				object? memberValue = RuntimeInterop.GetMemberValue(rayResults[i], "gameObject");
				GameObject val = (GameObject)((memberValue is GameObject) ? memberValue : null);
				if (val != null)
				{
					CustomerNameTooltipBinding customerNameTooltipBinding = FindTooltipBinding(val.transform);
					if (customerNameTooltipBinding != null && ((Component)customerNameTooltipBinding.LabelOriginRect).gameObject.activeInHierarchy && (!requiredContext.HasValue || customerNameTooltipBinding.Context == requiredContext.Value))
					{
						return customerNameTooltipBinding;
					}
				}
			}
			return null;
		}

		private Vector2 GetMousePosition()
		{
			//IL_0077: 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_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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_006b: 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)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			Type type = RuntimeInterop.ResolveGameType(GameTypeNames.GameInput);
			if (type != null)
			{
				object obj = AccessTools.Property(type, "MousePosition")?.GetValue(null);
				object obj2 = obj;
				object obj3 = obj2;
				if (obj3 is Vector2 result)
				{
					return result;
				}
				if (obj3 is Vector3 val)
				{
					return Vector2.op_Implicit(val);
				}
			}
			return Vector2.op_Implicit(Input.mousePosition);
		}

		private void EnsureTooltipStyle(object tooltipManager)
		{
			object? memberValue = RuntimeInterop.GetMemberValue(tooltipManager, "anchor");
			RectTransform val = (RectTransform)((memberValue is RectTransform) ? memberValue : null);
			if (val == null)
			{
				return;
			}
			object memberValue2 = RuntimeInterop.GetMemberValue(tooltipManager, "tooltipLabel");
			if (memberValue2 != null)
			{
				float num = Core.TooltipFontSize;
				float num2 = RuntimeInterop.ConvertToSingle(RuntimeInterop.GetMemberValue(memberValue2, "fontSize"));
				if (num2 > 0f && Math.Abs(num2 - num) > 0.01f)
				{
					RuntimeInterop.SetMemberValue(memberValue2, "fontSize", num);
				}
				float num3 = RuntimeInterop.ConvertToSingle(RuntimeInterop.GetMemberValue(memberValue2, "fontSizeMax"));
				if (num3 > 0f && Math.Abs(num3 - num) > 0.01f)
				{
					RuntimeInterop.SetMemberValue(memberValue2, "fontSizeMax", num);
				}
			}
			SetOpaqueGraphicAlpha((Transform)(object)val);
		}

		private static void SetOpaqueGraphicAlpha(Transform transform)
		{
			//IL_0015: 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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			Graphic component = ((Component)transform).GetComponent<Graphic>();
			if ((Object)(object)component != (Object)null)
			{
				Color color = component.color;
				if (color.a < 1f)
				{
					color.a = 1f;
					component.color = color;
				}
			}
			CanvasGroup component2 = ((Component)transform).GetComponent<CanvasGroup>();
			if ((Object)(object)component2 != (Object)null && component2.alpha < 1f)
			{
				component2.alpha = 1f;
			}
			for (int i = 0; i < transform.childCount; i++)
			{
				SetOpaqueGraphicAlpha(transform.GetChild(i));
			}
		}

		private static void SetRaycastTargets(Transform transform, bool raycastTarget)
		{
			Graphic component = ((Component)transform).GetComponent<Graphic>();
			if ((Object)(object)component != (Object)null)
			{
				component.raycastTarget = raycastTarget;
			}
			for (int i = 0; i < transform.childCount; i++)
			{
				SetRaycastTargets(transform.GetChild(i), raycastTarget);
			}
		}

		private void SetTooltipBinding(Transform trackedTransform, RectTransform labelOriginRect, object customer, Vector2 labelOffset, TooltipBindingContext context)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			_customerNameTooltips[((Object)((Component)trackedTransform).gameObject).GetInstanceID()] = new CustomerNameTooltipBinding(labelOriginRect, PreferenceTooltipFormatter.BuildPreferenceTooltip(customer), labelOffset, context);
		}

		private CustomerNameTooltipBinding? FindTooltipBinding(Transform? transform)
		{
			while ((Object)(object)transform != (Object)null)
			{
				if (_customerNameTooltips.TryGetValue(((Object)((Component)transform).gameObject).GetInstanceID(), out CustomerNameTooltipBinding value))
				{
					return value;
				}
				transform = transform.parent;
			}
			return null;
		}

		private static Vector2 GetTooltipOffset()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			return new Vector2(30f, -26f);
		}
	}
	internal static class PreferenceTooltipFormatter
	{
		private static readonly Dictionary<string, string> DrugTypeColors = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			["Weed"] = "6CBF59",
			["Marijuana"] = "6CBF59",
			["Meth"] = "E5D85C",
			["Methamphetamine"] = "E5D85C",
			["Cocaine"] = "F4F4F4",
			["Coke"] = "F4F4F4",
			["Mushrooms"] = "D2B48C",
			["Mushroom"] = "D2B48C",
			["Shrooms"] = "D2B48C",
			["Shroom"] = "D2B48C"
		};

		public static string BuildPreferenceTooltip(object? customer)
		{
			object memberValue = RuntimeInterop.GetMemberValue(customer, "CustomerData");
			StringBuilder stringBuilder = new StringBuilder();
			string value = BuildCustomerHeader(customer);
			if (!string.IsNullOrWhiteSpace(value))
			{
				stringBuilder.Append(value);
			}
			if (memberValue == null)
			{
				AppendSection(stringBuilder, "No preference data available.", Array.Empty<string>());
				return stringBuilder.ToString();
			}
			List<string> preferredProperties = GetPreferredProperties(memberValue);
			List<string> drugAffinities = GetDrugAffinities(memberValue, positive: true);
			List<string> drugAffinities2 = GetDrugAffinities(memberValue, positive: false);
			if (preferredProperties.Count > 0)
			{
				AppendSection(stringBuilder, "Favourite Effects:", preferredProperties);
			}
			if (drugAffinities.Count > 0)
			{
				AppendSection(stringBuilder, "Preferred Drug Types:", drugAffinities);
			}
			if (drugAffinities2.Count > 0)
			{
				AppendSection(stringBuilder, "Avoided Drug Types:", drugAffinities2);
			}
			if (stringBuilder.Length == 0)
			{
				stringBuilder.Append("No preference data available.");
			}
			return stringBuilder.ToString();
		}

		private static void AppendSection(StringBuilder builder, string label, IReadOnlyList<string> values)
		{
			if (builder.Length > 0)
			{
				builder.Append('\n');
			}
			builder.Append(label);
			if (values.Count != 0)
			{
				builder.Append('\n');
				builder.Append("    ");
				builder.Append(string.Join(", ", values));
			}
		}

		private static string BuildCustomerHeader(object? customer)
		{
			object memberValue = RuntimeInterop.GetMemberValue(customer, "NPC");
			string value = RuntimeInterop.GetMemberValue(memberValue, "fullName")?.ToString() ?? RuntimeInterop.GetMemberValue(memberValue, "FullName")?.ToString() ?? string.Empty;
			string value2 = RuntimeInterop.GetMemberValue(memberValue, "Region")?.ToString() ?? string.Empty;
			value2 = RuntimeInterop.HumanizeIdentifier(value2);
			if (string.IsNullOrWhiteSpace(value) && string.IsNullOrWhiteSpace(value2))
			{
				return string.Empty;
			}
			string text = EscapeRichText(value);
			string text2 = EscapeRichText(value2);
			if (string.IsNullOrWhiteSpace(value2))
			{
				return "<b>" + text + "</b>";
			}
			if (string.IsNullOrWhiteSpace(value))
			{
				return "<b>" + text2 + "</b>";
			}
			return "<b>" + text + " (" + text2 + ")</b>";
		}

		private static List<string> GetPreferredProperties(object customerData)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			List<string> list = new List<string>();
			foreach (object item in RuntimeInterop.MaterializeObjects(RuntimeInterop.GetMemberValue(customerData, "PreferredProperties")))
			{
				if (item != null)
				{
					string text = RuntimeInterop.GetMemberValue(item, "Name")?.ToString() ?? RuntimeInterop.HumanizeIdentifier(item.GetType().Name);
					if (!string.IsNullOrWhiteSpace(text) && hashSet.Add(text))
					{
						list.Add(FormatEffectLabel(item, text));
					}
				}
			}
			return list;
		}

		private static List<string> GetDrugAffinities(object customerData, bool positive)
		{
			List<(string, float)> list = new List<(string, float)>();
			object memberValue = RuntimeInterop.GetMemberValue(customerData, "DefaultAffinityData");
			foreach (object item in RuntimeInterop.MaterializeObjects(RuntimeInterop.GetMemberValue(memberValue, "ProductAffinities")))
			{
				if (item == null)
				{
					continue;
				}
				float num = RuntimeInterop.ConvertToSingle(RuntimeInterop.GetMemberValue(item, "Affinity"));
				if ((!positive || !(num <= 0.15f)) && (positive || !(num >= -0.15f)))
				{
					string text = RuntimeInterop.HumanizeIdentifier(RuntimeInterop.GetMemberValue(item, "DrugType")?.ToString() ?? string.Empty);
					if (!string.IsNullOrWhiteSpace(text))
					{
						list.Add((text, num));
					}
				}
			}
			list.Sort(((string Name, float Affinity) left, (string Name, float Affinity) right) => positive ? right.Affinity.CompareTo(left.Affinity) : left.Affinity.CompareTo(right.Affinity));
			List<string> list2 = new List<string>();
			int val = (positive ? 3 : 2);
			for (int i = 0; i < Math.Min(list.Count, val); i++)
			{
				var (label, affinity) = list[i];
				list2.Add(FormatDrugTypeLabel(label) + " (" + DescribeAffinity(affinity) + ")");
			}
			return list2;
		}

		private static string FormatEffectLabel(object effect, string label)
		{
			string text = EscapeRichText(label);
			string effectColorHex = GetEffectColorHex(effect);
			if (string.IsNullOrEmpty(effectColorHex))
			{
				return "<b>" + text + "</b>";
			}
			return "<b><color=#" + effectColorHex + ">" + text + "</color></b>";
		}

		private static string FormatDrugTypeLabel(string label)
		{
			string text = EscapeRichText(label);
			if (!DrugTypeColors.TryGetValue(label, out string value))
			{
				return "<b>" + text + "</b>";
			}
			return "<b><color=#" + value + ">" + text + "</color></b>";
		}

		private static string? GetEffectColorHex(object effect)
		{
			object memberValue = RuntimeInterop.GetMemberValue(effect, "LabelColor");
			if (TryGetColorHex(memberValue, out string colorHex))
			{
				return colorHex;
			}
			object memberValue2 = RuntimeInterop.GetMemberValue(effect, "TintColor");
			if (TryGetColorHex(memberValue2, out colorHex))
			{
				return colorHex;
			}
			return null;
		}

		private static bool TryGetColorHex(object? colorValue, out string colorHex)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!(colorValue is Color val))
			{
				if (colorValue is Color32 val2)
				{
					colorHex = ColorUtility.ToHtmlStringRGB(Color32.op_Implicit(val2));
					return true;
				}
				colorHex = string.Empty;
				return false;
			}
			colorHex = ColorUtility.ToHtmlStringRGB(val);
			return true;
		}

		private static string EscapeRichText(string value)
		{
			return value.Replace("&", "&amp;", StringComparison.Ordinal).Replace("<", "&lt;", StringComparison.Ordinal).Replace(">", "&gt;", StringComparison.Ordinal);
		}

		private static string DescribeAffinity(float affinity)
		{
			if (affinity >= 0.75f)
			{
				return "loves";
			}
			if (affinity >= 0.35f)
			{
				return "likes";
			}
			if (affinity > 0.15f)
			{
				return "prefers";
			}
			if (affinity <= -0.75f)
			{
				return "hates";
			}
			if (affinity <= -0.35f)
			{
				return "dislikes";
			}
			return "avoids";
		}
	}
}
namespace DealerCustomerPreferences.Integrations.Interop
{
	internal static class GameTypeNames
	{
		public static readonly string[] DealerManagementApp = new string[2] { "ScheduleOne.UI.Phone.Messages.DealerManagementApp", "Il2CppScheduleOne.UI.Phone.Messages.DealerManagementApp" };

		public static readonly string[] CustomerSelector = new string[2] { "ScheduleOne.UI.Phone.CustomerSelector", "Il2CppScheduleOne.UI.Phone.CustomerSelector" };

		public static readonly string[] Dealer = new string[2] { "ScheduleOne.Economy.Dealer", "Il2CppScheduleOne.Economy.Dealer" };

		public static readonly string[] TooltipManager = new string[2] { "ScheduleOne.UI.Tooltips.TooltipManager", "Il2CppScheduleOne.UI.Tooltips.TooltipManager" };

		public static readonly string[] GameInput = new string[2] { "ScheduleOne.GameInput", "Il2CppScheduleOne.GameInput" };
	}
	internal static class RuntimeInterop
	{
		private const BindingFlags MemberLookupFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly ConcurrentDictionary<(Type Type, string MemberName), MemberInfo?> MemberCache = new ConcurrentDictionary<(Type, string), MemberInfo>();

		private static readonly ConcurrentDictionary<string, Type?> TypeCache = new ConcurrentDictionary<string, Type>(StringComparer.Ordinal);

		public static float ConvertToSingle(object? value)
		{
			if (value == null)
			{
				return 0f;
			}
			if (1 == 0)
			{
			}
			float result = ((value is float num) ? num : ((!(value is double num2)) ? Convert.ToSingle(value) : ((float)num2)));
			if (1 == 0)
			{
			}
			return result;
		}

		public static IReadOnlyList<object?> MaterializeObjects(object? value)
		{
			if (value == null)
			{
				return Array.Empty<object>();
			}
			if (value is IEnumerable enumerable)
			{
				List<object> list = new List<object>();
				foreach (object item in enumerable)
				{
					list.Add(item);
				}
				return list;
			}
			object memberValue = GetMemberValue(value, "Count");
			if (memberValue == null)
			{
				return Array.Empty<object>();
			}
			int num = Convert.ToInt32(memberValue);
			PropertyInfo property = value.GetType().GetProperty("Item", new Type[1] { typeof(int) });
			if (property == null)
			{
				return Array.Empty<object>();
			}
			List<object> list2 = new List<object>(num);
			for (int i = 0; i < num; i++)
			{
				list2.Add(property.GetValue(value, new object[1] { i }));
			}
			return list2;
		}

		public static object? TryGetMappedValue(object? map, object key)
		{
			foreach (object item in MaterializeObjects(map))
			{
				if (item != null)
				{
					object memberValue = GetMemberValue(item, "Key");
					if (memberValue == key || object.Equals(memberValue, key))
					{
						return GetMemberValue(item, "Value");
					}
				}
			}
			return null;
		}

		public static object? GetMemberValue(object? instance, string memberName)
		{
			if (instance == null)
			{
				return null;
			}
			MemberInfo memberInfo = ResolveMember(instance.GetType(), memberName);
			if (memberInfo is PropertyInfo propertyInfo)
			{
				return propertyInfo.GetValue(instance);
			}
			return (memberInfo is FieldInfo fieldInfo) ? fieldInfo.GetValue(instance) : null;
		}

		public static object? GetStaticMemberValue(Type type, string memberName)
		{
			MemberInfo memberInfo = ResolveMember(type, memberName);
			if (memberInfo is PropertyInfo propertyInfo)
			{
				return propertyInfo.GetValue(null);
			}
			return (memberInfo is FieldInfo fieldInfo) ? fieldInfo.GetValue(null) : null;
		}

		public static void SetMemberValue(object instance, string memberName, object? value)
		{
			MemberInfo memberInfo = ResolveMember(instance.GetType(), memberName);
			if (memberInfo is PropertyInfo propertyInfo && propertyInfo.CanWrite)
			{
				propertyInfo.SetValue(instance, value);
			}
			else if (memberInfo is FieldInfo fieldInfo)
			{
				fieldInfo.SetValue(instance, value);
			}
		}

		public static Type? ResolveGameType(IEnumerable<string> typeNames)
		{
			foreach (string typeName in typeNames)
			{
				Type type = ResolveLoadedType(typeName);
				if (type != null)
				{
					return type;
				}
			}
			return null;
		}

		public static string HumanizeIdentifier(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(value.Length + 4);
			for (int i = 0; i < value.Length; i++)
			{
				char c = value[i];
				if (c == '_')
				{
					if (stringBuilder.Length > 0)
					{
						if (stringBuilder[stringBuilder.Length - 1] != ' ')
						{
							stringBuilder.Append(' ');
						}
					}
					continue;
				}
				if (i > 0 && char.IsUpper(c) && !char.IsUpper(value[i - 1]))
				{
					if (stringBuilder[stringBuilder.Length - 1] != ' ')
					{
						stringBuilder.Append(' ');
					}
				}
				stringBuilder.Append(c);
			}
			return stringBuilder.ToString().Trim();
		}

		private static MemberInfo? ResolveMember(Type type, string memberName)
		{
			return MemberCache.GetOrAdd((type, memberName), ((Type Type, string MemberName) key) => FindMember(key.Type, key.MemberName));
		}

		private static MemberInfo? FindMember(Type type, string memberName)
		{
			Type type2 = type;
			while (type2 != null)
			{
				PropertyInfo property = type2.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					return property;
				}
				FieldInfo field = type2.GetField(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field;
				}
				type2 = type2.BaseType;
			}
			return null;
		}

		public static bool IsIl2CppRuntime()
		{
			return ResolveLoadedType("Il2CppScheduleOne.UI.Phone.CustomerSelector") != null;
		}

		private static Type? ResolveLoadedType(string typeName)
		{
			return TypeCache.GetOrAdd(typeName, (string name) => FindLoadedType(name));
		}

		private static Type? FindLoadedType(string typeName)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				Type type = assembly.GetType(typeName, throwOnError: false, ignoreCase: false);
				if (type != null)
				{
					return type;
				}
			}
			return null;
		}
	}
}
namespace DealerCustomerPreferences.Config
{
	internal sealed class ModPreferences
	{
		public const int DefaultTooltipFontSize = 14;

		private readonly MelonPreferences_Entry<bool> _enabledPreference;

		private readonly MelonPreferences_Entry<int> _fontSizePreference;

		public bool IsEnabled => _enabledPreference.Value;

		public int TooltipFontSize => Math.Max(1, _fontSizePreference.Value);

		public ModPreferences()
		{
			MelonPreferences_Category val = MelonPreferences.CreateCategory("Dealer Customer Preferences");
			_enabledPreference = val.CreateEntry<bool>("enabled", true, "Whether the mod is enabled.", (string)null, false, false, (ValueValidator)null, (string)null);
			_fontSizePreference = val.CreateEntry<int>("fontSize", 14, "Font size used for the tooltip.", (string)null, false, false, (ValueValidator)null, (string)null);
		}

		public bool ApplySnapshot(PreferencesSnapshot snapshot)
		{
			bool result = false;
			bool? isEnabled = snapshot.IsEnabled;
			if (isEnabled.HasValue)
			{
				bool valueOrDefault = isEnabled.GetValueOrDefault();
				if (_enabledPreference.Value != valueOrDefault)
				{
					_enabledPreference.Value = valueOrDefault;
					result = true;
				}
			}
			int? fontSize = snapshot.FontSize;
			if (fontSize.HasValue)
			{
				int valueOrDefault2 = fontSize.GetValueOrDefault();
				if (_fontSizePreference.Value != valueOrDefault2)
				{
					_fontSizePreference.Value = valueOrDefault2;
					result = true;
				}
			}
			return result;
		}
	}
	internal static class PreferencesFileParser
	{
		public static bool TryReadSnapshot(string preferencesFilePath, out PreferencesSnapshot snapshot)
		{
			snapshot = new PreferencesSnapshot();
			if (!File.Exists(preferencesFilePath))
			{
				return false;
			}
			try
			{
				string[] array = File.ReadAllLines(preferencesFilePath);
				bool flag = false;
				bool? isEnabled = null;
				int? fontSize = null;
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length == 0)
					{
						continue;
					}
					if (text.StartsWith("[", StringComparison.Ordinal) && text.EndsWith("]", StringComparison.Ordinal))
					{
						string a = text.Substring(1, text.Length - 2);
						flag = string.Equals(a, "Dealer Customer Preferences", StringComparison.Ordinal);
					}
					else
					{
						if (!flag)
						{
							continue;
						}
						int num = text.IndexOf('=');
						if (num > 0)
						{
							string a2 = text.Substring(0, num).Trim();
							string text2 = text.Substring(num + 1).Trim();
							int result2;
							if (string.Equals(a2, "enabled", StringComparison.Ordinal) && bool.TryParse(text2, out var result))
							{
								isEnabled = result;
							}
							else if (string.Equals(a2, "fontSize", StringComparison.Ordinal) && int.TryParse(text2, NumberStyles.Integer, CultureInfo.InvariantCulture, out result2))
							{
								fontSize = result2;
							}
						}
					}
				}
				snapshot = new PreferencesSnapshot(isEnabled, fontSize);
				return true;
			}
			catch (IOException)
			{
				return false;
			}
			catch (UnauthorizedAccessException)
			{
				return false;
			}
		}
	}
	internal sealed class PreferencesFileWatcher : IDisposable
	{
		private const string PreferencesFileName = "MelonPreferences.cfg";

		private static readonly TimeSpan PreferencesReloadDebounce = TimeSpan.FromMilliseconds(150.0);

		private readonly object _preferencesReloadLock = new object();

		private FileSystemWatcher? _preferencesWatcher;

		private DateTime? _preferencesReloadDueUtc;

		public string? FilePath { get; }

		public PreferencesFileWatcher(Log logger)
		{
			FilePath = ResolvePreferencesFilePath();
			Initialize(logger);
		}

		public void ScheduleReload()
		{
			lock (_preferencesReloadLock)
			{
				_preferencesReloadDueUtc = DateTime.UtcNow + PreferencesReloadDebounce;
			}
		}

		public bool TryConsumeScheduledReload()
		{
			lock (_preferencesReloadLock)
			{
				DateTime? preferencesReloadDueUtc = _preferencesReloadDueUtc;
				if (!preferencesReloadDueUtc.HasValue || DateTime.UtcNow < _preferencesReloadDueUtc.Value)
				{
					return false;
				}
				_preferencesReloadDueUtc = null;
				return true;
			}
		}

		public void Dispose()
		{
			if (_preferencesWatcher != null)
			{
				_preferencesWatcher.EnableRaisingEvents = false;
				_preferencesWatcher.Changed -= OnPreferencesFileChanged;
				_preferencesWatcher.Created -= OnPreferencesFileChanged;
				_preferencesWatcher.Renamed -= OnPreferencesFileRenamed;
				_preferencesWatcher.Dispose();
				_preferencesWatcher = null;
			}
		}

		private void Initialize(Log logger)
		{
			if (string.IsNullOrWhiteSpace(FilePath))
			{
				logger.Warning("Could not resolve the MelonPreferences path; live config reload is disabled.");
				return;
			}
			string directoryName = Path.GetDirectoryName(FilePath);
			if (string.IsNullOrWhiteSpace(directoryName) || !Directory.Exists(directoryName))
			{
				logger.Warning("MelonPreferences directory was not found: " + directoryName);
				return;
			}
			_preferencesWatcher = new FileSystemWatcher(directoryName, "MelonPreferences.cfg")
			{
				NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime),
				IncludeSubdirectories = false,
				EnableRaisingEvents = true
			};
			_preferencesWatcher.Changed += OnPreferencesFileChanged;
			_preferencesWatcher.Created += OnPreferencesFileChanged;
			_preferencesWatcher.Renamed += OnPreferencesFileRenamed;
		}

		private void OnPreferencesFileChanged(object sender, FileSystemEventArgs e)
		{
			ScheduleReload();
		}

		private void OnPreferencesFileRenamed(object sender, RenamedEventArgs e)
		{
			ScheduleReload();
		}

		private static string? ResolvePreferencesFilePath()
		{
			string userDataDirectory = MelonEnvironment.UserDataDirectory;
			if (string.IsNullOrWhiteSpace(userDataDirectory))
			{
				return null;
			}
			return Path.Combine(userDataDirectory, "MelonPreferences.cfg");
		}
	}
	internal sealed class PreferencesSnapshot
	{
		public bool? IsEnabled { get; }

		public int? FontSize { get; }

		public PreferencesSnapshot(bool? isEnabled = null, int? fontSize = null)
		{
			IsEnabled = isEnabled;
			FontSize = fontSize;
		}
	}
}