Decompiled source of CustomTextureReplacer v1.7.0

CustomTextureReplacer.dll

Decompiled 2 weeks ago
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.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.U2D;
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: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("CustomTextureReplacer")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+17911c2ed48b158a6086bf0a3532610c3e64c3f7")]
[assembly: AssemblyProduct("CustomTextureReplacer")]
[assembly: AssemblyTitle("CustomTextureReplacer")]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace CustomTextureReplacer
{
	[BepInPlugin("com.duckieray.cardshop.customtextures", "Custom Texture Replacer", "1.7.0")]
	public class CustomTextureReplacer : BaseUnityPlugin
	{
		private void Awake()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			if ((Object)(object)ReplacerController.Instance != (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"[CustomTextureReplacer] Controller already initialised");
				Object.Destroy((Object)(object)this);
				return;
			}
			GameObject val = new GameObject("CustomTextureReplacerController")
			{
				hideFlags = (HideFlags)61
			};
			Object.DontDestroyOnLoad((Object)(object)val);
			ReplacerController replacerController = val.AddComponent<ReplacerController>();
			replacerController.Initialise(((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Config);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[CustomTextureReplacer] Controller created.");
		}
	}
	internal class ReplacerController : MonoBehaviour
	{
		private enum FolderPriorityMode
		{
			LastModified,
			PreferredFolder,
			FolderOrder
		}

		private struct TextureCandidate
		{
			public string Path;

			public DateTime TimestampUtc;

			public int FolderIndex;

			public long EventOrder;
		}

		internal sealed class MeshOverrideData
		{
			public string TargetName;

			public string MeshAssetName;

			public string MeshSelectionHint;

			public bool AutoSelectMesh;

			public bool AutoSelectMaterials;

			public string[] MaterialSelectionHints = Array.Empty<string>();

			public string SourceBundlePath;

			public Mesh MeshInstance;

			public bool ApplyToMeshFilter;

			public bool ApplyToSkinnedMeshRenderer;

			public bool ApplyToMeshCollider;

			public int DesiredInstanceCount = -1;

			public int DesiredShelfCapacity = -1;

			public bool ShelfAllocationsDirty = true;

			public CardTextOverride CardOverride;

			public List<MaterialOverrideData> MaterialOverrides { get; } = new List<MaterialOverrideData>();


			public HashSet<int> InstanceIds { get; } = new HashSet<int>();


			public List<WeakReference<GameObject>> InstanceReferences { get; } = new List<WeakReference<GameObject>>();


			public HashSet<int> HiddenInstanceIds { get; } = new HashSet<int>();


			public Dictionary<int, ShelfSlotState> ShelfSlots { get; } = new Dictionary<int, ShelfSlotState>();

		}

		internal sealed class ShelfSlotState
		{
			public int DefaultCapacity = -1;

			public int DesiredCapacity = -1;

			public int LastAnchorCount = -1;

			public int LastItemCount = -1;
		}

		internal sealed class MaterialOverrideData
		{
			public int Slot;

			public string MaterialAssetName;

			public string SourceBundlePath;

			public Material MaterialInstance;

			public List<MaterialTextureAssignment> TextureAssignments { get; } = new List<MaterialTextureAssignment>();

		}

		internal sealed class MaterialTextureAssignment
		{
			public string PropertyName;

			public string FilePath;

			public Texture Texture;
		}

		internal sealed class PersistentSpriteOverride
		{
			public string SpriteName;

			public string TargetTextureName;

			public RectInt Region;

			public int Width;

			public int Height;

			public Texture2D Replacement;
		}

		private static class CardUIBindings
		{
			internal static readonly FieldInfo CardDataField = AccessTools.Field(typeof(CardUI), "m_CardData");

			internal static readonly FieldInfo MonsterDataField = AccessTools.Field(typeof(CardUI), "m_MonsterData");

			internal static readonly FieldInfo MonsterNameTextField = AccessTools.Field(typeof(CardUI), "m_MonsterNameText");

			internal static readonly FieldInfo NumberTextField = AccessTools.Field(typeof(CardUI), "m_NumberText");

			internal static readonly FieldInfo SubtitleTextField = AccessTools.Field(typeof(CardUI), "m_ChampionText");

			internal static readonly FieldInfo DescriptionTextField = AccessTools.Field(typeof(CardUI), "m_DescriptionText");

			internal static readonly FieldInfo ArtistTextField = AccessTools.Field(typeof(CardUI), "m_ArtistText");

			internal static readonly FieldInfo RarityTextField = AccessTools.Field(typeof(CardUI), "m_RarityText");

			internal static readonly FieldInfo FameTextField = AccessTools.Field(typeof(CardUI), "m_FameText");

			internal static readonly FieldInfo Stat1TextField = AccessTools.Field(typeof(CardUI), "m_Stat1Text");

			internal static readonly FieldInfo Stat2TextField = AccessTools.Field(typeof(CardUI), "m_Stat2Text");

			internal static readonly FieldInfo Stat3TextField = AccessTools.Field(typeof(CardUI), "m_Stat3Text");

			internal static readonly FieldInfo Stat4TextField = AccessTools.Field(typeof(CardUI), "m_Stat4Text");

			internal static readonly FieldInfo EvoPreviousStageNameTextField = AccessTools.Field(typeof(CardUI), "m_EvoPreviousStageNameText");

			internal static readonly FieldInfo EvoGroupField = AccessTools.Field(typeof(CardUI), "m_EvoGrp");
		}

		private static class CheckPricePanelUIBindings
		{
			internal static readonly FieldInfo NameTextField = AccessTools.Field(typeof(CheckPricePanelUI), "m_NameText");

			internal static readonly FieldInfo CardUIField = AccessTools.Field(typeof(CheckPricePanelUI), "m_CardUI");
		}

		private static class CheckoutItemBarBindings
		{
			internal static readonly FieldInfo NameTextField = AccessTools.Field(typeof(UI_CheckoutItemBar), "m_NameText");
		}

		private static class ItemPriceGraphScreenBindings
		{
			internal static readonly FieldInfo CardNameTextField = AccessTools.Field(typeof(ItemPriceGraphScreen), "m_CardName");

			internal static readonly FieldInfo CardUIField = AccessTools.Field(typeof(ItemPriceGraphScreen), "m_CardUI");
		}

		internal class CardTextOverride
		{
			public string Id;

			public string[] Aliases;

			public string DisplayName;

			public string Subtitle;

			public string CardNumber;

			public string Description;

			public string EvolvesFrom;

			public string Artist;

			public string Stat1;

			public string Stat2;

			public string Stat3;

			public string Stat4;

			public string Rarity;

			public string Fame;

			public bool IsMeshOverride;
		}

		[CompilerGenerated]
		private sealed class <ApplyReplacementsNextFrame>d__150 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public string sceneName;

			public ReplacerController <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ApplyReplacementsNextFrame>d__150(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 2;
					return true;
				case 2:
					<>1__state = -1;
					<>4__this._logger.LogInfo((object)("[CustomTextureReplacer] Scene '" + sceneName + "' loaded. Reapplying replacements."));
					<>4__this.SafeAppendDebug("ApplyReplacementsNextFrame for " + sceneName);
					<>4__this.ReplaceAllTextures();
					if (!<>4__this._hasDumpedAfterSceneLoad && <>4__this._enableAutoDumps != null && <>4__this._enableAutoDumps.Value)
					{
						<>4__this._hasDumpedAfterSceneLoad = true;
						<>4__this.SafeAppendDebug("Dumping after scene load.");
						<>4__this.DumpAllTextures();
						<>4__this.DumpAllMeshes();
					}
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <EnumerateNearbyTextComponents>d__219 : IEnumerable<TMP_Text>, IEnumerable, IEnumerator<TMP_Text>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private TMP_Text <>2__current;

			private int <>l__initialThreadId;

			private GameObject instance;

			public GameObject <>3__instance;

			public ReplacerController <>4__this;

			private HashSet<int> <visited>5__1;

			private Transform <current>5__2;

			private int <depth>5__3;

			private TMP_Text[] <buffer>5__4;

			private TMP_Text[] <>s__5;

			private int <>s__6;

			private TMP_Text <text>5__7;

			private int <id>5__8;

			TMP_Text IEnumerator<TMP_Text>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <EnumerateNearbyTextComponents>d__219(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<visited>5__1 = null;
				<current>5__2 = null;
				<buffer>5__4 = null;
				<>s__5 = null;
				<text>5__7 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
					goto IL_0102;
				}
				<>1__state = -1;
				if ((Object)(object)instance == (Object)null)
				{
					return false;
				}
				<visited>5__1 = new HashSet<int>();
				<current>5__2 = instance.transform;
				<depth>5__3 = 0;
				goto IL_015d;
				IL_0102:
				<text>5__7 = null;
				goto IL_010a;
				IL_015d:
				if ((Object)(object)<current>5__2 != (Object)null && <depth>5__3 < 12)
				{
					try
					{
						<buffer>5__4 = ((Component)<current>5__2).GetComponentsInChildren<TMP_Text>(true);
					}
					catch
					{
						<buffer>5__4 = Array.Empty<TMP_Text>();
					}
					<>s__5 = <buffer>5__4;
					<>s__6 = 0;
					goto IL_0118;
				}
				return false;
				IL_0118:
				if (<>s__6 < <>s__5.Length)
				{
					<text>5__7 = <>s__5[<>s__6];
					if ((Object)(object)<text>5__7 == (Object)null)
					{
						goto IL_010a;
					}
					<id>5__8 = ((Object)<text>5__7).GetInstanceID();
					if (<visited>5__1.Add(<id>5__8))
					{
						<>2__current = <text>5__7;
						<>1__state = 1;
						return true;
					}
					goto IL_0102;
				}
				<>s__5 = null;
				<current>5__2 = <current>5__2.parent;
				<depth>5__3++;
				<buffer>5__4 = null;
				goto IL_015d;
				IL_010a:
				<>s__6++;
				goto IL_0118;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<TMP_Text> IEnumerable<TMP_Text>.GetEnumerator()
			{
				<EnumerateNearbyTextComponents>d__219 <EnumerateNearbyTextComponents>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<EnumerateNearbyTextComponents>d__ = this;
				}
				else
				{
					<EnumerateNearbyTextComponents>d__ = new <EnumerateNearbyTextComponents>d__219(0)
					{
						<>4__this = <>4__this
					};
				}
				<EnumerateNearbyTextComponents>d__.instance = <>3__instance;
				return <EnumerateNearbyTextComponents>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<TMP_Text>)this).GetEnumerator();
			}
		}

		[CompilerGenerated]
		private sealed class <EnumerateStringValues>d__179 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private string <>2__current;

			private int <>l__initialThreadId;

			private object value;

			public object <>3__value;

			private string <s>5__1;

			private IList <list>5__2;

			private string <fallback>5__3;

			private IEnumerator <>s__4;

			private object <item>5__5;

			private string <text>5__6;

			string IEnumerator<string>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <EnumerateStringValues>d__179(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 2)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<s>5__1 = null;
				<list>5__2 = null;
				<fallback>5__3 = null;
				<>s__4 = null;
				<item>5__5 = null;
				<text>5__6 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						if (value == null)
						{
							return false;
						}
						<s>5__1 = value as string;
						if (<s>5__1 != null)
						{
							<>2__current = <s>5__1;
							<>1__state = 1;
							return true;
						}
						<list>5__2 = value as IList;
						if (<list>5__2 != null)
						{
							<>s__4 = <list>5__2.GetEnumerator();
							<>1__state = -3;
							goto IL_0150;
						}
						<fallback>5__3 = value.ToString();
						if (!string.IsNullOrWhiteSpace(<fallback>5__3))
						{
							<>2__current = <fallback>5__3;
							<>1__state = 3;
							return true;
						}
						break;
					case 1:
						<>1__state = -1;
						return false;
					case 2:
						<>1__state = -3;
						goto IL_0141;
					case 3:
						{
							<>1__state = -1;
							break;
						}
						IL_0141:
						<text>5__6 = null;
						<item>5__5 = null;
						goto IL_0150;
						IL_0150:
						do
						{
							if (<>s__4.MoveNext())
							{
								<item>5__5 = <>s__4.Current;
								continue;
							}
							<>m__Finally1();
							<>s__4 = null;
							return false;
						}
						while (<item>5__5 == null);
						<text>5__6 = <item>5__5.ToString();
						if (!string.IsNullOrWhiteSpace(<text>5__6))
						{
							<>2__current = <text>5__6;
							<>1__state = 2;
							return true;
						}
						goto IL_0141;
					}
					return false;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<>s__4 is IDisposable disposable)
				{
					disposable.Dispose();
				}
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<string> IEnumerable<string>.GetEnumerator()
			{
				<EnumerateStringValues>d__179 <EnumerateStringValues>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<EnumerateStringValues>d__ = this;
				}
				else
				{
					<EnumerateStringValues>d__ = new <EnumerateStringValues>d__179(0);
				}
				<EnumerateStringValues>d__.value = <>3__value;
				return <EnumerateStringValues>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<string>)this).GetEnumerator();
			}
		}

		[CompilerGenerated]
		private sealed class <InitialDump>d__149 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public ReplacerController <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <InitialDump>d__149(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSecondsRealtime(1f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if (<>4__this._enableAutoDumps == null || !<>4__this._enableAutoDumps.Value)
					{
						return false;
					}
					<>4__this._logger.LogInfo((object)"[CustomTextureReplacer] Performing initial texture dump.");
					<>4__this.SafeAppendDebug("Initial dump coroutine running.");
					<>4__this.DumpAllTextures();
					<>4__this.DumpAllSprites();
					<>4__this.DumpAllMeshes();
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <ReapplySpriteAtlasDeferred>d__271 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public SpriteAtlas atlas;

			public string identifier;

			public float initialDelay;

			public ReplacerController <>4__this;

			private int <attempt>5__1;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ReapplySpriteAtlasDeferred>d__271(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00db: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e5: Expected O, but got Unknown
				//IL_004f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0059: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					if (initialDelay > 0f)
					{
						<>2__current = (object)new WaitForSecondsRealtime(initialDelay);
						<>1__state = 1;
						return true;
					}
					goto IL_0069;
				case 1:
					<>1__state = -1;
					goto IL_0069;
				case 2:
					<>1__state = -1;
					<>2__current = (object)new WaitForSecondsRealtime(0.1f);
					<>1__state = 3;
					return true;
				case 3:
					{
						<>1__state = -1;
						<attempt>5__1++;
						break;
					}
					IL_0069:
					<attempt>5__1 = 0;
					break;
				}
				if (<attempt>5__1 < 6 && !((Object)(object)atlas == (Object)null))
				{
					<>4__this.ReapplyPersistentSpriteOverridesForAtlas(atlas);
					<>4__this.RequestReapply($"SpriteAtlasRetry{<attempt>5__1}");
					<>2__current = null;
					<>1__state = 2;
					return true;
				}
				<>4__this._pendingSpriteAtlasReapply.Remove(identifier);
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private const string HarmonyId = "com.duckieray.cardshop.customtextures.harmony";

		private const float ScanIntervalSeconds = 2f;

		private const float MeshOverrideRetryWindowSeconds = 10f;

		private const float MeshOverrideRescanIntervalApplied = 0.25f;

		private const float MeshOverrideRescanIntervalPending = 0.5f;

		private const float MeshOverrideRescanMinimumDelay = 0.12f;

		private const int MeshLabelSearchParentDepth = 12;

		private static readonly Type UIImageType = typeof(Image);

		private static readonly PropertyInfo UIImageSpriteProperty = UIImageType.GetProperty("sprite", BindingFlags.Instance | BindingFlags.Public);

		private static readonly PropertyInfo UIImageOverrideSpriteProperty = UIImageType.GetProperty("overrideSprite", BindingFlags.Instance | BindingFlags.Public);

		private static readonly Type UIRawImageType = typeof(RawImage);

		private static readonly PropertyInfo UIRawImageTextureProperty = UIRawImageType.GetProperty("texture", BindingFlags.Instance | BindingFlags.Public);

		private static readonly string[] RendererTextureProperties = new string[7] { "_MainTex", "_BaseMap", "_BaseColorMap", "_BaseTex", "_DiffuseTex", "_EmissionMap", "_AlbedoTex" };

		private readonly Dictionary<string, Texture2D> _customTextures = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<int> _knownTextureIds = new HashSet<int>();

		private readonly HashSet<int> _customTextureIds = new HashSet<int>();

		private readonly HashSet<int> _collectionScratch = new HashSet<int>();

		private readonly HashSet<int> _spriteScratch = new HashSet<int>();

		private readonly List<Texture2D> _textureBuffer = new List<Texture2D>(512);

		private readonly List<Sprite> _spriteBuffer = new List<Sprite>(512);

		private readonly List<string> _newTextureNames = new List<string>(64);

		private readonly Dictionary<Texture, Texture> _textureOverrides = new Dictionary<Texture, Texture>();

		private readonly Dictionary<string, Texture> _textureOverridesByName = new Dictionary<string, Texture>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<Sprite, Sprite> _spriteOverrides = new Dictionary<Sprite, Sprite>();

		private readonly Dictionary<string, Sprite> _spriteOverridesByName = new Dictionary<string, Sprite>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<Sprite, Texture2D> _spriteOverrideTextures = new Dictionary<Sprite, Texture2D>();

		private readonly HashSet<int> _imageOverrideInstances = new HashSet<int>();

		private readonly HashSet<int> _rawImageOverrideInstances = new HashSet<int>();

		private bool _applyingImageOverride;

		private bool _applyingRawImageOverride;

		private bool _applyingLabelOverride;

		private readonly Dictionary<int, string> _meshInstanceTargetByComponent = new Dictionary<int, string>();

		private readonly Dictionary<int, string> _shelfMeshNameCache = new Dictionary<int, string>();

		private readonly HashSet<string> _loggedShelfCapacityWarnings = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> _loggedMeshLabelCandidates = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static readonly FieldInfo ShelfCompartmentItemTypeField = AccessTools.Field(typeof(ShelfCompartment), "m_ItemType");

		private static readonly FieldInfo ShelfCompartmentItemPosListField = AccessTools.Field(typeof(ShelfCompartment), "m_ItemPosList");

		internal static readonly Type InventoryBaseType = AccessTools.TypeByName("InventoryBase");

		internal static readonly Type ItemTypeEnumType = AccessTools.TypeByName("EItemType");

		internal static readonly MethodInfo InventoryGetItemMeshDataMethod = ((InventoryBaseType != null && ItemTypeEnumType != null) ? AccessTools.Method(InventoryBaseType, "GetItemMeshData", new Type[1] { ItemTypeEnumType }, (Type[])null) : null);

		internal static readonly Type ItemMeshDataType = AccessTools.TypeByName("ItemMeshData");

		internal static readonly FieldInfo ItemMeshDataMeshField = ((ItemMeshDataType != null) ? AccessTools.Field(ItemMeshDataType, "mesh") : null);

		internal static readonly FieldInfo ShelfCompartmentSizeXField = AccessTools.Field(typeof(ShelfCompartment), "m_SizeX");

		internal static readonly FieldInfo ShelfCompartmentCurrentItemSizeXField = AccessTools.Field(typeof(ShelfCompartment), "m_CurrentItemSizeX");

		internal static readonly FieldInfo ShelfCompartmentSizeYField = AccessTools.Field(typeof(ShelfCompartment), "m_SizeY");

		internal static readonly FieldInfo ShelfCompartmentSizeZField = AccessTools.Field(typeof(ShelfCompartment), "m_SizeZ");

		internal static readonly FieldInfo ShelfCompartmentCurrentItemSizeYField = AccessTools.Field(typeof(ShelfCompartment), "m_CurrentItemSizeY");

		internal static readonly FieldInfo ShelfCompartmentCurrentItemSizeZField = AccessTools.Field(typeof(ShelfCompartment), "m_CurrentItemSizeZ");

		internal static readonly FieldInfo ShelfCompartmentMaxItemXField = AccessTools.Field(typeof(ShelfCompartment), "m_MaxItemX");

		internal static readonly FieldInfo ShelfCompartmentMaxItemYField = AccessTools.Field(typeof(ShelfCompartment), "m_MaxItemY");

		internal static readonly FieldInfo ShelfCompartmentMaxItemZField = AccessTools.Field(typeof(ShelfCompartment), "m_MaxItemZ");

		internal static readonly FieldInfo ShelfCompartmentMaxItemCountField = AccessTools.Field(typeof(ShelfCompartment), "m_MaxItemCount");

		internal static readonly FieldInfo ShelfCompartmentTransformPosListField = AccessTools.Field(typeof(ShelfCompartment), "m_PosList");

		internal static readonly FieldInfo ShelfCompartmentStoredItemListGrpField = AccessTools.Field(typeof(ShelfCompartment), "m_StoredItemListGrp");

		private readonly Dictionary<string, PersistentSpriteOverride> _spritePersistence = new Dictionary<string, PersistentSpriteOverride>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, List<PersistentSpriteOverride>> _spritePersistenceByTexture = new Dictionary<string, List<PersistentSpriteOverride>>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<Texture2D> _generatedTextures = new HashSet<Texture2D>();

		private readonly HashSet<Sprite> _generatedSprites = new HashSet<Sprite>();

		private readonly Dictionary<string, CardTextOverride> _cardOverrides = new Dictionary<string, CardTextOverride>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, CardTextOverride> _capturedCardData = new Dictionary<string, CardTextOverride>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, CardTextOverride> _meshCardOverrides = new Dictionary<string, CardTextOverride>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, MeshOverrideData> _meshOverridesByTarget = new Dictionary<string, MeshOverrideData>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> _meshTextureFolders = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<int, WeakReference<MeshFilter>> _meshFilterReferences = new Dictionary<int, WeakReference<MeshFilter>>();

		private readonly Dictionary<int, Mesh> _meshFilterOriginalMeshes = new Dictionary<int, Mesh>();

		private readonly HashSet<int> _meshFilterOverrideIds = new HashSet<int>();

		private readonly Dictionary<int, WeakReference<SkinnedMeshRenderer>> _skinnedRendererReferences = new Dictionary<int, WeakReference<SkinnedMeshRenderer>>();

		private readonly Dictionary<int, Mesh> _skinnedOriginalMeshes = new Dictionary<int, Mesh>();

		private readonly HashSet<int> _skinnedRendererOverrideIds = new HashSet<int>();

		private readonly Dictionary<int, WeakReference<MeshCollider>> _meshColliderReferences = new Dictionary<int, WeakReference<MeshCollider>>();

		private readonly Dictionary<int, Mesh> _meshColliderOriginalMeshes = new Dictionary<int, Mesh>();

		private readonly HashSet<int> _meshColliderOverrideIds = new HashSet<int>();

		private readonly Dictionary<int, WeakReference<Renderer>> _rendererReferences = new Dictionary<int, WeakReference<Renderer>>();

		private readonly Dictionary<int, Material[]> _rendererOriginalMaterials = new Dictionary<int, Material[]>();

		private readonly HashSet<int> _rendererOverrideIds = new HashSet<int>();

		private readonly Dictionary<int, string> _lastAppliedMeshLabels = new Dictionary<int, string>();

		private readonly Dictionary<int, WeakReference<TMP_Text>> _meshLabelTextRefs = new Dictionary<int, WeakReference<TMP_Text>>();

		private readonly List<int> _meshLabelCleanupScratch = new List<int>(64);

		private int _meshLabelCleanupBudget = 64;

		private readonly Dictionary<string, AssetBundle> _meshAssetBundles = new Dictionary<string, AssetBundle>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, FileSystemWatcher> _meshBundleWatchers = new Dictionary<string, FileSystemWatcher>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> _pendingSpriteAtlasReapply = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private bool _overridesDirty;

		private readonly MaterialPropertyBlock _propertyBlock = new MaterialPropertyBlock();

		private readonly Dictionary<string, DateTime> _fileEventTimes = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, long> _fileEventOrders = new Dictionary<string, long>(StringComparer.OrdinalIgnoreCase);

		private long _fileEventCounter;

		private Sprite[] _spriteArray = Array.Empty<Sprite>();

		private ManualLogSource _logger;

		private ConfigEntry<bool> _logNewTextureNames;

		private ConfigEntry<bool> _logAssetLoads;

		private ConfigEntry<bool> _enableDebugLog;

		private ConfigEntry<bool> _enableAutoDumps;

		private ConfigEntry<bool> _suppressRectTransformWarning;

		private readonly List<string> _textureFolders = new List<string>();

		private string _dumpFile = string.Empty;

		private string _dumpTriggerFile = string.Empty;

		private string _refreshTriggerFile = string.Empty;

		private string _spriteDumpFile = string.Empty;

		private string _spriteDumpTriggerFile = string.Empty;

		private string _debugLogFile = string.Empty;

		private string _exportFolder = string.Empty;

		private string _meshDumpFile = string.Empty;

		private string _meshDumpTriggerFile = string.Empty;

		private readonly List<FileSystemWatcher> _watchers = new List<FileSystemWatcher>();

		private ConfigEntry<FolderPriorityMode> _folderPriorityMode;

		private ConfigEntry<string> _preferredFolderHint;

		private string[] _preferredFolderTokens = Array.Empty<string>();

		private static readonly char[] PreferredFolderSeparators = new char[3] { ';', ',', '|' };

		private string _cardOverridesPath = string.Empty;

		private string _meshOverridesPath = string.Empty;

		private bool _cardOverridesReloadRequested;

		private bool _meshOverridesReloadRequested;

		private FileSystemWatcher _cardOverridesWatcher;

		private FileSystemWatcher _meshOverridesWatcher;

		private Harmony _harmony;

		private volatile bool _reloadRequested;

		private bool _hasDumpedAfterSceneLoad;

		private bool _pendingDump;

		private bool _reapplyRequested;

		private bool _loggedHeartbeat;

		private float _nextScanTime;

		private bool _meshOverridesDirty;

		private float _nextMeshOverrideScanTime;

		private float _meshOverrideRetryDeadline;

		private bool _meshOverrideRetryLogged;

		internal static ReplacerController Instance { get; private set; }

		internal void Initialise(ManualLogSource logger, ConfigFile config)
		{
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Expected O, but got Unknown
			if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
			{
				logger.LogWarning((object)"[CustomTextureReplacer] Duplicate controller detected, destroying new instance.");
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			Instance = this;
			_logger = logger;
			_logNewTextureNames = config.Bind<bool>("Debug", "LogNewTextureNames", true, "Log the names of textures discovered during runtime scans.");
			_logAssetLoads = config.Bind<bool>("Debug", "LogAssetLoads", true, "Log texture and sprite loads coming from Resources/AssetBundle APIs.");
			_enableDebugLog = config.Bind<bool>("Debug", "EnableDebugLogFile", false, "Write CustomTextureReplacer.debug.log for troubleshooting. Disable to avoid extra disk writes.");
			_enableAutoDumps = config.Bind<bool>("Debug", "EnableAutomaticDumps", false, "Automatically dump texture/sprite/mesh lists after loading. Disable to avoid large writes at start-up.");
			_suppressRectTransformWarning = config.Bind<bool>("Debug", "SuppressRectTransformParentWarning", true, "Suppress the noisy 'Parent of RectTransform is being set with parent property' warnings Unity emits.");
			_folderPriorityMode = config.Bind<FolderPriorityMode>("General", "FolderPriorityMode", FolderPriorityMode.LastModified, "Determines which texture file wins when duplicates exist across folders. Options: LastModified, PreferredFolder, FolderOrder.");
			_preferredFolderHint = config.Bind<string>("General", "PreferredFolderHint", string.Empty, "When FolderPriorityMode=PreferredFolder, provide folder names or path fragments (separated by ';' ',' or '|') to prioritise.");
			UpdatePreferredFolderTokens();
			_folderPriorityMode.SettingChanged += delegate
			{
				SafeAppendDebug("FolderPriorityMode changed via config.");
				_reloadRequested = true;
			};
			_preferredFolderHint.SettingChanged += delegate
			{
				UpdatePreferredFolderTokens();
				SafeAppendDebug("PreferredFolderHint changed via config.");
				_reloadRequested = true;
			};
			DiscoverTextureFolders(logDetails: true);
			ResolveCardOverridesPath();
			LoadCardOverrides(logDetails: true);
			SetupCardOverridesWatcher();
			_dumpFile = Path.Combine(Paths.PluginPath, "TexturesList.txt");
			_dumpTriggerFile = Path.Combine(Paths.PluginPath, "CustomTextures.dump.now");
			_refreshTriggerFile = Path.Combine(Paths.PluginPath, "CustomTextures.refresh.now");
			_spriteDumpFile = Path.Combine(Paths.PluginPath, "SpritesList.txt");
			_spriteDumpTriggerFile = Path.Combine(Paths.PluginPath, "SpritesList.dump.now");
			_debugLogFile = Path.Combine(Paths.PluginPath, "CustomTextureReplacer.debug.log");
			_exportFolder = Path.Combine(Paths.PluginPath, "ExportedTextures");
			Directory.CreateDirectory(_exportFolder);
			_meshDumpFile = Path.Combine(Paths.PluginPath, "MeshesList.txt");
			_meshDumpTriggerFile = Path.Combine(Paths.PluginPath, "MeshesList.dump.now");
			SafeAppendDebug("Controller initialised.");
			_harmony = new Harmony("com.duckieray.cardshop.customtextures.harmony");
			_harmony.PatchAll(typeof(ReplacerController).Assembly);
			SceneManager.sceneLoaded += OnSceneLoaded;
			SpriteAtlasManager.atlasRegistered -= OnSpriteAtlasRegistered;
			SpriteAtlasManager.atlasRegistered += OnSpriteAtlasRegistered;
			ReloadCustomTextures();
			ReapplyExistingSpriteAtlases("Initialise");
			ScheduleMeshOverrideReapply(0f);
			if (_enableAutoDumps != null && _enableAutoDumps.Value)
			{
				((MonoBehaviour)this).StartCoroutine(InitialDump());
			}
			_nextScanTime = Time.realtimeSinceStartup + 2f;
		}

		private void OnDestroy()
		{
			SafeAppendDebug("Controller OnDestroy.");
			SceneManager.sceneLoaded -= OnSceneLoaded;
			SpriteAtlasManager.atlasRegistered -= OnSpriteAtlasRegistered;
			foreach (FileSystemWatcher watcher in _watchers)
			{
				try
				{
					watcher.EnableRaisingEvents = false;
					watcher.Created -= OnTextureFileChanged;
					watcher.Changed -= OnTextureFileChanged;
					watcher.Deleted -= OnTextureFileChanged;
					watcher.Renamed -= OnTextureFileRenamed;
					watcher.Dispose();
				}
				catch
				{
				}
			}
			_watchers.Clear();
			if (_cardOverridesWatcher != null)
			{
				try
				{
					_cardOverridesWatcher.EnableRaisingEvents = false;
					_cardOverridesWatcher.Changed -= OnCardOverridesFileChanged;
					_cardOverridesWatcher.Created -= OnCardOverridesFileChanged;
					_cardOverridesWatcher.Deleted -= OnCardOverridesFileChanged;
					_cardOverridesWatcher.Renamed -= OnCardOverridesFileRenamed;
					_cardOverridesWatcher.Dispose();
				}
				catch
				{
				}
				_cardOverridesWatcher = null;
			}
			if (_meshOverridesWatcher != null)
			{
				try
				{
					_meshOverridesWatcher.EnableRaisingEvents = false;
					_meshOverridesWatcher.Changed -= OnMeshOverridesFileChanged;
					_meshOverridesWatcher.Created -= OnMeshOverridesFileChanged;
					_meshOverridesWatcher.Deleted -= OnMeshOverridesFileChanged;
					_meshOverridesWatcher.Renamed -= OnMeshOverridesFileRenamed;
					_meshOverridesWatcher.Dispose();
				}
				catch
				{
				}
				_meshOverridesWatcher = null;
			}
			RevertAllMeshOverrides();
			DisposeMeshOverrideResources();
			if (_harmony != null)
			{
				try
				{
					_harmony.UnpatchSelf();
				}
				catch
				{
				}
				_harmony = null;
			}
			foreach (Texture2D value in _customTextures.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)value);
				}
			}
			_customTextures.Clear();
			_customTextureIds.Clear();
			_knownTextureIds.Clear();
			_textureBuffer.Clear();
			_spriteBuffer.Clear();
			_collectionScratch.Clear();
			_spriteScratch.Clear();
			_cardOverrides.Clear();
			foreach (Texture2D generatedTexture in _generatedTextures)
			{
				if ((Object)(object)generatedTexture != (Object)null)
				{
					Object.Destroy((Object)(object)generatedTexture);
				}
			}
			_generatedTextures.Clear();
			foreach (Sprite generatedSprite in _generatedSprites)
			{
				if ((Object)(object)generatedSprite != (Object)null)
				{
					Object.Destroy((Object)(object)generatedSprite);
				}
			}
			_generatedSprites.Clear();
			_textureOverrides.Clear();
			_textureOverridesByName.Clear();
			_spriteOverrides.Clear();
			_spriteOverridesByName.Clear();
			_spriteOverrideTextures.Clear();
			_imageOverrideInstances.Clear();
			_rawImageOverrideInstances.Clear();
			_meshTextureFolders.Clear();
			_meshCardOverrides.Clear();
			Instance = null;
			_pendingSpriteAtlasReapply.Clear();
		}

		private void Update()
		{
			if (!_loggedHeartbeat)
			{
				_loggedHeartbeat = true;
				_logger.LogInfo((object)"[CustomTextureReplacer] Update loop active. Use F8 or create 'CustomTextures.dump.now' to trigger dumps.");
				SafeAppendDebug("Update heartbeat.");
			}
			if (CheckAndConsumeFileTrigger(_dumpTriggerFile))
			{
				_logger.LogInfo((object)("[CustomTextureReplacer] Manual dump triggered via '" + Path.GetFileName(_dumpTriggerFile) + "'."));
				SafeAppendDebug("Dump trigger consumed.");
				DumpAllTextures();
				DumpAllSprites();
				DumpAllMeshes();
			}
			if (CheckAndConsumeFileTrigger(_refreshTriggerFile))
			{
				_logger.LogInfo((object)("[CustomTextureReplacer] Manual refresh triggered via '" + Path.GetFileName(_refreshTriggerFile) + "'."));
				SafeAppendDebug("Refresh trigger consumed.");
				ReloadCustomTextures();
			}
			if (CheckAndConsumeFileTrigger(_spriteDumpTriggerFile))
			{
				_logger.LogInfo((object)("[CustomTextureReplacer] Manual sprite dump triggered via '" + Path.GetFileName(_spriteDumpTriggerFile) + "'."));
				SafeAppendDebug("Sprite dump trigger consumed.");
				DumpAllSprites();
			}
			if (CheckAndConsumeFileTrigger(_meshDumpTriggerFile))
			{
				_logger.LogInfo((object)("[CustomTextureReplacer] Manual mesh dump triggered via '" + Path.GetFileName(_meshDumpTriggerFile) + "'."));
				SafeAppendDebug("Mesh dump trigger consumed.");
				DumpAllMeshes();
			}
			if (_reloadRequested)
			{
				_reloadRequested = false;
				SafeAppendDebug("Reload flag consumed.");
				ReloadCustomTextures();
			}
			if (_cardOverridesReloadRequested)
			{
				_cardOverridesReloadRequested = false;
				SafeAppendDebug("Card override reload flag consumed.");
				LoadCardOverrides(logDetails: true);
				ReapplyCardOverrides();
			}
			if (_meshOverridesReloadRequested)
			{
				_meshOverridesReloadRequested = false;
				SafeAppendDebug("Mesh override reload flag consumed.");
				LoadMeshOverrides(logDetails: true);
			}
			if (Input.GetKeyDown((KeyCode)289))
			{
				_logger.LogInfo((object)"[CustomTextureReplacer] Manual dump triggered (F8).");
				SafeAppendDebug("F8 pressed");
				DumpAllTextures();
				DumpAllSprites();
				DumpAllMeshes();
			}
			if (Input.GetKeyDown((KeyCode)290))
			{
				_logger.LogInfo((object)"[CustomTextureReplacer] Original card data dump requested (F9).");
				SafeAppendDebug("F9 pressed - dumping original card data.");
				DumpOriginalCardData();
			}
			if (Input.GetKeyDown((KeyCode)291))
			{
				_logger.LogInfo((object)"[CustomTextureReplacer] Manual mesh dump triggered (F10).");
				SafeAppendDebug("F10 pressed");
				DumpAllMeshes();
			}
			if (Time.realtimeSinceStartup >= _nextScanTime)
			{
				_nextScanTime = Time.realtimeSinceStartup + 2f;
				if (DetectNewTextures())
				{
					SafeAppendDebug("Periodic scan detected new textures.");
					RequestReapply("PeriodicScan");
				}
			}
			if (_reapplyRequested)
			{
				_reapplyRequested = false;
				SafeAppendDebug("Reapply flag consumed.");
				ReplaceAllTextures();
			}
			if (_pendingDump)
			{
				_pendingDump = false;
				SafeAppendDebug("Pending dump executed.");
				DumpAllTextures();
				DumpAllSprites();
				DumpAllMeshes();
			}
			ProcessExtractionRequests();
			if (_overridesDirty)
			{
				_overridesDirty = false;
				ApplyTextureOverridesToMaterials();
				ApplyTextureOverridesToRenderers();
				ApplySpriteOverridesToComponents();
				ScheduleMeshOverrideReapply(0f);
			}
			ApplyMeshOverrides();
		}

		private void RefreshWatchers()
		{
			foreach (FileSystemWatcher watcher in _watchers)
			{
				try
				{
					watcher.EnableRaisingEvents = false;
					watcher.Created -= OnTextureFileChanged;
					watcher.Changed -= OnTextureFileChanged;
					watcher.Deleted -= OnTextureFileChanged;
					watcher.Renamed -= OnTextureFileRenamed;
					watcher.Dispose();
				}
				catch
				{
				}
			}
			_watchers.Clear();
			foreach (string textureFolder in _textureFolders)
			{
				try
				{
					Directory.CreateDirectory(textureFolder);
					FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(textureFolder, "*.png")
					{
						NotifyFilter = (NotifyFilters.FileName | NotifyFilters.LastWrite),
						IncludeSubdirectories = false,
						EnableRaisingEvents = true
					};
					fileSystemWatcher.Created += OnTextureFileChanged;
					fileSystemWatcher.Changed += OnTextureFileChanged;
					fileSystemWatcher.Deleted += OnTextureFileChanged;
					fileSystemWatcher.Renamed += OnTextureFileRenamed;
					_watchers.Add(fileSystemWatcher);
				}
				catch (Exception ex)
				{
					ManualLogSource logger = _logger;
					if (logger != null)
					{
						logger.LogWarning((object)("[CustomTextureReplacer] Could not watch '" + textureFolder + "': " + ex.Message));
					}
				}
			}
			SafeAppendDebug($"FileSystemWatcher initialised for {_textureFolders.Count} folder(s).");
		}

		private bool DiscoverTextureFolders(bool logDetails)
		{
			List<string> list = new List<string>();
			try
			{
				string text = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Paths.PluginPath;
				if (!string.IsNullOrEmpty(text))
				{
					TryAddTextureFolder(list, Path.Combine(text, "CustomTextures"), logDetails);
					string text2 = Directory.GetParent(text)?.FullName;
					if (!string.IsNullOrEmpty(text2))
					{
						string[] directories = Directory.GetDirectories(text2);
						foreach (string path in directories)
						{
							TryAddTextureFolder(list, Path.Combine(path, "CustomTextures"), logDetails);
						}
						TryAddTextureFolder(list, Path.Combine(text2, "CustomTextures"), logDetails);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)("[CustomTextureReplacer] Unable to discover texture folders: " + ex.Message));
				}
			}
			if (list.Count == 0)
			{
				string path2 = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Paths.PluginPath;
				string path3 = Path.Combine(path2, "CustomTextures");
				Directory.CreateDirectory(path3);
				list.Add(Path.GetFullPath(path3));
				if (logDetails)
				{
					ManualLogSource logger2 = _logger;
					if (logger2 != null)
					{
						logger2.LogInfo((object)("[CustomTextureReplacer] No texture folders found; using default: " + list[0]));
					}
				}
			}
			if (_meshTextureFolders.Count > 0)
			{
				foreach (string meshTextureFolder in _meshTextureFolders)
				{
					TryAddTextureFolder(list, meshTextureFolder, logDetails);
				}
			}
			bool flag = !_textureFolders.SequenceEqual<string>(list, StringComparer.OrdinalIgnoreCase);
			if (flag)
			{
				_textureFolders.Clear();
				_textureFolders.AddRange(list);
				if (!logDetails)
				{
					foreach (string textureFolder in _textureFolders)
					{
						ManualLogSource logger3 = _logger;
						if (logger3 != null)
						{
							logger3.LogInfo((object)("[CustomTextureReplacer] Texture folder: " + textureFolder));
						}
					}
				}
			}
			return flag;
		}

		private void UpdatePreferredFolderTokens()
		{
			string text = _preferredFolderHint?.Value ?? string.Empty;
			if (string.IsNullOrWhiteSpace(text))
			{
				_preferredFolderTokens = Array.Empty<string>();
				return;
			}
			_preferredFolderTokens = (from token in text.Split(PreferredFolderSeparators, StringSplitOptions.RemoveEmptyEntries)
				select token.Trim() into token
				where token.Length > 0
				select token.Replace('\\', '/').ToLowerInvariant()).ToArray();
		}

		private bool TryAddTextureFolder(List<string> list, string candidate, bool logDetails)
		{
			if (string.IsNullOrEmpty(candidate))
			{
				return false;
			}
			string fullPath;
			try
			{
				fullPath = Path.GetFullPath(candidate);
			}
			catch
			{
				return false;
			}
			if (!Directory.Exists(fullPath))
			{
				return false;
			}
			if (list.Any((string entry) => string.Equals(entry, fullPath, StringComparison.OrdinalIgnoreCase)))
			{
				return false;
			}
			list.Add(fullPath);
			if (logDetails)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogInfo((object)("[CustomTextureReplacer] Found texture folder: " + fullPath));
				}
			}
			return true;
		}

		private int GetPreferredScore(string path)
		{
			if (_preferredFolderTokens.Length == 0)
			{
				return int.MaxValue;
			}
			string text = path.Replace('\\', '/').ToLowerInvariant();
			for (int i = 0; i < _preferredFolderTokens.Length; i++)
			{
				if (text.Contains(_preferredFolderTokens[i]))
				{
					return i;
				}
			}
			return int.MaxValue;
		}

		private static string NormalizePath(string path)
		{
			if (string.IsNullOrEmpty(path))
			{
				return string.Empty;
			}
			try
			{
				return Path.GetFullPath(path);
			}
			catch
			{
				return path;
			}
		}

		private void RecordFileEvent(string path)
		{
			string text = NormalizePath(path);
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			_fileEventCounter++;
			_fileEventOrders[text] = _fileEventCounter;
			try
			{
				if (File.Exists(text))
				{
					_fileEventTimes[text] = DateTime.UtcNow;
				}
				else
				{
					_fileEventTimes.Remove(text);
				}
			}
			catch
			{
				_fileEventTimes[text] = DateTime.UtcNow;
			}
		}

		private void RemoveFileEvent(string path)
		{
			string text = NormalizePath(path);
			if (!string.IsNullOrEmpty(text))
			{
				_fileEventTimes.Remove(text);
				_fileEventOrders.Remove(text);
			}
		}

		private bool IsBetterCandidate(in TextureCandidate candidate, in TextureCandidate existing)
		{
			switch (_folderPriorityMode.Value)
			{
			case FolderPriorityMode.LastModified:
			{
				if (candidate.EventOrder != existing.EventOrder)
				{
					return candidate.EventOrder > existing.EventOrder;
				}
				DateTime timestampUtc = candidate.TimestampUtc;
				int num2 = timestampUtc.CompareTo(existing.TimestampUtc);
				if (num2 != 0)
				{
					return num2 > 0;
				}
				return candidate.FolderIndex > existing.FolderIndex;
			}
			case FolderPriorityMode.FolderOrder:
				if (candidate.EventOrder != existing.EventOrder)
				{
					return candidate.EventOrder > existing.EventOrder;
				}
				if (candidate.FolderIndex != existing.FolderIndex)
				{
					return candidate.FolderIndex < existing.FolderIndex;
				}
				return candidate.TimestampUtc > existing.TimestampUtc;
			case FolderPriorityMode.PreferredFolder:
			{
				if (candidate.EventOrder != existing.EventOrder)
				{
					return candidate.EventOrder > existing.EventOrder;
				}
				int preferredScore = GetPreferredScore(candidate.Path);
				int preferredScore2 = GetPreferredScore(existing.Path);
				if (preferredScore != preferredScore2)
				{
					return preferredScore < preferredScore2;
				}
				DateTime timestampUtc = candidate.TimestampUtc;
				int num = timestampUtc.CompareTo(existing.TimestampUtc);
				if (num != 0)
				{
					return num > 0;
				}
				return candidate.FolderIndex > existing.FolderIndex;
			}
			default:
				return false;
			}
		}

		private void LoadCustomTextures()
		{
			Dictionary<string, TextureCandidate> dictionary = new Dictionary<string, TextureCandidate>(StringComparer.OrdinalIgnoreCase);
			for (int i = 0; i < _textureFolders.Count; i++)
			{
				string text = _textureFolders[i];
				try
				{
					if (!Directory.Exists(text))
					{
						continue;
					}
					string[] files = Directory.GetFiles(text, "*.png", SearchOption.TopDirectoryOnly);
					foreach (string path in files)
					{
						string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
						if (string.IsNullOrEmpty(fileNameWithoutExtension))
						{
							continue;
						}
						string text2 = NormalizePath(path);
						FileInfo fileInfo = new FileInfo(text2);
						if (!fileInfo.Exists)
						{
							continue;
						}
						long ticks = Math.Max(fileInfo.LastWriteTimeUtc.Ticks, fileInfo.CreationTimeUtc.Ticks);
						DateTime dateTime = new DateTime(ticks, DateTimeKind.Utc);
						if (_fileEventTimes.TryGetValue(text2, out var value) && value > dateTime)
						{
							dateTime = value;
						}
						_fileEventOrders.TryGetValue(text2, out var value2);
						TextureCandidate textureCandidate = default(TextureCandidate);
						textureCandidate.Path = text2;
						textureCandidate.TimestampUtc = dateTime;
						textureCandidate.FolderIndex = i;
						textureCandidate.EventOrder = value2;
						TextureCandidate candidate = textureCandidate;
						if (dictionary.TryGetValue(fileNameWithoutExtension, out var value3))
						{
							if (IsBetterCandidate(in candidate, in value3))
							{
								dictionary[fileNameWithoutExtension] = candidate;
							}
						}
						else
						{
							dictionary[fileNameWithoutExtension] = candidate;
						}
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logger = _logger;
					if (logger != null)
					{
						logger.LogWarning((object)("[CustomTextureReplacer] Failed to enumerate '" + text + "': " + ex.Message));
					}
				}
			}
			foreach (KeyValuePair<string, TextureCandidate> item in dictionary)
			{
				TextureCandidate value4 = item.Value;
				string path2 = value4.Path;
				if (TryLoadTexture(path2, out var texture) && (Object)(object)texture != (Object)null)
				{
					_customTextures[item.Key] = texture;
					_customTextureIds.Add(((Object)texture).GetInstanceID());
				}
				_fileEventTimes[path2] = value4.TimestampUtc;
				if (value4.EventOrder > 0)
				{
					_fileEventOrders[path2] = value4.EventOrder;
				}
			}
		}

		private void OnTextureFileChanged(object sender, FileSystemEventArgs e)
		{
			if (IsPng(e.FullPath))
			{
				if (e.ChangeType == WatcherChangeTypes.Deleted)
				{
					RemoveFileEvent(e.FullPath);
				}
				else
				{
					RecordFileEvent(e.FullPath);
				}
				_logger.LogInfo((object)("[CustomTextureReplacer] Detected change for '" + e.Name + "'. Reloading custom textures."));
				SafeAppendDebug("File change detected: " + e.Name);
				_reloadRequested = true;
			}
		}

		private void OnTextureFileRenamed(object sender, RenamedEventArgs e)
		{
			if (IsPng(e.FullPath) || IsPng(e.OldFullPath))
			{
				RemoveFileEvent(e.OldFullPath);
				RecordFileEvent(e.FullPath);
				_logger.LogInfo((object)("[CustomTextureReplacer] Detected rename from '" + e.OldName + "' to '" + e.Name + "'. Reloading custom textures."));
				SafeAppendDebug("File rename detected: " + e.OldName + " -> " + e.Name);
				_reloadRequested = true;
			}
		}

		private static bool IsPng(string path)
		{
			return string.Equals(Path.GetExtension(path), ".png", StringComparison.OrdinalIgnoreCase);
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			SafeAppendDebug("Scene loaded: " + ((Scene)(ref scene)).name);
			_hasDumpedAfterSceneLoad = false;
			((MonoBehaviour)this).StartCoroutine(ApplyReplacementsNextFrame(((Scene)(ref scene)).name));
		}

		[IteratorStateMachine(typeof(<InitialDump>d__149))]
		private IEnumerator InitialDump()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <InitialDump>d__149(0)
			{
				<>4__this = this
			};
		}

		[IteratorStateMachine(typeof(<ApplyReplacementsNextFrame>d__150))]
		private IEnumerator ApplyReplacementsNextFrame(string sceneName)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ApplyReplacementsNextFrame>d__150(0)
			{
				<>4__this = this,
				sceneName = sceneName
			};
		}

		private void ReloadCustomTextures()
		{
			foreach (Texture2D value in _customTextures.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					_customTextureIds.Remove(((Object)value).GetInstanceID());
					Object.Destroy((Object)(object)value);
				}
			}
			_customTextures.Clear();
			_customTextureIds.Clear();
			_spritePersistence.Clear();
			_spritePersistenceByTexture.Clear();
			if (DiscoverTextureFolders(logDetails: false) || _watchers.Count == 0)
			{
				RefreshWatchers();
			}
			if (ResolveCardOverridesPath())
			{
				LoadCardOverrides(logDetails: true);
				SetupCardOverridesWatcher();
			}
			bool flag = ResolveMeshOverridesPath();
			LoadCustomTextures();
			LoadMeshOverrides(flag || _meshOverridesByTarget.Count == 0);
			if ((_meshOverridesWatcher == null && !string.IsNullOrEmpty(_meshOverridesPath)) || flag)
			{
				SetupMeshOverridesWatcher();
			}
			_logger.LogInfo((object)$"[CustomTextureReplacer] Loaded {_customTextures.Count} custom textures from disk.");
			SafeAppendDebug($"ReloadCustomTextures complete: {_customTextures.Count} textures");
			RequestReapply("CustomTexturesReloaded");
			_overridesDirty = true;
			ReapplyCardOverrides();
		}

		private bool TryLoadTexture(string path, out Texture2D texture)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			texture = null;
			try
			{
				using FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
				using MemoryStream memoryStream = new MemoryStream();
				fileStream.CopyTo(memoryStream);
				byte[] array = memoryStream.ToArray();
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				if (!ImageConversion.LoadImage(val, array, false))
				{
					Object.Destroy((Object)(object)val);
					_logger.LogWarning((object)("[CustomTextureReplacer] Failed to decode '" + path + "'."));
					SafeAppendDebug("Failed to decode " + Path.GetFileName(path));
					return false;
				}
				((Object)val).name = Path.GetFileNameWithoutExtension(path);
				((Texture)val).wrapMode = (TextureWrapMode)1;
				texture = val;
				return true;
			}
			catch (Exception ex)
			{
				_logger.LogWarning((object)("[CustomTextureReplacer] Could not load '" + path + "': " + ex.Message));
				SafeAppendDebug("Exception loading " + Path.GetFileName(path) + ": " + ex.Message);
				return false;
			}
		}

		private bool DetectNewTextures()
		{
			CollectCandidateTextures(_textureBuffer);
			bool flag = false;
			_newTextureNames.Clear();
			foreach (Texture2D item in _textureBuffer)
			{
				if (!((Object)(object)item == (Object)null) && _knownTextureIds.Add(((Object)item).GetInstanceID()))
				{
					flag = true;
					if (_logNewTextureNames.Value)
					{
						_newTextureNames.Add($"{((Object)item).name} ({((Texture)item).width}x{((Texture)item).height})");
					}
				}
			}
			if (flag && _logNewTextureNames.Value && _newTextureNames.Count > 0)
			{
				string text = string.Join(", ", _newTextureNames.Take(10));
				if (_newTextureNames.Count > 10)
				{
					text += ", ...";
				}
				_logger.LogInfo((object)$"[CustomTextureReplacer] Detected {_newTextureNames.Count} new textures: {text}");
				SafeAppendDebug($"Detected {_newTextureNames.Count} new textures");
			}
			if (flag)
			{
				_overridesDirty = true;
			}
			return flag;
		}

		private void ReplaceAllTextures()
		{
			//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03af: Unknown result type (might be due to invalid IL or missing references)
			if (_customTextures.Count == 0)
			{
				SafeAppendDebug("ReplaceAllTextures aborted (no custom textures)");
				return;
			}
			CollectCandidateTextures(_textureBuffer);
			int num = 0;
			int num2 = 0;
			foreach (Texture2D item in _textureBuffer)
			{
				if ((Object)(object)item == (Object)null || !_customTextures.TryGetValue(((Object)item).name, out var value) || value == item)
				{
					continue;
				}
				bool flag = ((Texture)value).width != ((Texture)item).width || ((Texture)value).height != ((Texture)item).height;
				string extraMessage;
				bool usedFallback;
				if (!TryGetSliceDimensions((Texture)(object)item, 0, 0, ((Texture)item).width, ((Texture)item).height, out var sliceWidth, out var sliceHeight, out var clipped, out var failureReason))
				{
					if (!string.IsNullOrEmpty(failureReason))
					{
						_logger.LogWarning((object)failureReason);
						SafeAppendDebug(failureReason);
					}
					if (TryCreateTextureOverride((Texture)(object)item, value, ((Texture)item).width, ((Texture)item).height))
					{
						num++;
					}
					else
					{
						num2++;
					}
				}
				else if (TryBlitToTexture((Texture)(object)value, (Texture)(object)item, sliceWidth, sliceHeight, out extraMessage, out usedFallback))
				{
					num++;
					if (flag)
					{
						_logger.LogInfo((object)$"[CustomTextureReplacer] Resized + replaced '{((Object)item).name}' (custom {((Texture)value).width}x{((Texture)value).height} -> game {((Texture)item).width}x{((Texture)item).height}).");
					}
					else
					{
						_logger.LogInfo((object)$"[CustomTextureReplacer] Replaced '{((Object)item).name}' (custom {((Texture)value).width}x{((Texture)value).height}).");
					}
					if (clipped)
					{
						_logger.LogWarning((object)$"[CustomTextureReplacer] Replacement for '{((Object)item).name}' was clipped to {sliceWidth}x{sliceHeight}.");
						SafeAppendDebug($"Clipped texture copy for {((Object)item).name} to {sliceWidth}x{sliceHeight}");
					}
					if (!string.IsNullOrEmpty(extraMessage))
					{
						_logger.LogWarning((object)extraMessage);
						SafeAppendDebug(extraMessage);
					}
					if (usedFallback)
					{
						TryCreateTextureOverride((Texture)(object)item, value, ((Texture)item).width, ((Texture)item).height);
					}
				}
				else if (TryCreateTextureOverride((Texture)(object)item, value, ((Texture)item).width, ((Texture)item).height))
				{
					num++;
				}
				else
				{
					num2++;
				}
			}
			CollectCandidateSprites(_spriteBuffer);
			foreach (Sprite item2 in _spriteBuffer)
			{
				if ((Object)(object)item2 == (Object)null || !_customTextures.TryGetValue(((Object)item2).name, out var value2))
				{
					continue;
				}
				Texture2D texture = item2.texture;
				if ((Object)(object)texture == (Object)null || value2 == texture || _customTextureIds.Contains(((Object)texture).GetInstanceID()))
				{
					continue;
				}
				Rect textureRect = item2.textureRect;
				int num3 = Mathf.RoundToInt(((Rect)(ref textureRect)).width);
				int num4 = Mathf.RoundToInt(((Rect)(ref textureRect)).height);
				int num5 = Mathf.RoundToInt(((Rect)(ref textureRect)).x);
				int num6 = Mathf.RoundToInt(((Rect)(ref textureRect)).y);
				if (num3 <= 0 || num4 <= 0 || !TryGetTextureSize((Texture)(object)texture, out var _, out var _))
				{
					continue;
				}
				string extraMessage2;
				bool usedFallback2;
				if (!TryGetSliceDimensions((Texture)(object)texture, num5, num6, num3, num4, out var sliceWidth2, out var sliceHeight2, out var clipped2, out var failureReason2))
				{
					if (!string.IsNullOrEmpty(failureReason2))
					{
						_logger.LogWarning((object)failureReason2);
						SafeAppendDebug(failureReason2);
					}
					if (TryCreateSpriteOverride(item2, value2, num3, num4))
					{
						num++;
					}
					else
					{
						num2++;
					}
				}
				else if (TryBlitToTexture((Texture)(object)value2, (Texture)(object)texture, sliceWidth2, sliceHeight2, out extraMessage2, out usedFallback2, num5, num6))
				{
					num++;
					if (sliceWidth2 != num3 || sliceHeight2 != num4)
					{
						_logger.LogInfo((object)$"[CustomTextureReplacer] Resized + clipped sprite '{((Object)item2).name}' in atlas '{((Object)texture).name}' ({((Texture)value2).width}x{((Texture)value2).height} -> region {sliceWidth2}x{sliceHeight2} at {num5},{num6}).");
					}
					else if (num3 != ((Texture)value2).width || num4 != ((Texture)value2).height)
					{
						_logger.LogInfo((object)$"[CustomTextureReplacer] Resized + replaced sprite '{((Object)item2).name}' in atlas '{((Object)texture).name}' ({((Texture)value2).width}x{((Texture)value2).height} -> region {sliceWidth2}x{sliceHeight2} at {num5},{num6}).");
					}
					else
					{
						_logger.LogInfo((object)$"[CustomTextureReplacer] Replaced sprite '{((Object)item2).name}' in atlas '{((Object)texture).name}' (region {sliceWidth2}x{sliceHeight2} at {num5},{num6}).");
					}
					if (clipped2)
					{
						_logger.LogWarning((object)$"[CustomTextureReplacer] Sprite '{((Object)item2).name}' in atlas '{((Object)texture).name}' was clipped to {sliceWidth2}x{sliceHeight2}.");
						SafeAppendDebug($"Clipped sprite copy for {((Object)item2).name} to {sliceWidth2}x{sliceHeight2}");
					}
					if (!string.IsNullOrEmpty(extraMessage2))
					{
						_logger.LogWarning((object)extraMessage2);
						SafeAppendDebug(extraMessage2);
					}
					RegisterPersistentSpriteOverride(item2, value2, num3, num4);
					if (usedFallback2)
					{
						TryCreateSpriteOverride(item2, value2, num3, num4);
					}
				}
				else if (TryCreateSpriteOverride(item2, value2, num3, num4))
				{
					num++;
				}
				else
				{
					num2++;
				}
			}
			ApplyTextureOverridesToMaterials();
			ApplySpriteOverridesToComponents();
			if (num > 0 || num2 > 0)
			{
				_logger.LogInfo((object)$"[CustomTextureReplacer] Applied replacements. Success: {num}, skipped: {num2}.");
				SafeAppendDebug($"ReplaceAllTextures finished. Success={num} Skipped={num2}");
			}
			ScheduleMeshOverrideReapply(0f);
			ApplyMeshOverrides();
		}

		private bool TryBlitToTexture(Texture source, Texture destination, int width, int height, out string extraMessage, out bool usedFallback, int dstX = 0, int dstY = 0)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Invalid comparison between Unknown and I4
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: 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)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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_00ab: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			extraMessage = string.Empty;
			usedFallback = false;
			RenderTexture val = null;
			Texture2D val2 = null;
			RenderTexture val3 = null;
			bool flag = false;
			try
			{
				val = RenderTexture.GetTemporary(width, height, 0, (RenderTextureFormat)0);
				Graphics.Blit(source, val);
				TextureFormat val4 = (TextureFormat)4;
				bool flag2 = false;
				bool flag3 = (int)QualitySettings.activeColorSpace == 1;
				Texture2D val5 = (Texture2D)(object)((destination is Texture2D) ? destination : null);
				if (val5 != null)
				{
					val4 = val5.format;
					if (!ValidateTextureFormat(val4, width, height))
					{
						val4 = (TextureFormat)4;
						flag = true;
					}
				}
				val3 = RenderTexture.active;
				RenderTexture.active = val;
				val2 = new Texture2D(width, height, val4, flag2, flag3)
				{
					name = ((destination != null) ? ((Object)destination).name : null) + "_TempCopy"
				};
				val2.ReadPixels(new Rect(0f, 0f, (float)width, (float)height), 0, 0);
				val2.Apply(false, false);
				RenderTexture.active = val3;
				val3 = null;
				bool flag4 = false;
				try
				{
					Graphics.CopyTexture((Texture)(object)val2, 0, 0, 0, 0, width, height, destination, 0, 0, dstX, dstY);
					flag4 = true;
				}
				catch (Exception ex)
				{
					if (dstX == 0 && dstY == 0 && destination.width == width && destination.height == height)
					{
						try
						{
							Graphics.ConvertTexture((Texture)(object)val2, destination);
							extraMessage = "[CustomTextureReplacer] ConvertTexture used for '" + ((destination != null) ? ((Object)destination).name : null) + "' after CopyTexture fallback: " + ex.Message;
							usedFallback = true;
							flag4 = true;
						}
						catch (Exception ex2)
						{
							extraMessage = "[CustomTextureReplacer] ConvertTexture failed for '" + ((destination != null) ? ((Object)destination).name : null) + "': " + ex2.Message;
							flag4 = false;
						}
					}
					else
					{
						extraMessage = "[CustomTextureReplacer] CopyTexture failed for '" + ((destination != null) ? ((Object)destination).name : null) + "': " + ex.Message;
						flag4 = false;
					}
				}
				if (!flag4 && flag && string.IsNullOrEmpty(extraMessage))
				{
					extraMessage = "[CustomTextureReplacer] Unable to blit into '" + ((destination != null) ? ((Object)destination).name : null) + "' due to format mismatch.";
					usedFallback = true;
				}
				return flag4;
			}
			finally
			{
				if (flag && string.IsNullOrEmpty(extraMessage))
				{
					extraMessage = "[CustomTextureReplacer] Fallback format copy used for '" + ((destination != null) ? ((Object)destination).name : null) + "', possible truncation.";
					usedFallback = true;
				}
				if ((Object)(object)val3 != (Object)null)
				{
					RenderTexture.active = val3;
				}
				if ((Object)(object)val2 != (Object)null)
				{
					Object.Destroy((Object)(object)val2);
				}
				if ((Object)(object)val != (Object)null)
				{
					RenderTexture.ReleaseTemporary(val);
				}
			}
		}

		private bool TryCopyIntoTexture(Texture source, Texture2D destination, int width, int height, string label)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)source == (Object)null || (Object)(object)destination == (Object)null)
			{
				return false;
			}
			RenderTexture val = null;
			RenderTexture active = RenderTexture.active;
			try
			{
				val = RenderTexture.GetTemporary(width, height, 0, (RenderTextureFormat)0);
				Graphics.Blit(source, val);
				RenderTexture.active = val;
				destination.ReadPixels(new Rect(0f, 0f, (float)width, (float)height), 0, 0);
				destination.Apply(false, false);
				RenderTexture.active = active;
				SafeAppendDebug("Override texture refreshed for " + label);
				return true;
			}
			catch (Exception ex)
			{
				_logger.LogWarning((object)("[CustomTextureReplacer] Failed to refresh override for '" + label + "': " + ex.Message));
				SafeAppendDebug("Override refresh failed for " + label + ": " + ex.Message);
				return false;
			}
			finally
			{
				RenderTexture.active = active;
				if ((Object)(object)val != (Object)null)
				{
					RenderTexture.ReleaseTemporary(val);
				}
			}
		}

		private bool TryCreateTextureOverride(Texture target, Texture2D replacement, int width, int height)
		{
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Expected O, but got Unknown
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = (Texture2D)(object)((target is Texture2D) ? target : null);
			if (val == null || (Object)(object)replacement == (Object)null)
			{
				return false;
			}
			try
			{
				if (_textureOverrides.TryGetValue((Texture)(object)val, out var value))
				{
					Texture2D val2 = (Texture2D)(object)((value is Texture2D) ? value : null);
					if (val2 != null && ((Texture)val2).width == width && ((Texture)val2).height == height)
					{
						if (TryCopyIntoTexture((Texture)(object)replacement, val2, width, height, ((Object)val).name))
						{
							_textureOverrides[(Texture)(object)val] = (Texture)(object)val2;
							_textureOverridesByName[((Object)val).name] = (Texture)(object)val2;
							_overridesDirty = true;
							return true;
						}
						RemoveTextureOverride((Texture)(object)val);
					}
				}
				if (_textureOverridesByName.TryGetValue(((Object)val).name, out var value2))
				{
					Texture2D val3 = (Texture2D)(object)((value2 is Texture2D) ? value2 : null);
					if (val3 != null && ((Texture)val3).width == width && ((Texture)val3).height == height)
					{
						if (TryCopyIntoTexture((Texture)(object)replacement, val3, width, height, ((Object)val).name))
						{
							_textureOverrides[(Texture)(object)val] = (Texture)(object)val3;
							_textureOverridesByName[((Object)val).name] = (Texture)(object)val3;
							_overridesDirty = true;
							return true;
						}
						RemoveTextureOverride((Texture)(object)val);
					}
				}
				RemoveTextureOverride((Texture)(object)val);
				Texture2D val4 = new Texture2D(width, height, (TextureFormat)4, false)
				{
					name = ((Object)val).name + "_Custom",
					wrapMode = ((Texture)val).wrapMode,
					filterMode = ((Texture)val).filterMode,
					anisoLevel = ((Texture)val).anisoLevel
				};
				RenderTexture temporary = RenderTexture.GetTemporary(width, height, 0, (RenderTextureFormat)0);
				Graphics.Blit((Texture)(object)replacement, temporary);
				RenderTexture active = RenderTexture.active;
				RenderTexture.active = temporary;
				val4.ReadPixels(new Rect(0f, 0f, (float)width, (float)height), 0, 0);
				val4.Apply();
				RenderTexture.active = active;
				RenderTexture.ReleaseTemporary(temporary);
				_generatedTextures.Add(val4);
				_textureOverrides[(Texture)(object)val] = (Texture)(object)val4;
				_textureOverridesByName[((Object)val).name] = (Texture)(object)val4;
				Texture2D[] array = Resources.FindObjectsOfTypeAll<Texture2D>();
				foreach (Texture2D val5 in array)
				{
					if (!((Object)(object)val5 == (Object)null) && val5 != val && string.Equals(((Object)val5).name, ((Object)val).name, StringComparison.OrdinalIgnoreCase))
					{
						_textureOverrides[(Texture)(object)val5] = (Texture)(object)val4;
					}
				}
				_logger.LogInfo((object)("[CustomTextureReplacer] Using runtime texture override for '" + ((Object)val).name + "'."));
				_logger.LogDebug((object)$"[CustomTextureReplacer] Override '{((Object)val).name}' uses custom '{((Object)replacement).name}' ({((Texture)replacement).width}x{((Texture)replacement).height}).");
				SafeAppendDebug("Texture override created for " + ((Object)val).name);
				_overridesDirty = true;
				ApplyTextureOverridesToMaterials((Texture)(object)val);
				return true;
			}
			catch (Exception ex)
			{
				_logger.LogWarning((object)("[CustomTextureReplacer] Failed to create texture override for '" + ((target != null) ? ((Object)target).name : null) + "': " + ex.Message));
				SafeAppendDebug("Texture override failed for " + ((target != null) ? ((Object)target).name : null) + ": " + ex.Message);
				return false;
			}
		}

		private bool TryCreateSpriteOverride(Sprite originalSprite, Texture2D replacement, int width, int height)
		{
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Expected O, but got Unknown
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)originalSprite == (Object)null || (Object)(object)replacement == (Object)null)
			{
				return false;
			}
			try
			{
				if (_spriteOverridesByName.TryGetValue(((Object)originalSprite).name, out var value) && (Object)(object)value != (Object)null)
				{
					Texture2D texture = value.texture;
					if ((Object)(object)texture != (Object)null && ((Texture)texture).width == width && ((Texture)texture).height == height)
					{
						if (texture != null)
						{
							Texture2D val = texture;
							if (TryCopyIntoTexture((Texture)(object)replacement, val, width, height, ((Object)originalSprite).name))
							{
								_spriteOverrides[originalSprite] = value;
								_spriteOverridesByName[((Object)originalSprite).name] = value;
								_spriteOverrideTextures[value] = val;
								RegisterPersistentSpriteOverride(originalSprite, replacement, width, height);
								_overridesDirty = true;
								return true;
							}
						}
						RemoveSpriteOverride(((Object)originalSprite).name);
					}
				}
				RemoveSpriteOverride(((Object)originalSprite).name);
				RenderTexture temporary = RenderTexture.GetTemporary(width, height, 0, (RenderTextureFormat)0);
				Graphics.Blit((Texture)(object)replacement, temporary);
				RenderTexture active = RenderTexture.active;
				RenderTexture.active = temporary;
				Texture2D val2 = new Texture2D(width, height, (TextureFormat)4, false)
				{
					name = ((Object)originalSprite).name + "_Custom"
				};
				val2.ReadPixels(new Rect(0f, 0f, (float)width, (float)height), 0, 0);
				val2.Apply();
				RenderTexture.active = active;
				RenderTexture.ReleaseTemporary(temporary);
				_generatedTextures.Add(val2);
				Rect rect = originalSprite.rect;
				Vector2 size = ((Rect)(ref rect)).size;
				Vector2 val3 = ((((Vector2)(ref size)).sqrMagnitude > 0f) ? new Vector2(originalSprite.pivot.x / ((Rect)(ref rect)).width, originalSprite.pivot.y / ((Rect)(ref rect)).height) : new Vector2(0.5f, 0.5f));
				Sprite val4 = Sprite.Create(val2, new Rect(0f, 0f, (float)width, (float)height), val3, originalSprite.pixelsPerUnit, 0u, (SpriteMeshType)0, originalSprite.border, false);
				((Object)val4).name = ((Object)originalSprite).name;
				_generatedSprites.Add(val4);
				_spriteOverrideTextures[val4] = val2;
				_spriteOverrides[originalSprite] = val4;
				_spriteOverridesByName[((Object)originalSprite).name] = val4;
				_spriteOverrides[val4] = val4;
				Sprite[] array = Resources.FindObjectsOfTypeAll<Sprite>();
				foreach (Sprite val5 in array)
				{
					if (!((Object)(object)val5 == (Object)null) && val5 != originalSprite && string.Equals(((Object)val5).name, ((Object)originalSprite).name, StringComparison.OrdinalIgnoreCase))
					{
						_spriteOverrides[val5] = val4;
					}
				}
				_logger.LogInfo((object)("[CustomTextureReplacer] Created runtime sprite override for '" + ((Object)originalSprite).name + "'."));
				_logger.LogDebug((object)$"[CustomTextureReplacer] Sprite override '{((Object)originalSprite).name}' uses custom '{((Object)replacement).name}' ({((Texture)replacement).width}x{((Texture)replacement).height}).");
				SafeAppendDebug("Sprite override created for " + ((Object)originalSprite).name);
				RegisterPersistentSpriteOverride(originalSprite, replacement, width, height);
				_overridesDirty = true;
				ApplySpriteOverridesToComponents();
				return true;
			}
			catch (Exception ex)
			{
				_logger.LogWarning((object)("[CustomTextureReplacer] Failed to create sprite override for '" + ((originalSprite != null) ? ((Object)originalSprite).name : null) + "': " + ex.Message));
				SafeAppendDebug("Sprite override failed for " + ((originalSprite != null) ? ((Object)originalSprite).name : null) + ": " + ex.Message);
				return false;
			}
		}

		private void RemoveTextureOverride(Texture target)
		{
			if ((Object)(object)target == (Object)null)
			{
				return;
			}
			Texture value3;
			if (_textureOverrides.TryGetValue(target, out var value))
			{
				_textureOverrides.Remove(target);
				Texture2D val = (Texture2D)(object)((value is Texture2D) ? value : null);
				if (val != null && _generatedTextures.Remove(val))
				{
					Object.Destroy((Object)(object)val);
				}
				if (_textureOverridesByName.TryGetValue(((Object)target).name, out var value2) && value2 == value)
				{
					_textureOverridesByName.Remove(((Object)target).name);
				}
				_overridesDirty = true;
			}
			else if (_textureOverridesByName.TryGetValue(((Object)target).name, out value3))
			{
				Texture2D val2 = (Texture2D)(object)((value3 is Texture2D) ? value3 : null);
				if (val2 != null && _generatedTextures.Remove(val2))
				{
					_textureOverridesByName.Remove(((Object)target).name);
					Object.Destroy((Object)(object)val2);
					_overridesDirty = true;
				}
			}
		}

		private void RemoveSpriteOverride(string spriteName)
		{
			if (string.IsNullOrEmpty(spriteName))
			{
				return;
			}
			if (_spriteOverridesByName.TryGetValue(spriteName, out var existing))
			{
				List<Sprite> list = (from kvp in _spriteOverrides.Where(delegate(KeyValuePair<Sprite, Sprite> kvp)
					{
						int result;
						if (kvp.Value != existing)
						{
							Sprite key = kvp.Key;
							result = (string.Equals((key != null) ? ((Object)key).name : null, spriteName, StringComparison.OrdinalIgnoreCase) ? 1 : 0);
						}
						else
						{
							result = 1;
						}
						return (byte)result != 0;
					})
					select kvp.Key).ToList();
				foreach (Sprite item in list)
				{
					_spriteOverrides.Remove(item);
				}
				_spriteOverridesByName.Remove(spriteName);
				if (_spriteOverrideTextures.TryGetValue(existing, out var value))
				{
					_spriteOverrideTextures.Remove(existing);
					if ((Object)(object)value != (Object)null && _generatedTextures.Remove(value))
					{
						Object.Destroy((Object)(object)value);
					}
				}
				if (_generatedSprites.Remove(existing))
				{
					Object.Destroy((Object)(object)existing);
				}
				_overridesDirty = true;
			}
			if (!_spritePersistence.TryGetValue(spriteName, out var value2))
			{
				return;
			}
			_spritePersistence.Remove(spriteName);
			if (!string.IsNullOrEmpty(value2.TargetTextureName) && _spritePersistenceByTexture.TryGetValue(value2.TargetTextureName, out var value3))
			{
				value3.RemoveAll((PersistentSpriteOverride entry) => string.Equals(entry.SpriteName, spriteName, StringComparison.OrdinalIgnoreCase));
				if (value3.Count == 0)
				{
					_spritePersistenceByTexture.Remove(value2.TargetTextureName);
				}
			}
		}

		private Texture GetReplacementTexture(Texture original)
		{
			if ((Object)(object)original == (Object)null)
			{
				return null;
			}
			if (_textureOverrides.TryGetValue(original, out var value))
			{
				return value;
			}
			if (_textureOverridesByName.TryGetValue(((Object)original).name, out var value2))
			{
				return value2;
			}
			return null;
		}

		internal Sprite GetReplacementSprite(Sprite original)
		{
			if ((Object)(object)original == (Object)null)
			{
				return null;
			}
			if (_spriteOverrides.TryGetValue(original, out var value))
			{
				return value;
			}
			if (_spriteOverridesByName.TryGetValue(((Object)original).name, out var value2))
			{
				return value2;
			}
			return null;
		}

		private void ApplyTextureOverridesToMaterials(Texture targetHint = null)
		{
			if (_textureOverrides.Count == 0 && _textureOverridesByName.Count == 0)
			{
				return;
			}
			Material[] array = Resources.FindObjectsOfTypeAll<Material>();
			foreach (Material val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				string[] texturePropertyNames = val.GetTexturePropertyNames();
				foreach (string text in texturePropertyNames)
				{
					Texture texture = val.GetTexture(text);
					if (!((Object)(object)texture == (Object)null) && (!((Object)(object)targetHint != (Object)null) || texture == targetHint || string.Equals(((Object)texture).name, ((Object)targetHint).name, StringComparison.OrdinalIgnoreCase)))
					{
						Texture replacementTexture = GetReplacementTexture(texture);
						if ((Object)(object)replacementTexture != (Object)null && texture != replacementTexture)
						{
							val.SetTexture(text, replacementTexture);
						}
					}
				}
			}
		}

		private void ApplyTextureOverridesToRenderers()
		{
			if (_textureOverrides.Count == 0 && _textureOverridesByName.Count == 0)
			{
				return;
			}
			Renderer[] array = Resources.FindObjectsOfTypeAll<Renderer>();
			foreach (Renderer val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				bool flag = false;
				val.GetPropertyBlock(_propertyBlock);
				string[] rendererTextureProperties = RendererTextureProperties;
				foreach (string text in rendererTextureProperties)
				{
					Texture val2 = null;
					try
					{
						val2 = _propertyBlock.GetTexture(text);
					}
					catch
					{
						val2 = null;
					}
					if (!((Object)(object)val2 == (Object)null))
					{
						Texture replacementTexture = GetReplacementTexture(val2);
						if ((Object)(object)replacementTexture != (Object)null && val2 != replacementTexture)
						{
							_propertyBlock.SetTexture(text, replacementTexture);
							flag = true;
						}
					}
				}
				if (flag)
				{
					val.SetPropertyBlock(_propertyBlock);
				}
			}
		}

		private void ApplySpriteOverridesToComponents()
		{
			if (_spriteOverridesByName.Count == 0 && _spriteOverrides.Count == 0)
			{
				return;
			}
			SpriteRenderer[] array = Resources.FindObjectsOfTypeAll<SpriteRenderer>();
			foreach (SpriteRenderer val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					Sprite replacementSprite = GetReplacementSprite(val.sprite);
					if ((Object)(object)replacementSprite != (Object)null && val.sprite != replacementSprite)
					{
						string[] obj = new string[7]
						{
							"SpriteRenderer override applied on '",
							((Object)val).name,
							"' using sprite '",
							((Object)replacementSprite).name,
							"' (was '",
							null,
							null
						};
						Sprite sprite = val.sprite;
						obj[5] = ((sprite != null) ? ((Object)sprite).name : null) ?? "<null>";
						obj[6] = "').";
						SafeAppendDebug(string.Concat(obj));
						val.sprite = replacementSprite;
					}
				}
			}
			if (UIImageType != null && UIImageSpriteProperty != null)
			{
				Object[] array2 = Resources.FindObjectsOfTypeAll(UIImageType);
				foreach (Object val2 in array2)
				{
					Image val3 = (Image)(object)((val2 is Image) ? val2 : null);
					if (val3 != null && (Object)(object)val3 != (Object)null)
					{
						Sprite assigned = val3.overrideSprite ?? val3.sprite;
						ApplySpriteOverrideToImage(val3, assigned);
					}
				}
			}
			if (!(UIRawImageType != null) || !(UIRawImageTextureProperty != null))
			{
				return;
			}
			Object[] array3 = Resources.FindObjectsOfTypeAll(UIRawImageType);
			foreach (Object val4 in array3)
			{
				RawImage val5 = (RawImage)(object)((val4 is RawImage) ? val4 : null);
				if (val5 != null && (Object)(object)val5 != (Object)null)
				{
					ApplyTextureOverrideToRawImage(val5, val5.texture);
				}
			}
		}

		internal void ApplySpriteOverrideToImage(Image image, Sprite assigned)
		{
			if ((Object)(object)image == (Object)null || _applyingImageOverride)
			{
				return;
			}
			Sprite val = assigned ?? image.sprite;
			Sprite replacementSprite = GetReplacementSprite(val);
			int instanceID = ((Object)image).GetInstanceID();
			if ((Object)(object)replacementSprite != (Object)null && val != replacementSprite)
			{
				_imageOverrideInstances.Add(instanceID);
				if (image.overrideSprite != replacementSprite)
				{
					_applyingImageOverride = true;
					image.overrideSprite = replacementSprite;
					_applyingImageOverride = false;
					((Graphic)image).SetAllDirty();
					SafeAppendDebug("UI Image override applied: " + (((val != null) ? ((Object)val).name : null) ?? "<null>") + " -> " + ((Object)replacementSprite).name);
				}
			}
			else if (_imageOverrideInstances.Remove(instanceID))
			{
				_applyingImageOverride = true;
				if ((Object)(object)assigned != (Object)null && image.sprite != assigned)
				{
					image.overrideSprite = assigned;
				}
				else
				{
					image.overrideSprite = null;
				}
				_applyingImageOverride = false;
				((Graphic)image).SetAllDirty();
				SafeAppendDebug("UI Image override cleared for '" + (((val != null) ? ((Object)val).name : null) ?? "<null>") + "'.");
			}
		}

		internal void ApplyTextureOverrideToRawImage(RawImage rawImage, Texture assigned)
		{
			if ((Object)(object)rawImage == (Object)null || _applyingRawImageOverride)
			{
				return;
			}
			Texture val = assigned ?? rawImage.texture;
			Texture replacementTexture = GetReplacementTexture(val);
			int instanceID = ((Object)rawImage).GetInstanceID();
			if ((Object)(object)replacementTexture != (Object)null && val != replacementTexture)
			{
				_rawImageOverrideInstances.Add(instanceID);
				if (rawImage.texture != replacementTexture)
				{
					_applyingRawImageOverride = true;
					rawImage.texture = replacementTexture;
					_applyingRawImageOverride = false;
					((Graphic)rawImage).SetAllDirty();
					SafeAppendDebug("UI RawImage override applied: " + (((val != null) ? ((Object)val).name : null) ?? "<null>") + " -> " + ((Object)replacementTexture).name);
				}
			}
			else if (_rawImageOverrideInstances.Remove(instanceID))
			{
				if (rawImage.texture != val)
				{
					_applyingRawImageOverride = true;
					rawImage.texture = val;
					_applyingRawImageOverride = false;
				}
				((Graphic)rawImage).SetAllDirty();
				SafeAppendDebug("UI RawImage override cleared for '" + (((val != null) ? ((Object)val).name : null) ?? "<null>") + "'.");
			}
		}

		private bool ValidateTextureFormat(TextureFormat format, int width, int height)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//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)
			//IL_0004: 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_0008: Invalid comparison between Unknown and I4
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			if ((int)format <= 12)
			{
				if ((int)format == 10 || (int)format == 12)
				{
					goto IL_0028;
				}
			}
			else if ((int)format == 25 || format - 28 <= 1)
			{
				goto IL_0028;
			}
			return true;
			IL_0028:
			return false;
		}

		private bool TryGetTextureSize(Texture texture, out int width, out int height)
		{
			width = 0;
			height = 0;
			Texture2D val = (Texture2D)(object)((texture is Texture2D) ? texture : null);
			if (val == null)
			{
				RenderTexture val2 = (RenderTexture)(object)((texture is RenderTexture) ? texture : null);
				if (val2 != null)
				{
					width = ((Texture)val2).width;
					height = ((Texture)val2).height;
					return true;
				}
				if ((Object)(object)texture != (Object)null)
				{
					width = texture.width;
					height = texture.height;
					return width > 0 && height > 0;
				}
				return false;
			}
			width = ((Texture)val).width;
			height = ((Texture)val).height;
			return true;
		}

		private bool TryGetGraphicsFormat(Texture texture, out GraphicsFormat format)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected I4, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected I4, but got Unknown
			format = (GraphicsFormat)0;
			Texture2D val = (Texture2D)(object)((texture is Texture2D) ? texture : null);
			if (val == null)
			{
				RenderTexture val2 = (RenderTexture)(object)((texture is RenderTexture) ? texture : null);
				if (val2 != null)
				{
					format = (GraphicsFormat)(int)val2.graphicsFormat;
					return (int)format != 0;
				}
				return false;
			}
			format = (GraphicsFormat)(int)((Texture)val).graphicsFormat;
			return (int)format != 0;
		}

		private bool TryGetSliceDimensions(Texture texture, int dstX, int dstY, int width, int height, out int sliceWidth, out int sliceHeight, out bool clipped, out string failureReason)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			sliceWidth = 0;
			sliceHeight = 0;
			clipped = false;
			failureReason = string.Empty;
			if (!TryGetTextureSize(texture, out var width2, out var height2))
			{
				return false;
			}
			int num = width2 - dstX;
			int num2 = height2 - dstY;
			if (num <= 0 || num2 <= 0)
			{
				return false;
			}
			sliceWidth = Mathf.Min(width, num);
			sliceHeight = Mathf.Min(height, num2);
			if (sliceWidth <= 0 || sliceHeight <= 0)
			{
				return false;
			}
			if (TryGetGraphicsFormat(texture, out var format) && GraphicsFormatUtility.IsCompressedFormat(format))
			{
				failureReason = string.Format("[CustomTextureReplacer] Texture '{0}' uses compressed format ({1}), switching to runtime override.", ((texture != null) ? ((Object)texture).name : null) ?? "<unnamed>", format);
				return false;
			}
			clipped = sliceWidth != width || sliceHeight != height;
			return true;
		}

		private void DumpAllTextures()
		{
			try
			{
				CollectCandidateTextures(_textureBuffer);
				_textureBuffer.Sort((Texture2D a, Texture2D b) => string.Compare((a != null) ? ((Object)a).name : null, (b != null) ? ((Object)b).name : null, StringComparison.OrdinalIgnoreCase));
				_knownTextureIds.Clear();
				using StreamWriter streamWriter = new StreamWriter(_dumpFile, append: false);
				foreach (Texture2D item in _textureBuffer)
				{
					if (!((Object)(object)item == (Object)null))
					{
						_knownTextureIds.Add(((Object)item).GetInstanceID());
						string value = $"{((Object)item).name} ({((Texture)item).width}x{((Texture)item).height})";
						streamWriter.WriteLine(value);
					}
				}
				_logger.LogInfo((object)$"[CustomTextureReplacer] Dumped texture list to '{_dumpFile}' ({_knownTextureIds.Count} entries).");
				SafeAppendDebug($"DumpAllTextures wrote {_knownTextureIds.Count} entries");
			}
			catch (Exception ex)
			{
				_logger.LogWarning((object)("[CustomTextureReplacer] Unable to dump textures: " + ex.Message));
				SafeAppendDebug("DumpAllTextures exception: " + ex.Message);
			}
		}

		private void DumpAllSprites()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				CollectCandidateSprites(_spriteBuffer);
				_spriteBuffer.Sort((Sprite a, Sprite b) => string.Compare((a != null) ? ((Object)a).name : null, (b != null) ? ((Object)b).name : null, StringComparison.OrdinalIgnoreCase));
				using StreamWriter streamWriter = new StreamWriter(_spriteDumpFile, append: false);
				foreach (Sprite item in _spriteBuffer)
				{
					if (!((Object)(object)item == (Object)null))
					{
						Texture2D texture = item.texture;
						string text = (((Object)(object)texture != (Object)null) ? ((Object)texture).name : "<null>");
						Rect textureRect = item.textureRect;
						string value = $"{((Object)item).name} -> {text} ({Mathf.RoundToInt(((Rect)(ref textureRect)).width)}x{Mathf.RoundToInt(((Rect)(ref textureRect)).height)} at {Mathf.RoundToInt(((Rect)(ref textureRect)).x)},{Mathf.RoundToInt(((Rect)(ref textureRect)).y)})";
						streamWriter.WriteLine(value);
					}
				}
				_logger.LogInfo((object)$"[CustomTextureReplacer] Dumped sprite list to '{_spriteDumpFile}' ({_spriteBuffer.Count} entries).");
				SafeAppendDebug($"DumpAllSprites wrote {_spriteBuffer.Count} entries");
			}
			catch (Exception ex)
			{
				_logger.LogWarning((object)("[CustomTextureReplacer] Unable to dump sprites: " + ex.Message));
				SafeAppendDebug("DumpAllSprites exception: " + ex.Message);
			}
		}

		private void DumpAllMeshes()
		{
			if (string.IsNullOrEmpty(_meshDumpFile))
			{
				return;
			}
			try
			{
				Mesh[] source = Resources.FindObjectsOfTypeAll<Mesh>();
				List<Mesh> list = source.Where((Mesh mesh) => (Object)(object)mesh != (Object)null).OrderBy<Mesh, string>((Mesh mesh) => ((Object)mesh).name, StringComparer.OrdinalIgnoreCase).ToList();
				using StreamWriter streamWriter = new StreamWriter(_meshDumpFile, append: false);
				foreach (Mesh item in list)
				{
					int num = (((Object)(object)item != (Object)null) ? item.vertexCount : 0);
					int num2 = (((Object)(object)item != (Object)null) ? item.subMeshCount : 0);
					streamWriter.WriteLine($"{((Object)item).name} (vertices: {num}, subMeshes: {num2})");
				}
				_logger.LogInfo((object)$"[CustomTextureReplacer] Dumped mesh list to '{_meshDumpFile}' ({list.Count} entries).");
				SafeAppendDebug($"DumpAllMeshes wrote {list.Count} entries");
			}
			catch (Exception ex)
			{
				_logger.LogWarning((object)("[CustomTextureReplacer] Unable to dump meshes: " + ex.Message));
				SafeAppendDebug("DumpAllMeshes exception: " + ex.Message);
			}
		}

		private bool ResolveMeshOverridesPath()
		{
			string a = _meshOverridesPath ?? string.Empty;
			List<string> candidates = new List<string>();
			HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (_textureFolders != null)
			{
				foreach (string textureFolder in _textureFolders)
				{
					if (!string.IsNullOrWhiteSpace(textureFolder))
					{
						AddCandidate(Path.Combine(textureFolder, "MeshOverrides.json"));
					}
				}
			}
			AddCandidate(Path.Combine(Paths.PluginPath, "CustomTextures", "MeshOverrides.json"));
			AddCandidate(Path.Combine(Paths.PluginPath, "MeshOverrides.json"));
			string text = string.Empty;
			foreach (string item in candidates)
			{
				try
				{
					if (File.Exists(item))
					{
						text = item;
						break;
					}
				}
				catch
				{
				}
				if (string.IsNullOrEmpty(text))
				{
					text = item;
				}
			}
			if (string.IsNullOrEmpty(text))
			{
				text = Path.Combine(Paths.PluginPath, "MeshOverrides.json");
			}
			bool flag = !string.Equals(a, text, StringComparison.OrdinalIgnoreCase);
			_meshOverridesPath = text;
			if (flag)
			{
				SafeAppendDebug("MeshOverrides path resolved: " + _meshOverridesPath);
			}
			return flag;
			void AddCandidate(string candidate)
			{
				if (string.IsNullOrWhiteSpace(candidate))
				{
					return;
				}
				try
				{
					string fullPath = Path.GetFullPath(candidate);
					if (seen.Add(fullPath))
					{
						candidates.Add(fullPath);
					}
				}
				catch
				{
					if (seen.Add(candidate))
					{
						candidates.Add(candidate);
					}
				}
			}
		}

		private void LoadMeshOverrides(bool logDetails)
		{
			SafeAppendDebug("LoadMeshOverrides invoked.");
			RevertAllMeshOverrides();
			DisposeMeshOverrideResources();
			_meshCardOverrides.Clear();
			if (string.IsNullOrEmpty(_meshOverridesPath))
			{
				SafeAppendDebug("MeshOverrides path empty; nothing to load.");
				ScheduleMeshOverrideReapply(0f);
				RequestReapply("MeshOverridesReloaded");
				return;
			}
			string text = _meshOverridesPath;
			try
			{
				text = NormalizePath(text);
			}
			catch
			{
			}
			if (!File.Exists(text))
			{
				if (logDetails)
				{
					_logger.LogInfo((object)("[CustomTextureReplacer] Mesh override file not found at '" + text + "'."));
				}
				SafeAppendDebug("MeshOverrides file not found: " + text);
				ScheduleMeshOverrideReapply(0f);
				RequestReapply("MeshOverridesReloaded");
				return;
			}
			bool flag = false;
			try
			{
				string json = File.ReadAllText(text);
				object obj2 = MiniJson.Deserialize(json);
				IList list = null;
				HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				if (obj2 is IList list2)
				{
					list = list2;
				}
				else if (obj2 is Dictionary<string, object> dictionary)
				{
					CollectMeshTextureFolders(dictionary, "textureFolders", Path.GetDirectoryName(text) ?? Paths.PluginPath, hashSet);
					CollectMeshTextureFolders(dictionary, "textureFolder", Path.GetDirectoryName(text) ?? Paths.PluginPath, hashSet);
					object value2;
					if (dictionary.TryGetValue("overrides", out var value) && value is IList list3)
					{
						list = list3;
					}
					else if (dictionary.TryGetValue("entries", out value2) && value2 is IList list4)
					{
						list = list4;
					}
				}
				if (list == null)
				{
					list = Array.Empty<object>();
				}
				string text2 = Path.GetDirectoryName(text) ?? Paths.PluginPath;
				int num = 0;
				HashSet<string> hashSet2 = new HashSet<string>(hashSet, StringComparer.OrdinalIgnoreCase);
				foreach (object item in list)
				{
					if (!(item is Dictionary<string, object> dictionary2))
					{
						continue;
					}
					string @string = GetString(dictionary2, "target");
					if (string.IsNullOrWhiteSpace(@string))
					{
						SafeAppendDebug("MeshOverride entry skipped (missing target).");
						continue;
					}
					string text3 = GetString(dictionary2, "mesh");
					string string2 = GetString(dictionary2, "meshHint");
					string[] stringArray = GetStringArray(dictionary2, "materialHints");
					bool flag2 = dictionary2.ContainsKey("materials");
					bool autoSelectMesh = string.IsNullOrWhiteSpace(text3);
					bool autoSelectMaterials = !flag2;
					string string3 = GetString(dictionary2, "bundle");
					string resolvedPath = string.Empty;
					AssetBundle val = null;
					if (!string.IsNullOrWhiteSpace(string3))
					{
						if (!TryResolveMeshBundlePath(string3, text2, out resolvedPath))
						{
							_logger.LogWarning((object)("[CustomTextureReplacer] Could not loc