Decompiled source of OmniReplacer v1.0.0

BepInEx/plugins/OmniReplacer.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
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: AssemblyTitle("UniversalReplacer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("UniversalReplacer")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("97fbeecd-180d-4c38-89fb-1cb6096934fd")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
public static class FolderBootstrap
{
	public static bool Ensure(string parent, string folder, out string path, string readme = null)
	{
		path = null;
		bool result = false;
		try
		{
			string text = Path.Combine(parent, folder);
			if (!Directory.Exists(text))
			{
				Directory.CreateDirectory(text);
				result = true;
			}
			path = text;
		}
		catch (Exception)
		{
			try
			{
				path = Path.Combine(Paths.PluginPath, folder);
			}
			catch
			{
				path = folder;
			}
			try
			{
				Directory.CreateDirectory(path);
			}
			catch
			{
			}
		}
		if (!string.IsNullOrEmpty(readme))
		{
			try
			{
				string path2 = Path.Combine(path, "README.txt");
				if (!File.Exists(path2))
				{
					File.WriteAllText(path2, readme);
				}
			}
			catch
			{
			}
		}
		return result;
	}
}
namespace UniversalReplacer;

public class ConfigManager
{
	private class RawRule
	{
		public string Root;

		public string RootContains;

		public string Path;

		public string PathContains;

		public string ComponentType;

		public string PropertyName;

		public string RootNotContains;

		public string PathNotContains;

		public string Scene;

		public string SceneContains;

		public JToken Value;

		public int MaterialIndex = -1;

		public bool Enabled = true;

		public bool AddComponent = false;
	}

	private class ParseResult
	{
		public List<RawRule> Rules;

		public float EnforceSeconds;

		public KeyCode ReloadKey;

		public KeyCode AssetsKey;

		public JToken SceneSettings;
	}

	private const float ReloadDebounceSeconds = 0.5f;

	private readonly string _path;

	private FileSystemWatcher _watcher;

	private float _reloadAt = -1f;

	private int _reloadVersion;

	public Action OnConfigReloaded;

	public List<TargetRule> Rules { get; private set; } = new List<TargetRule>();

	public Dictionary<string, List<TargetRule>> RulesByRoot { get; } = new Dictionary<string, List<TargetRule>>(StringComparer.OrdinalIgnoreCase);

	public List<KeyValuePair<string, List<TargetRule>>> RulesByRootContains { get; } = new List<KeyValuePair<string, List<TargetRule>>>();

	public List<TargetRule> RulesByPathContains { get; } = new List<TargetRule>();

	public List<TargetRule> RulesGlobal { get; } = new List<TargetRule>();

	public float EnforceIntervalSeconds { get; private set; } = 1f;

	public JToken SceneSettings { get; private set; }

	public KeyCode ReloadConfigKey { get; private set; } = (KeyCode)287;

	public KeyCode ReloadAssetsKey { get; private set; } = (KeyCode)288;

	public ConfigManager(string path)
	{
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		_path = path;
		ReloadConfig(trigger: false);
		SetupWatcher();
	}

	public void RebakeRules()
	{
		foreach (TargetRule rule in Rules)
		{
			try
			{
				rule.BakeValue();
			}
			catch
			{
			}
		}
	}

	internal void Tick()
	{
		if (_reloadAt >= 0f && Time.time >= _reloadAt)
		{
			_reloadAt = -1f;
			ReloadConfig(trigger: true);
		}
	}

	public void ReloadConfig(bool trigger)
	{
		try
		{
			string directoryName = Path.GetDirectoryName(_path);
			if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))
			{
				Directory.CreateDirectory(directoryName);
			}
			if (!File.Exists(_path))
			{
				WriteTemplate(_path);
			}
			string path = Path.Combine(directoryName ?? "", "SceneSettings.json");
			if (!File.Exists(path))
			{
				WriteSceneSettingsTemplate(path);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Erro ao criar template: " + ex.Message));
			return;
		}
		string content;
		try
		{
			content = File.ReadAllText(_path);
		}
		catch (Exception ex2)
		{
			Plugin.Log.LogError((object)("Erro ao ler JSON: " + ex2.Message));
			return;
		}
		int version = ++_reloadVersion;
		Task.Run(delegate
		{
			ParseResult res;
			try
			{
				res = ParseRules(content, _path);
			}
			catch (Exception ex3)
			{
				Exception ex4 = ex3;
				Exception e = ex4;
				Plugin.EnqueueAction(delegate
				{
					Plugin.Log.LogError((object)("Erro ao ler JSON: " + e.Message));
				});
				return;
			}
			Plugin.EnqueueAction(delegate
			{
				if (version == _reloadVersion)
				{
					BuildRules(res, trigger);
				}
			});
		});
	}

	private static void WriteTemplate(string path)
	{
		var anon = new
		{
			Instructions = "Place your rules in the TargetRules array. Supported targets: Renderer, Graphic (UI), ParticleSystem, AudioSource, Light, and ANY component via reflection (ComponentType + PropertyName). Scene = scene name / build index limits the rule to that scene; SceneContains = substring match. Root/Path accept a JSON array of exact names — one rule hits every listed object. Append \"[n]\" to a Root/Path name to pick the n-th same-named sibling (0-based, for duplicates). RootContains/PathContains accept \"a|b\" or a JSON array (OR). RootNotContains/PathNotContains exclude matches. Path/PathContains support \"A//B//C\" chained deep search. PropertyName supports deep member paths with indexing: \"subtitles.lines[0].line\", \"subtitles.lines[*].line\" writes every element, \"subtitles.lines[+]\" appends element(s), \"subtitles.lines[-2]\"/\"[-]\" removes an element, and assigning a JSON array to a collection member replaces it. PropertyName \"mesh\" on MeshFilter or SkinnedMeshRenderer swaps geometry: Value = a file in Meshes/ (\".obj\" parsed at runtime, or \".bundle\" for a real Unity Mesh incl. skinning). \"Enabled\": false disables a rule without deleting it. \"AddComponent\": true creates the ComponentType on the object when it doesn't exist. Optional top-level \"SceneSettings\" edits RenderSettings: fog/fogColor/fogMode/fogDensity/fogStartDistance/fogEndDistance, ambientMode/ambientIntensity/ambientLight/ambientSkyColor/ambientEquatorColor/ambientGroundColor, subtractiveShadowColor, haloStrength, skybox (a .bundle Material from Meshes/), skyboxImage (a panorama png/jpg from Textures/ → Skybox/Panoramic), skyboxTint/skyboxExposure/skyboxRotation. An object applies to every scene; an array applies per-scene, each block gated by its own Scene/SceneContains. SceneSettings may also live in a separate SceneSettings.json next to this file (it overrides the inline key).",
			EnforceIntervalSeconds = 1.0,
			Hotkeys = new
			{
				ReloadConfig = "F6",
				ReloadAssets = "F7"
			},
			Examples = new object[15]
			{
				new
				{
					Description = "Change an Image color to Red",
					Root = "PlayerUI",
					RootContains = "",
					PathContains = "",
					ComponentType = "Image",
					PropertyName = "color",
					Value = new
					{
						r = 1.0,
						g = 0.0,
						b = 0.0,
						a = 1.0
					}
				},
				new
				{
					Description = "Change a Renderer material emission color",
					Root = "",
					RootContains = "",
					PathContains = "Boss/Body",
					ComponentType = "Renderer",
					PropertyName = "_EmissionColor",
					Value = new
					{
						r = 0.0,
						g = 1.0,
						b = 0.0,
						a = 0.0
					}
				},
				new
				{
					Description = "Change particle intensity",
					Root = "",
					RootContains = "",
					PathContains = "Explosion",
					ComponentType = "ParticleSystem",
					PropertyName = "startColor",
					Value = new
					{
						r = 1.0,
						g = 1.0,
						b = 0.0,
						a = 1.0
					}
				},
				new
				{
					Description = "Only in one scene (name or build index)",
					Scene = "Intro",
					SceneContains = "",
					RootContains = "",
					PathContains = "HUD",
					ComponentType = "Text",
					PropertyName = "color",
					Value = new
					{
						r = 1.0,
						g = 0.0,
						b = 0.0,
						a = 1.0
					}
				},
				new
				{
					Description = "Deep member path: set line 2 of a SubtitledAudioSource",
					Root = "",
					RootContains = "",
					PathContains = "SubtitleSource",
					ComponentType = "SubtitledAudioSource",
					PropertyName = "subtitles.lines[2].subtitle",
					Value = "New subtitle text"
				},
				new
				{
					Description = "Append a new subtitle line ([+]) to a SubtitledAudioSource",
					Root = "",
					RootContains = "",
					PathContains = "SubtitleSource",
					ComponentType = "SubtitledAudioSource",
					PropertyName = "subtitles.lines[+]",
					Value = new
					{
						subtitle = "Extra line",
						time = 3.5
					}
				},
				new
				{
					Description = "Replace all subtitle lines on a SubtitledAudioSource",
					Root = "",
					RootContains = "",
					PathContains = "SubtitleSource",
					ComponentType = "SubtitledAudioSource",
					PropertyName = "subtitles.lines",
					Value = new[]
					{
						new
						{
							subtitle = "Line A",
							time = 0.0
						},
						new
						{
							subtitle = "Line B",
							time = 1.5
						}
					}
				},
				new
				{
					Description = "Exclusions: red tint on enemies except bosses",
					Root = "",
					RootContains = "Enemy",
					RootNotContains = "Boss",
					PathContains = "",
					ComponentType = "SpriteRenderer",
					PropertyName = "color",
					Value = new
					{
						r = 1.0,
						g = 0.0,
						b = 0.0,
						a = 1.0
					}
				},
				new
				{
					Description = "Swap a mesh: .obj or .bundle from Meshes/ (SkinnedMeshRenderer 'mesh' maps to sharedMesh)",
					Root = "",
					RootContains = "",
					PathContains = "Weapon",
					ComponentType = "MeshFilter",
					PropertyName = "mesh",
					Value = "sword.obj"
				},
				new
				{
					Description = "One rule, many objects: Root/Path accept arrays of EXACT names (no substring over-matching)",
					Root = new string[3] { "Cube 1", "Cube 5", "Cube 8" },
					RootContains = "",
					PathContains = "",
					ComponentType = "Renderer",
					PropertyName = "material.color",
					Value = new
					{
						r = 1.0,
						g = 0.0,
						b = 0.0,
						a = 1.0
					}
				},
				new
				{
					Description = "Multiple deep objects: list each full child path under the root",
					Root = "Level1",
					Path = new string[2] { "Props/Cubes/Cube 1", "Props/Cubes/Cube 5" },
					ComponentType = "Renderer",
					PropertyName = "material.color",
					Value = new
					{
						r = 0.0,
						g = 0.0,
						b = 1.0,
						a = 1.0
					}
				},
				new
				{
					Description = "Same object in several roots: Root array x single Path = cross product (A/Cube and B/Cube)",
					Root = new string[2] { "A", "B" },
					Path = "Cube",
					ComponentType = "Renderer",
					PropertyName = "material.color",
					Value = new
					{
						r = 0.0,
						g = 1.0,
						b = 0.0,
						a = 1.0
					}
				},
				new
				{
					Description = "PathContains array = substring OR (looser; can over-match similar names)",
					Root = "",
					RootContains = "",
					PathContains = new string[2] { "Props/Cubes/Cube 1", "Props/Cubes/Cube 5" },
					ComponentType = "Renderer",
					PropertyName = "material.color",
					Value = new
					{
						r = 1.0,
						g = 1.0,
						b = 0.0,
						a = 1.0
					}
				},
				new
				{
					Description = "Disable a rule without deleting it",
					Enabled = false,
					Root = "",
					RootContains = "",
					PathContains = "HUD",
					ComponentType = "Image",
					PropertyName = "color",
					Value = new
					{
						r = 0.5,
						g = 0.5,
						b = 0.5,
						a = 1.0
					}
				},
				new
				{
					Description = "Random pick: array Value on a non-collection member chooses one entry per apply",
					Root = "",
					RootContains = "",
					PathContains = "Pickup",
					ComponentType = "AudioSource",
					PropertyName = "clip",
					Value = new string[3] { "snd_a.wav", "snd_b.wav", "snd_c.wav" }
				}
			},
			TargetRules = new List<TargetRule>()
		};
		File.WriteAllText(path, JsonConvert.SerializeObject((object)anon, (Formatting)1));
	}

	private static void WriteSceneSettingsTemplate(string path)
	{
		object[] array = new object[4]
		{
			new
			{
				Instructions = "Each object in this array applies RenderSettings edits to scenes matching its Scene (name/buildIndex/path suffix) or SceneContains (substring) key. Supported keys: fog, fogColor ({r,g,b,a} or \"#RRGGBB\"), fogMode (Linear/Exponential/ExponentialSquared), fogDensity, fogStartDistance, fogEndDistance, ambientMode (Skybox/Trilight/Flat/Custom), ambientIntensity, ambientLight, ambientSkyColor, ambientEquatorColor, ambientGroundColor, subtractiveShadowColor, haloStrength, skybox (.bundle Material in Meshes/), skyboxImage (panorama png/jpg in Textures/), skyboxTint, skyboxExposure, skyboxRotation."
			},
			new
			{
				Scene = "YourSceneName",
				fog = true,
				fogColor = new
				{
					r = 0.4,
					g = 0.1,
					b = 0.5,
					a = 1.0
				},
				fogMode = "Exponential",
				fogDensity = 0.05,
				ambientIntensity = 0.3,
				skyboxImage = "mysky.png",
				skyboxTint = new
				{
					r = 1.0,
					g = 0.3,
					b = 0.3,
					a = 1.0
				}
			},
			new
			{
				_Description = "Linear fog by distance + trilight ambient, only in scenes whose name contains 'cybergrind'",
				SceneContains = "cybergrind",
				fog = true,
				fogColor = "#8040A0",
				fogMode = "Linear",
				fogStartDistance = 10.0,
				fogEndDistance = 120.0,
				ambientMode = "Trilight",
				ambientSkyColor = new
				{
					r = 0.5,
					g = 0.2,
					b = 0.6,
					a = 1.0
				},
				ambientEquatorColor = new
				{
					r = 0.2,
					g = 0.1,
					b = 0.3,
					a = 1.0
				},
				ambientGroundColor = new
				{
					r = 0.05,
					g = 0.02,
					b = 0.1,
					a = 1.0
				}
			},
			new
			{
				_Description = "Skybox from a .bundle material + rotation/exposure tweaks",
				Scene = "AnotherScene",
				skybox = "nightsky.bundle",
				skyboxRotation = 45.0,
				skyboxExposure = 1.6,
				haloStrength = 0.5
			}
		};
		File.WriteAllText(path, JsonConvert.SerializeObject((object)array, (Formatting)1));
	}

	private static ParseResult ParseRules(string content, string configPath)
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Invalid comparison between Unknown and I4
		//IL_04fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0501: Invalid comparison between Unknown and I4
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_041a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0421: Invalid comparison between Unknown and I4
		//IL_04b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_04b9: Invalid comparison between Unknown and I4
		JObject val = JObject.Parse(content);
		ParseResult parseResult = new ParseResult
		{
			Rules = new List<RawRule>(),
			EnforceSeconds = 1f,
			ReloadKey = (KeyCode)287,
			AssetsKey = (KeyCode)288
		};
		JToken val2 = val["EnforceIntervalSeconds"] ?? val["enforceIntervalSeconds"];
		if (val2 != null && (int)val2.Type != 10)
		{
			try
			{
				parseResult.EnforceSeconds = Math.Max(0f, Extensions.Value<float>((IEnumerable<JToken>)val2));
			}
			catch
			{
			}
		}
		JToken val3 = val["Hotkeys"] ?? val["hotkeys"];
		JObject val4 = (JObject)(object)((val3 is JObject) ? val3 : null);
		if (val4 != null)
		{
			parseResult.ReloadKey = ParseKey(val4["ReloadConfig"] ?? val4["reloadConfig"], parseResult.ReloadKey);
			parseResult.AssetsKey = ParseKey(val4["ReloadAssets"] ?? val4["reloadAssets"], parseResult.AssetsKey);
		}
		JToken val5 = val["SceneSettings"] ?? val["sceneSettings"];
		if (val5 is JObject || val5 is JArray)
		{
			parseResult.SceneSettings = val5;
		}
		try
		{
			string path = Path.Combine(Path.GetDirectoryName(configPath) ?? "", "SceneSettings.json");
			if (File.Exists(path))
			{
				JToken val6 = JToken.Parse(File.ReadAllText(path));
				if (val6 is JObject || val6 is JArray)
				{
					parseResult.SceneSettings = val6;
				}
				else
				{
					Plugin.Log.LogWarning((object)"SceneSettings.json: expected an object or array — ignored.");
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("SceneSettings.json: " + ex.Message));
		}
		JToken val7 = val["TargetRules"] ?? val["targetRules"];
		JArray val8 = (JArray)(object)((val7 is JArray) ? val7 : null);
		if (val8 == null)
		{
			return parseResult;
		}
		foreach (JToken item in val8)
		{
			JToken t = item[(object)"Root"];
			JToken t2 = item[(object)"Path"];
			List<string> list = ListValues(t);
			List<string> list2 = ListValues(t2);
			int num = list?.Count ?? 1;
			int num2 = list2?.Count ?? 1;
			for (int i = 0; i < num; i++)
			{
				for (int j = 0; j < num2; j++)
				{
					JToken val9 = ((num == 1 && num2 == 1) ? item : item.DeepClone());
					if (list != null)
					{
						val9[(object)"Root"] = JToken.op_Implicit(list[i]);
					}
					if (list2 != null)
					{
						val9[(object)"Path"] = JToken.op_Implicit(list2[j]);
					}
					RawRule rawRule = new RawRule
					{
						Root = (((object)val9[(object)"Root"])?.ToString() ?? ""),
						RootContains = StringOrArray(val9[(object)"RootContains"]),
						Path = (((object)val9[(object)"Path"])?.ToString() ?? ""),
						PathContains = StringOrArray(val9[(object)"PathContains"]),
						RootNotContains = StringOrArray(val9[(object)"RootNotContains"]),
						PathNotContains = StringOrArray(val9[(object)"PathNotContains"]),
						Scene = (((object)val9[(object)"Scene"])?.ToString() ?? ""),
						SceneContains = (((object)val9[(object)"SceneContains"])?.ToString() ?? ""),
						AddComponent = (val9[(object)"AddComponent"] != null && (int)val9[(object)"AddComponent"].Type == 9 && Extensions.Value<bool>((IEnumerable<JToken>)val9[(object)"AddComponent"])),
						ComponentType = (((object)val9[(object)"ComponentType"])?.ToString() ?? ""),
						PropertyName = (((object)val9[(object)"PropertyName"])?.ToString() ?? ""),
						Value = val9[(object)"Value"]
					};
					JToken val10 = val9[(object)"MaterialIndex"];
					if (val10 != null && (int)val10.Type != 10)
					{
						try
						{
							rawRule.MaterialIndex = val10.ToObject<int>();
						}
						catch
						{
							rawRule.MaterialIndex = -1;
						}
					}
					JToken val11 = val9[(object)"Enabled"];
					if (val11 != null && (int)val11.Type == 9)
					{
						rawRule.Enabled = Extensions.Value<bool>((IEnumerable<JToken>)val11);
					}
					parseResult.Rules.Add(rawRule);
				}
			}
		}
		return parseResult;
	}

	private static List<string> ListValues(JToken t)
	{
		JArray val = (JArray)(object)((t is JArray) ? t : null);
		if (val == null)
		{
			return null;
		}
		List<string> list = new List<string>();
		foreach (JToken item in val)
		{
			string text = ((object)item)?.ToString();
			if (!string.IsNullOrEmpty(text))
			{
				list.Add(text);
			}
		}
		return (list.Count > 0) ? list : null;
	}

	private static string StringOrArray(JToken t)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Invalid comparison between Unknown and I4
		if (t == null || (int)t.Type == 10)
		{
			return "";
		}
		JArray val = (JArray)(object)((t is JArray) ? t : null);
		if (val != null)
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (JToken item in val)
			{
				string value = ((object)item)?.ToString();
				if (!string.IsNullOrEmpty(value))
				{
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append('|');
					}
					stringBuilder.Append(value);
				}
			}
			return stringBuilder.ToString();
		}
		return ((object)t).ToString();
	}

	private static KeyCode ParseKey(JToken t, KeyCode fallback)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		string value = ((object)t)?.ToString();
		KeyCode result;
		return (!string.IsNullOrEmpty(value) && Enum.TryParse<KeyCode>(value, ignoreCase: true, out result)) ? result : fallback;
	}

	private void BuildRules(ParseResult res, bool trigger)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		ResourceManager.RefreshChangedTextures();
		EnforceIntervalSeconds = res.EnforceSeconds;
		ReloadConfigKey = res.ReloadKey;
		ReloadAssetsKey = res.AssetsKey;
		SceneSettings = res.SceneSettings;
		List<TargetRule> list = new List<TargetRule>(res.Rules.Count);
		Dictionary<string, List<TargetRule>> dictionary = new Dictionary<string, List<TargetRule>>(StringComparer.OrdinalIgnoreCase);
		Dictionary<string, List<TargetRule>> dictionary2 = new Dictionary<string, List<TargetRule>>(StringComparer.OrdinalIgnoreCase);
		List<TargetRule> list2 = new List<TargetRule>();
		List<TargetRule> list3 = new List<TargetRule>();
		int num = 0;
		foreach (RawRule rule in res.Rules)
		{
			num++;
			if (!rule.Enabled)
			{
				continue;
			}
			TargetRule targetRule;
			try
			{
				targetRule = new TargetRule
				{
					Root = rule.Root,
					RootContains = rule.RootContains,
					Path = rule.Path,
					PathContains = rule.PathContains,
					RootNotContains = rule.RootNotContains,
					PathNotContains = rule.PathNotContains,
					Scene = rule.Scene,
					SceneContains = rule.SceneContains,
					ComponentType = rule.ComponentType,
					PropertyName = rule.PropertyName,
					Value = rule.Value,
					MaterialIndex = rule.MaterialIndex,
					AddComponent = rule.AddComponent
				};
				targetRule.Init();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)$"Skipping rule #{num} ({rule.PropertyName}): {ex.Message}");
				continue;
			}
			list.Add(targetRule);
			if (!string.IsNullOrEmpty(targetRule.RootExact))
			{
				if (!dictionary.TryGetValue(targetRule.RootExact, out var value))
				{
					value = new List<TargetRule>();
					dictionary[targetRule.RootExact] = value;
				}
				value.Add(targetRule);
			}
			else if (!string.IsNullOrEmpty(targetRule.RootContains))
			{
				string key = targetRule.RootContains + "\u0001" + targetRule.RootNotContains;
				if (!dictionary2.TryGetValue(key, out var value2))
				{
					value2 = (dictionary2[key] = new List<TargetRule>());
				}
				value2.Add(targetRule);
			}
			else if (!string.IsNullOrEmpty(targetRule.PathContains) || !string.IsNullOrEmpty(targetRule.Path))
			{
				list2.Add(targetRule);
			}
			else
			{
				list3.Add(targetRule);
			}
		}
		Rules = list;
		RulesByRoot.Clear();
		foreach (KeyValuePair<string, List<TargetRule>> item in dictionary)
		{
			RulesByRoot[item.Key] = item.Value;
		}
		RulesByRootContains.Clear();
		foreach (KeyValuePair<string, List<TargetRule>> item2 in dictionary2)
		{
			RulesByRootContains.Add(new KeyValuePair<string, List<TargetRule>>(item2.Key, item2.Value));
		}
		RulesByPathContains.Clear();
		RulesByPathContains.AddRange(list2);
		RulesGlobal.Clear();
		RulesGlobal.AddRange(list3);
		Plugin.Log.LogInfo((object)$"ColorizedInk: {list.Count} rules ({dictionary.Count} root / {dictionary2.Count} rootContains / {list2.Count} pathContains / {list3.Count} global).");
		if (trigger)
		{
			try
			{
				OnConfigReloaded?.Invoke();
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogDebug((object)("Aviso: " + ex2.Message));
			}
		}
	}

	private void SetupWatcher()
	{
		string directoryName = Path.GetDirectoryName(_path);
		if (string.IsNullOrEmpty(directoryName) || !Directory.Exists(directoryName))
		{
			return;
		}
		_watcher = new FileSystemWatcher(directoryName, Path.GetFileName(_path))
		{
			EnableRaisingEvents = true,
			NotifyFilter = (NotifyFilters.FileName | NotifyFilters.LastWrite)
		};
		_watcher.Changed += delegate
		{
			Plugin.EnqueueAction(delegate
			{
				_reloadAt = Time.time + 0.5f;
			});
		};
		_watcher.Error += delegate(object s, ErrorEventArgs e)
		{
			Plugin.EnqueueAction(delegate
			{
				Plugin.Log.LogWarning((object)("Config watcher error: " + e.GetException().Message + " — re-enabling."));
				try
				{
					_watcher.EnableRaisingEvents = true;
				}
				catch
				{
				}
			});
		};
	}

	public void EnableWatcher(bool en)
	{
		if (_watcher != null)
		{
			_watcher.EnableRaisingEvents = en;
		}
	}
}
public static class ResourceManager
{
	private class TexEntry
	{
		public Texture2D Tex;

		public DateTime Stamp;
	}

	private struct SpriteEntry
	{
		public Sprite Sprite;

		public Texture2D Source;
	}

	private static readonly Dictionary<string, TexEntry> _textures = new Dictionary<string, TexEntry>(StringComparer.OrdinalIgnoreCase);

	private static readonly Dictionary<string, SpriteEntry> _sprites = new Dictionary<string, SpriteEntry>(StringComparer.OrdinalIgnoreCase);

	private static readonly Dictionary<string, AudioClip> _audios = new Dictionary<string, AudioClip>(StringComparer.OrdinalIgnoreCase);

	private static readonly Dictionary<string, Mesh> _meshes = new Dictionary<string, Mesh>(StringComparer.OrdinalIgnoreCase);

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

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

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

	private static readonly List<string> _scratch = new List<string>(32);

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

	private static string _basePath;

	private static string _texturesDir;

	private static string _audioDir;

	private static string _meshesDir;

	internal static bool AudioPreloadDone;

	public static void Init()
	{
		_basePath = Path.Combine(Paths.ConfigPath, "ColorizedInk");
		_texturesDir = Path.Combine(_basePath, "Textures");
		_audioDir = Path.Combine(_basePath, "Audio");
		_meshesDir = Path.Combine(_basePath, "Meshes");
		try
		{
			Directory.CreateDirectory(_texturesDir);
			Directory.CreateDirectory(_audioDir);
			Directory.CreateDirectory(_meshesDir);
			WriteReadmeOnce();
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("ResourceManager: could not create asset folders: " + ex.Message));
		}
	}

	private static void WriteReadmeOnce()
	{
		string path = Path.Combine(_basePath, "README.txt");
		if (!File.Exists(path))
		{
			File.WriteAllText(path, "ColorizedInk / Universal Replacer assets\r\n======================================\r\nTextures/  -> .png / .jpg referenced by rules as e.g. \"mypic.png\"\r\nAudio/     -> .wav / .mp3 / .ogg referenced by rules as e.g. \"hit.wav\"\r\nMeshes/    -> .obj (static) or .bundle (full Unity mesh, skinned included)\r\n            referenced by rules as PropertyName \"mesh\", Value e.g. \"sword.obj\"\r\nRules live in BepInEx\\config\\ColorizedInk.json.\r\nHotkeys (rebindable in the json Hotkeys block):\r\n  F6 = reload config + textures + audio\r\n  F7 = reload textures + audio only (no config re-parse)\r\n");
		}
	}

	public static IEnumerator PreloadAudioClips()
	{
		if (!Directory.Exists(_audioDir))
		{
			AudioPreloadDone = true;
			yield break;
		}
		string[] files = Directory.GetFiles(_audioDir);
		foreach (string file in files)
		{
			yield return LoadAudioFile(file);
		}
		AudioPreloadDone = true;
	}

	public static IEnumerator ReloadAudioClips()
	{
		if (!Directory.Exists(_audioDir))
		{
			AudioPreloadDone = true;
			yield break;
		}
		_scratch.Clear();
		string[] files = Directory.GetFiles(_audioDir);
		foreach (string file in files)
		{
			string name = Path.GetFileName(file);
			_scratch.Add(name);
			DateTime stamp;
			try
			{
				stamp = File.GetLastWriteTimeUtc(file);
			}
			catch
			{
				stamp = DateTime.MinValue;
			}
			if (!_audios.ContainsKey(name) || !_audioStamps.TryGetValue(name, out var old) || !(old == stamp))
			{
				yield return LoadAudioFile(file);
			}
		}
		List<string> gone = null;
		foreach (string name2 in _audios.Keys)
		{
			if (!_scratch.Contains(name2))
			{
				if (gone == null)
				{
					gone = new List<string>();
				}
				gone.Add(name2);
			}
		}
		if (gone != null)
		{
			foreach (string n in gone)
			{
				_audios.Remove(n);
				_audioStamps.Remove(n);
			}
		}
		_scratch.Clear();
		AudioPreloadDone = true;
	}

	private static IEnumerator LoadAudioFile(string file)
	{
		AudioType type = GetAudioType(Path.GetExtension(file));
		if ((int)type == -1)
		{
			yield break;
		}
		UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(new Uri(file).AbsoluteUri, type);
		try
		{
			yield return req.SendWebRequest();
			if (string.IsNullOrEmpty(req.error))
			{
				AudioClip clip = DownloadHandlerAudioClip.GetContent(req);
				if ((Object)(object)clip != (Object)null)
				{
					string fileName = (((Object)clip).name = Path.GetFileName(file));
					_audios[fileName] = clip;
					try
					{
						_audioStamps[fileName] = File.GetLastWriteTimeUtc(file);
					}
					catch
					{
					}
				}
			}
			else
			{
				Plugin.Log.LogWarning((object)("Failed to load audio '" + file + "': " + req.error));
			}
		}
		finally
		{
			((IDisposable)req)?.Dispose();
		}
		yield return null;
	}

	public static AudioClip GetAudio(string name)
	{
		if (_audios.TryGetValue(name, out var value))
		{
			return value;
		}
		if (AudioPreloadDone)
		{
			WarnOnce("audio", name);
		}
		return null;
	}

	public static Texture2D GetTexture(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return null;
		}
		if (_textures.TryGetValue(name, out var value) && (Object)(object)value.Tex != (Object)null)
		{
			return value.Tex;
		}
		if (_reportedMissing.Contains("tex:" + name))
		{
			return null;
		}
		Texture2D val = LoadTextureFromDisk(name);
		if ((Object)(object)val == (Object)null)
		{
			WarnOnce("tex", name);
			return null;
		}
		_textures[name] = new TexEntry
		{
			Tex = val,
			Stamp = StampOf(name)
		};
		return val;
	}

	public static Sprite GetSprite(string name)
	{
		//IL_008c: 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)
		if (_sprites.TryGetValue(name, out var value) && (Object)(object)value.Sprite != (Object)null && (!_textures.TryGetValue(name, out var value2) || (Object)(object)value2.Tex == (Object)(object)value.Source))
		{
			return value.Sprite;
		}
		Texture2D texture = GetTexture(name);
		if ((Object)(object)texture == (Object)null)
		{
			return null;
		}
		Sprite val = Sprite.Create(texture, new Rect(0f, 0f, (float)((Texture)texture).width, (float)((Texture)texture).height), new Vector2(0.5f, 0.5f), 100f);
		((Object)val).name = name;
		_sprites[name] = new SpriteEntry
		{
			Sprite = val,
			Source = texture
		};
		return val;
	}

	internal static void RefreshChangedTextures()
	{
		List<string> list = null;
		List<string> list2 = null;
		foreach (KeyValuePair<string, TexEntry> texture in _textures)
		{
			DateTime lastWriteTimeUtc;
			try
			{
				lastWriteTimeUtc = File.GetLastWriteTimeUtc(Path.Combine(_texturesDir, texture.Key));
			}
			catch
			{
				if (list == null)
				{
					list = new List<string>();
				}
				list.Add(texture.Key);
				continue;
			}
			if (lastWriteTimeUtc != texture.Value.Stamp)
			{
				if (list2 == null)
				{
					list2 = new List<string>();
				}
				list2.Add(texture.Key);
			}
		}
		if (list != null)
		{
			foreach (string item in list)
			{
				_textures.Remove(item);
			}
		}
		if (list2 != null)
		{
			foreach (string item2 in list2)
			{
				Texture2D val = LoadTextureFromDisk(item2);
				if ((Object)(object)val != (Object)null)
				{
					_textures[item2] = new TexEntry
					{
						Tex = val,
						Stamp = StampOf(item2)
					};
				}
				else
				{
					_textures.Remove(item2);
				}
			}
		}
		List<string> list3 = null;
		foreach (string key in _meshes.Keys)
		{
			DateTime lastWriteTimeUtc2;
			try
			{
				lastWriteTimeUtc2 = File.GetLastWriteTimeUtc(Path.Combine(_meshesDir, key));
			}
			catch
			{
				if (list3 == null)
				{
					list3 = new List<string>();
				}
				list3.Add(key);
				continue;
			}
			if (!_meshStamps.TryGetValue(key, out var value) || value != lastWriteTimeUtc2)
			{
				if (list3 == null)
				{
					list3 = new List<string>();
				}
				list3.Add(key);
			}
		}
		if (list3 != null)
		{
			foreach (string item3 in list3)
			{
				_meshes.Remove(item3);
				_meshStamps.Remove(item3);
				if (_meshBundles.TryGetValue(item3, out var value2))
				{
					try
					{
						value2.Unload(true);
					}
					catch
					{
					}
					_meshBundles.Remove(item3);
				}
			}
		}
		_reportedMissing.Clear();
	}

	public static void ClearCaches()
	{
		List<string> list = null;
		foreach (KeyValuePair<string, SpriteEntry> sprite in _sprites)
		{
			if (!_textures.TryGetValue(sprite.Key, out var value) || (Object)(object)value.Tex != (Object)(object)sprite.Value.Source)
			{
				if (list == null)
				{
					list = new List<string>();
				}
				list.Add(sprite.Key);
			}
		}
		if (list != null)
		{
			foreach (string item in list)
			{
				_sprites.Remove(item);
			}
		}
		_reportedMissing.Clear();
	}

	private static Texture2D LoadTextureFromDisk(string name)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Expected O, but got Unknown
		string path = Path.Combine(_texturesDir, name);
		if (!File.Exists(path))
		{
			return null;
		}
		try
		{
			Texture2D val = new Texture2D(2, 2);
			if (ImageConversion.LoadImage(val, File.ReadAllBytes(path)))
			{
				((Object)val).name = name;
				return val;
			}
			Object.Destroy((Object)(object)val);
			return null;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Failed to load texture '" + name + "': " + ex.Message));
			return null;
		}
	}

	private static DateTime StampOf(string name)
	{
		try
		{
			return File.GetLastWriteTimeUtc(Path.Combine(_texturesDir, name));
		}
		catch
		{
			return DateTime.MinValue;
		}
	}

	private static AudioType GetAudioType(string ext)
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		return (AudioType)(ext.ToLowerInvariant() switch
		{
			".wav" => 20, 
			".ogg" => 14, 
			".mp3" => 13, 
			_ => -1, 
		});
	}

	public static Mesh GetMesh(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return null;
		}
		if (_meshes.TryGetValue(name, out var value) && (Object)(object)value != (Object)null)
		{
			return value;
		}
		if (_reportedMissing.Contains("mesh:" + name))
		{
			return null;
		}
		Mesh val = LoadMeshFromDisk(name);
		if ((Object)(object)val == (Object)null)
		{
			WarnOnce("mesh", name);
			return null;
		}
		_meshes[name] = val;
		try
		{
			_meshStamps[name] = File.GetLastWriteTimeUtc(Path.Combine(_meshesDir, name));
		}
		catch
		{
		}
		return val;
	}

	private static Mesh LoadMeshFromDisk(string name)
	{
		string text = Path.Combine(_meshesDir, name);
		if (!File.Exists(text))
		{
			return null;
		}
		string text2 = Path.GetExtension(name).ToLowerInvariant();
		try
		{
			if (text2 == ".obj")
			{
				return ParseObj(text, name);
			}
			if (text2 == ".bundle" || text2 == ".ab")
			{
				AssetBundle val = AssetBundle.LoadFromFile(text);
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Log.LogWarning((object)("Failed to load bundle '" + name + "'"));
					return null;
				}
				_meshBundles[name] = val;
				Mesh val2 = ((val.LoadAllAssets<Mesh>().Length != 0) ? val.LoadAllAssets<Mesh>()[0] : null);
				if ((Object)(object)val2 == (Object)null)
				{
					Plugin.Log.LogWarning((object)("Bundle '" + name + "' contains no Mesh asset."));
				}
				else
				{
					((Object)val2).name = name;
				}
				return val2;
			}
			Plugin.Log.LogWarning((object)("Unsupported mesh file '" + name + "' — use .obj or .bundle."));
			return null;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Failed to load mesh '" + name + "': " + ex.Message));
			return null;
		}
	}

	private static Mesh ParseObj(string path, string name)
	{
		//IL_0298: Unknown result type (might be due to invalid IL or missing references)
		//IL_029d: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c2: Expected O, but got Unknown
		//IL_0128: Unknown result type (might be due to invalid IL or missing references)
		//IL_015c: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		List<Vector3> list = new List<Vector3>();
		List<Vector3> list2 = new List<Vector3>();
		List<Vector2> list3 = new List<Vector2>();
		List<int> list4 = new List<int>();
		bool flag = false;
		bool flag2 = false;
		Dictionary<string, int> remap = new Dictionary<string, int>();
		List<Vector3> list5 = new List<Vector3>();
		List<Vector2> list6 = new List<Vector2>();
		List<Vector3> list7 = new List<Vector3>();
		foreach (string item in File.ReadLines(path))
		{
			string text = item.Trim();
			if (text.Length == 0 || text[0] == '#')
			{
				continue;
			}
			string[] array = text.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 0)
			{
				continue;
			}
			switch (array[0])
			{
			case "v":
				if (array.Length >= 4)
				{
					list.Add(new Vector3(F(array[1]), F(array[2]), F(array[3])));
				}
				break;
			case "vt":
				if (array.Length >= 3)
				{
					list3.Add(new Vector2(F(array[1]), F(array[2])));
					flag = true;
				}
				break;
			case "vn":
				if (array.Length >= 4)
				{
					list2.Add(new Vector3(F(array[1]), F(array[2]), F(array[3])));
					flag2 = true;
				}
				break;
			case "f":
			{
				List<int> list8 = new List<int>(array.Length - 1);
				for (int i = 1; i < array.Length; i++)
				{
					int num = MapCorner(array[i], list, list3, list2, remap, list5, list6, list7);
					if (num >= 0)
					{
						list8.Add(num);
					}
				}
				for (int j = 2; j < list8.Count; j++)
				{
					list4.Add(list8[0]);
					list4.Add(list8[j - 1]);
					list4.Add(list8[j]);
				}
				break;
			}
			}
		}
		if (list5.Count == 0 || list4.Count == 0)
		{
			return null;
		}
		Mesh val = new Mesh
		{
			name = name,
			vertices = list5.ToArray(),
			triangles = list4.ToArray()
		};
		if (flag && list6.Count == list5.Count)
		{
			val.uv = list6.ToArray();
		}
		if (flag2 && list7.Count == list5.Count)
		{
			val.normals = list7.ToArray();
		}
		else
		{
			val.RecalculateNormals();
		}
		val.RecalculateBounds();
		return val;
	}

	private static int MapCorner(string spec, List<Vector3> verts, List<Vector2> uvs, List<Vector3> norms, Dictionary<string, int> remap, List<Vector3> outV, List<Vector2> outUV, List<Vector3> outN)
	{
		//IL_0085: 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_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		if (remap.TryGetValue(spec, out var value))
		{
			return value;
		}
		string[] array = spec.Split(new char[1] { '/' });
		int num = ObjIdx((array.Length != 0) ? array[0] : null, verts.Count);
		int num2 = ((array.Length > 1) ? ObjIdx(array[1], uvs.Count) : (-1));
		int num3 = ((array.Length > 2) ? ObjIdx(array[2], norms.Count) : (-1));
		if (num < 0)
		{
			return -1;
		}
		outV.Add(verts[num]);
		outUV.Add((num2 >= 0) ? uvs[num2] : Vector2.zero);
		outN.Add((num3 >= 0) ? norms[num3] : Vector3.zero);
		return remap[spec] = outV.Count - 1;
	}

	private static int ObjIdx(string s, int count)
	{
		if (string.IsNullOrEmpty(s))
		{
			return -1;
		}
		if (!int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
		{
			return -1;
		}
		if (result < 0)
		{
			result = count + result + 1;
		}
		return (result >= 1 && result <= count) ? (result - 1) : (-1);
	}

	private static float F(string s)
	{
		float result;
		return float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out result) ? result : 0f;
	}

	private static void WarnOnce(string kind, string name)
	{
		if (_reportedMissing.Add(kind + ":" + name))
		{
			Plugin.Log.LogWarning((object)(kind + " asset '" + name + "' not found (looked in '" + ((kind == "tex") ? _texturesDir : ((kind == "mesh") ? _meshesDir : _audioDir)) + "') — rules using it are skipped."));
		}
	}
}
public class TargetRule
{
	public string Root;

	public string RootContains;

	public string Path;

	public string PathContains;

	public string RootNotContains;

	public string PathNotContains;

	public string Scene;

	public string SceneContains;

	public string ComponentType;

	public bool AddComponent;

	public string PropertyName;

	public int MaterialIndex = -1;

	public JToken Value;

	[JsonIgnore]
	public string[] RootContainsParts;

	[JsonIgnore]
	public string[][] RootContainsPartSets;

	[JsonIgnore]
	public string[][] RootNotContainsPartSets;

	[JsonIgnore]
	public string[][] PathContainsPartSets;

	[JsonIgnore]
	public string[][] PathNotContainsPartSets;

	[JsonIgnore]
	public string[] PathContainsValues;

	[JsonIgnore]
	public string[] PathContainsClean;

	[JsonIgnore]
	public string[] PathNotContainsValues;

	[JsonIgnore]
	public string[][] PathMatchSets;

	[JsonIgnore]
	public int FastMask;

	[JsonIgnore]
	public string[] PathParts;

	[JsonIgnore]
	public bool IsDeepSearch;

	[JsonIgnore]
	public string CleanDeepTarget;

	[JsonIgnore]
	public List<string> DeepTargets;

	[JsonIgnore]
	public string CleanPathContains;

	[JsonIgnore]
	public string NormalizedPropName;

	[JsonIgnore]
	public string NormalizedLeafName;

	[JsonIgnore]
	public bool IsColorRule;

	[JsonIgnore]
	public int SceneBuildIndex = -1;

	[JsonIgnore]
	public string RootExact;

	[JsonIgnore]
	public int RootIndex = -1;

	[JsonIgnore]
	public string PathExact;

	[JsonIgnore]
	public string PathLeaf;

	[JsonIgnore]
	public int PathIndex = -1;

	[JsonIgnore]
	public object ParsedValue;

	[JsonIgnore]
	public Color BakedColor;

	[JsonIgnore]
	public Gradient BakedGradient;

	[JsonIgnore]
	public float BakedFloat;

	[JsonIgnore]
	public string BakedString;

	[JsonIgnore]
	public bool BakedBool;

	private static readonly char[] Slash = new char[1] { '/' };

	private static readonly char[] Pipe = new char[1] { '|' };

	private static readonly string[] DoubleSlash = new string[1] { "//" };

	internal const int FastGraphic = 1;

	internal const int FastRenderer = 2;

	internal const int FastLight = 4;

	internal const int FastParticle = 8;

	internal const int FastAudio = 16;

	internal const int FastAll = 31;

	public void Init()
	{
		RootContainsPartSets = SplitSets(RootContains);
		RootNotContainsPartSets = SplitSets(RootNotContains);
		PathContainsPartSets = SplitSets(PathContains);
		PathNotContainsPartSets = SplitSets(PathNotContains);
		PathContainsValues = SplitValues(PathContains);
		PathNotContainsValues = SplitValues(PathNotContains);
		if (PathContainsValues != null)
		{
			PathContainsClean = new string[PathContainsValues.Length];
			for (int i = 0; i < PathContainsValues.Length; i++)
			{
				PathContainsClean[i] = PathContainsValues[i].TrimEnd('*', '/');
			}
		}
		if (RootContainsPartSets != null && RootContainsPartSets.Length != 0)
		{
			RootContainsParts = RootContainsPartSets[0];
		}
		if (!string.IsNullOrEmpty(Scene))
		{
			Scene = Scene.Trim();
			if (int.TryParse(Scene, out var result))
			{
				SceneBuildIndex = result;
			}
		}
		string text = ((!string.IsNullOrEmpty(Path)) ? Path : FirstValue(PathContainsValues));
		if (!string.IsNullOrEmpty(text))
		{
			if (text.Contains("//"))
			{
				IsDeepSearch = true;
				string[] array = text.Split(DoubleSlash, StringSplitOptions.None);
				DeepTargets = new List<string>(array.Length - 1);
				for (int j = 1; j < array.Length; j++)
				{
					string text2 = array[j].TrimEnd('*', '/');
					if (text2.Length > 0)
					{
						DeepTargets.Add(text2);
					}
				}
				CleanDeepTarget = ((DeepTargets.Count > 0) ? DeepTargets[DeepTargets.Count - 1] : "");
				PathParts = array[0].Split(Slash, StringSplitOptions.RemoveEmptyEntries);
			}
			else
			{
				PathParts = text.TrimEnd('*', '/').Split(Slash, StringSplitOptions.RemoveEmptyEntries);
			}
		}
		CleanPathContains = (IsDeepSearch ? CleanDeepTarget : FirstClean(PathContainsValues));
		NormalizedPropName = (PropertyName ?? "").ToLowerInvariant().Replace("_", "").Replace("behavior.", "")
			.Replace("behaviour.", "");
		string text3 = NormalizedPropName;
		int num = text3.LastIndexOf('.');
		if (num >= 0)
		{
			text3 = text3.Substring(num + 1);
		}
		int num2 = text3.IndexOf('[');
		if (num2 >= 0)
		{
			text3 = text3.Substring(0, num2);
		}
		NormalizedLeafName = text3;
		IsColorRule = text3.Contains("color") || text3.Contains("tint");
		PathMatchSets = PathContainsPartSets ?? ((PathParts == null) ? null : new string[1][] { PathParts });
		RootExact = StripIndex(Root, out RootIndex);
		PathExact = StripIndex(Path, out PathIndex);
		PathLeaf = ((PathExact == null) ? null : PathExact.Substring(PathExact.LastIndexOf('/') + 1));
		FastMask = 0;
		if (string.IsNullOrEmpty(ComponentType))
		{
			FastMask = 31;
		}
		else
		{
			if (MatchesComponentName("Graphic"))
			{
				FastMask |= 1;
			}
			if (MatchesComponentName("Renderer"))
			{
				FastMask |= 2;
			}
			if (MatchesComponentName("Light"))
			{
				FastMask |= 4;
			}
			if (MatchesComponentName("ParticleSystem"))
			{
				FastMask |= 8;
			}
			if (MatchesComponentName("AudioSource"))
			{
				FastMask |= 16;
			}
		}
		BakeValue();
	}

	private static string[] SplitValues(string raw)
	{
		if (string.IsNullOrEmpty(raw))
		{
			return null;
		}
		string[] array = raw.Split(Pipe, StringSplitOptions.RemoveEmptyEntries);
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = array[i].Trim();
		}
		return (array.Length != 0) ? array : null;
	}

	private static string[][] SplitSets(string raw)
	{
		string[] array = SplitValues(raw);
		if (array == null)
		{
			return null;
		}
		string[][] array2 = new string[array.Length][];
		for (int i = 0; i < array.Length; i++)
		{
			array2[i] = array[i].Split(Slash, StringSplitOptions.RemoveEmptyEntries);
		}
		return array2;
	}

	private bool MatchesComponentName(string typeName)
	{
		return typeName.IndexOf(ComponentType, StringComparison.OrdinalIgnoreCase) >= 0 || ComponentType.IndexOf(typeName, StringComparison.OrdinalIgnoreCase) >= 0;
	}

	internal static string StripIndex(string raw, out int index)
	{
		index = -1;
		if (string.IsNullOrEmpty(raw))
		{
			return raw;
		}
		int num = raw.LastIndexOf('[');
		if (num < 0 || !raw.EndsWith("]"))
		{
			return raw;
		}
		if (!int.TryParse(raw.Substring(num + 1, raw.Length - num - 2), out var result) || result < 0)
		{
			return raw;
		}
		index = result;
		return raw.Substring(0, num);
	}

	internal static bool MatchesSiblingIndex(Transform t, int idx)
	{
		if (idx < 0)
		{
			return true;
		}
		Transform parent = t.parent;
		if ((Object)(object)parent == (Object)null)
		{
			return t.GetSiblingIndex() == idx;
		}
		int num = 0;
		for (int i = 0; i < parent.childCount; i++)
		{
			Transform child = parent.GetChild(i);
			if ((Object)(object)child == (Object)(object)t)
			{
				return num == idx;
			}
			if (((Object)child).name == ((Object)t).name)
			{
				num++;
			}
		}
		return false;
	}

	private static string FirstValue(string[] vals)
	{
		return (vals != null && vals.Length != 0) ? vals[0] : null;
	}

	private static string FirstClean(string[] vals)
	{
		return (vals != null && vals.Length != 0) ? vals[0].TrimEnd('*', '/') : null;
	}

	public bool MatchesScene()
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		if (string.IsNullOrEmpty(Scene) && string.IsNullOrEmpty(SceneContains))
		{
			return true;
		}
		Scene activeScene = SceneManager.GetActiveScene();
		if (!string.IsNullOrEmpty(Scene))
		{
			if (SceneBuildIndex >= 0)
			{
				return ((Scene)(ref activeScene)).buildIndex == SceneBuildIndex;
			}
			if (string.Equals(((Scene)(ref activeScene)).name, Scene, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (!string.IsNullOrEmpty(((Scene)(ref activeScene)).path) && ((Scene)(ref activeScene)).path.EndsWith(Scene, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			return false;
		}
		return ((Scene)(ref activeScene)).name.IndexOf(SceneContains, StringComparison.OrdinalIgnoreCase) >= 0;
	}

	internal void BakeValue()
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Expected I4, but got Unknown
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0181: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_0198: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a2: Expected O, but got Unknown
		//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0210: Unknown result type (might be due to invalid IL or missing references)
		//IL_0215: Unknown result type (might be due to invalid IL or missing references)
		if (Value == null)
		{
			return;
		}
		try
		{
			JTokenType type = Value.Type;
			JTokenType val = type;
			switch (val - 1)
			{
			case 0:
				if (Value[(object)"r"] != null || Value[(object)"g"] != null || Value[(object)"x"] != null || Value[(object)"y"] != null)
				{
					BakedColor = new Color(Comp("r") ?? Comp("x").GetValueOrDefault(), Comp("g") ?? Comp("y").GetValueOrDefault(), Comp("b") ?? Comp("z").GetValueOrDefault(), Comp("a") ?? Comp("w") ?? 1f);
					ParsedValue = BakedColor;
					BakedGradient = new Gradient();
					BakedGradient.SetKeys((GradientColorKey[])(object)new GradientColorKey[2]
					{
						new GradientColorKey(BakedColor, 0f),
						new GradientColorKey(BakedColor, 1f)
					}, (GradientAlphaKey[])(object)new GradientAlphaKey[2]
					{
						new GradientAlphaKey(BakedColor.a, 0f),
						new GradientAlphaKey(BakedColor.a, 1f)
					});
				}
				else
				{
					ParsedValue = Value;
				}
				break;
			case 7:
			{
				BakedString = (string)Value;
				string text = BakedString.ToLowerInvariant();
				if (text.EndsWith(".png") || text.EndsWith(".jpg"))
				{
					ParsedValue = ResourceManager.GetTexture(BakedString);
				}
				else if (text.EndsWith(".wav") || text.EndsWith(".mp3") || text.EndsWith(".ogg"))
				{
					ParsedValue = ResourceManager.GetAudio(BakedString);
				}
				else if (text.EndsWith(".obj") || text.EndsWith(".bundle") || text.EndsWith(".ab"))
				{
					ParsedValue = ResourceManager.GetMesh(BakedString);
				}
				else
				{
					ParsedValue = BakedString;
				}
				break;
			}
			case 5:
			case 6:
				BakedFloat = Extensions.Value<float>((IEnumerable<JToken>)Value);
				ParsedValue = BakedFloat;
				break;
			case 8:
				BakedBool = Extensions.Value<bool>((IEnumerable<JToken>)Value);
				ParsedValue = BakedBool;
				break;
			default:
				ParsedValue = Value.ToObject<object>();
				break;
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Rule '" + PropertyName + "': unreadable Value (" + ex.Message + ") — rule will not apply."));
		}
		float? Comp(string key)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			JToken val2 = Value[(object)key];
			return (val2 != null && (int)val2.Type != 10) ? new float?(Extensions.Value<float>((IEnumerable<JToken>)val2)) : ((float?)null);
		}
	}
}
public static class InstantProcessor
{
	private static readonly List<Transform> _transformBuffer = new List<Transform>(256);

	private static readonly List<TargetRule> _candidateRules = new List<TargetRule>(32);

	private static readonly List<int> _candidateMemo = new List<int>(32);

	private static readonly List<string> _memoKeys = new List<string>(4);

	private static readonly List<bool[]> _memoArrays = new List<bool[]>(4);

	private static readonly List<Component> _compScratch = new List<Component>(32);

	private static readonly MaterialPropertyBlock _mpb = new MaterialPropertyBlock();

	private static string MemoSignature(TargetRule rule)
	{
		return rule.RootContains + "\u0001" + rule.RootNotContains;
	}

	public static void ApplyInstant(GameObject root, List<TargetRule> rules)
	{
		if ((Object)(object)root == (Object)null || rules == null || rules.Count == 0)
		{
			return;
		}
		_transformBuffer.Clear();
		root.GetComponentsInChildren<Transform>(true, _transformBuffer);
		string name = ((Object)root).name;
		_candidateRules.Clear();
		bool flag = false;
		for (int i = 0; i < rules.Count; i++)
		{
			TargetRule targetRule = rules[i];
			if ((string.IsNullOrEmpty(targetRule.RootExact) || !(name != targetRule.RootExact)) && (targetRule.RootIndex < 0 || TargetRule.MatchesSiblingIndex(root.transform, targetRule.RootIndex)) && targetRule.MatchesScene())
			{
				_candidateRules.Add(targetRule);
				if (targetRule.RootContainsPartSets != null)
				{
					flag = true;
				}
			}
		}
		if (_candidateRules.Count == 0)
		{
			return;
		}
		_memoKeys.Clear();
		if (flag)
		{
			for (int j = 0; j < _candidateRules.Count; j++)
			{
				TargetRule targetRule2 = _candidateRules[j];
				if (targetRule2.RootContainsPartSets == null)
				{
					continue;
				}
				string text = MemoSignature(targetRule2);
				bool flag2 = false;
				for (int k = 0; k < _memoKeys.Count; k++)
				{
					if (string.Equals(_memoKeys[k], text, StringComparison.Ordinal))
					{
						flag2 = true;
						break;
					}
				}
				if (!flag2)
				{
					_memoKeys.Add(text);
					BuildMemo(targetRule2, _memoKeys.Count - 1);
				}
			}
		}
		_candidateMemo.Clear();
		for (int l = 0; l < _candidateRules.Count; l++)
		{
			TargetRule targetRule3 = _candidateRules[l];
			int item = -1;
			if (targetRule3.RootContainsPartSets != null)
			{
				string b = MemoSignature(targetRule3);
				for (int m = 0; m < _memoKeys.Count; m++)
				{
					if (string.Equals(_memoKeys[m], b, StringComparison.Ordinal))
					{
						item = m;
						break;
					}
				}
			}
			_candidateMemo.Add(item);
		}
		int count = _memoKeys.Count;
		for (int n = 0; n < _transformBuffer.Count; n++)
		{
			Transform val = _transformBuffer[n];
			string name2 = ((Object)val).name;
			for (int num = 0; num < _candidateRules.Count; num++)
			{
				if (count > 0)
				{
					int num2 = _candidateMemo[num];
					if (num2 >= 0 && !_memoArrays[num2][n])
					{
						continue;
					}
				}
				TargetRule targetRule4 = _candidateRules[num];
				if (!string.IsNullOrEmpty(targetRule4.PathExact) && (name2 != targetRule4.PathLeaf || !TargetRule.MatchesSiblingIndex(val, targetRule4.PathIndex)))
				{
					continue;
				}
				if (targetRule4.PathNotContainsValues != null)
				{
					bool flag3 = false;
					for (int num3 = 0; num3 < targetRule4.PathNotContainsValues.Length; num3++)
					{
						if (name2.IndexOf(targetRule4.PathNotContainsValues[num3], StringComparison.OrdinalIgnoreCase) >= 0)
						{
							flag3 = true;
							break;
						}
					}
					if (flag3)
					{
						continue;
					}
				}
				if (targetRule4.IsDeepSearch)
				{
					if (string.IsNullOrEmpty(targetRule4.CleanDeepTarget) || name2.IndexOf(targetRule4.CleanDeepTarget, StringComparison.OrdinalIgnoreCase) < 0 || !MatchAnyPartSet(val, targetRule4.PathMatchSets))
					{
						continue;
					}
				}
				else if (targetRule4.PathContainsClean != null)
				{
					bool flag4 = false;
					for (int num4 = 0; num4 < targetRule4.PathContainsClean.Length; num4++)
					{
						if (name2.IndexOf(targetRule4.PathContainsClean[num4], StringComparison.OrdinalIgnoreCase) >= 0)
						{
							flag4 = true;
							break;
						}
					}
					if (!flag4)
					{
						continue;
					}
				}
				ApplyRuleToGameObject(((Component)val).gameObject, targetRule4);
			}
		}
	}

	internal static bool MatchAnyPartSet(Transform t, string[][] sets)
	{
		if (sets == null || sets.Length == 0)
		{
			return false;
		}
		for (int i = 0; i < sets.Length; i++)
		{
			if (RuleProcessor.MatchesPath(t, sets[i]))
			{
				return true;
			}
		}
		return false;
	}

	private static void BuildMemo(TargetRule rule, int memoIndex)
	{
		if (_memoArrays.Count <= memoIndex)
		{
			_memoArrays.Add(new bool[64]);
		}
		bool[] array = _memoArrays[memoIndex];
		if (array.Length < _transformBuffer.Count)
		{
			array = new bool[Math.Max(_transformBuffer.Count, array.Length * 2)];
			_memoArrays[memoIndex] = array;
		}
		for (int i = 0; i < _transformBuffer.Count; i++)
		{
			Transform t = _transformBuffer[i];
			array[i] = MatchAnyPartSet(t, rule.RootContainsPartSets) && !MatchAnyPartSet(t, rule.RootNotContainsPartSets);
		}
	}

	private static void ApplyRuleToGameObject(GameObject go, TargetRule rule)
	{
		bool flag = !string.IsNullOrEmpty(rule.ComponentType);
		Graphic val = default(Graphic);
		if ((rule.FastMask & 1) != 0 && go.TryGetComponent<Graphic>(ref val) && (!flag || ((object)val).GetType().Name.IndexOf(rule.ComponentType, StringComparison.OrdinalIgnoreCase) >= 0))
		{
			ApplyToGraphic(val, rule);
		}
		Renderer val2 = default(Renderer);
		if ((rule.FastMask & 2) != 0 && go.TryGetComponent<Renderer>(ref val2) && (!flag || ((object)val2).GetType().Name.IndexOf(rule.ComponentType, StringComparison.OrdinalIgnoreCase) >= 0))
		{
			ApplyToRenderer(val2, rule);
		}
		Light val3 = default(Light);
		if ((rule.FastMask & 4) != 0 && go.TryGetComponent<Light>(ref val3) && (!flag || ((object)val3).GetType().Name.IndexOf(rule.ComponentType, StringComparison.OrdinalIgnoreCase) >= 0))
		{
			ApplyToLight(val3, rule);
		}
		ParticleSystem val4 = default(ParticleSystem);
		if ((rule.FastMask & 8) != 0 && go.TryGetComponent<ParticleSystem>(ref val4) && (!flag || ((object)val4).GetType().Name.IndexOf(rule.ComponentType, StringComparison.OrdinalIgnoreCase) >= 0))
		{
			ApplyToParticle(val4, rule);
		}
		AudioSource val5 = default(AudioSource);
		if ((rule.FastMask & 0x10) != 0 && go.TryGetComponent<AudioSource>(ref val5) && (!flag || ((object)val5).GetType().Name.IndexOf(rule.ComponentType, StringComparison.OrdinalIgnoreCase) >= 0))
		{
			ApplyToAudio(val5, rule);
		}
		if (!flag || string.IsNullOrEmpty(rule.PropertyName))
		{
			return;
		}
		go.GetComponents<Component>(_compScratch);
		for (int i = 0; i < _compScratch.Count; i++)
		{
			Component val6 = _compScratch[i];
			if ((Object)(object)val6 == (Object)null || (flag && ((object)val6).GetType().Name.IndexOf(rule.ComponentType, StringComparison.OrdinalIgnoreCase) < 0))
			{
				continue;
			}
			try
			{
				if (RuleProcessor.TryPatchViaReflection(val6, rule, out var _))
				{
					RuleProcessor.MarkSpawnApplied(val6, rule);
				}
			}
			catch
			{
			}
		}
		_compScratch.Clear();
	}

	private static void ApplyToGraphic(Graphic g, TargetRule rule)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		if (rule.ParsedValue == null)
		{
			return;
		}
		if (rule.IsColorRule && rule.ParsedValue is Color val && true && g.color != val)
		{
			g.color = val;
		}
		Image val2 = (Image)(object)((g is Image) ? g : null);
		if (val2 != null && rule.NormalizedLeafName == "sprite")
		{
			object parsedValue = rule.ParsedValue;
			Sprite val3 = (Sprite)((parsedValue is Sprite) ? parsedValue : null);
			if (val3 != null)
			{
				if ((Object)(object)val2.sprite != (Object)(object)val3)
				{
					val2.sprite = val3;
				}
				return;
			}
		}
		RawImage val4 = (RawImage)(object)((g is RawImage) ? g : null);
		if (val4 != null && (rule.NormalizedLeafName == "texture" || rule.NormalizedLeafName == "maintex"))
		{
			object parsedValue2 = rule.ParsedValue;
			Texture2D val5 = (Texture2D)((parsedValue2 is Texture2D) ? parsedValue2 : null);
			if (val5 != null && (Object)(object)val4.texture != (Object)(object)val5)
			{
				val4.texture = (Texture)(object)val5;
			}
		}
	}

	private static void ApplyToRenderer(Renderer r, TargetRule rule)
	{
		//IL_0055: 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_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_033c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0343: Expected O, but got Unknown
		//IL_022a: Unknown result type (might be due to invalid IL or missing references)
		//IL_027b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_031f: Unknown result type (might be due to invalid IL or missing references)
		if (rule.ParsedValue == null)
		{
			return;
		}
		if (rule.IsColorRule && rule.ParsedValue is Color)
		{
			TrailRenderer val = (TrailRenderer)(object)((r is TrailRenderer) ? r : null);
			if (val != null)
			{
				val.colorGradient = rule.BakedGradient;
				val.startColor = rule.BakedColor;
				val.endColor = rule.BakedColor;
			}
			else
			{
				LineRenderer val2 = (LineRenderer)(object)((r is LineRenderer) ? r : null);
				if (val2 != null)
				{
					val2.colorGradient = rule.BakedGradient;
					val2.startColor = rule.BakedColor;
					val2.endColor = rule.BakedColor;
				}
				else
				{
					SpriteRenderer val3 = (SpriteRenderer)(object)((r is SpriteRenderer) ? r : null);
					if (val3 != null && val3.color != rule.BakedColor)
					{
						val3.color = rule.BakedColor;
					}
				}
			}
		}
		else if (rule.NormalizedLeafName.Contains("width") || rule.NormalizedLeafName.Contains("multiplier"))
		{
			TrailRenderer val4 = (TrailRenderer)(object)((r is TrailRenderer) ? r : null);
			if (val4 != null)
			{
				val4.widthMultiplier = rule.BakedFloat;
			}
			else
			{
				LineRenderer val5 = (LineRenderer)(object)((r is LineRenderer) ? r : null);
				if (val5 != null)
				{
					val5.widthMultiplier = rule.BakedFloat;
				}
			}
		}
		if (!(rule.ParsedValue is Color) && !(rule.ParsedValue is Texture2D))
		{
			return;
		}
		Material[] sharedMaterials = r.sharedMaterials;
		for (int i = 0; i < sharedMaterials.Length; i++)
		{
			if (rule.MaterialIndex >= 0 && rule.MaterialIndex != i)
			{
				continue;
			}
			Material val6 = sharedMaterials[i];
			if ((Object)(object)val6 == (Object)null)
			{
				continue;
			}
			r.GetPropertyBlock(_mpb, i);
			bool flag = false;
			if (rule.ParsedValue is Color)
			{
				string normalizedLeafName = rule.NormalizedLeafName;
				if (val6.HasProperty(rule.PropertyName))
				{
					_mpb.SetColor(rule.PropertyName, rule.BakedColor);
					flag = true;
				}
				if (normalizedLeafName.Contains("emission") || normalizedLeafName.Contains("emis"))
				{
					if (val6.HasProperty("_EmissionColor"))
					{
						_mpb.SetColor("_EmissionColor", rule.BakedColor);
						flag = true;
					}
				}
				else if (normalizedLeafName.Contains("tint"))
				{
					if (val6.HasProperty("_TintColor"))
					{
						_mpb.SetColor("_TintColor", rule.BakedColor);
						flag = true;
					}
				}
				else
				{
					if (val6.HasProperty("_Color"))
					{
						_mpb.SetColor("_Color", rule.BakedColor);
						flag = true;
					}
					if (val6.HasProperty("_TintColor"))
					{
						_mpb.SetColor("_TintColor", rule.BakedColor);
						flag = true;
					}
				}
			}
			else
			{
				Texture2D val7 = (Texture2D)rule.ParsedValue;
				string normalizedLeafName2 = rule.NormalizedLeafName;
				if (val6.HasProperty(rule.PropertyName))
				{
					_mpb.SetTexture(rule.PropertyName, (Texture)(object)val7);
					flag = true;
				}
				else if (!normalizedLeafName2.StartsWith("_") && val6.HasProperty("_" + rule.PropertyName))
				{
					_mpb.SetTexture("_" + rule.PropertyName, (Texture)(object)val7);
					flag = true;
				}
				else
				{
					switch (normalizedLeafName2)
					{
					default:
						if (!(normalizedLeafName2 == "basemap"))
						{
							break;
						}
						goto case "maintex";
					case "maintex":
					case "maintexture":
					case "albedo":
						if (val6.HasProperty("_MainTex"))
						{
							_mpb.SetTexture("_MainTex", (Texture)(object)val7);
							flag = true;
						}
						if (val6.HasProperty("_BaseMap"))
						{
							_mpb.SetTexture("_BaseMap", (Texture)(object)val7);
							flag = true;
						}
						break;
					}
				}
			}
			if (flag)
			{
				r.SetPropertyBlock(_mpb, i);
			}
		}
	}

	private static void ApplyToLight(Light l, TargetRule rule)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		if (rule.IsColorRule && rule.ParsedValue is Color)
		{
			l.color = rule.BakedColor;
		}
		if (rule.NormalizedLeafName == "intensity")
		{
			l.intensity = rule.BakedFloat;
		}
	}

	private static void ApplyToParticle(ParticleSystem ps, TargetRule rule)
	{
		//IL_0021: 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_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		if (rule.IsColorRule && rule.ParsedValue is Color)
		{
			SetParticleStartColor(ps, rule.BakedColor);
			TrailModule trails = ps.trails;
			if (((TrailModule)(ref trails)).enabled)
			{
				((TrailModule)(ref trails)).colorOverLifetime = new MinMaxGradient(rule.BakedColor);
			}
		}
		ParticleSystemRenderer r = default(ParticleSystemRenderer);
		if (((Component)ps).TryGetComponent<ParticleSystemRenderer>(ref r))
		{
			ApplyToRenderer((Renderer)(object)r, rule);
		}
	}

	internal static bool SetParticleStartColor(ParticleSystem ps, Color c)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		MainModule main = ps.main;
		MinMaxGradient startColor = ((MainModule)(ref main)).startColor;
		if ((int)((MinMaxGradient)(ref startColor)).mode == 0 && ((MinMaxGradient)(ref startColor)).color == c)
		{
			return false;
		}
		((MainModule)(ref main)).startColor = new MinMaxGradient(c);
		if ((Object)(object)Plugin.Instance != (Object)null)
		{
			EmissionModule emission = ps.emission;
			if (((EmissionModule)(ref emission)).enabled)
			{
				((EmissionModule)(ref emission)).enabled = false;
				((MonoBehaviour)Plugin.Instance).StartCoroutine(ReenableNextFrame(ps));
			}
		}
		return true;
	}

	private static IEnumerator ReenableNextFrame(ParticleSystem ps)
	{
		yield return null;
		if ((Object)(object)ps != (Object)null)
		{
			EmissionModule emission = ps.emission;
			((EmissionModule)(ref emission)).enabled = true;
			emission = default(EmissionModule);
		}
	}

	private static void ApplyToAudio(AudioSource a, TargetRule rule)
	{
		if (rule.NormalizedLeafName == "clip")
		{
			object parsedValue = rule.ParsedValue;
			AudioClip val = (AudioClip)((parsedValue is AudioClip) ? parsedValue : null);
			if (val != null && (Object)(object)a.clip != (Object)(object)val)
			{
				bool isPlaying = a.isPlaying;
				a.clip = val;
				if (isPlaying)
				{
					a.Play();
				}
				return;
			}
		}
		if (rule.NormalizedLeafName == "volume")
		{
			a.volume = rule.BakedFloat;
		}
		else if (rule.NormalizedLeafName == "pitch")
		{
			a.pitch = rule.BakedFloat;
		}
	}
}
public static class SceneSettingsApplier
{
	private static readonly Dictionary<string, AssetBundle> _matBundles = new Dictionary<string, AssetBundle>(StringComparer.OrdinalIgnoreCase);

	private static readonly Dictionary<string, Material> _skyboxImages = new Dictionary<string, Material>(StringComparer.OrdinalIgnoreCase);

	public static void ApplyAll(JToken sceneSettings)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Invalid comparison between Unknown and I4
		if (sceneSettings == null || (int)sceneSettings.Type == 10)
		{
			return;
		}
		JArray val = (JArray)(object)((sceneSettings is JArray) ? sceneSettings : null);
		if (val != null)
		{
			foreach (JToken item in val)
			{
				JObject val2 = (JObject)(object)((item is JObject) ? item : null);
				if (val2 != null)
				{
					string scene = S(val2["Scene"] ?? val2["scene"]);
					string sceneContains = S(val2["SceneContains"] ?? val2["sceneContains"]);
					if (MatchesScene(scene, sceneContains))
					{
						Apply(val2);
					}
				}
			}
			return;
		}
		JObject val3 = (JObject)(object)((sceneSettings is JObject) ? sceneSettings : null);
		if (val3 != null)
		{
			Apply(val3);
		}
	}

	private static bool MatchesScene(string scene, string sceneContains)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		if (string.IsNullOrEmpty(scene) && string.IsNullOrEmpty(sceneContains))
		{
			return true;
		}
		Scene activeScene = SceneManager.GetActiveScene();
		if (!string.IsNullOrEmpty(scene))
		{
			if (int.TryParse(scene, out var result))
			{
				return ((Scene)(ref activeScene)).buildIndex == result;
			}
			if (string.Equals(((Scene)(ref activeScene)).name, scene, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (!string.IsNullOrEmpty(((Scene)(ref activeScene)).path) && ((Scene)(ref activeScene)).path.EndsWith(scene, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			return false;
		}
		return ((Scene)(ref activeScene)).name.IndexOf(sceneContains, StringComparison.OrdinalIgnoreCase) >= 0;
	}

	private static string S(JToken t)
	{
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Invalid comparison between Unknown and I4
		return (t != null && (int)t.Type != 10) ? ((object)t).ToString() : null;
	}

	public static void Apply(JObject settings)
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Invalid comparison between Unknown and I4
		//IL_041d: Unknown result type (might be due to invalid IL or missing references)
		//IL_054a: Unknown result type (might be due to invalid IL or missing references)
		//IL_03fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0638: Unknown result type (might be due to invalid IL or missing references)
		//IL_0510: Unknown result type (might be due to invalid IL or missing references)
		//IL_04f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0567: Unknown result type (might be due to invalid IL or missing references)
		//IL_0498: Unknown result type (might be due to invalid IL or missing references)
		//IL_052d: Unknown result type (might be due to invalid IL or missing references)
		if (settings == null)
		{
			return;
		}
		try
		{
			foreach (KeyValuePair<string, JToken> setting in settings)
			{
				string text = setting.Key.ToLowerInvariant();
				JToken value = setting.Value;
				if (value == null || (int)value.Type == 10 || text == "instructions" || text.StartsWith("_"))
				{
					continue;
				}
				switch (text)
				{
				case "fog":
					RenderSettings.fog = B(value);
					break;
				case "fogcolor":
				{
					if (Clr(value, out var c5))
					{
						RenderSettings.fogColor = c5;
					}
					break;
				}
				case "fogmode":
				{
					if (Enum.TryParse<FogMode>(N(value), ignoreCase: true, out FogMode result))
					{
						RenderSettings.fogMode = result;
					}
					else
					{
						Plugin.Log.LogWarning((object)("SceneSettings: unknown fogMode '" + N(value) + "' (Linear/Exponential/ExponentialSquared)."));
					}
					break;
				}
				case "fogdensity":
					RenderSettings.fogDensity = F(value);
					break;
				case "fogstartdistance":
					RenderSettings.fogStartDistance = F(value);
					break;
				case "fogenddistance":
					RenderSettings.fogEndDistance = F(value);
					break;
				case "ambientmode":
				{
					if (Enum.TryParse<AmbientMode>(N(value), ignoreCase: true, out AmbientMode result2))
					{
						RenderSettings.ambientMode = result2;
					}
					else
					{
						Plugin.Log.LogWarning((object)("SceneSettings: unknown ambientMode '" + N(value) + "' (Skybox/Trilight/Flat/Custom)."));
					}
					break;
				}
				case "ambientintensity":
					RenderSettings.ambientIntensity = F(value);
					break;
				case "ambientlight":
				{
					if (Clr(value, out var c4))
					{
						RenderSettings.ambientMode = (AmbientMode)3;
						RenderSettings.ambientLight = c4;
					}
					break;
				}
				case "ambientskycolor":
				{
					if (Clr(value, out var c2))
					{
						RenderSettings.ambientSkyColor = c2;
					}
					break;
				}
				case "ambientequatorcolor":
				{
					if (Clr(value, out var c))
					{
						RenderSettings.ambientEquatorColor = c;
					}
					break;
				}
				case "ambientgroundcolor":
				{
					if (Clr(value, out var c7))
					{
						RenderSettings.ambientGroundColor = c7;
					}
					break;
				}
				case "subtractiveshadowcolor":
				{
					if (Clr(value, out var c6))
					{
						RenderSettings.subtractiveShadowColor = c6;
					}
					break;
				}
				case "halostrength":
					RenderSettings.haloStrength = F(value);
					break;
				case "skybox":
				{
					Material val2 = LoadMaterial(N(value));
					if ((Object)(object)val2 != (Object)null)
					{
						RenderSettings.skybox = val2;
					}
					else
					{
						Plugin.Log.LogWarning((object)("SceneSettings: skybox material '" + N(value) + "' not found in Meshes/ (expected a .bundle containing a Material)."));
					}
					break;
				}
				case "skyboximage":
				{
					Material val = LoadSkyboxImage(N(value));
					if ((Object)(object)val != (Object)null)
					{
						RenderSettings.skybox = val;
					}
					break;
				}
				case "skyboxtint":
				{
					if (Clr(value, out var c3) && (Object)(object)RenderSettings.skybox != (Object)null && RenderSettings.skybox.HasProperty("_Tint"))
					{
						RenderSettings.skybox.SetColor("_Tint", c3);
					}
					break;
				}
				case "skyboxexposure":
					if ((Object)(object)RenderSettings.skybox != (Object)null && RenderSettings.skybox.HasProperty("_Exposure"))
					{
						RenderSettings.skybox.SetFloat("_Exposure", F(value));
					}
					break;
				case "skyboxrotation":
					if ((Object)(object)RenderSettings.skybox != (Object)null && RenderSettings.skybox.HasProperty("_Rotation"))
					{
						RenderSettings.skybox.SetFloat("_Rotation", F(value));
					}
					break;
				default:
					Plugin.Log.LogWarning((object)("SceneSettings: unknown key '" + setting.Key + "' — ignored."));
					break;
				case "scene":
				case "scenecontains":
					break;
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("SceneSettings apply failed: " + ex.Message));
		}
	}

	private static Material LoadMaterial(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return null;
		}
		string text = Path.Combine(Paths.ConfigPath, "ColorizedInk", "Meshes", name);
		if (!File.Exists(text))
		{
			return null;
		}
		try
		{
			if (!_matBundles.TryGetValue(name, out var value) || (Object)(object)value == (Object)null)
			{
				value = AssetBundle.LoadFromFile(text);
				if ((Object)(object)value == (Object)null)
				{
					return null;
				}
				_matBundles[name] = value;
			}
			Material[] array = value.LoadAllAssets<Material>();
			return (array.Length != 0) ? array[0] : null;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Failed to load material bundle '" + name + "': " + ex.Message));
			return null;
		}
	}

	private static Material LoadSkyboxImage(string name)
	{
		//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_00be: Expected O, but got Unknown
		if (string.IsNullOrEmpty(name))
		{
			return null;
		}
		if (_skyboxImages.TryGetValue(name, out var value) && (Object)(object)value != (Object)null)
		{
			return value;
		}
		Texture2D texture = ResourceManager.GetTexture(name);
		if ((Object)(object)texture == (Object)null)
		{
			Plugin.Log.LogWarning((object)("SceneSettings: skyboxImage '" + name + "' not found in Textures/ (png/jpg)."));
			return null;
		}
		Shader val = Shader.Find("Skybox/Panoramic");
		if ((Object)(object)val == (Object)null)
		{
			Plugin.Log.LogWarning((object)"SceneSettings: 'Skybox/Panoramic' shader not in this build — skyboxImage can't be used.");
			return null;
		}
		Material val2 = new Material(val)
		{
			name = "OmniSkybox:" + name
		};
		val2.SetTexture("_MainTex", (Texture)(object)texture);
		if (val2.HasProperty("_Mapping"))
		{
			val2.SetFloat("_Mapping", 0f);
		}
		if (val2.HasProperty("_ImageType"))
		{
			val2.SetFloat("_ImageType", 0f);
		}
		if (val2.HasProperty("_MirrorOnBack"))
		{
			val2.SetFloat("_MirrorOnBack", 0f);
		}
		if (val2.HasProperty("_Layout"))
		{
			val2.SetFloat("_Layout", 0f);
		}
		((Object)val2).hideFlags = (HideFlags)61;
		_skyboxImages[name] = val2;
		return val2;
	}

	private static string N(JToken t)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Invalid comparison between Unknown and I4
		return ((int)t.Type == 8) ? ((string)t) : ((object)t).ToString();
	}

	private static bool B(JToken t)
	{
		try
		{
			return Extensions.Value<bool>((IEnumerable<JToken>)t);
		}
		catch
		{
			return false;
		}
	}

	private static float F(JToken t)
	{
		try
		{
			return Extensions.Value<float>((IEnumerable<JToken>)t);
		}
		catch
		{
			return 0f;
		}
	}

	private static bool Clr(JToken t, out Color c)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: 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_00b6: Invalid comparison between Unknown and I4
		//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_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		c = Color.white;
		try
		{
			JObject o = (JObject)(object)((t is JObject) ? t : null);
			if (o != null)
			{
				c = new Color(g("r", g("x", 0f)), g("g", g("y", 0f)), g("b", g("z", 0f)), g("a", g("w", 1f)));
				return true;
			}
			Color val = default(Color);
			if ((int)t.Type == 8 && ColorUtility.TryParseHtmlString((string)t, ref val))
			{
				c = val;
				return true;
			}
			float g(string k, float d)
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Invalid comparison between Unknown and I4
				JToken val2 = o[k];
				return (val2 != null && (int)val2.Type != 10) ? Extensions.Value<float>((IEnumerable<JToken>)val2) : d;
			}
		}
		catch
		{
		}
		return false;
	}
}
public class RuleProcessor
{
	private struct CacheKey : IEquatable<CacheKey>
	{
		public int InstanceID;

		public TargetRule Rule;

		public string PropertyName;

		public int MaterialIndex;

		public bool Equals(CacheKey other)
		{
			return InstanceID == other.InstanceID && Rule == other.Rule && MaterialIndex == other.MaterialIndex && PropertyName == other.PropertyName;
		}

		public override int GetHashCode()
		{
			return (InstanceID * 397) ^ (PropertyName?.GetHashCode() ?? 0) ^ MaterialIndex ^ (Rule?.GetHashCode() ?? 0);
		}
	}

	private class CachedState
	{
		public Component Target;

		public TargetRule Rule;

		public object OriginalValue;

		public Action<Component, TargetRule, object> RevertAction;

		public List<MemberStep> Chain;

		public Type FinalMemberType;

		public object EnforceValue;

		public bool EnforceValueCached;

		public Func<Type, object> EnforceFn;
	}

	private class MemberStep
	{
		public MemberInfo Member;

		public int Index = -1;

		public bool All;

		public bool Append;

		public bool Remove;
	}

	private readonly ConfigManager _config;

	private readonly Dictionary<CacheKey, CachedState> _cache = new Dictionary<CacheKey, CachedState>();

	private static readonly List<Component> _sharedCompList = new List<Component>(64);

	private static readonly Queue<Transform> _deepQueue = new Queue<Transform>(256);

	private static readonly List<CacheKey> _deadKeys = new List<CacheKey>(32);

	private static readonly Dictionary<(Type, string), List<MemberStep>> _reflectionCache = new Dictionary<(Type, string), List<MemberStep>>();

	private static readonly Random _rng = new Random();

	private readonly Dictionary<Type, Dictionary<string, bool>> _typeMatchCache = new Dictionary<Type, Dictionary<string, bool>>();

	private static readonly HashSet<(int, TargetRule)> _spawnApplied = new HashSet<(int, TargetRule)>();

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

	private int _sweptRootCount = -1;

	private float _sweepFallbackTimer;

	private readonly HashSet<TargetRule> _warnedNoMatch = new HashSet<TargetRule>();

	private readonly List<Transform> _deepFrontier = new List<Transform>(64);

	private readonly List<Transform> _deepNext = new List<Transform>(64);

	private static readonly Dictionary<string, Type> _typeByName = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);

	private static readonly MaterialPropertyBlock _mpb = new MaterialPropertyBlock();

	private static readonly Dictionary<Type, Func<object, float>> _timeAccessors = new Dictionary<Type, Func<object, float>>();

	internal static void MarkSpawnApplied(Component c, TargetRule r)
	{
		_spawnApplied.Add((((Object)c).GetInstanceID(), r));
	}

	private static bool ConsumeSpawnApplied(Component c, TargetRule r)
	{
		return _spawnApplied.Remove((((Object)c).GetInstanceID(), r));
	}

	public RuleProcessor(ConfigManager config)
	{
		_config = config;
	}

	public void ScanFullScene()
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		_sweptRoots.Clear();
		_warnedNoMatch.Clear();
		Scene activeScene = SceneManager.GetActiveScene();
		GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects();
		foreach (GameObject root in rootGameObjects)
		{
			ProcessObject(root);
		}
	}

	public void AddComponentSweep()
	{
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		if (_config.Rules == null)
		{
			return;
		}
		bool flag = false;
		for (int i = 0; i < _config.Rules.Count; i++)
		{
			if (_config.Rules[i].AddComponent)
			{
				flag = true;
				break;
			}
		}
		if (!flag)
		{
			return;
		}
		Scene activeScene = SceneManager.GetActiveScene();
		int rootCount = ((Scene)(ref activeScene)).rootCount;
		_sweepFallbackTimer -= Time.deltaTime;
		if (rootCount == _sweptRootCount && _sweepFallbackTimer > 0f)
		{
			return;
		}
		_sweptRootCount = rootCount;
		_sweepFallbackTimer = 2f;
		GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects();
		foreach (GameObject val in rootGameObjects)
		{
			if ((Object)(object)val == (Object)null || !_sweptRoots.Add(((Object)val).GetInstanceID()))
			{
				continue;
			}
			Transform transform = val.transform;
			string name = ((Object)val).name;
			for (int k = 0; k < _config.Rules.Count; k++)
			{
				TargetRule targetRule = _config.Rules[k];
				if (targetRule.AddComponent && targetRule.MatchesScene() && ((!string.IsNullOrEmpty(targetRule.RootExact) && name == targetRule.RootExact) || InstantProcessor.MatchAnyPartSet(transform, targetRule.RootContainsPartSets)) && !InstantProcessor.MatchAnyPartSet(transform, targetRule.RootNotContainsPartSets) && (targetRule.RootIndex < 0 || TargetRule.MatchesSiblingIndex(transform, targetRule.RootIndex)))
				{
					ApplyToTarget(val, targetRule);
				}
			}
		}
	}

	public void ProcessObject(GameObject root)
	{
		if ((Object)(object)root == (Object)null || _config.Rules == null || _config.Rules.Count == 0)
		{
			return;
		}
		Transform transform = root.transform;
		if (_config.RulesByRoot.TryGetValue(((Object)root).name, out var value))
		{
			for (int i = 0; i < value.Count; i++)
			{
				ApplyPathing(transform, value[i]);
			}
		}
		for (int j = 0; j < _config.RulesByRootContains.Count; j++)
		{
			KeyValuePair<string, List<TargetRule>> keyValuePair = _config.RulesByRootContains[j];
			if (keyValuePair.Value == null || keyValuePair.Value.Count == 0)
			{
				continue;
			}
			TargetRule targetRule = keyValuePair.Value[0];
			if (InstantProcessor.MatchAnyPartSet(transform, targetRule.RootContainsPartSets) && !InstantProcessor.MatchAnyPartSet(transform, targetRule.RootNotContainsPartSets))
			{
				for (int k = 0; k < keyValuePair.Value.Count; k++)
				{
					ApplyPathing(transform, keyValuePair.Value[k]);
				}
			}
		}
		for (int l = 0; l < _config.RulesByPathContains.Count; l++)
		{
			TargetRule targetRule2 = _config.RulesByPathContains[l];
			if (!targetRule2.MatchesScene() || InstantProcessor.MatchAnyPartSet(transform, targetRule2.PathNotContainsPartSets))
			{
				continue;
			}
			string[][] pathMatchSets = targetRule2.PathMatchSets;
			if (pathMatchSets == null || targetRule2.IsDeepSearch || InstantProcessor.MatchAnyPartSet(transform, pathMatchSets))
			{
				if (targetRule2.IsDeepSearch)
				{
					DoDeepSearch(transform, targetRule2);
				}
				else
				{
					ApplyToTarget(((Component)transform).gameObject, targetRule2);
				}
			}
		}
		for (int m = 0; m < _config.RulesGlobal.Count; m++)
		{
			TargetRule targetRule3 = _config.RulesGlobal[m];
			if (targetRule3.MatchesScene())
			{
				ApplyPathing(transform, targetRule3);
			}
		}
	}

	private void ApplyPathing(Transform root, TargetRule rule)
	{
		if (!rule.MatchesScene())
		{
			return;
		}
		if (string.IsNullOrEmpty(rule.Path) && string.IsNullOrEmpty(rule.PathContains))
		{
			ApplyToTarget(((Component)root).gameObject, rule);
			return;
		}
		if (rule.IsDeepSearch)
		{
			DoDeepSearch(root, rule);
			return;
		}
		string text = ((!string.IsNullOrEmpty(rule.Path)) ? rule.Path : rule.PathContains);
		Transform val = ((!string.IsNullOrEmpty(rule.Path)) ? FindIndexed(root, rule.Path) : root.Find(rule.PathContains));
		if ((Object)(object)val != (Object)null && !NameExcluded(((Object)val).name, rule) && TargetRule.MatchesSiblingIndex(val, rule.PathIndex))
		{
			ApplyToTarget(((Component)val).gameObject, rule);
		}
		else if (_warnedNoMatch.Add(rule))
		{
			Plugin.Log.LogWarning((object)("Rule found nothing: " + rule.ComponentType + " " + rule.PropertyName + " — path '" + text + "' under root '" + ((Object)root).name + "'"));
		}
	}

	private static Transform FindIndexed(Transform root, string path)
	{
		Transform val = root;
		string[] array = path.Split(new char[1] { '/' });
		foreach (string text in array)
		{
			if ((Object)(object)val == (Object)null || text.Length == 0)
			{
				continue;
			}
			int index;
			string text2 = TargetRule.StripIndex(text, out index);
			int num = 0;
			Transform val2 = null;
			for (int j = 0; j < val.childCount; j++)
			{
				Transform child = val.GetChild(j);
				if (!(((Object)child).name != text2) && (index < 0 || num++ == index))
				{
					val2 = child;
					break;
				}
			}
			val = val2;
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
		}
		return val;
	}

	private static bool NameExcluded(string name, TargetRule rule)
	{
		string[] pathNotContainsValues = rule.PathNotContainsValues;
		if (pathNotContainsValues == null)
		{
			return false;
		}
		for (int i = 0; i < pathNotContainsValues.Length; i++)
		{
			if (name.IndexOf(pathNotContainsValues[i], StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		return false;
	}

	private void DoDeepSearch(Transform root, TargetRule rule)
	{
		List<string> list = rule.DeepTargets ?? new List<string> { rule.CleanDeepTarget };
		if (list.Count == 0)
		{
			return;
		}
		List<Transform> list2 = _deepFrontier;
		list2.Clear();
		list2.Add(root);
		List<Transform> list3 = _deepNext;
		list3.Clear();
		for (int i = 0; i < list.Count; i++)
		{
			if (list2.Count <= 0)
			{
				break;
			}
			string value = list[i];
			bool flag = i == list.Count - 1;
			_deepQueue.Clear();
			for (int j = 0; j < list2.Count; j++)
			{
				_deepQueue.Enqueue(list2[j]);
			}
			while (_deepQueue.Count > 0)
			{
				Transform val = _deepQueue.Dequeue();
				if (((Object)val).name.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					if (flag)
					{
						if (!NameExcluded(((Object)val).name, rule))
						{
							ApplyToTarget(((Component)val).gameObject, rule);
						}
					}
					else
					{
						list3.Add(val);
					}
				}
				else
				{
					for (int k = 0; k < val.childCount; k++)
					{
						_deepQueue.Enqueue(val.GetChild(k));
					}
				}
			}
			List<Transform> list4 = list2;
			list2 = list3;
			list3 = list4;
			list3.Clear();
		}
	}

	internal static bool MatchesPath(Transform t, string[] parts)
	{
		if (parts == null || parts.Length == 0)
		{
			return true;
		}
		Transform val = t;
		for (int num = parts.Length - 1; num >= 0; num--)
		{
			bool flag = false;
			while ((Object)(object)val != (Object)null)
			{
				if (((Object)val).name.IndexOf(parts[num], StringComparison.OrdinalIgnoreCase) >= 0)
				{
					flag = true;
					val = val.parent;
					break;
				}
				val = val.parent;
			}
			if (!flag)
			{
				return false;
			}
		}
		return true;
	}

	private bool IsComponentTypeMatch(Type compType, string ruleTargetType)
	{
		if (string.IsNullOrEmpty(ruleTargetType))
		{
			return true;
		}
		if (!_typeMatchCache.TryGetValue(compType, out var value))
		{
			value = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
			_typeMatchCache[compType] = value;
		}
		if (!value.TryGetValue(ruleTargetType, out var value2))
		{
			value2 = (value[ruleTargetType] = compType.Name.IndexOf(ruleTargetType, StringComparison.OrdinalIgnoreCase) >= 0);
		}
		return value2;
	}

	private void ApplyToTarget(GameObject go, TargetRule rule)
	{
		go.GetComponents<Component>(_sharedCompList);
		bool flag = false;
		for (int i = 0; i < _sharedCompList.Count; i++)
		{
			Component val = _sharedCompList[i];
			if ((Object)(object)val == (Object)null || !IsComponentTypeMatch(((object)val).GetType(), rule.ComponentType))
			{
				continue;
			}
			flag = true;
			CacheKey key = new CacheKey
			{
				InstanceID = ((Object)val).GetInstanceID(),
				Rule = rule,
				PropertyName = rule.PropertyName,
				MaterialIndex = rule.MaterialIndex
			};
			if (_cache.ContainsKey(key))
			{
				continue;
			}
			bool flag2 = false;
			object orig = null;
			Action<Component, TargetRule, object> revertAction = null;
			if (rule.NormalizedLeafName == "enabled" && rule.PropertyName.IndexOf('.') < 0)
			{
				Behaviour val2 = (Behaviour)(object)((val is Behaviour) ? val : null);
				if (val2 != null && rule.ParsedValue is bool enabled)
				{
					orig = val2.enabled;
					val2.enabled = enabled;
					flag2 = true;
					revertAction = RevertBehaviour;
				}
			}
			if (!flag2)
			{
				Graphic val3 = (Graphic)(object)((val is Graphic) ? val : null);
				if (val3 != null && TryPatchGraphic(val3, rule, out orig))
				{
					flag2 = true;
					revertAction = RevertGraphic;
				}
				else
				{
					ParticleSystem val4 = (ParticleSystem)(object)((val is ParticleSystem) ? val : null);
					if (val4 != null && TryPatchParticleSystem(val4, rule))
					{
						flag2 = true;
					}
					else
					{
						Renderer val5 = (Renderer)(object)((val is Renderer) ? val : null);
						object originalValue;
						if (val5 != null && TryPatchRendererMaterial(val5, rule))
						{
							flag2 = true;
							revertAction = RevertRenderer;
						}
						else if ((val is LineRenderer || val is TrailRenderer) && TryPatchTrailColors(val, rule))
						{
							flag2 = true;
						}
						else if (ConsumeSpawnApplied(val, rule))
						{
							flag2 = true;
							revertAction = RevertReflection;
						}
						else if (TryPatchViaReflection(val, rule, out originalValue))
						{
							orig = originalValue;
							flag2 = true;
							revertAction = RevertReflection;
						}
					}
				}
			}
			if (!flag2)
			{
				continue;
			}
			Plugin.Log.LogInfo((object)("Rule applied: " + rule.PropertyName + " on " + ((object)val).GetType().Name + " (" + ((Object)go).name + ")"));
			List<MemberStep> list = ResolveChain(((object)val).GetType(), rule.PropertyName);
			Type type = null;
			if (list != null)
			{
				MemberStep memberStep = list[list.Count - 1];
				MemberInfo member = memberStep.Member;
				type = ((member is PropertyInfo propertyInfo) ? propertyInfo.PropertyType : ((FieldInfo)member).FieldType);
				if (memberStep.Index >= 0 || memberStep.All)
				{
					type = ElementType(type) ?? type;
				}
			}
			_cache[key] = new CachedState
			{
				Target = val,
				Rule = rule,
				OriginalValue = orig,
				RevertAction = revertAction,
				Chain = list,
				FinalMemberType = type
			};
		}
		_sharedCompList.Clear();
		if (flag || !rule.AddComponent || string.IsNullOrEmpty(rule.ComponentType))
		{
			return;
		}
		Type type2 = FindTypeByName(rule.ComponentType);
		if (type2 == null || !typeof(Component).IsAssignableFrom(type2))
		{
			Plugin.Log.LogWarning((object)("AddComponent: type '" + rule.ComponentType + "' not found or not a Component (" + ((Object)go).name + ")."));
			return;
		}
		try
		{
			Component val6 = go.AddComponent(type2);
			Plugin.Log.LogInfo((object)("AddComponent: created " + type2.Name + " on " + ((Object)go).name));
			if (!string.IsNullOrEmpty(rule.PropertyName))
			{
				CacheKey key2 = new CacheKey
				{
					InstanceID = ((Object)val6).GetInstanceID(),
					Rule = rule,
					PropertyName = rule.PropertyName,
					MaterialIndex = rule.MaterialIndex
				};
				if (TryPatchViaReflection(val6, rule, out var originalValue2))
				{
					_cache[key2] = new CachedState
					{
						Target = val6,
						Rule = rule,
						OriginalValue = originalValue2,
						RevertAction = RevertReflection,
						Chain = ResolveChain(type2, rule.PropertyName)
					};
				}
				else
				{
					Plugin.Log.LogWarning((object)("AddComponent: '" + rule.PropertyName + "' did not apply on new " + type2.Name + " (" + ((Object)go).name + ") — check the member names."));
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("AddComponent failed (" + rule.ComponentType + " on " + ((Object)go).name + "): " + ex.Message));
		}
	}

	private static Type FindTypeByName(string name)
	{
		if (_typeByName.TryGetValue(name, out var value))
		{
			return value;
		}
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly in assemblies)
		{
			try
			{
				value = assembly.GetType(name) ?? assembly.GetTypes().FirstOrDefault((Type x) => x.Name == name);
			}
			catch
			{
				continue;
			}
			if (value != null)
			{
				_typeByName[name] = value;
				return value;
			}
		}
		return null;
	}

	private static void RevertBehaviour(Component comp, TargetRule rule, object orig)
	{
		Behaviour val = (Behaviour)(object)((comp is Behaviour) ? comp : null);
		if (val != null && orig is bool enabled)
		{
			val.enabled = enabled;
		}
	}

	private static bool TryPatchGraphic(Graphic g, TargetRule rule, out object orig)
	{
		//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_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		orig = null;
		if (rule.ParsedValue == null)
		{
			return false;
		}
		if (rule.IsColorRule && rule.ParsedValue is Color val)
		{
			orig = g.color;
			if (g.color != val)
			{
				g.color = val;
			}
			return true;
		}
		Image val2 = (Image)(object)((g is Image) ? g : null);
		if (val2 != null && rule.NormalizedLeafName == "sprite")
		{
			object parsedValue = rule.ParsedValue;
			Sprite val3 = (Sprite)((parsedValue is Sprite) ? parsedValue : null);
			if (val3 != null)
			{
				orig = val2.sprite;
				if ((Object)(object)val2.sprite != (Object)(object)val3)
				{
					val2.sprite = val3;
				}
				return true;
			}
		}
		RawImage val4 = (RawImage)(object)((g is RawImage) ? g : null);
		if (val4 != null && (rule.NormalizedLeafName == "texture" || rule.NormalizedLeafName == "maintex"))
		{
			object parsedValue2 = rule.ParsedValue;
			Texture2D val5 = (Texture2D)((parsedValue2 is Texture2D) ? parsedValue2 : null);
			if (val5 != null)
			{
				orig = val4.texture;
				if ((Object)(object)val4.texture != (Object)(object)val5)
				{
					val4.texture = (Texture)(object)val5;
				}