Decompiled source of TextureSwapper Il2Cpp v0.3.0

Mods/TextureSwapper_Il2cpp.dll

Decompiled a month 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 System.Threading;
using Il2CppInterop.Runtime.Attributes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
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.3.0", "Bars", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("TextureSwapper_Il2cpp")]
[assembly: AssemblyConfiguration("Il2cpp")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+3b3f92c400d4caf495d98ad692c946a4c5e62d8e")]
[assembly: AssemblyProduct("TextureSwapper_Il2cpp")]
[assembly: AssemblyTitle("TextureSwapper_Il2cpp")]
[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");
				}
			}
			if (Input.GetKey((KeyCode)306) && Input.GetKeyDown((KeyCode)109) && ReplacementIndex != null)
			{
				((MelonBase)this).LoggerInstance.Msg("[TextureSwapper] Memory usage report:");
				ReplacementIndex.LogCurrentMemoryUsage();
			}
		}
	}
}
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;
			}
			Il2CppReferenceArray<Material> sharedMaterials = componentInChildren.sharedMaterials;
			if (sharedMaterials == null || ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length == 0)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("Target: " + ((Object)target).name);
			for (int i = 0; i < ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length; i++)
			{
				Material val = ((Il2CppArrayBase<Material>)(object)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={((Il2CppArrayBase<Material>)(object)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
{
	[RegisterTypeInIl2Cpp]
	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 InspectorUI(IntPtr ptr)
			: base(ptr)
		{
		}//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Expected O, but got Unknown
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Expected O, but got Unknown


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

		[HideFromIl2Cpp]
		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)
			{
				Il2CppArrayBase<Renderer> componentsInChildren = currentTarget.GetComponentsInChildren<Renderer>(true);
				if (componentsInChildren != null && componentsInChildren.Length > 0)
				{
					foreach (Renderer item in componentsInChildren)
					{
						if ((Object)(object)item == (Object)null)
						{
							continue;
						}
						Il2CppReferenceArray<Material> sharedMaterials = item.sharedMaterials;
						if (sharedMaterials == null || ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length == 0)
						{
							continue;
						}
						bool flag = false;
						foreach (Material item2 in (Il2CppArrayBase<Material>)(object)sharedMaterials)
						{
							if ((Object)(object)item2 == (Object)null)
							{
								continue;
							}
							for (int i = 0; i < PropertyNames.TexturePropertyNames.Length; i++)
							{
								string text = PropertyNames.TexturePropertyNames[i];
								if (item2.HasProperty(text) && (Object)(object)item2.GetTexture(text) != (Object)null)
								{
									flag = true;
									break;
								}
							}
							if (!flag)
							{
								continue;
							}
							cachedRenderableRenderers.Add(item);
							break;
						}
					}
				}
			}
			lastTarget = currentTarget;
		}

		private GameObject FindTargetFromRaycast(Ray ray)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			RaycastHit[] array = Il2CppArrayBase<RaycastHit>.op_Implicit((Il2CppArrayBase<RaycastHit>)(object)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;
			}
			Il2CppReferenceArray<Material> sharedMaterials = component.sharedMaterials;
			if (sharedMaterials == null || ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length != 1)
			{
				return false;
			}
			Material val = ((Il2CppArrayBase<Material>)(object)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;
				}
			}
			Il2CppArrayBase<Renderer> componentsInChildren = original.GetComponentsInChildren<Renderer>(false);
			foreach (Renderer item in componentsInChildren)
			{
				GameObject gameObject2 = ((Component)item).gameObject;
				if ((Object)(object)gameObject2 == (Object)(object)original || ShouldSkipObject(gameObject2))
				{
					continue;
				}
				return gameObject2;
			}
			if ((Object)(object)parent != (Object)null)
			{
				for (int i = 0; i < parent.childCount; i++)
				{
					GameObject gameObject3 = ((Component)parent.GetChild(i)).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, (Il2CppReferenceArray<GUILayoutOption>)null);
			GUILayout.Space(6f);
			if ((Object)(object)currentTarget == (Object)null)
			{
				GUILayout.Label("Look at an object to inspect its materials.", smallStyle, (Il2CppReferenceArray<GUILayoutOption>)null);
				GUILayout.EndScrollView();
				GUILayout.EndArea();
				return;
			}
			GUILayout.Label("Target: " + GetPath(currentTarget), smallStyle, (Il2CppReferenceArray<GUILayoutOption>)null);
			GUILayout.Space(4f);
			GUILayout.Label("Drop files named by material or material__Property: e.g. MyMat.png or MyMat__EmissionMap.png", smallStyle, (Il2CppReferenceArray<GUILayoutOption>)null);
			GUILayout.Label("For UI elements: SpriteName.png or TextureName.png", smallStyle, (Il2CppReferenceArray<GUILayoutOption>)null);
			Il2CppArrayBase<Renderer> componentsInChildren = currentTarget.GetComponentsInChildren<Renderer>(true);
			if (componentsInChildren == null || componentsInChildren.Length == 0)
			{
				GUILayout.Label("No Renderers found in hierarchy.", smallStyle, (Il2CppReferenceArray<GUILayoutOption>)null);
				GUILayout.EndScrollView();
				GUILayout.EndArea();
				return;
			}
			bool flag = false;
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Renderer val2 = componentsInChildren[i];
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Il2CppReferenceArray<Material> sharedMaterials = val2.sharedMaterials;
				if (sharedMaterials == null || ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length == 0)
				{
					continue;
				}
				flag = true;
				string relativePath = GetRelativePath(currentTarget, ((Component)val2).gameObject);
				if (relativePath != ".")
				{
					GUILayout.Space(6f);
					GUILayout.Label("Object: " + relativePath, headerStyle, (Il2CppReferenceArray<GUILayoutOption>)null);
				}
				for (int j = 0; j < ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length; j++)
				{
					Material val3 = ((Il2CppArrayBase<Material>)(object)sharedMaterials)[j];
					if ((Object)(object)val3 == (Object)null)
					{
						continue;
					}
					GUILayout.Space(4f);
					GUILayout.Label($"Material [{j}]: {((Object)val3).name}", smallStyle, (Il2CppReferenceArray<GUILayoutOption>)null);
					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((Il2CppReferenceArray<GUILayoutOption>)null);
					GUILayout.Label("Suggestion: Mods/TextureSwapper/" + ((Object)val3).name + ".png", smallStyle, (Il2CppReferenceArray<GUILayoutOption>)null);
					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((Il2CppReferenceArray<GUILayoutOption>)null);
							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, (Il2CppReferenceArray<GUILayoutOption>)null);
				GUILayout.EndScrollView();
				GUILayout.EndArea();
				return;
			}
			Il2CppArrayBase<Image> componentsInChildren2 = currentTarget.GetComponentsInChildren<Image>(true);
			Il2CppArrayBase<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, (Il2CppReferenceArray<GUILayoutOption>)null);
				GUILayout.Space(4f);
				GUILayout.Label("Drop files named by sprite/texture: e.g. MySprite.png or MyTexture.png", smallStyle, (Il2CppReferenceArray<GUILayoutOption>)null);
				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, (Il2CppReferenceArray<GUILayoutOption>)null);
							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((Il2CppReferenceArray<GUILayoutOption>)null);
							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, (Il2CppReferenceArray<GUILayoutOption>)null);
							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((Il2CppReferenceArray<GUILayoutOption>)null);
							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, (Il2CppReferenceArray<GUILayoutOption>)null);
			}
			GUILayout.EndScrollView();
			GUILayout.EndArea();
		}

		[HideFromIl2Cpp]
		public void ExportCurrentSelection()
		{
			if ((Object)(object)currentTarget == (Object)null)
			{
				return;
			}
			Il2CppArrayBase<Renderer> componentsInChildren = currentTarget.GetComponentsInChildren<Renderer>(true);
			Il2CppArrayBase<Image> componentsInChildren2 = currentTarget.GetComponentsInChildren<Image>(true);
			Il2CppArrayBase<RawImage> componentsInChildren3 = currentTarget.GetComponentsInChildren<RawImage>(true);
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("Target: " + GetPath(currentTarget));
			stringBuilder.AppendLine($"Renderer Count: {componentsInChildren?.Length ?? 0}");
			stringBuilder.AppendLine($"Image Count: {componentsInChildren2?.Length ?? 0}");
			stringBuilder.AppendLine($"RawImage Count: {componentsInChildren3?.Length ?? 0}");
			stringBuilder.AppendLine();
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Renderer val = componentsInChildren[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Il2CppReferenceArray<Material> sharedMaterials = val.sharedMaterials;
				if (sharedMaterials == null || ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length == 0)
				{
					continue;
				}
				string relativePath = GetRelativePath(currentTarget, ((Component)val).gameObject);
				stringBuilder.AppendLine("Object: " + relativePath);
				for (int j = 0; j < ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length; j++)
				{
					Material val2 = ((Il2CppArrayBase<Material>)(object)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 ===");
				for (int l = 0; l < componentsInChildren2.Length; l++)
				{
					Image val4 = componentsInChildren2[l];
					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 ===");
				for (int m = 0; m < componentsInChildren3.Length; m++)
				{
					RawImage val5 = componentsInChildren3[m];
					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
{
	[RegisterTypeInIl2Cpp]
	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 MaterialChangeMonitor(IntPtr ptr)
			: base(ptr)
		{
		}

		[HideFromIl2Cpp]
		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();
				ProcessMaterialChanges();
				ProcessImageChanges();
				DiscoverAndRegisterNewUI();
				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;
				}
				Il2CppReferenceArray<Material> sharedMaterials = key.sharedMaterials;
				if (sharedMaterials == null || value == null)
				{
					continue;
				}
				if (((Il2CppArrayBase<Material>)(object)sharedMaterials).Length != value.Length)
				{
					renderersWithChanges.Add(key);
					continue;
				}
				for (int i = 0; i < ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length; i++)
				{
					if (i >= value.Length || (Object)(object)((Il2CppArrayBase<Material>)(object)sharedMaterials)[i] != (Object)(object)value[i] || ((Object)(object)((Il2CppArrayBase<Material>)(object)sharedMaterials)[i] != (Object)null && (Object)(object)value[i] != (Object)null && ((Object)((Il2CppArrayBase<Material>)(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;
				}
				Il2CppReferenceArray<Material> sharedMaterials = renderersWithChange.sharedMaterials;
				if (sharedMaterials == null)
				{
					continue;
				}
				bool flag = false;
				for (int i = 0; i < ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length; i++)
				{
					Material val = ((Il2CppArrayBase<Material>)(object)sharedMaterials)[i];
					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] = ((IEnumerable<Material>)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;
			}
			Il2CppReferenceArray<Material> sharedMaterials = renderer.sharedMaterials;
			if (sharedMaterials != null)
			{
				originalMaterials[renderer] = ((IEnumerable<Material>)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;
			}
			Il2CppArrayBase<Renderer> val = Object.FindObjectsOfType<Renderer>(true);
			foreach (Renderer item in val)
			{
				if ((Object)(object)item != (Object)null && item.sharedMaterials != null && ((Il2CppArrayBase<Material>)(object)item.sharedMaterials).Length > 0)
				{
					RegisterRenderer(item);
				}
			}
			Il2CppArrayBase<Image> val2 = Object.FindObjectsOfType<Image>(true);
			foreach (Image item2 in val2)
			{
				if ((Object)(object)item2 != (Object)null && (Object)(object)item2.sprite != (Object)null)
				{
					RegisterImage(item2);
				}
			}
			Il2CppArrayBase<RawImage> val3 = Object.FindObjectsOfType<RawImage>(true);
			foreach (RawImage item3 in val3)
			{
				if ((Object)(object)item3 != (Object)null && (Object)(object)item3.texture != (Object)null)
				{
					RegisterRawImage(item3);
				}
			}
			if (Preferences.DebugEnabled)
			{
				MelonLogger.Msg($"[MaterialChangeMonitor] Registered {monitoredRenderers.Count} renderers, {monitoredImages.Count} images, {monitoredRawImages.Count} raw images for monitoring");
			}
		}

		private void DiscoverAndRegisterNewUI()
		{
			Il2CppArrayBase<Image> val = Object.FindObjectsOfType<Image>(true);
			if (val == null)
			{
				return;
			}
			for (int i = 0; i < val.Length; i++)
			{
				Image val2 = val[i];
				if ((Object)(object)val2 == (Object)null || originalSprites.ContainsKey(val2) || (Object)(object)val2.sprite == (Object)null)
				{
					continue;
				}
				MelonPreferences_Entry<bool> onlyScanActive = Preferences.OnlyScanActive;
				if (onlyScanActive != null && onlyScanActive.Value && (!((Component)val2).gameObject.activeInHierarchy || !((Behaviour)val2).enabled))
				{
					continue;
				}
				RegisterImage(val2);
				try
				{
					if (ReplacementApplicator.ApplyToImage(val2, replacementIndex) && Preferences.DebugEnabled)
					{
						MelonLogger.Msg("[MaterialChangeMonitor] Applied replacements to newly discovered Image '" + ((Object)((Component)val2).gameObject).name + "'");
					}
				}
				catch (Exception ex)
				{
					if (Preferences.DebugEnabled)
					{
						GameObject gameObject = ((Component)val2).gameObject;
						MelonLogger.Warning("[MaterialChangeMonitor] Error applying to new Image '" + ((gameObject != null) ? ((Object)gameObject).name : null) + "': " + ex.Message);
					}
				}
			}
		}
	}
	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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			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;
				}
				Texture texture3 = material.GetTexture(text);
				Texture2D val2 = (Texture2D)(object)((texture3 is Texture2D) ? texture3 : null);
				if ((Object)(object)val2 == (Object)(object)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 bool ApplyTexturePreserveTiling(Material material, string prop, Texture2D replacement)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			Texture texture = material.GetTexture(prop);
			Texture2D val = (Texture2D)(object)((texture is Texture2D) ? texture : null);
			if ((Object)(object)val == (Object)(object)replacement)
			{
				return false;
			}
			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;
				}
			}
			return true;
		}

		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)
			{
				if ((Object)(object)sprite != (Object)null && (Object)(object)sprite.texture == (Object)(object)texture2)
				{
					return false;
				}
				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))
			{
				if ((Object)(object)rawImage.texture == (Object)(object)texture2)
				{
					return false;
				}
				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;

			public long EstimatedMemoryBytes;

			public TextureFormat OriginalFormat;

			public bool IsCompressed;
		}

		private struct TextureFormatInfo
		{
			public TextureFormat Format;

			public bool CanCompress;

			public string Reason;
		}

		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 long totalMemoryUsageBytes = 0L;

		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))
			{
				if (Preferences.DebugEnabled)
				{
					MelonLogger.Msg("[ReplacementIndex] Base directory does not exist: " + baseDirectory);
				}
				return;
			}
			int num = 0;
			int num2 = 0;
			while (num2 < 3 && num == 0)
			{
				num2++;
				if (Preferences.DebugEnabled && num2 > 1)
				{
					MelonLogger.Msg($"[ReplacementIndex] Load attempt {num2}/{3}");
				}
				try
				{
					List<string> list = Directory.EnumerateFiles(baseDirectory, "*.*", SearchOption.AllDirectories).ToList();
					if (Preferences.DebugEnabled)
					{
						MelonLogger.Msg($"[ReplacementIndex] Found {list.Count} total files in directory");
					}
					foreach (string item in list)
					{
						try
						{
							if (item.IndexOf("_Exports", StringComparison.OrdinalIgnoreCase) >= 0)
							{
								continue;
							}
							string extension = Path.GetExtension(item);
							if (!SupportedExtensions.Contains(extension))
							{
								continue;
							}
							if (!File.Exists(item))
							{
								if (Preferences.DebugEnabled)
								{
									MelonLogger.Msg("[ReplacementIndex] File no longer exists: " + item);
								}
								continue;
							}
							string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item);
							LoadOrReplace(fileNameWithoutExtension, item);
							num++;
							if (Preferences.DebugEnabled)
							{
								MelonLogger.Msg("[ReplacementIndex] Preloaded '" + fileNameWithoutExtension + "' from '" + item + "'");
							}
						}
						catch (Exception ex)
						{
							MelonLogger.Error("[ReplacementIndex] Error processing file '" + item + "': " + ex.Message);
						}
					}
				}
				catch (Exception ex2)
				{
					MelonLogger.Error("[ReplacementIndex] Error enumerating files in '" + baseDirectory + "': " + ex2.Message);
					if (num2 < 3)
					{
						Thread.Sleep(500);
					}
				}
				if (num == 0 && num2 < 3)
				{
					if (Preferences.DebugEnabled)
					{
						MelonLogger.Msg($"[ReplacementIndex] No files loaded on attempt {num2}, retrying...");
					}
					Thread.Sleep(1000);
				}
			}
			if (Preferences.DebugEnabled)
			{
				MelonLogger.Msg($"[ReplacementIndex] Finished loading after {num2} attempt(s). Loaded {num} files. Keys={keyToTexture.Count} materialNames={materialNameToTexture.Count} materialProperties={materialPropertyToTexture.Count}");
				LogMemoryUsage();
			}
			else if (num > 0)
			{
				MelonLogger.Msg($"[ReplacementIndex] Loaded {num} texture replacement files");
				LogMemoryUsage();
			}
			if (num == 0)
			{
				MelonLogger.Msg("[ReplacementIndex] Warning: No texture files found in '" + baseDirectory + "'. Make sure your .png/.jpg/.jpeg files are in the TextureSwapper mod folder.");
			}
		}

		private void LoadOrReplace(string key, string filePath)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Texture2D val = LoadTextureFromFile(filePath);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				long num = 0L;
				if (keyToTexture.TryGetValue(key, out var value) && value != null)
				{
					if ((Object)(object)value.Texture != (Object)null)
					{
						num = value.EstimatedMemoryBytes;
						Object.Destroy((Object)(object)value.Texture);
					}
					value.FilePath = filePath;
					value.Texture = val;
				}
				else
				{
					keyToTexture[key] = new LoadedTexture
					{
						FilePath = filePath,
						Texture = val
					};
				}
				LoadedTexture loadedTexture = keyToTexture[key];
				loadedTexture.EstimatedMemoryBytes = EstimateTextureMemoryUsage(val);
				loadedTexture.OriginalFormat = val.format;
				loadedTexture.IsCompressed = IsCompressedFormat(val.format);
				totalMemoryUsageBytes = totalMemoryUsageBytes - num + loadedTexture.EstimatedMemoryBytes;
				string[] array = key.Split(new string[1] { "__" }, StringSplitOptions.None);
				if (array.Length == 1)
				{
					string key2 = NormalizeMaterialName(array[0]);
					materialNameToTexture[key2] = loadedTexture;
				}
				else if (array.Length >= 2)
				{
					string item = NormalizeMaterialName(array[0]);
					string item2 = array[1];
					materialPropertyToTexture[(item, item2)] = loadedTexture;
				}
				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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				byte[] array = File.ReadAllBytes(filePath);
				TextureFormatInfo textureFormatInfo = DetermineTextureFormat(array, filePath);
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true);
				if (!ImageConversion.LoadImage(val, Il2CppStructArray<byte>.op_Implicit(array), false))
				{
					Object.Destroy((Object)(object)val);
					val = new Texture2D(2, 2, (TextureFormat)4, true);
					if (!ImageConversion.LoadImage(val, Il2CppStructArray<byte>.op_Implicit(array), false))
					{
						Object.Destroy((Object)(object)val);
						return null;
					}
				}
				val = ApplyTextureSizeLimit(val, filePath);
				if ((Object)(object)val == (Object)null)
				{
					return null;
				}
				MelonPreferences_Entry<bool> useDXTCompression = Preferences.UseDXTCompression;
				if (useDXTCompression != null && useDXTCompression.Value && textureFormatInfo.CanCompress)
				{
					try
					{
						bool flag = ((Texture)val).width >= 4 && ((Texture)val).height >= 4 && ((Texture)val).width % 4 == 0 && ((Texture)val).height % 4 == 0;
						bool flag2 = SystemInfo.SupportsTextureFormat((TextureFormat)10);
						bool flag3 = SystemInfo.SupportsTextureFormat((TextureFormat)12);
						bool flag4 = flag2 || flag3;
						if (flag && flag4)
						{
							val.Compress(true);
							if (Preferences.DebugEnabled)
							{
								MelonLogger.Msg($"[ReplacementIndex] Compressed texture '{filePath}' using {val.format}");
							}
						}
						else if (Preferences.DebugEnabled)
						{
							string text = ((!flag) ? "invalid size for DXT (must be multiples of 4 and >= 4x4)" : "DXT not supported on this platform");
							MelonLogger.Msg("[ReplacementIndex] Skipped compression for '" + filePath + "': " + text);
						}
					}
					catch (Exception ex)
					{
						if (Preferences.DebugEnabled)
						{
							MelonLogger.Warning("[ReplacementIndex] Compression failed for '" + filePath + "': " + ex.Message);
						}
					}
				}
				((Object)val).name = Path.GetFileNameWithoutExtension(filePath);
				((Texture)val).wrapMode = (TextureWrapMode)0;
				((Texture)val).filterMode = (FilterMode)1;
				Object.DontDestroyOnLoad((Object)(object)val);
				if (Preferences.DebugEnabled)
				{
					string text2 = (IsCompressedFormat(val.format) ? "compressed" : "uncompressed");
					MelonLogger.Msg($"[ReplacementIndex] Loaded '{filePath}' as {val.format} ({text2}) - {((Texture)val).width}x{((Texture)val).height}");
				}
				return val;
			}
			catch (Exception ex2)
			{
				MelonLogger.Error("[ReplacementIndex] Error reading '" + filePath + "': " + ex2.Message);
				return null;
			}
		}

		private static TextureFormatInfo DetermineTextureFormat(byte[] imageData, string filePath)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			MelonPreferences_Entry<bool> useDXTCompression = Preferences.UseDXTCompression;
			TextureFormatInfo result;
			if (useDXTCompression == null || !useDXTCompression.Value)
			{
				result = default(TextureFormatInfo);
				result.Format = (TextureFormat)4;
				result.CanCompress = false;
				result.Reason = "DXT compression disabled";
				return result;
			}
			string text = Path.GetFileNameWithoutExtension(filePath).ToLowerInvariant();
			bool flag = text.Contains("normal") || text.Contains("bump") || text.Contains("_n") || text.EndsWith("n") || text.Contains("bumpmap") || text.Contains("normalmap");
			bool flag2 = HasSignificantAlpha(imageData);
			if (flag)
			{
				result = default(TextureFormatInfo);
				result.Format = (TextureFormat)12;
				result.CanCompress = true;
				result.Reason = "normal map detected";
				return result;
			}
			if (flag2)
			{
				result = default(TextureFormatInfo);
				result.Format = (TextureFormat)12;
				result.CanCompress = true;
				result.Reason = "alpha channel detected";
				return result;
			}
			result = default(TextureFormatInfo);
			result.Format = (TextureFormat)10;
			result.CanCompress = true;
			result.Reason = "opaque image";
			return result;
		}

		private static bool HasSignificantAlpha(byte[] imageData)
		{
			if (imageData.Length > 8 && imageData[0] == 137 && imageData[1] == 80 && imageData[2] == 78 && imageData[3] == 71)
			{
				for (int i = 8; i < Math.Min(imageData.Length - 4, 100); i++)
				{
					if (imageData[i] == 73 && imageData[i + 1] == 72 && imageData[i + 2] == 68 && imageData[i + 3] == 82 && i + 12 < imageData.Length)
					{
						byte b = imageData[i + 12];
						return b == 4 || b == 6;
					}
				}
			}
			return false;
		}

		private static Texture2D ApplyTextureSizeLimit(Texture2D tex, string filePath)
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected O, but got Unknown
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)tex == (Object)null)
			{
				return null;
			}
			int num = Preferences.MaxTextureSize?.Value ?? 2048;
			if (num <= 0)
			{
				return tex;
			}
			if (((Texture)tex).width <= num && ((Texture)tex).height <= num)
			{
				return tex;
			}
			try
			{
				float num2 = (float)((Texture)tex).width / (float)((Texture)tex).height;
				int num3;
				int num4;
				if (((Texture)tex).width > ((Texture)tex).height)
				{
					num3 = num;
					num4 = Mathf.RoundToInt((float)num / num2);
				}
				else
				{
					num4 = num;
					num3 = Mathf.RoundToInt((float)num * num2);
				}
				num3 = Mathf.Max(1, num3);
				num4 = Mathf.Max(1, num4);
				Texture2D val = new Texture2D(num3, num4, tex.format, true);
				Color[] array = Il2CppArrayBase<Color>.op_Implicit((Il2CppArrayBase<Color>)(object)tex.GetPixels());
				Color[] array2 = (Color[])(object)new Color[num3 * num4];
				for (int i = 0; i < num4; i++)
				{
					for (int j = 0; j < num3; j++)
					{
						float num5 = (float)j / (float)(num3 - 1);
						float num6 = (float)i / (float)(num4 - 1);
						int num7 = Mathf.RoundToInt(num5 * (float)(((Texture)tex).width - 1));
						int num8 = Mathf.RoundToInt(num6 * (float)(((Texture)tex).height - 1));
						array2[i * num3 + j] = array[num8 * ((Texture)tex).width + num7];
					}
				}
				val.SetPixels(Il2CppStructArray<Color>.op_Implicit(array2));
				val.Apply();
				((Object)val).name = ((Object)tex).name;
				((Texture)val).wrapMode = ((Texture)tex).wrapMode;
				((Texture)val).filterMode = ((Texture)tex).filterMode;
				Object.Destroy((Object)(object)tex);
				if (Preferences.DebugEnabled)
				{
					MelonLogger.Msg($"[ReplacementIndex] Resized texture '{filePath}' from {((Texture)tex).width}x{((Texture)tex).height} to {num3}x{num4}");
				}
				return val;
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[ReplacementIndex] Failed to resize texture '" + filePath + "': " + ex.Message);
				return tex;
			}
		}

		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");
			}
		}

		private static long EstimateTextureMemoryUsage(Texture2D texture)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected I4, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Invalid comparison between Unknown and I4
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Invalid comparison between Unknown and I4
			//IL_009c: 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)
			//IL_00b5: Expected I4, but got Unknown
			if ((Object)(object)texture == (Object)null)
			{
				return 0L;
			}
			int num = Math.Max(1, ((Texture)texture).mipmapCount);
			TextureFormat format = texture.format;
			if (1 == 0)
			{
			}
			int? num2 = (format - 1) switch
			{
				3 => 4, 
				4 => 4, 
				2 => 3, 
				0 => 1, 
				_ => null, 
			};
			if (1 == 0)
			{
			}
			int? num3 = num2;
			if (1 == 0)
			{
			}
			(int, int, int)? tuple = (((int)format == 10) ? new(int, int, int)?((4, 4, 8)) : (((int)format == 12) ? new(int, int, int)?((4, 4, 16)) : ((format - 24) switch
			{
				2 => (4, 4, 8), 
				3 => (4, 4, 16), 
				0 => (4, 4, 16), 
				1 => (4, 4, 16), 
				_ => null, 
			})));
			if (1 == 0)
			{
			}
			(int, int, int)? tuple2 = tuple;
			long num4 = 0L;
			int num5 = Math.Max(1, ((Texture)texture).width);
			int num6 = Math.Max(1, ((Texture)texture).height);
			for (int i = 0; i < num; i++)
			{
				if (num3.HasValue)
				{
					num4 += (long)num5 * (long)num6 * num3.Value;
				}
				else if (tuple2.HasValue)
				{
					int item = tuple2.Value.Item1;
					int item2 = tuple2.Value.Item2;
					int item3 = tuple2.Value.Item3;
					int num7 = Math.Max(1, (num5 + item - 1) / item);
					int num8 = Math.Max(1, (num6 + item2 - 1) / item2);
					num4 += (long)num7 * (long)num8 * item3;
				}
				else
				{
					num4 += (long)num5 * (long)num6 * 4;
				}
				num5 = Math.Max(1, num5 >> 1);
				num6 = Math.Max(1, num6 >> 1);
			}
			return num4;
		}

		private static bool IsCompressedFormat(TextureFormat format)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			if ((int)format == 10 || (int)format == 12 || format - 24 <= 3)
			{
				return true;
			}
			return false;
		}

		private void LogMemoryUsage()
		{
			if (keyToTexture.Count == 0)
			{
				return;
			}
			long num = 0L;
			long num2 = 0L;
			int num3 = 0;
			int num4 = 0;
			foreach (KeyValuePair<string, LoadedTexture> item in keyToTexture)
			{
				LoadedTexture value = item.Value;
				if (!((Object)(object)value?.Texture == (Object)null))
				{
					if (value.IsCompressed)
					{
						num += value.EstimatedMemoryBytes;
						num3++;
					}
					else
					{
						num2 += value.EstimatedMemoryBytes;
						num4++;
					}
				}
			}
			MelonPreferences_Entry<bool> useDXTCompression = Preferences.UseDXTCompression;
			string text = ((useDXTCompression != null && useDXTCompression.Value) ? "enabled" : "disabled");
			double num5 = (double)totalMemoryUsageBytes / 1048576.0;
			MelonLogger.Msg($"[ReplacementIndex] Memory usage: {num5:F1} MB total ({num3} compressed: {(double)num / 1048576.0:F1} MB, {num4} uncompressed: {(double)num2 / 1048576.0:F1} MB) - DXT compression {text}");
		}

		public void LogCurrentMemoryUsage()
		{
			LogMemoryUsage();
		}
	}
	[RegisterTypeInIl2Cpp]
	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;

		public MaterialScanner(IntPtr ptr)
			: base(ptr)
		{
		}

		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;
				}
			}
			Il2CppReferenceArray<Material> sharedMaterials = renderer.sharedMaterials;
			if (sharedMaterials == null || ((Il2CppArrayBase<Material>)(object)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;
		}

		[HideFromIl2Cpp]
		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");
			}
		}

		[HideFromIl2Cpp]
		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();
			Il2CppReferenceArray<Camera> allCameras = Camera.allCameras;
			if (allCameras != null)
			{
				cameraCache.AddRange((IEnumerable<Camera>)allCameras);
			}
		}

		private void DiscoverAndFilterRenderers()
		{
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			Il2CppArrayBase<Renderer> val = Object.FindObjectsOfType<Renderer>(true);
			totalRenderersFound = val?.Length ?? 0;
			if (val == null || val.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 < val.Length)
			{
				rendererBuffer.Capacity = val.Length;
			}
			for (int i = 0; i < val.Length; i++)
			{
				Renderer val2 = val[i];
				if (ShouldSkipRenderer(val2) || (flag4 && !IsRendererActive(val2)) || (flag2 && !IsRendererVisible(val2)))
				{
					continue;
				}
				if (flag3)
				{
					Bounds bounds = val2.bounds;
					float num2 = SquaredDistanceToNearestCamera(((Bounds)(ref bounds)).center);
					if (num2 > num)
					{
						continue;
					}
				}
				rendererBuffer.Add(val2);
			}
		}

		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;
				}
			}
			Il2CppReferenceArray<Material> sharedMaterials = renderer.sharedMaterials;
			if (sharedMaterials == null)
			{
				return false;
			}
			bool result = false;
			for (int i = 0; i < ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length; i++)
			{
				Material val = ((Il2CppArrayBase<Material>)(object)sharedMaterials)[i];
				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();
		}
	}
	[RegisterTypeInIl2Cpp]
	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;

		public UITextureScanner(IntPtr ptr)
			: base(ptr)
		{
		}

		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;
			}
			GameObje