Decompiled source of TextureSwapper Mono v0.2.0

Mods/TextureSwapper_Mono.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using TextureSwapper;
using TextureSwapper.Config;
using TextureSwapper.IO;
using TextureSwapper.Runtime;
using TextureSwapper.UI;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Core), "TextureSwapper", "0.2.0", "Bars", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("TextureSwapper_Mono")]
[assembly: AssemblyConfiguration("Mono")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+3b3f92c400d4caf495d98ad692c946a4c5e62d8e")]
[assembly: AssemblyProduct("TextureSwapper_Mono")]
[assembly: AssemblyTitle("TextureSwapper_Mono")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TextureSwapper
{
	public class Core : MelonMod
	{
		[CompilerGenerated]
		private sealed class <DelayedRegistration>d__41 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public float delaySeconds;

			public Core <>4__this;

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

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

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

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

			private bool MoveNext()
			{
				//IL_0056: Unknown result type (might be due to invalid IL or missing references)
				//IL_0060: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					if (Preferences.DebugEnabled)
					{
						((MelonBase)<>4__this).LoggerInstance.Msg($"Waiting {delaySeconds}s for change monitor registration...");
					}
					<>2__current = (object)new WaitForSeconds(delaySeconds);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if ((Object)(object)<>4__this.MaterialChangeMonitor != (Object)null)
					{
						if (Preferences.DebugEnabled)
						{
							((MelonBase)<>4__this).LoggerInstance.Msg("Executing change monitor registration");
						}
						<>4__this.MaterialChangeMonitor.ScanAndRegisterAll();
					}
					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 <DelayedSceneScan>d__40 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public float delaySeconds;

			public string scanType;

			public Core <>4__this;

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

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

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

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

			private bool MoveNext()
			{
				//IL_005c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0066: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					if (Preferences.DebugEnabled)
					{
						((MelonBase)<>4__this).LoggerInstance.Msg($"Waiting {delaySeconds}s for {scanType} scan...");
					}
					<>2__current = (object)new WaitForSeconds(delaySeconds);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if ((Object)(object)<>4__this.MaterialScanner != (Object)null)
					{
						if (Preferences.DebugEnabled)
						{
							((MelonBase)<>4__this).LoggerInstance.Msg("Executing " + scanType + " material scan");
						}
						<>4__this.MaterialScanner.RequestFullRescan();
					}
					if ((Object)(object)<>4__this.UITextureScanner != (Object)null)
					{
						if (Preferences.DebugEnabled)
						{
							((MelonBase)<>4__this).LoggerInstance.Msg("Executing " + scanType + " UI texture scan");
						}
						<>4__this.UITextureScanner.RequestFullRescan();
					}
					if ((Object)(object)<>4__this.MaterialChangeMonitor != (Object)null && scanType == "Initial")
					{
						MelonCoroutines.Start(<>4__this.DelayedRegistration(2f));
					}
					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 GameObject managerGameObject;

		private bool isInitialized;

		public static Core Instance { get; private set; }

		public static string ModFolderPath { get; private set; }

		public static string ExportsFolderPath { get; private set; }

		internal ReplacementIndex ReplacementIndex { get; private set; }

		internal MaterialScanner MaterialScanner { get; private set; }

		internal UITextureScanner UITextureScanner { get; private set; }

		internal MaterialChangeMonitor MaterialChangeMonitor { get; private set; }

		internal InspectorUI InspectorUI { get; private set; }

		internal FileWatcher FileWatcher { get; private set; }

		public override void OnInitializeMelon()
		{
			Instance = this;
			Preferences.Initialize();
			try
			{
				ModFolderPath = Path.Combine(MelonEnvironment.ModsDirectory, "TextureSwapper");
				ExportsFolderPath = Path.Combine(ModFolderPath, "_Exports");
				Directory.CreateDirectory(ModFolderPath);
				Directory.CreateDirectory(ExportsFolderPath);
				((MelonBase)this).LoggerInstance.Msg("TextureSwapper ready. Waiting for Menu scene to initialize.");
				if (Preferences.DebugEnabled)
				{
					((MelonBase)this).LoggerInstance.Msg("[Debug] ModsDir=" + MelonEnvironment.ModsDirectory);
					((MelonBase)this).LoggerInstance.Msg("[Debug] ModFolderPath=" + ModFolderPath);
					((MelonBase)this).LoggerInstance.Msg("[Debug] ExportsFolderPath=" + ExportsFolderPath);
				}
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Error("Basic setup error: " + ex.Message);
			}
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			if (!Preferences.Enabled.Value)
			{
				return;
			}
			if (Preferences.DebugEnabled)
			{
				((MelonBase)this).LoggerInstance.Msg($"[Debug] SceneInitialized: {sceneName} ({buildIndex})");
			}
			if (!isInitialized && sceneName == "Menu")
			{
				try
				{
					managerGameObject = new GameObject("TextureSwapper_Manager");
					Object.DontDestroyOnLoad((Object)(object)managerGameObject);
					ReplacementIndex = new ReplacementIndex(ModFolderPath);
					MaterialScanner = managerGameObject.AddComponent<MaterialScanner>();
					UITextureScanner = managerGameObject.AddComponent<UITextureScanner>();
					MaterialChangeMonitor = managerGameObject.AddComponent<MaterialChangeMonitor>();
					InspectorUI = managerGameObject.AddComponent<InspectorUI>();
					if ((Object)(object)MaterialScanner != (Object)null)
					{
						MaterialScanner.Initialize(ReplacementIndex, MaterialChangeMonitor);
					}
					if ((Object)(object)UITextureScanner != (Object)null)
					{
						UITextureScanner.Initialize(ReplacementIndex, MaterialChangeMonitor);
					}
					if ((Object)(object)MaterialChangeMonitor != (Object)null)
					{
						MaterialChangeMonitor.Initialize(ReplacementIndex);
					}
					if ((Object)(object)InspectorUI != (Object)null)
					{
						InspectorUI.Initialize(ReplacementIndex, MaterialScanner);
					}
					FileWatcher = new FileWatcher(ModFolderPath);
					FileWatcher.Start();
					((MelonBase)this).LoggerInstance.Msg("Initialized. Mod folder: " + ModFolderPath);
					isInitialized = true;
					MelonCoroutines.Start(DelayedSceneScan(1f, "Initial"));
					return;
				}
				catch (Exception ex)
				{
					((MelonBase)this).LoggerInstance.Error("Initialization error: " + ex.Message);
					((MelonBase)this).LoggerInstance.Error(ex.StackTrace);
					return;
				}
			}
			if ((Object)(object)MaterialScanner != (Object)null || (Object)(object)UITextureScanner != (Object)null)
			{
				MelonCoroutines.Start(DelayedSceneScan(0.5f, "Scene change"));
			}
		}

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

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

		public override void OnUpdate()
		{
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			if (!Preferences.Enabled.Value || !isInitialized)
			{
				return;
			}
			List<string> list = FileWatcher?.DrainChangedPaths();
			if (list != null && list.Count > 0 && ReplacementIndex != null)
			{
				if (Preferences.DebugEnabled)
				{
					((MelonBase)this).LoggerInstance.Msg($"[Debug] File changes detected: {list.Count}");
				}
				ReplacementIndex.ReloadChangedFiles(list);
				if ((Object)(object)MaterialScanner != (Object)null)
				{
					MaterialScanner.RequestFullRescan();
				}
				if ((Object)(object)UITextureScanner != (Object)null)
				{
					UITextureScanner.HandleFileChanges(list);
				}
			}
			if ((Object)(object)InspectorUI != (Object)null && Input.GetKeyDown(Preferences.InspectorToggleKey))
			{
				InspectorUI.ToggleVisibility();
				if (Preferences.DebugEnabled)
				{
					((MelonBase)this).LoggerInstance.Msg("[Debug] Inspector toggle pressed");
				}
			}
			if ((Object)(object)InspectorUI != (Object)null && Input.GetKeyDown(Preferences.ExportKey))
			{
				InspectorUI.ExportCurrentSelection();
				if (Preferences.DebugEnabled)
				{
					((MelonBase)this).LoggerInstance.Msg("[Debug] Export hotkey pressed");
				}
			}
		}
	}
}
namespace TextureSwapper.IO
{
	public sealed class FileWatcher : IDisposable
	{
		private readonly string directory;

		private readonly FileSystemWatcher watcherPng;

		private readonly FileSystemWatcher watcherJpg;

		private readonly FileSystemWatcher watcherJpeg;

		private readonly ConcurrentQueue<string> changedQueue = new ConcurrentQueue<string>();

		private volatile bool started;

		public FileWatcher(string directory)
		{
			this.directory = directory;
			watcherPng = CreateWatcher("*.png");
			watcherJpg = CreateWatcher("*.jpg");
			watcherJpeg = CreateWatcher("*.jpeg");
		}

		public void Start()
		{
			if (started)
			{
				return;
			}
			started = true;
			if (Preferences.LiveReload.Value)
			{
				Enable(watcherPng);
				Enable(watcherJpg);
				Enable(watcherJpeg);
				if (Preferences.DebugEnabled)
				{
					MelonLogger.Msg("[FileWatcher] Started watching: " + directory);
				}
			}
		}

		private FileSystemWatcher CreateWatcher(string filter)
		{
			FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(directory, filter)
			{
				IncludeSubdirectories = true,
				NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime)
			};
			fileSystemWatcher.Changed += OnChanged;
			fileSystemWatcher.Created += OnChanged;
			fileSystemWatcher.Deleted += OnChanged;
			fileSystemWatcher.Renamed += OnRenamed;
			return fileSystemWatcher;
		}

		private void Enable(FileSystemWatcher fsw)
		{
			try
			{
				fsw.EnableRaisingEvents = true;
			}
			catch
			{
			}
		}

		private void OnChanged(object sender, FileSystemEventArgs e)
		{
			if (!string.IsNullOrEmpty(e.FullPath))
			{
				changedQueue.Enqueue(e.FullPath);
				if (Preferences.DebugEnabled)
				{
					MelonLogger.Msg($"[FileWatcher] {e.ChangeType}: {e.FullPath}");
				}
			}
		}

		private void OnRenamed(object sender, RenamedEventArgs e)
		{
			if (!string.IsNullOrEmpty(e.OldFullPath))
			{
				changedQueue.Enqueue(e.OldFullPath);
			}
			if (!string.IsNullOrEmpty(e.FullPath))
			{
				changedQueue.Enqueue(e.FullPath);
			}
			if (Preferences.DebugEnabled)
			{
				MelonLogger.Msg("[FileWatcher] Renamed: " + e.OldFullPath + " -> " + e.FullPath);
			}
		}

		public List<string> DrainChangedPaths()
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			string result;
			while (changedQueue.TryDequeue(out result))
			{
				hashSet.Add(result);
			}
			if (hashSet.Count == 0)
			{
				return null;
			}
			if (Preferences.DebugEnabled)
			{
				MelonLogger.Msg($"[FileWatcher] Drained {hashSet.Count} path(s)");
			}
			return new List<string>(hashSet);
		}

		public void Dispose()
		{
			try
			{
				watcherPng?.Dispose();
			}
			catch
			{
			}
			try
			{
				watcherJpg?.Dispose();
			}
			catch
			{
			}
			try
			{
				watcherJpeg?.Dispose();
			}
			catch
			{
			}
		}
	}
	public static class ExportService
	{
		public static void ExportRenderer(GameObject target)
		{
			if ((Object)(object)target == (Object)null)
			{
				return;
			}
			Renderer componentInChildren = target.GetComponentInChildren<Renderer>();
			if ((Object)(object)componentInChildren == (Object)null)
			{
				return;
			}
			Material[] sharedMaterials = componentInChildren.sharedMaterials;
			if (sharedMaterials == null || sharedMaterials.Length == 0)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("Target: " + ((Object)target).name);
			for (int i = 0; i < sharedMaterials.Length; i++)
			{
				Material val = sharedMaterials[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				stringBuilder.AppendLine($"Material[{i}]: {((Object)val).name}");
				for (int j = 0; j < PropertyNames.TexturePropertyNames.Length; j++)
				{
					string text = PropertyNames.TexturePropertyNames[j];
					if (val.HasProperty(text))
					{
						Texture texture = val.GetTexture(text);
						Texture2D val2 = (Texture2D)(object)((texture is Texture2D) ? texture : null);
						if (!((Object)(object)val2 == (Object)null))
						{
							stringBuilder.AppendLine("  " + text + " → " + ((Object)val2).name);
						}
					}
				}
			}
			try
			{
				string path = $"{Sanitize(((Object)target).name)}_{DateTime.Now:yyyyMMdd_HHmmss}.txt";
				string text2 = Path.Combine(Core.ExportsFolderPath, path);
				File.WriteAllText(text2, stringBuilder.ToString());
				MelonLogger.Msg("Exported: " + text2);
				if (Preferences.DebugEnabled)
				{
					MelonLogger.Msg($"[ExportService] Exported target='{((Object)target).name}' materials={sharedMaterials.Length}");
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Error("Export failed: " + ex.Message);
			}
		}

		private static string Sanitize(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "Object";
			}
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			foreach (char oldChar in invalidFileNameChars)
			{
				s = s.Replace(oldChar, '_');
			}
			return s;
		}
	}
}
namespace TextureSwapper.UI
{
	public sealed class InspectorUI : MonoBehaviour
	{
		private bool visible;

		private Camera mainCamera;

		private ReplacementIndex index;

		private MaterialScanner scanner;

		private GameObject currentTarget;

		private string lastCopy;

		private Vector2 scrollPosition;

		private float lastInspectTime = 0f;

		private readonly float inspectInterval = 0.12f;

		private GameObject lastTarget;

		private readonly List<Renderer> cachedRenderableRenderers = new List<Renderer>(16);

		private readonly GUIStyle headerStyle = new GUIStyle();

		private readonly GUIStyle smallStyle = new GUIStyle();

		public void Initialize(ReplacementIndex index, MaterialScanner scanner)
		{
			this.index = index;
			this.scanner = scanner;
			mainCamera = Camera.main;
			ConfigureStyles();
		}

		public void ToggleVisibility()
		{
			visible = !visible;
		}

		private void ConfigureStyles()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			headerStyle.fontSize = 16;
			headerStyle.normal.textColor = Color.white;
			smallStyle.fontSize = 12;
			smallStyle.normal.textColor = Color.white;
		}

		private void Update()
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			if (!visible)
			{
				return;
			}
			if ((Object)(object)mainCamera == (Object)null)
			{
				mainCamera = Camera.main;
			}
			if ((Object)(object)mainCamera == (Object)null)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup - lastInspectTime < inspectInterval)
			{
				return;
			}
			lastInspectTime = realtimeSinceStartup;
			Ray ray = default(Ray);
			((Ray)(ref ray))..ctor(((Component)mainCamera).transform.position, ((Component)mainCamera).transform.forward);
			currentTarget = FindTargetFromRaycast(ray);
			if (!((Object)(object)currentTarget != (Object)(object)lastTarget))
			{
				return;
			}
			cachedRenderableRenderers.Clear();
			if ((Object)(object)currentTarget != (Object)null)
			{
				Renderer[] componentsInChildren = currentTarget.GetComponentsInChildren<Renderer>(true);
				if (componentsInChildren != null && componentsInChildren.Length != 0)
				{
					Renderer[] array = componentsInChildren;
					foreach (Renderer val in array)
					{
						if ((Object)(object)val == (Object)null)
						{
							continue;
						}
						Material[] sharedMaterials = val.sharedMaterials;
						if (sharedMaterials == null || sharedMaterials.Length == 0)
						{
							continue;
						}
						bool flag = false;
						Material[] array2 = sharedMaterials;
						foreach (Material val2 in array2)
						{
							if ((Object)(object)val2 == (Object)null)
							{
								continue;
							}
							for (int k = 0; k < PropertyNames.TexturePropertyNames.Length; k++)
							{
								string text = PropertyNames.TexturePropertyNames[k];
								if (val2.HasProperty(text) && (Object)(object)val2.GetTexture(text) != (Object)null)
								{
									flag = true;
									break;
								}
							}
							if (flag)
							{
								cachedRenderableRenderers.Add(val);
								break;
							}
						}
					}
				}
			}
			lastTarget = currentTarget;
		}

		private GameObject FindTargetFromRaycast(Ray ray)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			RaycastHit[] array = Physics.RaycastAll(ray, 4f);
			if (array.Length == 0)
			{
				return null;
			}
			RaycastHit[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				RaycastHit val = array2[i];
				if ((Object)(object)((RaycastHit)(ref val)).collider == (Object)null)
				{
					continue;
				}
				GameObject gameObject = ((Component)((RaycastHit)(ref val)).collider).gameObject;
				if (!((Object)gameObject).name.ToLower().Contains("collider"))
				{
					if (!ShouldSkipObject(gameObject))
					{
						return gameObject;
					}
					GameObject val2 = FindBetterAlternative(gameObject);
					if ((Object)(object)val2 != (Object)null)
					{
						return val2;
					}
				}
			}
			return null;
		}

		private bool ShouldSkipObject(GameObject obj)
		{
			if ((Object)(object)obj == (Object)null)
			{
				return true;
			}
			Renderer component = obj.GetComponent<Renderer>();
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			Material[] sharedMaterials = component.sharedMaterials;
			if (sharedMaterials == null || sharedMaterials.Length != 1)
			{
				return false;
			}
			Material val = sharedMaterials[0];
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			return ((Object)val).name == "Lit";
		}

		private GameObject FindBetterAlternative(GameObject original)
		{
			if ((Object)(object)original == (Object)null)
			{
				return null;
			}
			Transform parent = original.transform.parent;
			if ((Object)(object)parent != (Object)null)
			{
				GameObject gameObject = ((Component)parent).gameObject;
				if (!ShouldSkipObject(gameObject))
				{
					return gameObject;
				}
			}
			Renderer[] componentsInChildren = original.GetComponentsInChildren<Renderer>(false);
			Renderer[] array = componentsInChildren;
			foreach (Renderer val in array)
			{
				GameObject gameObject2 = ((Component)val).gameObject;
				if (!((Object)(object)gameObject2 == (Object)(object)original) && !ShouldSkipObject(gameObject2))
				{
					return gameObject2;
				}
			}
			if ((Object)(object)parent != (Object)null)
			{
				for (int j = 0; j < parent.childCount; j++)
				{
					GameObject gameObject3 = ((Component)parent.GetChild(j)).gameObject;
					if (!((Object)(object)gameObject3 == (Object)(object)original) && !ShouldSkipObject(gameObject3))
					{
						return gameObject3;
					}
				}
			}
			return null;
		}

		private void OnGUI()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			if (!visible)
			{
				return;
			}
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(20f, 20f, 600f, (float)(Screen.height - 40));
			GUI.color = Color.black;
			GUI.DrawTexture(new Rect(((Rect)(ref val)).x - 2f, ((Rect)(ref val)).y - 2f, ((Rect)(ref val)).width + 4f, ((Rect)(ref val)).height + 4f), (Texture)(object)Texture2D.whiteTexture);
			GUI.color = new Color(0.12f, 0.12f, 0.12f, 1f);
			GUI.DrawTexture(val, (Texture)(object)Texture2D.whiteTexture);
			GUI.color = Color.white;
			GUILayout.BeginArea(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 10f, ((Rect)(ref val)).width - 20f, ((Rect)(ref val)).height - 20f));
			scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, true, Array.Empty<GUILayoutOption>());
			GUILayout.Label("TextureSwapper Inspector", headerStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Space(6f);
			if ((Object)(object)currentTarget == (Object)null)
			{
				GUILayout.Label("Look at an object to inspect its materials.", smallStyle, Array.Empty<GUILayoutOption>());
				GUILayout.EndScrollView();
				GUILayout.EndArea();
				return;
			}
			GUILayout.Label("Target: " + GetPath(currentTarget), smallStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Space(4f);
			GUILayout.Label("Drop files named by material or material__Property: e.g. MyMat.png or MyMat__EmissionMap.png", smallStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Label("For UI elements: SpriteName.png or TextureName.png", smallStyle, Array.Empty<GUILayoutOption>());
			Renderer[] componentsInChildren = currentTarget.GetComponentsInChildren<Renderer>(true);
			if (componentsInChildren == null || componentsInChildren.Length == 0)
			{
				GUILayout.Label("No Renderers found in hierarchy.", smallStyle, Array.Empty<GUILayoutOption>());
				GUILayout.EndScrollView();
				GUILayout.EndArea();
				return;
			}
			bool flag = false;
			foreach (Renderer val2 in componentsInChildren)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Material[] sharedMaterials = val2.sharedMaterials;
				if (sharedMaterials == null || sharedMaterials.Length == 0)
				{
					continue;
				}
				flag = true;
				string relativePath = GetRelativePath(currentTarget, ((Component)val2).gameObject);
				if (relativePath != ".")
				{
					GUILayout.Space(6f);
					GUILayout.Label("Object: " + relativePath, headerStyle, Array.Empty<GUILayoutOption>());
				}
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					Material val3 = sharedMaterials[j];
					if ((Object)(object)val3 == (Object)null)
					{
						continue;
					}
					GUILayout.Space(4f);
					GUILayout.Label($"Material [{j}]: {((Object)val3).name}", smallStyle, Array.Empty<GUILayoutOption>());
					if (GUILayout.Button("Copy Material Name", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) }))
					{
						GUIUtility.systemCopyBuffer = ((Object)val3).name;
						lastCopy = ((Object)val3).name;
					}
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					GUILayout.Label("Suggestion: Mods/TextureSwapper/" + ((Object)val3).name + ".png", smallStyle, Array.Empty<GUILayoutOption>());
					if (GUILayout.Button("Copy", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }))
					{
						GUIUtility.systemCopyBuffer = ((Object)val3).name + ".png";
						lastCopy = ((Object)val3).name + ".png";
					}
					GUILayout.EndHorizontal();
					for (int k = 0; k < PropertyNames.TexturePropertyNames.Length; k++)
					{
						string text = PropertyNames.TexturePropertyNames[k];
						if (!val3.HasProperty(text))
						{
							continue;
						}
						Texture texture = val3.GetTexture(text);
						Texture2D val4 = (Texture2D)(object)((texture is Texture2D) ? texture : null);
						if (!((Object)(object)val4 == (Object)null))
						{
							GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
							GUILayout.Label(text + " → " + (((Object)(object)val4 != (Object)null) ? ((Object)val4).name : "<null>"), smallStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(420f) });
							if (GUILayout.Button("Copy Tex Name", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }) && (Object)(object)val4 != (Object)null)
							{
								GUIUtility.systemCopyBuffer = ((Object)val4).name;
								lastCopy = ((Object)val4).name;
							}
							if (GUILayout.Button("Copy Mat__Prop", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) }))
							{
								string text3 = (GUIUtility.systemCopyBuffer = ((Object)val3).name + "__" + text + ".png");
								lastCopy = text3;
							}
							GUILayout.EndHorizontal();
						}
					}
				}
			}
			if (!flag)
			{
				GUILayout.Label("No materials found in hierarchy.", smallStyle, Array.Empty<GUILayoutOption>());
				GUILayout.EndScrollView();
				GUILayout.EndArea();
				return;
			}
			Image[] componentsInChildren2 = currentTarget.GetComponentsInChildren<Image>(true);
			RawImage[] componentsInChildren3 = currentTarget.GetComponentsInChildren<RawImage>(true);
			if ((componentsInChildren2 != null && componentsInChildren2.Length != 0) || (componentsInChildren3 != null && componentsInChildren3.Length != 0))
			{
				GUILayout.Space(10f);
				GUILayout.Label("UI Elements", headerStyle, Array.Empty<GUILayoutOption>());
				GUILayout.Space(4f);
				GUILayout.Label("Drop files named by sprite/texture: e.g. MySprite.png or MyTexture.png", smallStyle, Array.Empty<GUILayoutOption>());
				if (componentsInChildren2 != null && componentsInChildren2.Length != 0)
				{
					for (int l = 0; l < componentsInChildren2.Length; l++)
					{
						Image val5 = componentsInChildren2[l];
						if ((Object)(object)val5 == (Object)null)
						{
							continue;
						}
						Sprite sprite = val5.sprite;
						if (!((Object)(object)sprite == (Object)null))
						{
							GUILayout.Space(4f);
							GUILayout.Label($"Image [{l}]: {((Object)((Component)val5).gameObject).name}", smallStyle, Array.Empty<GUILayoutOption>());
							if (GUILayout.Button("Copy Sprite Name", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) }))
							{
								GUIUtility.systemCopyBuffer = ((Object)sprite).name;
								lastCopy = ((Object)sprite).name;
							}
							GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
							GUILayout.Label("Sprite: " + (((Object)(object)sprite != (Object)null) ? ((Object)sprite).name : "<null>"), smallStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(350f) });
							if (GUILayout.Button("Copy Sprite__File", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) }))
							{
								string text5 = (GUIUtility.systemCopyBuffer = ((Object)sprite).name + ".png");
								lastCopy = text5;
							}
							GUILayout.EndHorizontal();
						}
					}
				}
				if (componentsInChildren3 != null && componentsInChildren3.Length != 0)
				{
					for (int m = 0; m < componentsInChildren3.Length; m++)
					{
						RawImage val6 = componentsInChildren3[m];
						if ((Object)(object)val6 == (Object)null)
						{
							continue;
						}
						Texture texture2 = val6.texture;
						Texture2D val7 = (Texture2D)(object)((texture2 is Texture2D) ? texture2 : null);
						if (!((Object)(object)val7 == (Object)null))
						{
							GUILayout.Space(4f);
							GUILayout.Label($"RawImage [{m}]: {((Object)((Component)val6).gameObject).name}", smallStyle, Array.Empty<GUILayoutOption>());
							if (GUILayout.Button("Copy Texture Name", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) }))
							{
								GUIUtility.systemCopyBuffer = ((Object)val7).name;
								lastCopy = ((Object)val7).name;
							}
							GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
							GUILayout.Label("Texture: " + (((Object)(object)val7 != (Object)null) ? ((Object)val7).name : "<null>"), smallStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(350f) });
							if (GUILayout.Button("Copy Texture__File", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) }))
							{
								string text7 = (GUIUtility.systemCopyBuffer = ((Object)val7).name + ".png");
								lastCopy = text7;
							}
							GUILayout.EndHorizontal();
						}
					}
				}
			}
			if (!string.IsNullOrEmpty(lastCopy))
			{
				GUILayout.Space(4f);
				GUILayout.Label("Copied: " + lastCopy, smallStyle, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndScrollView();
			GUILayout.EndArea();
		}

		public void ExportCurrentSelection()
		{
			if ((Object)(object)currentTarget == (Object)null)
			{
				return;
			}
			Renderer[] componentsInChildren = currentTarget.GetComponentsInChildren<Renderer>(true);
			Image[] componentsInChildren2 = currentTarget.GetComponentsInChildren<Image>(true);
			RawImage[] componentsInChildren3 = currentTarget.GetComponentsInChildren<RawImage>(true);
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("Target: " + GetPath(currentTarget));
			stringBuilder.AppendLine($"Renderer Count: {((componentsInChildren != null) ? componentsInChildren.Length : 0)}");
			stringBuilder.AppendLine($"Image Count: {((componentsInChildren2 != null) ? componentsInChildren2.Length : 0)}");
			stringBuilder.AppendLine($"RawImage Count: {((componentsInChildren3 != null) ? componentsInChildren3.Length : 0)}");
			stringBuilder.AppendLine();
			foreach (Renderer val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Material[] sharedMaterials = val.sharedMaterials;
				if (sharedMaterials == null || sharedMaterials.Length == 0)
				{
					continue;
				}
				string relativePath = GetRelativePath(currentTarget, ((Component)val).gameObject);
				stringBuilder.AppendLine("Object: " + relativePath);
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					Material val2 = sharedMaterials[j];
					if ((Object)(object)val2 == (Object)null)
					{
						continue;
					}
					stringBuilder.AppendLine($"  Material[{j}]: {((Object)val2).name}");
					for (int k = 0; k < PropertyNames.TexturePropertyNames.Length; k++)
					{
						string text = PropertyNames.TexturePropertyNames[k];
						if (val2.HasProperty(text))
						{
							Texture texture = val2.GetTexture(text);
							Texture2D val3 = (Texture2D)(object)((texture is Texture2D) ? texture : null);
							if (!((Object)(object)val3 == (Object)null))
							{
								stringBuilder.AppendLine("    " + text + " → " + ((Object)val3).name);
								stringBuilder.AppendLine("      Suggestion: Mods/TextureSwapper/" + ((Object)val3).name + ".png");
							}
						}
					}
				}
				stringBuilder.AppendLine();
			}
			if (componentsInChildren2 != null && componentsInChildren2.Length != 0)
			{
				stringBuilder.AppendLine("=== UI IMAGES ===");
				foreach (Image val4 in componentsInChildren2)
				{
					if (!((Object)(object)val4 == (Object)null))
					{
						Sprite sprite = val4.sprite;
						string relativePath2 = GetRelativePath(currentTarget, ((Component)val4).gameObject);
						stringBuilder.AppendLine("Image: " + relativePath2);
						if ((Object)(object)sprite != (Object)null)
						{
							stringBuilder.AppendLine("  Sprite: " + ((Object)sprite).name);
							stringBuilder.AppendLine("  Suggestion: Mods/TextureSwapper/" + ((Object)sprite).name + ".png");
						}
						else
						{
							stringBuilder.AppendLine("  Sprite: <null>");
						}
						stringBuilder.AppendLine();
					}
				}
			}
			if (componentsInChildren3 != null && componentsInChildren3.Length != 0)
			{
				stringBuilder.AppendLine("=== UI RAW IMAGES ===");
				foreach (RawImage val5 in componentsInChildren3)
				{
					if (!((Object)(object)val5 == (Object)null))
					{
						Texture texture2 = val5.texture;
						Texture2D val6 = (Texture2D)(object)((texture2 is Texture2D) ? texture2 : null);
						string relativePath3 = GetRelativePath(currentTarget, ((Component)val5).gameObject);
						stringBuilder.AppendLine("RawImage: " + relativePath3);
						if ((Object)(object)val6 != (Object)null)
						{
							stringBuilder.AppendLine("  Texture: " + ((Object)val6).name);
							stringBuilder.AppendLine("  Suggestion: Mods/TextureSwapper/" + ((Object)val6).name + ".png");
						}
						else
						{
							stringBuilder.AppendLine("  Texture: <null>");
						}
						stringBuilder.AppendLine();
					}
				}
			}
			try
			{
				string path = $"{Sanitize(((Object)currentTarget).name)}_{DateTime.Now:yyyyMMdd_HHmmss}.txt";
				string text2 = Path.Combine(Core.ExportsFolderPath, path);
				File.WriteAllText(text2, stringBuilder.ToString());
				MelonLogger.Msg("Exported: " + text2);
			}
			catch (Exception ex)
			{
				MelonLogger.Error("Export failed: " + ex.Message);
			}
		}

		private static string GetPath(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return "<null>";
			}
			string text = ((Object)go).name;
			Transform val = go.transform;
			while ((Object)(object)val.parent != (Object)null)
			{
				text = ((Object)val.parent).name + "/" + text;
				val = val.parent;
			}
			return text;
		}

		private static string Sanitize(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "Object";
			}
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			foreach (char oldChar in invalidFileNameChars)
			{
				s = s.Replace(oldChar, '_');
			}
			return s;
		}

		private static string GetRelativePath(GameObject root, GameObject child)
		{
			if ((Object)(object)root == (Object)null || (Object)(object)child == (Object)null)
			{
				return "?";
			}
			if ((Object)(object)root == (Object)(object)child)
			{
				return ".";
			}
			StringBuilder stringBuilder = new StringBuilder();
			Transform val = child.transform;
			while ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)(object)root)
			{
				if (stringBuilder.Length > 0)
				{
					stringBuilder.Insert(0, "/");
				}
				stringBuilder.Insert(0, ((Object)val).name);
				val = val.parent;
			}
			if ((Object)(object)val == (Object)null)
			{
				return "?";
			}
			return (stringBuilder.Length > 0) ? stringBuilder.ToString() : ".";
		}
	}
}
namespace TextureSwapper.Runtime
{
	public sealed class MaterialChangeMonitor : MonoBehaviour
	{
		private readonly Dictionary<Renderer, Material[]> originalMaterials = new Dictionary<Renderer, Material[]>(128);

		private readonly Dictionary<Image, Sprite> originalSprites = new Dictionary<Image, Sprite>(64);

		private readonly Dictionary<RawImage, Texture> originalTextures = new Dictionary<RawImage, Texture>(32);

		private readonly List<Renderer> monitoredRenderers = new List<Renderer>(128);

		private readonly List<Image> monitoredImages = new List<Image>(64);

		private readonly List<RawImage> monitoredRawImages = new List<RawImage>(32);

		private readonly HashSet<Renderer> renderersWithChanges = new HashSet<Renderer>();

		private readonly HashSet<Image> imagesWithChanges = new HashSet<Image>();

		private readonly HashSet<RawImage> rawImagesWithChanges = new HashSet<RawImage>();

		private ReplacementIndex replacementIndex;

		private float nextCheckTime;

		private float checkInterval;

		private bool isInitialized;

		public void Initialize(ReplacementIndex index)
		{
			replacementIndex = index;
			checkInterval = Preferences.DynamicChangeCheckInterval?.Value ?? 5f;
			isInitialized = true;
			if (Preferences.DebugEnabled)
			{
				MelonLogger.Msg($"[MaterialChangeMonitor] Initialized with check interval: {checkInterval}s");
			}
		}

		private void Update()
		{
			if (Preferences.Enabled.Value && isInitialized && replacementIndex != null && Preferences.MonitorDynamicChanges.Value && Time.time >= nextCheckTime)
			{
				CheckForMaterialChanges();
				CheckForImageChanges();
				CheckForRawImageChanges();
				ProcessMaterialChanges();
				ProcessImageChanges();
				ProcessRawImageChanges();
				nextCheckTime = Time.time + checkInterval;
			}
		}

		private void CheckForMaterialChanges()
		{
			renderersWithChanges.Clear();
			foreach (KeyValuePair<Renderer, Material[]> originalMaterial in originalMaterials)
			{
				Renderer key = originalMaterial.Key;
				Material[] value = originalMaterial.Value;
				if ((Object)(object)key == (Object)null)
				{
					continue;
				}
				Material[] sharedMaterials = key.sharedMaterials;
				if (sharedMaterials == null || value == null)
				{
					continue;
				}
				if (sharedMaterials.Length != value.Length)
				{
					renderersWithChanges.Add(key);
					continue;
				}
				for (int i = 0; i < sharedMaterials.Length; i++)
				{
					if (i >= value.Length || (Object)(object)sharedMaterials[i] != (Object)(object)value[i] || ((Object)(object)sharedMaterials[i] != (Object)null && (Object)(object)value[i] != (Object)null && ((Object)sharedMaterials[i]).name != ((Object)value[i]).name))
					{
						renderersWithChanges.Add(key);
						break;
					}
				}
			}
		}

		private void CheckForImageChanges()
		{
			imagesWithChanges.Clear();
			foreach (KeyValuePair<Image, Sprite> originalSprite in originalSprites)
			{
				Image key = originalSprite.Key;
				Sprite value = originalSprite.Value;
				if (!((Object)(object)key == (Object)null))
				{
					Sprite sprite = key.sprite;
					if ((Object)(object)sprite != (Object)(object)value || ((Object)(object)sprite != (Object)null && (Object)(object)value != (Object)null && ((Object)sprite).name != ((Object)value).name))
					{
						imagesWithChanges.Add(key);
					}
				}
			}
		}

		private void CheckForRawImageChanges()
		{
			rawImagesWithChanges.Clear();
			foreach (KeyValuePair<RawImage, Texture> originalTexture in originalTextures)
			{
				RawImage key = originalTexture.Key;
				Texture value = originalTexture.Value;
				if (!((Object)(object)key == (Object)null))
				{
					Texture texture = key.texture;
					if ((Object)(object)texture != (Object)(object)value || ((Object)(object)texture != (Object)null && (Object)(object)value != (Object)null && ((Object)texture).name != ((Object)value).name))
					{
						rawImagesWithChanges.Add(key);
					}
				}
			}
		}

		private void ProcessMaterialChanges()
		{
			foreach (Renderer renderersWithChange in renderersWithChanges)
			{
				if ((Object)(object)renderersWithChange == (Object)null)
				{
					continue;
				}
				Material[] sharedMaterials = renderersWithChange.sharedMaterials;
				if (sharedMaterials == null)
				{
					continue;
				}
				bool flag = false;
				foreach (Material val in sharedMaterials)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					bool flag2 = false;
					flag2 |= ReplacementApplicator.ApplyByMaterialMapping(renderersWithChange, val, replacementIndex);
					if (!flag2)
					{
						flag2 |= ReplacementApplicator.ApplyAllTextureProperties(val, replacementIndex);
					}
					if (flag2)
					{
						flag = true;
						if (Preferences.DebugEnabled)
						{
							MelonLogger.Msg("[MaterialChangeMonitor] Reapplied replacements to material '" + ((Object)val).name + "' on '" + ((Object)((Component)renderersWithChange).gameObject).name + "' after material change");
						}
					}
				}
				if (flag)
				{
					originalMaterials[renderersWithChange] = sharedMaterials.ToArray();
				}
			}
		}

		private void ProcessImageChanges()
		{
			foreach (Image imagesWithChange in imagesWithChanges)
			{
				if (!((Object)(object)imagesWithChange == (Object)null) && ReplacementApplicator.ApplyToImage(imagesWithChange, replacementIndex))
				{
					originalSprites[imagesWithChange] = imagesWithChange.sprite;
					if (Preferences.DebugEnabled)
					{
						MelonLogger.Msg("[MaterialChangeMonitor] Reapplied replacements to Image '" + ((Object)((Component)imagesWithChange).gameObject).name + "' after sprite change");
					}
				}
			}
		}

		private void ProcessRawImageChanges()
		{
			foreach (RawImage rawImagesWithChange in rawImagesWithChanges)
			{
				if (!((Object)(object)rawImagesWithChange == (Object)null) && ReplacementApplicator.ApplyToRawImage(rawImagesWithChange, replacementIndex))
				{
					originalTextures[rawImagesWithChange] = rawImagesWithChange.texture;
					if (Preferences.DebugEnabled)
					{
						MelonLogger.Msg("[MaterialChangeMonitor] Reapplied replacements to RawImage '" + ((Object)((Component)rawImagesWithChange).gameObject).name + "' after texture change");
					}
				}
			}
		}

		public void RegisterRenderer(Renderer renderer)
		{
			if ((Object)(object)renderer == (Object)null || !isInitialized || !Preferences.MonitorDynamicChanges.Value || originalMaterials.ContainsKey(renderer))
			{
				return;
			}
			Material[] sharedMaterials = renderer.sharedMaterials;
			if (sharedMaterials != null)
			{
				originalMaterials[renderer] = sharedMaterials.ToArray();
				monitoredRenderers.Add(renderer);
				if (Preferences.DebugEnabled)
				{
					MelonLogger.Msg("[MaterialChangeMonitor] Registered renderer: " + ((Object)((Component)renderer).gameObject).name);
				}
			}
		}

		public void RegisterImage(Image image)
		{
			if ((Object)(object)image == (Object)null || !isInitialized || !Preferences.MonitorDynamicChanges.Value || originalSprites.ContainsKey(image))
			{
				return;
			}
			Sprite sprite = image.sprite;
			if ((Object)(object)sprite != (Object)null)
			{
				originalSprites[image] = sprite;
				monitoredImages.Add(image);
				if (Preferences.DebugEnabled)
				{
					MelonLogger.Msg("[MaterialChangeMonitor] Registered image: " + ((Object)((Component)image).gameObject).name);
				}
			}
		}

		public void RegisterRawImage(RawImage rawImage)
		{
			if ((Object)(object)rawImage == (Object)null || !isInitialized || !Preferences.MonitorDynamicChanges.Value || originalTextures.ContainsKey(rawImage))
			{
				return;
			}
			Texture texture = rawImage.texture;
			if ((Object)(object)texture != (Object)null)
			{
				originalTextures[rawImage] = texture;
				monitoredRawImages.Add(rawImage);
				if (Preferences.DebugEnabled)
				{
					MelonLogger.Msg("[MaterialChangeMonitor] Registered raw image: " + ((Object)((Component)rawImage).gameObject).name);
				}
			}
		}

		public void CleanupDestroyedObjects()
		{
			List<Renderer> list = originalMaterials.Keys.Where((Renderer r) => (Object)(object)r == (Object)null).ToList();
			foreach (Renderer item in list)
			{
				originalMaterials.Remove(item);
				monitoredRenderers.Remove(item);
			}
			List<Image> list2 = originalSprites.Keys.Where((Image i) => (Object)(object)i == (Object)null).ToList();
			foreach (Image item2 in list2)
			{
				originalSprites.Remove(item2);
				monitoredImages.Remove(item2);
			}
			List<RawImage> list3 = originalTextures.Keys.Where((RawImage r) => (Object)(object)r == (Object)null).ToList();
			foreach (RawImage item3 in list3)
			{
				originalTextures.Remove(item3);
				monitoredRawImages.Remove(item3);
			}
		}

		public void ScanAndRegisterAll()
		{
			if (!isInitialized || !Preferences.MonitorDynamicChanges.Value)
			{
				return;
			}
			Renderer[] array = Object.FindObjectsOfType<Renderer>(true);
			Renderer[] array2 = array;
			foreach (Renderer val in array2)
			{
				if ((Object)(object)val != (Object)null && val.sharedMaterials != null && val.sharedMaterials.Length != 0)
				{
					RegisterRenderer(val);
				}
			}
			Image[] array3 = Object.FindObjectsOfType<Image>(true);
			Image[] array4 = array3;
			foreach (Image val2 in array4)
			{
				if ((Object)(object)val2 != (Object)null && (Object)(object)val2.sprite != (Object)null)
				{
					RegisterImage(val2);
				}
			}
			RawImage[] array5 = Object.FindObjectsOfType<RawImage>(true);
			RawImage[] array6 = array5;
			foreach (RawImage val3 in array6)
			{
				if ((Object)(object)val3 != (Object)null && (Object)(object)val3.texture != (Object)null)
				{
					RegisterRawImage(val3);
				}
			}
			if (Preferences.DebugEnabled)
			{
				MelonLogger.Msg($"[MaterialChangeMonitor] Registered {monitoredRenderers.Count} renderers, {monitoredImages.Count} images, {monitoredRawImages.Count} raw images for monitoring");
			}
		}
	}
	public static class PropertyNames
	{
		public static readonly string[] TexturePropertyNames = new string[17]
		{
			"_BaseMap", "_MainTex", "_BaseColorMap", "_BumpMap", "_NormalMap", "_MetallicGlossMap", "_MetallicSpecGlossMap", "_SpecGlossMap", "_ParallaxMap", "_OcclusionMap",
			"_DetailAlbedoMap", "_DetailNormalMap", "_EmissionMap", "_EmissiveColorMap", "_SpecularGlossMap", "_SmoothnessTexture", "_DetailMask"
		};

		public static readonly string[] EmissionPropertyNames = new string[2] { "_EmissionMap", "_EmissiveColorMap" };
	}
	public static class ReplacementApplicator
	{
		public static bool ApplyAllTextureProperties(Material material, ReplacementIndex index)
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: 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_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			bool result = false;
			if ((Object)(object)material == (Object)null)
			{
				return false;
			}
			string[] texturePropertyNames = PropertyNames.TexturePropertyNames;
			foreach (string text in texturePropertyNames)
			{
				if (!material.HasProperty(text))
				{
					continue;
				}
				Texture texture = material.GetTexture(text);
				Texture2D val = (Texture2D)(object)((texture is Texture2D) ? texture : null);
				if ((Object)(object)val == (Object)null || !index.TryGetByTextureName(((Object)val).name, out var texture2))
				{
					continue;
				}
				Vector2 textureScale = material.GetTextureScale(text);
				Vector2 textureOffset = material.GetTextureOffset(text);
				material.SetTexture(text, (Texture)(object)texture2);
				material.SetTextureScale(text, textureScale);
				material.SetTextureOffset(text, textureOffset);
				material.enabledKeywords = null;
				material.color = Color.white;
				for (int j = 0; j < PropertyNames.EmissionPropertyNames.Length; j++)
				{
					if (text == PropertyNames.EmissionPropertyNames[j])
					{
						material.EnableKeyword("_EMISSION");
						break;
					}
				}
				result = true;
				if (Preferences.DebugEnabled)
				{
					MelonLogger.Msg("[ApplyAllTextureProperties] " + ((Object)material).name + " set " + text + " via texture-name mapping");
				}
			}
			return result;
		}

		public static bool ApplyByMaterialMapping(Renderer renderer, Material material, ReplacementIndex index)
		{
			if ((Object)(object)renderer == (Object)null || (Object)(object)material == (Object)null || index == null)
			{
				return false;
			}
			bool result = false;
			for (int i = 0; i < PropertyNames.TexturePropertyNames.Length; i++)
			{
				string text = PropertyNames.TexturePropertyNames[i];
				if (material.HasProperty(text) && index.TryGetByMaterialProperty(((Object)material).name, text, out var texture) && (Object)(object)texture != (Object)null)
				{
					ApplyTexturePreserveTiling(material, text, texture);
					if (Preferences.DebugEnabled)
					{
						MelonLogger.Msg("[ApplyByMaterialMapping] " + ((Object)((Component)renderer).gameObject).name + "." + ((Object)material).name + " set " + text + " via material__property mapping");
					}
					result = true;
				}
			}
			if (index.TryGetByMaterialName(((Object)material).name, out var texture2) && (Object)(object)texture2 != (Object)null)
			{
				string text2 = FindPrimaryTextureProperty(material);
				if (text2 != null)
				{
					ApplyTexturePreserveTiling(material, text2, texture2);
					if (Preferences.DebugEnabled)
					{
						MelonLogger.Msg("[ApplyByMaterialMapping] " + ((Object)((Component)renderer).gameObject).name + "." + ((Object)material).name + " set " + text2 + " via material mapping");
					}
					result = true;
				}
			}
			return result;
		}

		private static void ApplyTexturePreserveTiling(Material material, string prop, Texture2D replacement)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			Vector2 textureScale = material.GetTextureScale(prop);
			Vector2 textureOffset = material.GetTextureOffset(prop);
			int num = Shader.PropertyToID(prop);
			material.SetTexture(num, (Texture)(object)replacement);
			material.SetTextureScale(num, textureScale);
			material.SetTextureOffset(num, textureOffset);
			material.enabledKeywords = null;
			material.color = Color.white;
			for (int i = 0; i < PropertyNames.EmissionPropertyNames.Length; i++)
			{
				if (prop == PropertyNames.EmissionPropertyNames[i])
				{
					material.EnableKeyword("_EMISSION");
					break;
				}
			}
		}

		private static string FindPrimaryTextureProperty(Material material)
		{
			if (material.HasProperty("_BaseMap"))
			{
				return "_BaseMap";
			}
			if (material.HasProperty("_MainTex"))
			{
				return "_MainTex";
			}
			for (int i = 0; i < PropertyNames.TexturePropertyNames.Length; i++)
			{
				string text = PropertyNames.TexturePropertyNames[i];
				if (material.HasProperty(text))
				{
					return text;
				}
			}
			return null;
		}

		public static bool ApplyToImage(Image image, ReplacementIndex index)
		{
			if ((Object)(object)image == (Object)null || index == null)
			{
				return false;
			}
			bool result = false;
			Sprite sprite = image.sprite;
			if ((Object)(object)sprite == (Object)null)
			{
				return false;
			}
			Texture2D texture = sprite.texture;
			if ((Object)(object)texture == (Object)null)
			{
				return false;
			}
			Texture2D texture2 = null;
			string text = "";
			if (index.TryGetByTextureName(((Object)sprite).name, out texture2))
			{
				text = "sprite-name mapping: " + ((Object)sprite).name;
			}
			else if (index.TryGetByTextureName(((Object)texture).name, out texture2))
			{
				text = "texture-name mapping: " + ((Object)texture).name;
			}
			if ((Object)(object)texture2 != (Object)null)
			{
				try
				{
					Sprite val = CreateCompatibleSprite(texture2, sprite);
					((Object)val).name = ((Object)sprite).name;
					image.sprite = val;
					ForceUIUpdate(image);
					result = true;
					if (Preferences.DebugEnabled)
					{
						MelonLogger.Msg("[ApplyToImage] " + ((Object)((Component)image).gameObject).name + " set sprite via " + text);
					}
				}
				catch (Exception ex)
				{
					MelonLogger.Error("[ApplyToImage] Failed to create replacement sprite for " + ((Object)((Component)image).gameObject).name + ": " + ex.Message);
				}
			}
			return result;
		}

		private static void ForceUIUpdate(Image image)
		{
			if ((Object)(object)image == (Object)null)
			{
				return;
			}
			try
			{
				Canvas componentInParent = ((Component)image).GetComponentInParent<Canvas>();
				if ((Object)(object)componentInParent != (Object)null)
				{
					MelonPreferences_Entry<bool> aggressiveUIUpdates = Preferences.AggressiveUIUpdates;
					if (aggressiveUIUpdates != null && aggressiveUIUpdates.Value)
					{
						((Behaviour)componentInParent).enabled = false;
						((Behaviour)componentInParent).enabled = true;
					}
					else
					{
						Canvas.ForceUpdateCanvases();
					}
				}
				LayoutElement component = ((Component)image).GetComponent<LayoutElement>();
				if ((Object)(object)component != (Object)null)
				{
					Transform transform = ((Component)component).transform;
					LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)(object)((transform is RectTransform) ? transform : null));
				}
				((Graphic)image).SetAllDirty();
				((Graphic)image).Rebuild((CanvasUpdate)3);
			}
			catch (Exception ex)
			{
				if (Preferences.DebugEnabled)
				{
					MelonLogger.Warning("[ForceUIUpdate] Error updating UI for " + ((Object)((Component)image).gameObject).name + ": " + ex.Message);
				}
			}
		}

		private static Sprite CreateCompatibleSprite(Texture2D replacement, Sprite originalSprite)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			Rect val = default(Rect);
			if (((Texture)replacement).width >= ((Texture)originalSprite.texture).width && ((Texture)replacement).height >= ((Texture)originalSprite.texture).height)
			{
				Rect rect = originalSprite.rect;
				float x = ((Rect)(ref rect)).x;
				float num = ((Texture)replacement).width;
				rect = originalSprite.rect;
				float num2 = Mathf.Min(x, num - ((Rect)(ref rect)).width);
				rect = originalSprite.rect;
				float y = ((Rect)(ref rect)).y;
				float num3 = ((Texture)replacement).height;
				rect = originalSprite.rect;
				float num4 = Mathf.Min(y, num3 - ((Rect)(ref rect)).height);
				rect = originalSprite.rect;
				float num5 = Mathf.Min(((Rect)(ref rect)).width, (float)((Texture)replacement).width);
				rect = originalSprite.rect;
				float num6 = Mathf.Min(((Rect)(ref rect)).height, (float)((Texture)replacement).height);
				if (num5 > 0f && num6 > 0f)
				{
					((Rect)(ref val))..ctor(num2, num4, num5, num6);
				}
				else
				{
					((Rect)(ref val))..ctor(0f, 0f, (float)((Texture)replacement).width, (float)((Texture)replacement).height);
				}
			}
			else
			{
				((Rect)(ref val))..ctor(0f, 0f, (float)((Texture)replacement).width, (float)((Texture)replacement).height);
			}
			return Sprite.Create(replacement, val, originalSprite.pivot, originalSprite.pixelsPerUnit, 0u, (SpriteMeshType)0, originalSprite.border, false);
		}

		public static bool ApplyToRawImage(RawImage rawImage, ReplacementIndex index)
		{
			if ((Object)(object)rawImage == (Object)null || index == null)
			{
				return false;
			}
			bool result = false;
			Texture texture = rawImage.texture;
			Texture2D val = (Texture2D)(object)((texture is Texture2D) ? texture : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if (index.TryGetByTextureName(((Object)val).name, out var texture2))
			{
				rawImage.texture = (Texture)(object)texture2;
				result = true;
				if (Preferences.DebugEnabled)
				{
					MelonLogger.Msg("[ApplyToRawImage] " + ((Object)((Component)rawImage).gameObject).name + " set texture via texture-name mapping: " + ((Object)val).name);
				}
			}
			return result;
		}
	}
	public sealed class ReplacementIndex
	{
		private sealed class LoadedTexture
		{
			public string FilePath;

			public Texture2D Texture;
		}

		private readonly string baseDirectory;

		private readonly string exportsDirectory;

		private readonly Dictionary<string, LoadedTexture> keyToTexture;

		private readonly Dictionary<string, LoadedTexture> materialNameToTexture;

		private readonly Dictionary<(string materialName, string property), LoadedTexture> materialPropertyToTexture;

		private static readonly HashSet<string> SupportedExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".jpg", ".jpeg" };

		public bool HasAnyEntries => (keyToTexture != null && keyToTexture.Count > 0) || (materialNameToTexture != null && materialNameToTexture.Count > 0) || (materialPropertyToTexture != null && materialPropertyToTexture.Count > 0);

		private static string NormalizeMaterialName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return name;
			}
			string text = name.Trim();
			if (text.EndsWith(" (Instance)", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(0, text.Length - " (Instance)".Length);
			}
			if (text.EndsWith(" (Clone)", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(0, text.Length - " (Clone)".Length);
			}
			return text.Trim();
		}

		public ReplacementIndex(string baseDirectory)
		{
			this.baseDirectory = baseDirectory;
			exportsDirectory = Path.Combine(baseDirectory, "_Exports");
			Directory.CreateDirectory(this.baseDirectory);
			Directory.CreateDirectory(exportsDirectory);
			keyToTexture = new Dictionary<string, LoadedTexture>(StringComparer.OrdinalIgnoreCase);
			materialNameToTexture = new Dictionary<string, LoadedTexture>(StringComparer.OrdinalIgnoreCase);
			materialPropertyToTexture = new Dictionary<(string, string), LoadedTexture>();
			LoadAll();
		}

		private static bool IsTextureValid(Texture2D texture)
		{
			return (Object)(object)texture != (Object)null && texture != null;
		}

		private bool ValidateOrReloadTexture(LoadedTexture loadedTexture)
		{
			if (loadedTexture == null)
			{
				return false;
			}
			if (IsTextureValid(loadedTexture.Texture))
			{
				return true;
			}
			if (string.IsNullOrEmpty(loadedTexture.FilePath) || !File.Exists(loadedTexture.FilePath))
			{
				return false;
			}
			try
			{
				Texture2D val = LoadTextureFromFile(loadedTexture.FilePath);
				if ((Object)(object)val != (Object)null)
				{
					loadedTexture.Texture = val;
					if (Preferences.DebugEnabled)
					{
						MelonLogger.Msg("[ReplacementIndex] Reloaded destroyed texture from '" + loadedTexture.FilePath + "'");
					}
					return true;
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[ReplacementIndex] Failed to reload texture from '" + loadedTexture.FilePath + "': " + ex.Message);
			}
			return false;
		}

		public bool TryGetByTextureName(string textureName, out Texture2D texture)
		{
			texture = null;
			if (string.IsNullOrEmpty(textureName))
			{
				return false;
			}
			if (keyToTexture.TryGetValue(textureName, out var value) && ValidateOrReloadTexture(value))
			{
				texture = value.Texture;
				return true;
			}
			return false;
		}

		public bool HasTexture(string textureName)
		{
			if (string.IsNullOrEmpty(textureName))
			{
				return false;
			}
			return keyToTexture.ContainsKey(textureName);
		}

		public void ReloadChangedFiles(ICollection<string> filePaths)
		{
			if (filePaths == null || filePaths.Count == 0)
			{
				return;
			}
			foreach (string filePath in filePaths)
			{
				if (string.IsNullOrEmpty(filePath))
				{
					continue;
				}
				string extension = Path.GetExtension(filePath);
				if (!SupportedExtensions.Contains(extension))
				{
					continue;
				}
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath);
				if (!File.Exists(filePath))
				{
					if (keyToTexture.TryGetValue(fileNameWithoutExtension, out var value))
					{
						if ((Object)(object)value.Texture != (Object)null)
						{
							Object.Destroy((Object)(object)value.Texture);
						}
						keyToTexture.Remove(fileNameWithoutExtension);
					}
					string[] array = fileNameWithoutExtension.Split(new string[1] { "__" }, StringSplitOptions.None);
					if (array.Length == 1)
					{
						string key = NormalizeMaterialName(array[0]);
						materialNameToTexture.Remove(key);
					}
					else if (array.Length >= 2)
					{
						string item = NormalizeMaterialName(array[0]);
						string item2 = array[1];
						materialPropertyToTexture.Remove((item, item2));
					}
					if (Preferences.DebugEnabled)
					{
						MelonLogger.Msg("[ReplacementIndex] Deleted mapping for '" + fileNameWithoutExtension + "'");
					}
				}
				else
				{
					LoadOrReplace(fileNameWithoutExtension, filePath);
					if (Preferences.DebugEnabled)
					{
						MelonLogger.Msg("[ReplacementIndex] Loaded/Updated '" + fileNameWithoutExtension + "' from '" + filePath + "'");
					}
				}
			}
		}

		private void LoadAll()
		{
			if (!Directory.Exists(baseDirectory))
			{
				return;
			}
			IEnumerable<string> enumerable = Directory.EnumerateFiles(baseDirectory, "*.*", SearchOption.AllDirectories);
			foreach (string item in enumerable)
			{
				if (item.IndexOf("_Exports", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					continue;
				}
				string extension = Path.GetExtension(item);
				if (SupportedExtensions.Contains(extension))
				{
					string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item);
					LoadOrReplace(fileNameWithoutExtension, item);
					if (Preferences.DebugEnabled)
					{
						MelonLogger.Msg("[ReplacementIndex] Preloaded '" + fileNameWithoutExtension + "' from '" + item + "'");
					}
				}
			}
			if (Preferences.DebugEnabled)
			{
				MelonLogger.Msg($"[ReplacementIndex] Finished loading. Keys={keyToTexture.Count} materialNames={materialNameToTexture.Count} materialProperties={materialPropertyToTexture.Count}");
			}
		}

		private void LoadOrReplace(string key, string filePath)
		{
			try
			{
				Texture2D val = LoadTextureFromFile(filePath);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				if (keyToTexture.TryGetValue(key, out var value) && value != null)
				{
					if ((Object)(object)value.Texture != (Object)null)
					{
						Object.Destroy((Object)(object)value.Texture);
					}
					value.FilePath = filePath;
					value.Texture = val;
				}
				else
				{
					keyToTexture[key] = new LoadedTexture
					{
						FilePath = filePath,
						Texture = val
					};
				}
				LoadedTexture value2 = keyToTexture[key];
				string[] array = key.Split(new string[1] { "__" }, StringSplitOptions.None);
				if (array.Length == 1)
				{
					string key2 = NormalizeMaterialName(array[0]);
					materialNameToTexture[key2] = value2;
				}
				else if (array.Length >= 2)
				{
					string item = NormalizeMaterialName(array[0]);
					string item2 = array[1];
					materialPropertyToTexture[(item, item2)] = value2;
				}
				if (Preferences.DebugEnabled)
				{
					MelonLogger.Msg("[ReplacementIndex] Indexed key='" + key + "' mat='" + ((array.Length != 0) ? NormalizeMaterialName(array[0]) : "-") + "' prop='" + ((array.Length > 1) ? array[1] : "-") + "'");
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[ReplacementIndex] Failed to load '" + filePath + "': " + ex.Message);
			}
		}

		private static Texture2D LoadTextureFromFile(string filePath)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			try
			{
				byte[] array = File.ReadAllBytes(filePath);
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true);
				if (!ImageConversion.LoadImage(val, array, false))
				{
					Object.Destroy((Object)(object)val);
					return null;
				}
				((Object)val).name = Path.GetFileNameWithoutExtension(filePath);
				((Texture)val).wrapMode = (TextureWrapMode)0;
				((Texture)val).filterMode = (FilterMode)1;
				Object.DontDestroyOnLoad((Object)(object)val);
				return val;
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[ReplacementIndex] Error reading '" + filePath + "': " + ex.Message);
				return null;
			}
		}

		public bool TryGetByMaterialName(string materialName, out Texture2D texture)
		{
			texture = null;
			if (string.IsNullOrEmpty(materialName))
			{
				return false;
			}
			string key = NormalizeMaterialName(materialName);
			if (materialNameToTexture.TryGetValue(key, out var value) && ValidateOrReloadTexture(value))
			{
				texture = value.Texture;
				return true;
			}
			return false;
		}

		public bool TryGetByMaterialProperty(string materialName, string propertyName, out Texture2D texture)
		{
			texture = null;
			if (string.IsNullOrEmpty(materialName) || string.IsNullOrEmpty(propertyName))
			{
				return false;
			}
			string item = NormalizeMaterialName(materialName);
			if (materialPropertyToTexture.TryGetValue((item, propertyName), out var value) && ValidateOrReloadTexture(value))
			{
				texture = value.Texture;
				return true;
			}
			return false;
		}

		public void ValidateAndReloadAllTextures()
		{
			int num = 0;
			int num2 = 0;
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, LoadedTexture> item in keyToTexture)
			{
				num2++;
				bool flag = IsTextureValid(item.Value?.Texture);
				if (!ValidateOrReloadTexture(item.Value))
				{
					list.Add(item.Key);
				}
				else if (!flag && IsTextureValid(item.Value.Texture))
				{
					num++;
				}
			}
			foreach (string item2 in list)
			{
				keyToTexture.Remove(item2);
			}
			List<string> list2 = new List<string>();
			foreach (KeyValuePair<string, LoadedTexture> item3 in materialNameToTexture)
			{
				num2++;
				bool flag2 = IsTextureValid(item3.Value?.Texture);
				if (!ValidateOrReloadTexture(item3.Value))
				{
					list2.Add(item3.Key);
				}
				else if (!flag2 && IsTextureValid(item3.Value.Texture))
				{
					num++;
				}
			}
			foreach (string item4 in list2)
			{
				materialNameToTexture.Remove(item4);
			}
			List<(string, string)> list3 = new List<(string, string)>();
			foreach (KeyValuePair<(string, string), LoadedTexture> item5 in materialPropertyToTexture)
			{
				num2++;
				bool flag3 = IsTextureValid(item5.Value?.Texture);
				if (!ValidateOrReloadTexture(item5.Value))
				{
					list3.Add(item5.Key);
				}
				else if (!flag3 && IsTextureValid(item5.Value.Texture))
				{
					num++;
				}
			}
			foreach (var item6 in list3)
			{
				materialPropertyToTexture.Remove(item6);
			}
			if (Preferences.DebugEnabled)
			{
				MelonLogger.Msg($"[ReplacementIndex] Validated {num2} textures, reloaded {num}, removed {list.Count + list2.Count + list3.Count} invalid entries");
			}
		}
	}
	public sealed class MaterialScanner : MonoBehaviour
	{
		private readonly List<Renderer> rendererBuffer = new List<Renderer>(256);

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

		private ReplacementIndex replacementIndex;

		private MaterialChangeMonitor materialChangeMonitor;

		private bool fullRescanRequested;

		private int lastRendererIndex;

		private float nextAllowedRescanTime;

		private readonly List<Camera> cameraCache = new List<Camera>(4);

		private bool scanningInProgress;

		private int totalRenderersFound;

		private int totalRenderersProcessed;

		private static bool ShouldSkipRenderer(Renderer renderer)
		{
			if ((Object)(object)renderer == (Object)null)
			{
				return true;
			}
			GameObject gameObject = ((Component)renderer).gameObject;
			if ((Object)(object)gameObject != (Object)null)
			{
				string name = ((Object)gameObject).name;
				if (!string.IsNullOrEmpty(name) && name.ToLowerInvariant().Contains("collider"))
				{
					return true;
				}
			}
			Material[] sharedMaterials = renderer.sharedMaterials;
			if (sharedMaterials == null || sharedMaterials.Length == 0)
			{
				return true;
			}
			return false;
		}

		private static bool IsRendererActive(Renderer renderer)
		{
			if ((Object)(object)renderer == (Object)null)
			{
				return false;
			}
			GameObject gameObject = ((Component)renderer).gameObject;
			return (Object)(object)gameObject != (Object)null && gameObject.activeInHierarchy && renderer.enabled;
		}

		private bool IsRendererVisible(Renderer renderer)
		{
			return (Object)(object)renderer != (Object)null && renderer.isVisible;
		}

		private float SquaredDistanceToNearestCamera(Vector3 position)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			float num = float.MaxValue;
			for (int i = 0; i < cameraCache.Count; i++)
			{
				Camera val = cameraCache[i];
				if (!((Object)(object)val == (Object)null) && ((Behaviour)val).enabled)
				{
					Vector3 val2 = ((Component)val).transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						num = sqrMagnitude;
					}
				}
			}
			return (num == float.MaxValue) ? 0f : num;
		}

		public void Initialize(ReplacementIndex index, MaterialChangeMonitor monitor = null)
		{
			replacementIndex = index;
			materialChangeMonitor = monitor;
			fullRescanRequested = true;
			if (Preferences.DebugEnabled)
			{
				MelonLogger.Msg("[MaterialScanner] Initialize and request full rescan");
			}
		}

		public void RequestFullRescan()
		{
			fullRescanRequested = true;
			if (Preferences.DebugEnabled)
			{
				MelonLogger.Msg("[MaterialScanner] Full rescan requested");
			}
		}

		private void LateUpdate()
		{
			if (Preferences.Enabled.Value && replacementIndex != null)
			{
				float num = Preferences.RescanIntervalSeconds?.Value ?? 0f;
				if (num > 0f)
				{
					HandlePeriodicRescan();
				}
				if (fullRescanRequested)
				{
					StartFullRescan();
				}
				if (scanningInProgress && rendererBuffer.Count > 0)
				{
					ProcessRendererBatch();
				}
			}
		}

		private void HandlePeriodicRescan()
		{
			float num = Preferences.RescanIntervalSeconds?.Value ?? 0f;
			if (num > 0f && Time.unscaledTime >= nextAllowedRescanTime)
			{
				fullRescanRequested = true;
				nextAllowedRescanTime = Time.unscaledTime + num;
			}
		}

		private void StartFullRescan()
		{
			processedMaterialIds.Clear();
			rendererBuffer.Clear();
			lastRendererIndex = 0;
			totalRenderersProcessed = 0;
			if (replacementIndex != null)
			{
				replacementIndex.ValidateAndReloadAllTextures();
			}
			RefreshCameraCache();
			DiscoverAndFilterRenderers();
			scanningInProgress = rendererBuffer.Count > 0;
			fullRescanRequested = false;
			if (Preferences.DebugEnabled)
			{
				string arg = ((cameraCache.Count > 0 && cameraCache.Exists((Camera c) => (Object)(object)c != (Object)null && ((Behaviour)c).enabled && ((Component)c).gameObject.activeInHierarchy)) ? $"cameras={cameraCache.Count}" : "no active cameras, skipping vis/dist filters");
				MelonLogger.Msg($"[MaterialScanner] Started full rescan: found {totalRenderersFound} renderers, {rendererBuffer.Count} after filters ({arg})");
			}
		}

		private void RefreshCameraCache()
		{
			cameraCache.Clear();
			Camera[] allCameras = Camera.allCameras;
			if (allCameras != null)
			{
				cameraCache.AddRange(allCameras);
			}
		}

		private void DiscoverAndFilterRenderers()
		{
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			Renderer[] array = Object.FindObjectsOfType<Renderer>(true);
			totalRenderersFound = ((array != null) ? array.Length : 0);
			if (array == null || array.Length == 0)
			{
				return;
			}
			bool flag = cameraCache.Count > 0 && cameraCache.Exists((Camera c) => (Object)(object)c != (Object)null && ((Behaviour)c).enabled && ((Component)c).gameObject.activeInHierarchy);
			bool flag2 = (Preferences.OnlyScanVisible?.Value ?? false) && flag;
			MelonPreferences_Entry<float> maxScanDistance = Preferences.MaxScanDistance;
			bool flag3 = maxScanDistance != null && maxScanDistance.Value > 0f && flag;
			bool flag4 = Preferences.OnlyScanActive?.Value ?? false;
			float num = (flag3 ? (Preferences.MaxScanDistance.Value * Preferences.MaxScanDistance.Value) : 0f);
			if (rendererBuffer.Capacity < array.Length)
			{
				rendererBuffer.Capacity = array.Length;
			}
			foreach (Renderer val in array)
			{
				if (ShouldSkipRenderer(val) || (flag4 && !IsRendererActive(val)) || (flag2 && !IsRendererVisible(val)))
				{
					continue;
				}
				if (flag3)
				{
					Bounds bounds = val.bounds;
					float num2 = SquaredDistanceToNearestCamera(((Bounds)(ref bounds)).center);
					if (num2 > num)
					{
						continue;
					}
				}
				rendererBuffer.Add(val);
			}
		}

		private void ProcessRendererBatch()
		{
			int num = Mathf.Max(8, Preferences.ScanBatchSize.Value);
			int num2 = Mathf.Min(rendererBuffer.Count, lastRendererIndex + num);
			int num3 = 0;
			for (int i = lastRendererIndex; i < num2; i++)
			{
				Renderer renderer = rendererBuffer[i];
				if (ProcessSingleRenderer(renderer))
				{
					num3++;
					totalRenderersProcessed++;
				}
			}
			lastRendererIndex = num2;
			bool flag = lastRendererIndex >= rendererBuffer.Count;
			if (flag)
			{
				CompleteScanPass();
			}
			if (Preferences.DebugEnabled && num3 > 0)
			{
				float num4 = (flag ? 100f : ((float)lastRendererIndex / (float)rendererBuffer.Count * 100f));
				MelonLogger.Msg($"[MaterialScanner] Processed batch: {num3} renderers, progress: {num4:F1}% ({lastRendererIndex}/{rendererBuffer.Count})");
			}
		}

		private bool ProcessSingleRenderer(Renderer renderer)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)renderer == (Object)null)
			{
				return false;
			}
			MelonPreferences_Entry<bool> onlyScanActive = Preferences.OnlyScanActive;
			if (onlyScanActive != null && onlyScanActive.Value && !IsRendererActive(renderer))
			{
				return false;
			}
			MelonPreferences_Entry<bool> onlyScanVisible = Preferences.OnlyScanVisible;
			if (onlyScanVisible != null && onlyScanVisible.Value && !IsRendererVisible(renderer))
			{
				return false;
			}
			float num = Preferences.MaxScanDistance?.Value ?? 0f;
			if (num > 0f)
			{
				Bounds bounds = renderer.bounds;
				float num2 = SquaredDistanceToNearestCamera(((Bounds)(ref bounds)).center);
				if (num2 > num * num)
				{
					return false;
				}
			}
			Material[] sharedMaterials = renderer.sharedMaterials;
			if (sharedMaterials == null)
			{
				return false;
			}
			bool result = false;
			foreach (Material val in sharedMaterials)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				int instanceID = ((Object)val).GetInstanceID();
				if (!processedMaterialIds.Contains(instanceID))
				{
					bool flag = false;
					flag |= ReplacementApplicator.ApplyByMaterialMapping(renderer, val, replacementIndex);
					if (!flag)
					{
						flag |= ReplacementApplicator.ApplyAllTextureProperties(val, replacementIndex);
					}
					if (flag && Preferences.DebugEnabled)
					{
						MelonLogger.Msg("[MaterialScanner] Applied replacements to material '" + ((Object)val).name + "' on '" + ((Object)((Component)renderer).gameObject).name + "'");
					}
					if (flag && (Object)(object)materialChangeMonitor != (Object)null)
					{
						materialChangeMonitor.RegisterRenderer(renderer);
					}
					processedMaterialIds.Add(instanceID);
					result = true;
				}
			}
			return result;
		}

		private void CompleteScanPass()
		{
			scanningInProgress = false;
			lastRendererIndex = 0;
			if (Preferences.DebugEnabled)
			{
				MelonLogger.Msg($"[MaterialScanner] Completed scan pass: processed {totalRenderersProcessed}/{rendererBuffer.Count} renderers");
			}
			rendererBuffer.Clear();
			processedMaterialIds.Clear();
		}
	}
	public sealed class UITextureScanner : MonoBehaviour
	{
		private readonly List<Image> imageBuffer = new List<Image>(128);

		private readonly List<RawImage> rawImageBuffer = new List<RawImage>(64);

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

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

		private ReplacementIndex replacementIndex;

		private MaterialChangeMonitor materialChangeMonitor;

		private bool fullRescanRequested;

		private int lastImageIndex;

		private int lastRawImageIndex;

		private float nextAllowedRescanTime;

		private readonly List<Canvas> canvasCache = new List<Canvas>(8);

		private bool scanningInProgress;

		private int totalImagesFound;

		private int totalRawImagesFound;

		private int totalImagesProcessed;

		private int totalRawImagesProcessed;

		private static bool ShouldSkipImage(Image image)
		{
			if ((Object)(object)image == (Object)null)
			{
				return true;
			}
			if ((Object)(object)image.sprite == (Object)null)
			{
				return true;
			}
			if ((Object)(object)image.sprite.texture == (Object)null)
			{
				return true;
			}
			GameObject gameObject = ((Component)image).gameObject;
			if ((Object)(object)gameObject != (Object)null)
			{
				string name = ((Object)gameObject).name;
				if (!string.IsNullOrEmpty(name) && name.ToLowerInvariant().Contains("collider"))
				{
					return true;
				}
			}
			return false;
		}

		private static bool ShouldSkipRawImage(RawImage rawImage)
		{
			if ((Object)(object)rawImage == (Object)null)
			{
				return true;
			}
			if ((Object)(object)rawImage.texture == (Object)null)
			{
				return true;
			}
			GameObject gameObject = ((Component)rawImage).gameObject;
			if ((Object)(object)gameObject != (Object)null)
			{
				string name = ((Object)gameObject).name;
				if (!string.IsNullOrEmpty(name) && name.ToLowerInvariant().Contains("collider"))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsImageActive(Image image)
		{
			if ((Object)(object)image == (Object)null)
			{
				return false;
			}
			GameObject gameObject = ((Component)image).gameObject;
			return (Object)(object)gameObject != (Object)null && gameObject.activeInHierarchy && ((Behaviour)image).enabled;
		}

		private static bool IsRawImageActive(RawImage rawImage)
		{
			if ((Object)(object)rawImage == (Object)null)
			{
				return false;
			}
			GameObject gameObject = ((Component)rawImage).gameObject;
			return (Object)(object)gameObject != (Object)null && gameObject.activeInHierarchy && ((Behaviour)rawImage).enabled;
		}

		private static bool IsCanvasActive(Canvas canvas)
		{
			if ((Object)(object)canvas == (Object)null)
			{
				return false;
			}
			GameObject gameObject = ((Component)canvas).gameObject;
			return (Object)(object)gameObject != (Object)null && gameObject.activeInHierarchy && ((Behaviour)canvas).enabled;
		}

		public void Initialize(ReplacementIndex index, MaterialChangeMonitor monitor = null)
		{
			replacementIndex = index;
			materialChangeMonitor = monitor;
			fullRescanRequested = true;
			if (Preferences.DebugEnabled)
			{
				MelonLogger.Msg("[UITextureScanner] Initialize and request full rescan");
			}
		}

		public void RequestFullRescan()
		{
			fullRescanRequested = true;
			if (Preferences.DebugEnabled)
			{
				MelonLogger.Msg("[UITextureScanner] Full rescan requested");
			}
		}

		private void LateUpdate()
		{
			if (Preferences.Enabled.Value && replacementIndex != null && Preferences.ScanUIElements.Value)
			{
				float num = Preferences.RescanIntervalSeconds?.Value ?? 0f;
				if (num > 0f)
				{
					HandlePeriodicRescan();
				}
				if (fullRescanRequested)
				{
					StartFullRescan();
				}
				if (scanningInProgress && (imageBuffer.Count > 0 || rawImageBuffer.Count > 0))
				{
					ProcessImageBatch();
				}
			}
		}

		public void HandleFileChanges(ICollection<string> changedPaths)
		{
			if (changedPaths == null || changedPaths.Count == 0 || !Preferences.Enabled.Value || !Preferences.ScanUIElements.Value)
			{
				return;
			}
			bool flag = false;
			foreach (string changedPath in changedPaths)
			{
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(changedPath);
				if (HasReplacementForSprite(fileNameWithoutExtension))
				{
					flag = true;
					break;
				}
			}
			if (flag)
			{
				if (Preferences.DebugEnabled)
				{
					MelonLogger.Msg("[UITextureScanner] File changes detected, requesting UI rescan");
				}
				RequestFullRescan();
			}
		}

		private void HandlePeriodicRescan()
		{
			float num = Preferences.RescanIntervalSeconds?.Value ?? 0f;
			if (num > 0f && Time.unscaledTime >= nextAllowedRescanTime)
			{
				fullRescanRequested = true;
				nextAllowedRescanTime = Time.unscaledTime + num;
			}
		}

		private void StartFullRescan()
		{
			processedImageIds.Clear();
			processedRawImageIds.Clear();
			imageBuffer.Clear();
			rawImageBuffer.Clear();
			lastImageIndex = 0;
			lastRawImageIndex = 0;
			totalImagesProcessed = 0;
			totalRawImagesProcessed = 0;
			if (replacementIndex != null)
			{
				replacementIndex.ValidateAndReloadAllTextures();
			}
			RefreshCanvasCache();
			DiscoverAndFilterImages();
			scanningInProgress = imageBuffer.Count > 0 || rawImageBuffer.Count > 0;
			fullRescanRequested = false;
			if (!Preferences.DebugEnabled)
			{
				return;
			}
			string text = ((canvasCache.Count > 0 && canvasCache.Exists((Canvas c) => (Object)(object)c != (Object)null && ((Behaviour)c).enabled && ((Component)c).gameObject.activeInHierarchy)) ? $"canvases={canvasCache.Count}" : "no active canvases");
			MelonLogger.Msg($"[UITextureScanner] Started full rescan: found {totalImagesFound} images, {totalRawImagesFound} raw images, {imageBuffer.Count + rawImageBuffer.Count} after filters ({text})");
			if (imageBuffer.Count > 0)
			{
				Image val = imageBuffer[0];
				if ((Object)(object)val != (Object)null && (Object)(object)val.sprite != (Object)null)
				{
					string[] obj = new string[7]
					{
						"[UITextureScanner] Sample Image: '",
						((Object)((Component)val).gameObject).name,
						"' with sprite '",
						((Object)val.sprite).name,
						"' (texture: ",
						null,
						null
					};
					Texture2D texture = val.sprite.texture;
					obj[5] = ((texture != null) ? ((Object)texture).name : null) ?? "null";
					obj[6] = ")";
					MelonLogger.Msg(string.Concat(obj));
				}
			}
		}

		private void RefreshCanvasCache()
		{
			canvasCache.Clear();
			Canvas[] array = Object.FindObjectsOfType<Canvas>(true);
			if (array != null)
			{
				canvasCache.AddRange(array);
			}
		}

		private void DiscoverAndFilterImages()
		{
			Image[] array = Object.FindObjectsOfType<Image>(true);
			totalImagesFound = ((array != null) ? array.Length : 0);
			if (array != null && array.Length != 0)
			{
				if (imageBuffer.Capacity < array.Length)
				{
					imageBuffer.Capacity = array.Length;
				}
				foreach (Image val in array)
				{
					if (!ShouldSkipImage(val) && ShouldIncludeImage(val))
					{
						imageBuffer.Add(val);
					}
				}
			}
			RawImage[] array2 = Object.FindObjectsOfType<RawImage>(true);
			totalRawImagesFound = ((array2 != null) ? array2.Length : 0);
			if (array2 == null || array2.Length == 0)
			{
				return;
			}
			if (rawImageBuffer.Capacity < array2.Length)
			{
				rawImageBuffer.Capacity = array2.Length;
			}
			foreach (RawImage val2 in array2)
			{
				if (!ShouldSkipRawImage(val2) && ShouldIncludeRawImage(val2))
				{
					rawImageBuffer.Add(val2);
				}
			}
		}

		private bool ShouldIncludeImage(Image image)
		{
			if ((Object)(object)image == (Object)null)
			{
				return false;
			}
			if ((Object)(object)image.sprite == (Object)null)
			{
				return false;
			}
			if ((Object)(object)image.sprite.texture == (Object)null)
			{
				return false;
			}
			Canvas componentInParent = ((Component)image).GetComponentInParent<Canvas>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				return false;
			}
			MelonPreferences_Entry<bool> onlyScanActive = Preferences.OnlyScanActive;
			if (onlyScanActive != null && onlyScanActive.Value)
			{
				if (!((Component)componentInParent).gameObject.activeInHierarchy || !((Behaviour)componentInParent).enabled)
				{
					return false;
				}
				if (!((Component)image).gameObject.activeInHierarchy || !((Behaviour)image).enabled)
				{
					return false;
				}
			}
			if (((Object)image.sprite).name.Contains("(Clone)") && !HasReplacementForSprite(((Object)image.sprite).name))
			{
				string spriteName = ((Object)image.sprite).name.Replace("(Clone)", "").Trim();
				if (!HasReplacementForSprite(spriteName))
				{
					return false;
				}
			}
			return true;
		}

		private bool ShouldIncludeRawImage(RawImage rawImage)
		{
			if ((Object)(object)rawImage == (Object)null)
			{
				return false;
			}
			if ((Object)(object)rawImage.texture == (Object)null)
			{
				return false;
			}
			Canvas componentInParent = ((Component)rawImage).GetComponentInParent<Canvas>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				return false;
			}
			MelonPreferences_Entry<bool> onlyScanActive = Preferences.OnlyScanActive;
			if (onlyScanActive != null && onlyScanActive.Value)
			{
				if (!((Component)componentInParent).gameObject.activeInHierarchy || !((Behaviour)componentInParent).enabled)
				{
					return false;
				}
				if (!((Component)rawImage).gameObject.activeInHierarchy || !((Behaviour)rawImage).enabled)
				{
					return false;
				}
			}
			return true;
		}

		private bool HasReplacementForSprite(string spriteName)
		{
			if (replacementIndex == null)
			{
				return false;
			}
			return replacementIndex.HasTexture(spriteName);
		}

		private void ProcessImageBatch()
		{
			int num = Mathf.Max(16, Preferences.ScanBatchSize.Value / 2);
			int num2 = 0;
			int num3 = Mathf.Min(imageBuffer.Count, lastImageIndex + num);
			for (int i = lastImageIndex; i < num3; i++)
			{
				Image image = imageBuffer[i];
				if (ProcessSingleImage(image))
				{
					num2++;
					totalImagesProcessed++;
				}
			}
			lastImageIndex = num3;
			int num4 = Mathf.Min(rawImageBuffer.Count, lastRawImageIndex + num);
			for (int j = lastRawImageIndex; j < num4; j++)
			{
				RawImage rawImage = rawImageBuffer[j];
				if (ProcessSingleRawImage(rawImage))
				{
					num2++;
					totalRawImagesProcessed++;
				}
			}
			lastRawImageIndex = num4;
			bool flag = lastImageIndex >= imageBuffer.Count;
			bool flag2 = lastRawImageIndex >= rawImageBuffer.Count;
			bool flag3 = flag && flag2;
			if (flag3)
			{
				CompleteScanPass();
			}
			if (Preferences.DebugEnabled && num2 > 0)
			{
				float num5 = (flag3 ? 100f : ((float)(lastImageIndex + lastRawImageIndex) / (float)(imageBuffer.Count + rawImageBuffer.Count) * 100f));
				MelonLogger.Msg($"[UITextureScanner] Processed batch: {num2} UI elements, progress: {num5:F1}% ({lastImageIndex + lastRawImageIndex}/{imageBuffer.Count + rawImageBuffer.Count})");
			}
		}

		private bool ProcessSingleImage(Image image)
		{
			if ((Object)(object)image == (Object)null)
			{
				return false;
			}
			MelonPreferences_Entry<bool> onlyScanActive = Preferences.OnlyScanActive;
			if (onlyScanActive != null && onlyScanActive.Value && !IsImageActive(image))
			{
				return false;
			}
			Sprite sprite = image.sprite;
			if ((Object)(object)sprite == (Object)null)
			{
				return false;
			}
			Texture2D texture = sprite.texture;
			if ((Object)(object)texture == (Object)null)
			{
				return false;
			}
			int instanceID = ((Object)image).GetInstanceID();
			if (processedImageIds.Contains(instanceID))
			{
				return false;
			}
			bool flag = false;
			try
			{
				flag = ReplacementApplicator.ApplyToImage(image, replacementIndex);
			}
			catch (Exception ex)
			{
				if (Preferences.DebugEnabled)
				{
					MelonLogger.Error("[UITextureScanner] Error processing Image '" + ((Object)((Component)image).gameObject).name + "': " + ex.Message);
				}
				return false;
			}
			if (flag && Preferences.DebugEnabled)
			{
				MelonLogger.Msg("[UITextureScanner] Applied replacements to Image '" + ((Object)((Component)image).gameObject).name + "' with sprite '" + ((Object)sprite).name + "'");
			}
			if (flag && (Object)(object)materialChangeMonitor != (Object)null)
			{
				try
				{
					materialChangeMonitor.RegisterImage(image);
				}
				catch (Exception ex2)
				{
					if (Preferences.DebugEnabled)
					{
						MelonLogger.Error("[UITextureScanner] Error registering Image '" + ((Object)((Component)image).gameObject).name + "' with monitor: " + ex2.Message);
					}
				}
			}
			processedImageIds.Add(instanceID);
			return flag;
		}

		private bool ProcessSingleRawImage(RawImage rawImage)
		{
			if ((Object)(object)rawImage == (Object)null)
			{
				return false;
			}
			MelonPreferences_Entry<bool> onlyScanActive = Preferences.OnlyScanActive;
			if (onlyScanActive != null && onlyScanActive.Value && !IsRawImageActive(rawImage))
			{
				return false;
			}
			Texture texture = rawImage.texture;
			Texture2D val = (Texture2D)(object)((texture is Texture2D) ? texture : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			int instanceID = ((Object)rawImage).GetInstanceID();
			if (processedRawImageIds.Contains(instanceID))
			{
				return false;
			}
			bool flag = false;
			flag |= ReplacementApplicator.ApplyToRawImage(rawImage, replacementIndex);
			if (flag && Preferences.DebugEnabled)
			{
				MelonLogger.Msg("[UITextureScanner] Applied replacements to RawImage '" + ((Object)((Component)rawImage).gameObject).name + "' with texture '" + ((Object)val).name + "'");
			}
			if (flag && (Object)(object)materialChangeMonitor != (Object)null)
			{
				materialChangeMonitor.RegisterRawImage(rawImage);
			}
			processedRawImageIds.Add(instanceID);
			return flag;
		}

		private void CompleteScanPass()
		{
			scanningInProgress = false;
			lastImageIndex = 0;
			lastRawImageIndex = 0;
			if (Preferences.DebugEnabled)
			{
				MelonLogger.Msg($"[UITextureScanner] Completed scan pass: processed {totalImagesProcessed}/{imageBuffer.Count} images, {totalRawImagesProcessed}/{rawImageBuffer.Count} raw images");
			}
			imageBuffer.Clear();
			rawImageBuffer.Clear();
			processedImageIds.Clear();
			processedRawImageIds.Clear();
		}
	}
}
namespace TextureSwapper.Config
{
	public static class Preferences
	{
		public static MelonPreferences_Category Category { get; private set; }

		public static MelonPreferences_Entry<bool> Enabled { get; private set; }

		public static MelonPreferences_Entry<bool> LiveReload { get; private set; }

		public static MelonPreferences_Entry<string> InspectorKeyPref { get; private set; }

		public static MelonPreferences_Entry<string> ExportKeyPref { get; private set; }

		public static MelonPreferences_Entry<int> ScanBatchSize { get; private set; }

		public static MelonPreferences_Entry<bool> DebugLogging { get; private set; }

		public static MelonPreferences_Entry<bool> OnlyScanActive { get; private set; }

		public static MelonPreferences_Entry<bool> OnlyScanVisible { get; private set; }

		public static MelonPreferences_Entry<float> MaxScanDistance { get; private set; }

		public static MelonPreferences_Entry<float> RescanIntervalSeconds { get; private set; }

		public static MelonPreferences_Entry<bool> MonitorDynamicChanges { get; private set; }

		public static MelonPreferences_Entry<bool> ScanUIElements { get; private set; }

		public static MelonPreferences_Entry<float> DynamicChangeCheckInterval { get; private set; }

		public static MelonPreferences_Entry<bool> AggressiveUIUpdates { get; private set; }

		public static KeyCode InspectorToggleKey => ParseKeyCode(InspectorKeyPref.Value, (KeyCode)288);

		public static KeyCode ExportKey => ParseKeyCode(ExportKeyPref.Value, (KeyCode)289);

		public static bool DebugEnabled => DebugLogging != null && DebugLogging.Value;

		public static void Initialize()
		{
			Category = MelonPreferences.CreateCategory("TextureSwapper");
			Enabled = Category.CreateEntry<bool>("Enabled", true, "Enable the TextureSwapper mod", (string)null, false, false, (ValueValidator)null, (string)null);
			LiveReload = Category.CreateEntry<bool>("LiveReload", true, "Auto-reload textures when files change", (string)null, false, false, (ValueValidator)null, (string)null);
			InspectorKeyPref = Category.CreateEntry<string>("InspectorKey", "F8", "Toggle inspector overlay key", (string)null, false, false, (ValueValidator)null, (string)null);
			ExportKeyPref = Category.CreateEntry<string>("ExportKey", "F9", "Export current target info key", (string)null, false, false, (ValueValidator)null, (string)null);
			ScanBatchSize = Category.CreateEntry<int>("ScanBatchSize", 96, "Renderer scan batch size per frame", (string)null, false, false, (ValueValidator)null, (string)null);
			DebugLogging = Category.CreateEntry<bool>("DebugLogging", false, "Enable verbose debug logging for troubleshooting", (string)null, false, false, (ValueValidator)null, (string)null);
			OnlyScanActive = Category.CreateEntry<bool>("OnlyScanActive", true, "Scan only active renderers (exclude inactive)", (string)null, false, false, (ValueValidator)null, (string)null);
			OnlyScanVisible = Category.CreateEntry<bool>("OnlyScanVisible", false, "Scan only renderers currently visible to a camera", (string)null, false, false, (ValueValidator)null, (string)null);
			MaxScanDistance = Category.CreateEntry<float>("MaxScanDistance", 500f, "Max distance from camera for scanning (0 = unlimited)", (string)null, false, false, (ValueValidator)null, (string)null);
			RescanIntervalSeconds = Category.CreateEntry<float>("RescanIntervalSeconds", 0f, "Periodic rescan interval in seconds (0 = scan only on scene changes and file changes - recommended for performance)", (string)null, false, false, (ValueValidator)null, (string)null);
			MonitorDynamicChanges = Category.CreateEntry<bool>("MonitorDynamicChanges", true, "Monitor objects for dynamic material/sprite changes (recommended for day/night cycles)", (string)null, false, false, (ValueValidator)null, (string)null);
			ScanUIElements = Category.CreateEntry<bool>("ScanUIElements", true, "Scan and replace UI elements (Images, RawImages)", (string)null, false, false, (ValueValidator)null, (string)null);
			DynamicChangeCheckInterval = Category.CreateEntry<float>("DynamicChangeCheckInterval", 5f, "How often to check for dynamic material/sprite changes (seconds, lower = more responsive but higher CPU)", (string)null, false, false, (ValueValidator)null, (string)null);
			AggressiveUIUpdates = Category.CreateEntry<bool>("AggressiveUIUpdates", false, "Use aggressive UI update methods (better compatibility but higher CPU usage)", (string)null, false, false, (ValueValidator)null, (string)null);
		}

		private static KeyCode ParseKeyCode(string value, KeyCode fallback)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_0029: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrWhiteSpace(value))
			{
				return fallback;
			}
			if (Enum.TryParse<KeyCode>(value.Trim(), ignoreCase: true, out KeyCode result))
			{
				return result;
			}
			return fallback;
		}
	}
}