Decompiled source of OmniReplacer v1.0.0
BepInEx/plugins/OmniReplacer.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
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; }