Decompiled source of ULTRAESPANOL o UltraEsp v2.1.0
plugins/UltrakULL/UltrakULL.dll
Decompiled 6 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.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using AngryLevelLoader.Containers; using AngryLevelLoader.Fields; using AngryLevelLoader.Managers; using AngryLevelLoader.Notifications; using AngryLevelLoader.Patches; using AngryLevelLoader.UserInterface; using AngryUiComponents; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Discord; using GameConsole; using GameConsole.CommandTree; using GreyAnnouncer.AudioClipLoad; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PluginConfig.API; using PluginConfig.API.Decorators; using PluginConfig.API.Functionals; using Sandbox; using ScriptableObjects; using SettingsMenu.Components; using TMPro; using UltrakULL.Harmony_Patches; using UltrakULL.audio; using UltrakULL.json; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.Networking; using UnityEngine.SceneManagement; using UnityEngine.UI; using plog; using plog.Models; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("AngryLevelLoader")] [assembly: IgnoresAccessChecksTo("AngryUiComponents")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("plog")] [assembly: IgnoresAccessChecksTo("RudeLevelScripts.Essentials")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: AssemblyCompany("UltrakULL")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+741cfab974b6ba7b1dfcc673ec496a58ad58c0ba")] [assembly: AssemblyProduct("UltrakULL")] [assembly: AssemblyTitle("UltrakULL")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class ExtensionMarkerAttribute : Attribute { public ExtensionMarkerAttribute(string name) { } } } namespace GreyAnnouncer.AudioClipLoad { public static class StringExtension { [SpecialName] public sealed class <G>$34505F560D9EACF86A87F3ED1F85E448 { [SpecialName] public static class <M>$D2C87DABA57FE47E39897F3C24A0705C { } [ExtensionMarker("<M>$D2C87DABA57FE47E39897F3C24A0705C")] public AudioType TryGetAudioType() { throw null; } } public static AudioType TryGetAudioType(this string value) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_0025: Unknown result type (might be due to invalid IL or missing references) string[] array = value.Split('.'); for (int i = 1; i <= array.Length; i++) { AudioType unityAudioType = GetUnityAudioType(array[^i]); if ((int)unityAudioType > 0) { return unityAudioType; } } return (AudioType)0; } [Description("https://docs.unity3d.com/2022.3/Documentation//ScriptReference/AudioType.html")] private static AudioType GetUnityAudioType(string extension) { //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) if (1 == 0) { } AudioType result = (AudioType)(extension switch { "aac" => 1, "aiff" => 2, "aif" => 2, "aifc" => 2, "it" => 10, "mod" => 12, "mp3" => 13, "mpga" => 13, "mpeg" => 13, "ogg" => 14, "s3m" => 17, "wav" => 20, "xm" => 21, "xma" => 22, "vag" => 23, _ => 0, }); if (1 == 0) { } return result; } } } namespace UltrakULL { public class NeedDebugMode : Attribute { } public class NeedRework : Attribute { } public class NonSenseNeedChange : Attribute { } [AttributeUsage(AttributeTargets.Class)] public class PatchForMod : Attribute { public string Guid; public string ClassName; public string MethodName; public string[] Args; public PatchForMod(string guid, string className = null, string methodName = null, string[] args = null) { Guid = guid; ClassName = className; MethodName = methodName; Args = args; } } public static class SubtitledAudioSourcesReplacer { public static SubtitledSourcesConfig Config; public static void ReplaceSubsAndAudio() { if (!TryLoadMetadata(out var references)) { return; } List<(AudioSource source, string path)> audioReplacements = new List<(AudioSource, string)>(); foreach (SubtitledObjectReference item in references) { foreach (string @object in item.Objects) { GameObject val = SceneObjects.GetObject(@object); SubtitledAudioSource component = val.GetComponent<SubtitledAudioSource>(); if (ActiveDubbingEnabled()) { audioReplacements.Add((val.GetComponentInChildren<AudioSource>(), Path.Combine(AudioSwapper.SpeechFolder, item.AudioPath))); } if ((Object)(object)component != (Object)null) { SubtitleData val2 = item.ToSubtitleData(); if (val2.lines.Any((SubtitleDataLine line) => !StringHelper.IsEmpty(line.subtitle))) { ReflectionUtils.SetPrivate<SubtitledAudioSource, SubtitleData>(component, typeof(SubtitledAudioSource), "subtitles", val2); } } } } AudioSwapper.WhenReady(delegate { foreach (var item2 in audioReplacements) { item2.source.clip = AudioSwapper.SwapClipWithFile(item2.source.clip, item2.path); } }); } private static bool ActiveDubbingEnabled() { return Settings.activeDubbing.Value; } private static bool TryLoadMetadata(out List<SubtitledObjectReference> references) { if (Config != null && Config.Scenes.TryGetValue(SceneObjects.GetCurrentSceneName(), out references)) { return true; } references = null; return false; } } public class SubtitledSourcesConfig { [JsonProperty("scenes")] public Dictionary<string, List<SubtitledObjectReference>> Scenes; } public class SubtitledObjectReference { [JsonProperty("objects")] public List<string> Objects; [JsonProperty("audio")] public string AudioPath; [JsonProperty("lines")] public List<Line> Lines; public SubtitleData ToSubtitleData() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown return new SubtitleData { lines = ((IEnumerable<Line>)Lines).Select((Func<Line, SubtitleDataLine>)((Line line) => new SubtitleDataLine { subtitle = ColorIfNecessary(LanguageManager.CurrentLanguage.subtitles.GetField(line.Reference), line.Color), time = line.Delay })).ToArray() }; } private static string ColorIfNecessary(string line, string color) { if (color == null || StringHelper.IsEmpty(line)) { return line; } return "<color=#" + color + ">" + line + "</color>"; } } public class Line { [JsonProperty("line")] public string Reference; [JsonProperty("delay")] public float Delay; [JsonProperty("color")] public string Color; } public static class SubtitleLocalizer { private static readonly Dictionary<string, string> map = new Dictionary<string, string>(StringComparer.Ordinal); private static readonly MethodInfo DisplaySubtitle = AccessTools.Method(typeof(SubtitleController), "DisplaySubtitle", new Type[3] { typeof(string), typeof(AudioSource), typeof(bool) }, (Type[])null); private static readonly MethodInfo LocalizeMethod = AccessTools.Method(typeof(SubtitleLocalizer), "Localize", (Type[])null, (Type[])null); public static void Initialize() { LanguageManager.OnLanguageChanged += delegate { Rebuild(); }; } private static void Rebuild() { map.Clear(); Subtitles subtitles = LanguageManager.allLanguages.Values.FirstOrDefault((Lang l) => l.IsEnglish)?.Json?.subtitles; Subtitles subtitles2 = LanguageManager.Current?.Json?.subtitles; if (subtitles == null || subtitles2 == null) { return; } FieldInfo[] fields = typeof(Subtitles).GetFields(); foreach (FieldInfo fieldInfo in fields) { string text = fieldInfo.GetValue(subtitles) as string; string text2 = fieldInfo.GetValue(subtitles2) as string; if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(text2) && text != text2) { map[text] = text2; } } } public static string Localize(string caption) { if (string.IsNullOrEmpty(caption) || LanguageManager.IsEnglish) { return caption; } string value; return map.TryGetValue(caption, out value) ? value : caption; } public static IEnumerable<CodeInstruction> InjectLocalize(IEnumerable<CodeInstruction> instructions) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown List<CodeInstruction> list = instructions.ToList(); List<CodeInstruction> list2 = new List<CodeInstruction>(list.Count + 4); for (int i = 0; i < list.Count; i++) { list2.Add(list[i]); if (i + 3 < list.Count && IsDisplaySubtitleCall(list[i + 3])) { list2.Add(new CodeInstruction(OpCodes.Call, (object)LocalizeMethod)); } } return list2; } private static bool IsDisplaySubtitleCall(CodeInstruction instruction) { return instruction.opcode == OpCodes.Callvirt && CodeInstructionExtensions.OperandIs(instruction, (MemberInfo)DisplaySubtitle); } } public sealed class CommandToRegister : CommandRoot, IConsoleLogger { public override string Name => "ultrakull"; public override string Description => "tons of setting"; public Logger Log { get; } = new Logger("ultrakull"); public CommandToRegister(Console con) : base(con) { }//IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown public override Branch BuildTree(Console con) { return CommandRoot.Branch(((CommandRoot)this).Name, (Node[])(object)new Node[4] { (Node)CommandRoot.Leaf("migrate", (Action)delegate { LegacyLanguageMigrator.LegacyMigrationResult legacyMigrationResult = LegacyLanguageMigrator.Migrate(); Log.Info($"Migrated {legacyMigrationResult.MigratedLanguages} legacy language package(s).", (IEnumerable<Tag>)null, (string)null, (object)null); foreach (string warning in legacyMigrationResult.Warnings) { Log.Warning(warning, (IEnumerable<Tag>)null, (string)null, (object)null); } if (legacyMigrationResult.SkippedLanguages > 0) { Log.Warning($"Skipped {legacyMigrationResult.SkippedLanguages} language package(s). See messages above.", (IEnumerable<Tag>)null, (string)null, (object)null); } if (legacyMigrationResult.MigratedLanguages > 0) { Log.Info("A copy of every migrated file was kept in \"" + legacyMigrationResult.BackupDirectory + "\".", (IEnumerable<Tag>)null, (string)null, (object)null); } Log.Info("Restart the game before editing or removing the backup.", (IEnumerable<Tag>)null, (string)null, (object)null); }, false), (Node)CommandRoot.Leaf("dhm", (Action)delegate { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) Logging.Info("=========================", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Commands\\CommandsToRegister.cs"); Scene activeScene = SceneManager.GetActiveScene(); GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); foreach (GameObject val in rootGameObjects) { HudMessage[] componentsInChildren = val.GetComponentsInChildren<HudMessage>(true); foreach (HudMessage val2 in componentsInChildren) { string arg = (((Object)(object)val2.actionReference == (Object)null) ? "-" : val2.actionReference.action.id.ToString()); Logging.Info($"\n[HudMsgScan] obj='{((Object)((Component)val2).gameObject).name}' (active={((Component)val2).gameObject.activeInHierarchy})" + "\n msg ='" + val2.message + "'\n m2 ='" + val2.message2 + "'\n fm = '" + GetFullMessage(val2) + "'" + $"\n advanced={val2.advancedMessage} action={arg}" + "\n ------------------------------", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Commands\\CommandsToRegister.cs"); } } }, false), (Node)CommandRoot.Branch("gene", (Node[])(object)new Node[1] { (Node)CommandRoot.Leaf("angry", (Action)delegate { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown try { string path; JObject val = LoadAngryFile(out path); if (val["angryUi"] == null) { JObject val2 = new JObject(); string[] array = (from property in val.Properties() select property.Name).ToArray(); foreach (string text in array) { if (!(text == "angryBundles")) { val2[text] = val[text]; val.Remove(text); } } if (((JToken)val2).HasValues) { val["angryUi"] = (JToken)(object)val2; Log.Info("Moved the flat UI sections under \"angryUi\".", (IEnumerable<Tag>)null, (string)null, (object)null); } } JToken obj = val["angryUi"]; JObject val3 = (JObject)(object)((obj is JObject) ? obj : null); if (val3 != null) { FillGaps(val3, BlankOf(new AngryUi())); } else { val["angryUi"] = (JToken)(object)BlankOf(new AngryUi()); } File.WriteAllText(path, ((JToken)val).ToString((Formatting)1, Array.Empty<JsonConverter>())); Log.Info("Written " + path + " (angry ui).", (IEnumerable<Tag>)null, (string)null, (object)null); } catch (Exception arg) { Log.Error($"Failed to write the angry ui: {arg}", (IEnumerable<Tag>)null, (string)null, (object)null); } }, false) }), (Node)CommandRoot.Branch("scan", (Node[])(object)new Node[1] { (Node)CommandRoot.Leaf("angrylevel", (Action)delegate { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_007b: Expected O, but got Unknown //IL_0081: Expected O, but got Unknown //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown //IL_0110: Expected O, but got Unknown //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Expected O, but got Unknown //IL_0174: Expected O, but got Unknown //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Expected O, but got Unknown //IL_01a1: Expected O, but got Unknown try { if (!AngrySceneTracker.InAngryLevel) { Log.Warning("Not in an Angry level. Load a custom level first, then scan it.", (IEnumerable<Tag>)null, (string)null, (object)null); } else { string path; JObject val = LoadAngryFile(out path); JToken obj = val["angryBundles"]; JToken obj2 = ((obj != null) ? obj[(object)"bundles"] : null); JObject val2 = (JObject)(object)((obj2 is JObject) ? obj2 : null); if (val2 == null) { JObject val3 = new JObject(); JObject val4 = new JObject(); val2 = val4; val3["bundles"] = (JToken)val4; val["angryBundles"] = (JToken)val3; } string bundleGuid = AngryLevelText.BundleGuid; string levelId = AngryLevelText.LevelId; if (string.IsNullOrEmpty(bundleGuid) || string.IsNullOrEmpty(levelId)) { Log.Error("In an Angry level but its ids are empty... (bundle='" + bundleGuid + "', level='" + levelId + "').", (IEnumerable<Tag>)null, (string)null, (object)null); } else { JToken obj3 = val2[bundleGuid]; JObject val5 = (JObject)(object)((obj3 is JObject) ? obj3 : null); if (val5 == null) { JObject obj4 = val2; JObject val6 = new JObject(); val5 = val6; obj4[bundleGuid] = (JToken)val6; } JObject val7 = val5; if (val7["bundleName"] == null) { JToken val8 = (val7["bundleName"] = JToken.op_Implicit(AngrySceneManager.currentBundleContainer.BundleName)); } JToken obj5 = val5["levels"]; JObject val10 = (JObject)(object)((obj5 is JObject) ? obj5 : null); if (val10 == null) { JObject obj6 = val5; JObject val11 = new JObject(); val10 = val11; obj6["levels"] = (JToken)val11; } JToken obj7 = val10[levelId]; JObject val12 = (JObject)(object)((obj7 is JObject) ? obj7 : null); if (val12 == null) { JObject obj8 = val10; JObject val13 = new JObject(); val12 = val13; obj8[levelId] = (JToken)val13; } FillGaps(val12, BlankOf(new AngryLevel())); JArray array = ArrayIn(val12, "books"); JArray array2 = ArrayIn(val12, "hudMessages"); Scene activeScene = SceneManager.GetActiveScene(); GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); foreach (GameObject val14 in rootGameObjects) { HudMessage[] componentsInChildren = val14.GetComponentsInChildren<HudMessage>(true); foreach (HudMessage hm in componentsInChildren) { AddMatch(array2, GetFullMessage(hm), ""); } Readable[] componentsInChildren2 = val14.GetComponentsInChildren<Readable>(true); foreach (Readable val15 in componentsInChildren2) { AddMatch(array, val15.content, ""); } } File.WriteAllText(path, ((JToken)val).ToString((Formatting)1, Array.Empty<JsonConverter>())); Log.Info("Written " + path + ": " + AngrySceneManager.currentBundleContainer.BundleName + " / " + levelId + ".", (IEnumerable<Tag>)null, (string)null, (object)null); } } } catch (Exception arg) { Log.Error($"Failed to scan the angry level: {arg}", (IEnumerable<Tag>)null, (string)null, (object)null); } }, false) }) }); } private static string GetFullMessage(HudMessage hm) { if (hm.advancedMessage) { return hm.message; } if ((Object)(object)hm.actionReference == (Object)null) { return hm.message; } return hm.message + "{0}" + hm.message2; } private static JObject LoadAngryFile(out string path) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) string angryLevelFolder = LanguageManager.Current.AngryLevelFolder; path = Path.Combine(angryLevelFolder, "angry.json"); Directory.CreateDirectory(angryLevelFolder); return (JObject)(File.Exists(path) ? (((object)JObject.Parse(File.ReadAllText(path))) ?? ((object)new JObject())) : ((object)new JObject())); } private static JObject BlankOf(object target) { JObject val = JObject.FromObject(target); foreach (JToken item in (from t in ((JContainer)val).Descendants() where (int)t.Type == 10 select t).ToList()) { item.Replace(JToken.op_Implicit("")); } return val; } private static void FillGaps(JObject node, JObject template) { foreach (JProperty item in template.Properties()) { JToken value = item.Value; JObject val = (JObject)(object)((value is JObject) ? value : null); if (val != null) { JToken obj = node[item.Name]; JObject val2 = (JObject)(object)((obj is JObject) ? obj : null); if (val2 != null) { FillGaps(val2, val); } else { node[item.Name] = ((JToken)val).DeepClone(); } } else if (node[item.Name] == null) { node[item.Name] = item.Value.DeepClone(); } } } private static JArray ArrayIn(JObject parent, string name) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown JToken obj = parent[name]; JArray val = (JArray)(object)((obj is JArray) ? obj : null); if (val != null) { return val; } return (JArray)(object)(parent[name] = (JToken)new JArray()); } private static void AddMatch(JArray array, string match, string text) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown if (string.IsNullOrEmpty(match)) { return; } foreach (JToken item in array) { JObject val = (JObject)(object)((item is JObject) ? item : null); if (val != null && (string)val["match"] == match) { return; } } array.Add((JToken)new JObject { ["match"] = JToken.op_Implicit(match), ["text"] = JToken.op_Implicit(text) }); } } [HarmonyPatch(typeof(Console))] public class ConsolePatcher { [HarmonyPatch("Awake")] [HarmonyPrefix] public static void AddConsoleCommands(Console __instance) { CommandToRegister commandToRegister = new CommandToRegister(__instance); __instance.RegisterCommand((ICommand)(object)commandToRegister); } } public static class ConfigPaths { public static string RootDirectory => Path.Combine(Paths.ConfigPath, "ultrakull"); public static string LanguagesDirectory => Path.Combine(RootDirectory, "languages"); public static string TemplatesDirectory => Path.Combine(RootDirectory, "templates"); public static string LegacyBackupDirectory => Path.Combine(RootDirectory, "legacy-backup"); public static string SettingsFile => Path.Combine(RootDirectory, "lastLang.cfg"); public static string GetLegacyAudioDirectory(string languageId) { return Path.Combine(RootDirectory, "audio", languageId); } public static string GetLegacyTextureDirectory(string languageId) { return Path.Combine(RootDirectory, "textures", languageId); } public static string GetLegacyAngryDirectory(string languageId) { return Path.Combine(RootDirectory, "angry", languageId); } public static string GetFontBundlePath(string languageId) { return Path.Combine(MainPatch.ModFolder, "fonts", languageId); } public static string GetUIOverridePath(string languageId) { return Path.Combine(MainPatch.ModFolder, "fonts", languageId, "layout.json"); } } public static class Core { public static bool wasLanguageReset; public static void LocalizeScene(GameObject canvasObj, bool sceneEntry) { string currentSceneName = SceneObjects.GetCurrentSceneName(); if (currentSceneName == "Intro" || currentSceneName == "Bootstrap") { return; } if (!Object.op_Implicit((Object)(object)canvasObj)) { Logging.Fatal("UNABLE TO FIND CANVAS IN CURRENT SCENE", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Core.cs"); return; } string text = currentSceneName; string text2 = text; if (text2 == "Main Menu") { if (wasLanguageReset) { wasLanguageReset = false; HudMessageReceiver instance = MonoSingleton<HudMessageReceiver>.Instance; if (instance != null) { instance.SendHudMessage("<color=orange>The currently set language file could not be loaded.\nLanguage has been reset to English to avoid problems.</color>", "", "", 0, false, false, true); } } MainMenu.Patch(canvasObj); Options.Patch(canvasObj); } else { if (sceneEntry) { UILayoutOverride.AdjustOptionTextPosition(); } if (LanguageManager.IsEnglish) { Logging.Warn("Current language is English, not patching.", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Core.cs"); return; } Logging.Message("Regular scene", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Core.cs"); PatchingBaseElements(canvasObj); LevelPatcher.Patch(currentSceneName, canvasObj); } } private static void PatchingBaseElements(GameObject canvasObj) { Logging.Message("Attempting to patch base elements", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Core.cs"); try { _PauseMenu.PatchPauseMenu(canvasObj); } catch (Exception ex) { Logging.Error(ex.ToString(), addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Core.cs"); } try { Cheats.PatchCheatConsentPanel(canvasObj); } catch (Exception ex2) { Logging.Error(ex2.ToString(), addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Core.cs"); } try { Sandbox.PatchAlterMenu(); } catch (Exception ex3) { Logging.Error(ex3.ToString(), addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Core.cs"); } try { HUDMessages.PatchDeathScreen(canvasObj); } catch (Exception ex4) { Logging.Error(ex4.ToString(), addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Core.cs"); } try { LevelStatWindow.PatchStats(canvasObj); } catch (Exception ex5) { Logging.Error(ex5.ToString(), addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Core.cs"); } try { HUDMessages.PatchMisc(canvasObj); } catch (Exception ex6) { Logging.Error(ex6.ToString(), addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Core.cs"); } try { Options.Patch(canvasObj); } catch (Exception ex7) { Logging.Error(ex7.ToString(), addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Core.cs"); } Logging.Message("Base elements patched", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Core.cs"); } } internal static class DependencyGuid { public const string ANGRY_LEVEL_LOADER = "com.eternalUnion.angryLevelLoader"; } public static class LocalizationExtensions { internal static bool DebugMode; public static T Localize<T>(this T target, string translation, bool uppercase = false) where T : Component { if ((Object)(object)target == (Object)null) { return target; } object obj = target; TMP_Text val = (TMP_Text)((obj is TMP_Text) ? obj : null); if (val == null) { object obj2 = target; Text val2 = (Text)((obj2 is Text) ? obj2 : null); if (val2 != null) { val2.text = translation.Or(val2.text).ToUpperIf(uppercase); } } else { val.text = translation.Or(val.text).ToUpperIf(uppercase); } return target; } public static OptionData Localize(this OptionData option, string translation, bool uppercase = false) { if (option == null) { return option; } option.text = translation.Or(option.text).ToUpperIf(uppercase); return option; } public static T Localize<T>(this GameObject parent, string translation, string[] path, bool uppercase = false, bool saveToCache = true) where T : Component { return SceneObjects.FindComponent<T>(parent, path).Localize(translation, uppercase); } public static string Or(this string translation, string original) { return StringHelper.IsEmpty(translation) ? original : Penis(original, translation); } public static string ToUpperIf(this string text, bool uppercase) { return uppercase ? text.ToUpper() : text; } private static string Penis(string original, string replacement) { original = ((!DebugMode) ? replacement : (original + "/" + replacement)); return original; } public static string Format(string format, params object[] parts) { if (string.IsNullOrEmpty(format) || parts == null || parts.Length == 0) { return null; } foreach (object obj in parts) { if (obj == null || (obj is string text && StringHelper.IsEmpty(text))) { return null; } } try { return string.Format(format, parts); } catch (FormatException) { Logging.Warn("Format failed for template: '" + format + "'", addCallerFilePath: true, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Extensions\\LocalizationExtensions.cs"); return null; } } public static string FormatWith(this string format, params object[] parts) { return Format(format, parts); } } public class FontConfig { public string FontPackName; public string MainFont; public string TerminalFont; public string MuseumFont; public string SecretFont; } public static class FontManager { public static bool TMPFontReady; public static TMP_FontAsset MeseumFontAsset; public static TMP_FontAsset TwinFont; private static TMP_FontAsset defaultMainFont; private static TMP_FontAsset defaultMuseumFont; private static TMP_FontAsset defaultTerminalFont; private static TMP_FontAsset defaultSecretFont; public static void Initialize() { LanguageManager.OnLanguageChanged += ApplyLanguageFonts; } public static void LoadFonts() { Logging.Message("Loading font resource bundle...", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\FontManager.cs"); AssetBundle val = AssetBundle.LoadFromFile(Path.Combine(MainPatch.ModFolder, "basegameasset.bundle")); MeseumFontAsset = val.LoadAsset<TMP_FontAsset>("GFSGaraldus SDF"); TwinFont = val.LoadAsset<TMP_FontAsset>("VCR_OSD_MONO_TWINFONT"); TMPFontReady = true; } public static void LoadLangFonts(Lang lang) { if ((Object)(object)lang.FontBundle != (Object)null) { return; } string fontBundlePath = ConfigPaths.GetFontBundlePath(lang.Name); FontConfig fontConfig = JsonConvert.DeserializeObject<FontConfig>(File.ReadAllText(Path.Combine(fontBundlePath, "fontconfig.json"))); AssetBundle val = AssetBundle.LoadFromFile(Path.Combine(fontBundlePath, fontConfig.FontPackName)); if ((Object)(object)val == (Object)null) { Logging.Warn("Failed to load language font bundle: " + fontBundlePath, addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\FontManager.cs"); return; } StringBuilder stringBuilder = new StringBuilder(); TMP_FontAsset[] array = val.LoadAllAssets<TMP_FontAsset>(); foreach (TMP_FontAsset val2 in array) { stringBuilder.AppendLine(((Object)val2).name); } Logging.Info(stringBuilder.ToString() ?? "", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\FontManager.cs"); lang.FontBundle = val; lang.MainFontAsset = val.LoadAsset<TMP_FontAsset>(fontConfig.MainFont); lang.TerminalAsset = val.LoadAsset<TMP_FontAsset>(fontConfig.TerminalFont); lang.MuseumAsset = val.LoadAsset<TMP_FontAsset>(fontConfig.MuseumFont); lang.SecretTerminalAsset = val.LoadAsset<TMP_FontAsset>(fontConfig.SecretFont); } private static void ResolveFonts(Lang lang) { Lang lang2 = lang; if (lang2.MainFontAsset == null) { TMP_FontAsset val = (lang2.MainFontAsset = defaultMainFont); } lang2 = lang; if (lang2.MuseumAsset == null) { TMP_FontAsset val = (lang2.MuseumAsset = defaultMuseumFont); } lang2 = lang; if (lang2.TerminalAsset == null) { TMP_FontAsset val = (lang2.TerminalAsset = defaultTerminalFont); } lang2 = lang; if (lang2.SecretTerminalAsset == null) { TMP_FontAsset val = (lang2.SecretTerminalAsset = defaultSecretFont); } } private static void ApplyLanguageFonts(ValueChangedEvent<Lang> change) { if (!TMPFontReady) { return; } RemoveFallbacksOf(change.OldValue); Lang newValue = change.NewValue; if (newValue != null && !newValue.IsEnglish) { LoadLangFonts(newValue); ResolveFonts(newValue); if (newValue.UseFontFallback) { RegisterFallbacksForLoadedFonts(newValue); } } } public static void RefreshFallback() { Lang current = LanguageManager.Current; if (TMPFontReady && current != null && !current.IsEnglish && current.UseFontFallback) { RegisterFallbacksForLoadedFonts(current); } } private static void RegisterFallbacksForLoadedFonts(Lang lang) { TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>(); foreach (TMP_FontAsset val in array) { if ((Object)(object)val == (Object)null) { continue; } string text = ((Object)val).name?.ToLowerInvariant(); if (!string.IsNullOrEmpty(text) && !text.Contains("_alignedto_") && !((Object)(object)val == (Object)(object)lang.MainFontAsset) && !((Object)(object)val == (Object)(object)lang.MuseumAsset) && !((Object)(object)val == (Object)(object)lang.TerminalAsset) && !((Object)(object)val == (Object)(object)lang.SecretTerminalAsset)) { TMP_FontAsset fallback = null; if (text.Contains("tahoma")) { fallback = lang.TerminalAsset; } else if (text.Contains("bittypix")) { fallback = lang.SecretTerminalAsset; } else if (text.Contains("garaldus") || text.Contains("garamond") || text.Contains("museum")) { fallback = lang.MuseumAsset; } else if (text.Contains("vcr-osd-replayed")) { fallback = lang.MainFontAsset; } else if ((Object)(object)val == (Object)(object)TwinFont) { fallback = lang.MainFontAsset; } AddFallback(lang, val, fallback); } } } public static TMP_FontAsset GetTwinFont(string sourceFontName) { string text = sourceFontName?.ToLowerInvariant(); bool flag = !string.IsNullOrEmpty(text) && (text.Contains("garaldus") || text.Contains("garamond") || text.Contains("museum")); TMP_FontAsset val = (flag ? (MeseumFontAsset ?? TwinFont) : TwinFont); Lang current = LanguageManager.Current; if (current == null || current.IsEnglish || current.UseFontFallback) { return val; } return flag ? (current.MuseumAsset ?? current.MainFontAsset ?? val) : (current.MainFontAsset ?? val); } private static void AddFallback(Lang lang, TMP_FontAsset primary, TMP_FontAsset fallback) { if (!((Object)(object)primary == (Object)null) && !((Object)(object)fallback == (Object)null) && !((Object)(object)primary == (Object)(object)fallback)) { if (primary.fallbackFontAssetTable == null) { List<TMP_FontAsset> list = (primary.fallbackFontAssetTable = new List<TMP_FontAsset>()); } if (!primary.fallbackFontAssetTable.Contains(fallback)) { primary.fallbackFontAssetTable.Add(fallback); lang.AppliedFallbacks.Add((primary, fallback)); } } } private static void RemoveFallbacksOf(Lang lang) { if (lang == null) { return; } foreach (var (val, item) in lang.AppliedFallbacks) { if (val != null) { val.fallbackFontAssetTable?.Remove(item); } } lang.AppliedFallbacks.Clear(); } } public static class LegacyLanguageMigrator { public struct LegacyMigrationResult { public int MigratedLanguages; public int SkippedLanguages; public IReadOnlyList<string> Warnings; public string BackupDirectory; public LegacyMigrationResult(int migrated, int skipped, IReadOnlyList<string> warnings, string backupDirectory) { MigratedLanguages = migrated; SkippedLanguages = skipped; Warnings = warnings; BackupDirectory = backupDirectory; } } public static LegacyMigrationResult Migrate() { int num = 0; int num2 = 0; List<string> list = new List<string>(); if (!Directory.Exists(ConfigPaths.RootDirectory)) { return new LegacyMigrationResult(0, 0, Array.Empty<string>(), null); } string legacyBackupDirectory = ConfigPaths.LegacyBackupDirectory; MirrorLegacyLayout(legacyBackupDirectory); foreach (string item in Directory.EnumerateFiles(ConfigPaths.RootDirectory, "*.json", SearchOption.TopDirectoryOnly)) { try { if (MoveToPackage(item)) { num++; } } catch (Exception ex) { num2++; list.Add("Skipped '" + item + "': " + ex.Message); } } return new LegacyMigrationResult(num, num2, list, legacyBackupDirectory); } private static void MirrorLegacyLayout(string backupRoot) { Directory.CreateDirectory(backupRoot); foreach (string item in Directory.EnumerateFiles(ConfigPaths.RootDirectory, "*.json", SearchOption.TopDirectoryOnly)) { File.Copy(item, Path.Combine(backupRoot, Path.GetFileName(item)), overwrite: true); } CopyDirectoryIfExists(Path.Combine(ConfigPaths.RootDirectory, "audio"), Path.Combine(backupRoot, "audio")); CopyDirectoryIfExists(Path.Combine(ConfigPaths.RootDirectory, "textures"), Path.Combine(backupRoot, "textures")); } private static bool MoveToPackage(string sourceFile) { string text = JsonConvert.DeserializeObject<JsonFormat>(File.ReadAllText(sourceFile))?.metadata?.langName; if (text == "te-mp") { MoveTemplate(sourceFile); return false; } if (!IsSafeLanguageId(text)) { throw new InvalidDataException("Invalid or missing language id"); } string text2 = Path.Combine(ConfigPaths.LanguagesDirectory, text); string text3 = Path.Combine(text2, "language.json"); string legacyAudioDirectory = ConfigPaths.GetLegacyAudioDirectory(text); string legacyTextureDirectory = ConfigPaths.GetLegacyTextureDirectory(text); string text4 = Path.Combine(text2, "audio"); string text5 = Path.Combine(text2, "textures"); if (File.Exists(text3) || (Directory.Exists(legacyAudioDirectory) && Directory.Exists(text4)) || (Directory.Exists(legacyTextureDirectory) && Directory.Exists(text5))) { throw new IOException("The destination package already contains data"); } Directory.CreateDirectory(text2); MoveDirectoryIfExists(legacyAudioDirectory, text4); MoveDirectoryIfExists(legacyTextureDirectory, text5); File.Move(sourceFile, text3); return true; } private static void MoveTemplate(string sourceFile) { string text = Path.Combine(ConfigPaths.TemplatesDirectory, "language.json"); if (!File.Exists(text)) { Directory.CreateDirectory(ConfigPaths.TemplatesDirectory); File.Move(sourceFile, text); } } private static void CopyDirectoryIfExists(string source, string destination) { if (Directory.Exists(source)) { CopyDirectory(source, destination); } } private static void MoveDirectoryIfExists(string source, string destination) { if (Directory.Exists(source)) { Directory.Move(source, destination); } } private static void CopyDirectory(string source, string destination) { Directory.CreateDirectory(destination); foreach (string item in Directory.EnumerateFiles(source)) { File.Copy(item, Path.Combine(destination, Path.GetFileName(item)), overwrite: true); } foreach (string item2 in Directory.EnumerateDirectories(source)) { CopyDirectory(item2, Path.Combine(destination, Path.GetFileName(item2))); } } private static bool IsSafeLanguageId(string languageId) { return !string.IsNullOrWhiteSpace(languageId) && languageId != "." && languageId != ".." && languageId.IndexOfAny(Path.GetInvalidFileNameChars()) < 0 && languageId.IndexOf(Path.DirectorySeparatorChar) < 0 && languageId.IndexOf(Path.AltDirectorySeparatorChar) < 0; } } public static class Logging { public static ManualLogSource UllLogger = Logger.CreateLogSource("ULL LOGGING"); public static void Debug(string text, bool addCallerFilePath = false, [CallerFilePath] string callerFilePath = "") { UllLogger.LogDebug((object)TryAddCallerName(text, addCallerFilePath, callerFilePath)); } public static void Message(string text, bool addCallerFilePath = false, [CallerFilePath] string callerFilePath = "") { UllLogger.LogMessage((object)TryAddCallerName(text, addCallerFilePath, callerFilePath)); } public static void Warn(string text, bool addCallerFilePath = false, [CallerFilePath] string callerFilePath = "") { UllLogger.LogWarning((object)TryAddCallerName(text, addCallerFilePath, callerFilePath)); } public static void Error(string text, bool addCallerFilePath = false, [CallerFilePath] string callerFilePath = "") { UllLogger.LogError((object)TryAddCallerName(text, addCallerFilePath, callerFilePath)); } public static void Fatal(string text, bool addCallerFilePath = false, [CallerFilePath] string callerFilePath = "") { UllLogger.LogFatal((object)TryAddCallerName(text, addCallerFilePath, callerFilePath)); } public static void Info(string text, bool addCallerFilePath = false, [CallerFilePath] string callerFilePath = "") { UllLogger.LogInfo((object)TryAddCallerName(text, addCallerFilePath, callerFilePath)); } private static string TryAddCallerName(string text, bool addCallerFilePath, string callerFilePath) { return addCallerFilePath ? ("[" + GetFileName(callerFilePath) + "] " + text) : text; } private static string GetFileName(string callerFilePath) { return Path.GetFileNameWithoutExtension(callerFilePath); } } [BepInPlugin("clearwater.ultrakill.ultrakull", "clearwater.ultrakull.ultrakULL", "1.3.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class MainPatch : BaseUnityPlugin { private const string Guid = "clearwater.ultrakill.ultrakull"; private const string InternalName = "clearwater.ultrakull.ultrakULL"; internal const string InternalVersion = "1.3.0"; public static MainPatch Instance; public bool ready; public static string ModFolder => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); public static string GetVersion() { return "1.3.0"; } public void DisableMod() { ready = false; } private void Awake() { Instance = this; Debug.unityLogger.filterLogType = (LogType)4; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)4; Logging.Warn("UltrakULL Loading... | Version v.1.3.0", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Main.cs"); try { InitializeLocalization(); Logging.Warn(" --- All done. Enjoy! ---", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Main.cs"); ready = true; SceneManager.sceneLoaded += OnSceneLoaded; } catch (Exception ex) { Logging.Fatal("An error occured while initialising!", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Main.cs"); Logging.Fatal(ex.ToString(), addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Main.cs"); Logging.Fatal("Scene name: " + SceneObjects.GetCurrentSceneName(), addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Main.cs"); ready = false; } } private static void InitializeLocalization() { Logging.Warn("--- Initializing config ---", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Main.cs"); Settings.InitializeConfig(); Logging.Warn("--- Loading shared font assets ---", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Main.cs"); FontManager.LoadFonts(); Logging.Warn("--- Registering language-change handlers ---", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Main.cs"); FontManager.Initialize(); TextMeshProFontSwap.Initialize(); SubtitleLocalizer.Initialize(); TextureSwapper.Initialize(); UILayoutOverride.Initialize(); Logging.Warn("--- Loading languages and selecting the active language ---", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Main.cs"); LanguageManager.InitializeManager("1.3.0"); Logging.Warn("--- Installing game hooks ---", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Main.cs"); LoadPatches(); } private static void LoadPatches() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown Harmony val = new Harmony("clearwater.ultrakill.ultrakull"); TriggerEngine.Init(val); AngrySceneTracker.Init(); Type[] types = typeof(MainPatch).Assembly.GetTypes(); foreach (Type type in types) { try { PatchForMod customAttribute = type.GetCustomAttribute<PatchForMod>(); if (customAttribute != null) { if (Chainloader.PluginInfos.ContainsKey(customAttribute.Guid)) { if (string.IsNullOrEmpty(customAttribute.ClassName)) { val.PatchAll(type); continue; } TriggerEngine.Bind(type, new MethodTrigger { className = customAttribute.ClassName, methodName = customAttribute.MethodName, argTypes = customAttribute.Args }); } } else if (((MemberInfo)type).GetCustomAttribute<HarmonyPatch>() != null) { val.PatchAll(type); } } catch (TypeLoadException) { } catch (Exception arg) { Logging.Error($"Failed to inspect {type}: {arg}", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Main.cs"); } } } private void ApplySceneLocalization(Scene scene, LoadSceneMode mode, bool clearObjectCaches, bool sceneEntry) { //IL_0031: 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) if (!ready || LanguageManager.CurrentLanguage == null) { Logging.Error("UltrakULL has been deactivated to prevent crashing. Check the console for any errors!", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Main.cs"); return; } if (clearObjectCaches) { SceneObjects.ClearObjectCaches(scene, mode); } FontManager.RefreshFallback(); GameObject inactiveRootObject = SceneObjects.GetInactiveRootObject("Canvas"); AngrySceneTracker.ReadCurrentScene(); Core.LocalizeScene(inactiveRootObject, sceneEntry); AudioSwapper.OnSceneLoaded(SceneObjects.GetCurrentSceneName()); RunDeferred(((Scene)(ref scene)).handle); } private async void RunDeferred(int sceneHandle) { await Task.Delay(250); Scene activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).handle == sceneHandle) { SubtitledAudioSourcesReplacer.ReplaceSubsAndAudio(); TextureSwapper.Apply(); } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) ApplySceneLocalization(scene, mode, clearObjectCaches: true, sceneEntry: true); } public void RefreshCurrentScene() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) ApplySceneLocalization(SceneManager.GetActiveScene(), (LoadSceneMode)0, clearObjectCaches: false, sceneEntry: false); } } public static class LanguageOptions { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static UnityAction <>9__6_0; internal void <BuildLanguagePage>b__6_0() { Application.OpenURL(ConfigPaths.LanguagesDirectory); } } private const string PageName = "Language Page"; private static TextMeshProUGUI navigationLabel; private static TextMeshProUGUI pageTitle; private static TextMeshProUGUI openFolderLabel; public static void Initialize(SettingsMenu settingsMenu) { Transform pageContainer = settingsMenu.pageContainer; if (!((Object)(object)pageContainer.Find("Language Page") != (Object)null)) { Logging.Message("Adding language option to options menu...", addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Menus\\LanguageOptions.cs"); Transform navigationRail = settingsMenu.navigationRail; GameObject gameObject = ((Component)navigationRail.Find("General")).gameObject; GameObject languagePage = BuildLanguagePage(((Component)settingsMenu).transform, pageContainer, gameObject); GameObject val = BuildButton(gameObject, navigationRail, "Language", LanguageManager.CurrentLanguage.options.language_title, null, resizeText: false); val.transform.SetSiblingIndex(7); navigationLabel = val.GetComponentInChildren<TextMeshProUGUI>(); ConfigureNavigationButton(settingsMenu, languagePage, val); RefreshText(); } } public static void RefreshText() { Option options = LanguageManager.CurrentLanguage.options; if ((Object)(object)navigationLabel != (Object)null) { ((TMP_Text)navigationLabel).text = options.language_title; } if ((Object)(object)pageTitle != (Object)null) { ((TMP_Text)pageTitle).text = "--" + options.language_languages + "--"; } if ((Object)(object)openFolderLabel != (Object)null) { ((TMP_Text)openFolderLabel).text = "<color=#03fc07>" + options.language_openLanguageFolder + "</color>"; } } private static GameObject BuildLanguagePage(Transform optionsMenu, Transform pages, GameObject buttonTemplate) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Expected O, but got Unknown //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Expected O, but got Unknown //IL_0360: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_036b: Expected O, but got Unknown Transform val = pages.Find("General"); Transform val2 = val.Find("Scroll Rect"); Transform val3 = val2.Find("Contents"); GameObject val4 = new GameObject("Language Page", new Type[2] { typeof(RectTransform), typeof(CanvasRenderer) }); val4.transform.SetParent(pages, false); val4.GetComponent<RectTransform>().sizeDelta = new Vector2(600f, 800f); val4.SetActive(false); GameObject val5 = new GameObject("Scroll Rect", new Type[4] { typeof(RectTransform), typeof(ScrollRect), typeof(Image), typeof(Mask) }); val5.transform.SetParent(val4.transform, false); CopyRect(((Component)val2).GetComponent<RectTransform>(), val5.GetComponent<RectTransform>()); ((Graphic)val5.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.5f); val5.GetComponent<Mask>().showMaskGraphic = false; ScrollRect component = val5.GetComponent<ScrollRect>(); component.horizontal = false; component.vertical = true; component.verticalScrollbarVisibility = (ScrollbarVisibility)2; component.scrollSensitivity = 20f; GameObject val6 = new GameObject("Contents", new Type[3] { typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter) }); val6.transform.SetParent(val5.transform, false); RectTransform component2 = val6.GetComponent<RectTransform>(); CopyRect(((Component)val3).GetComponent<RectTransform>(), component2); VerticalLayoutGroup component3 = val6.GetComponent<VerticalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)component3).spacing = 10f; ((LayoutGroup)component3).childAlignment = (TextAnchor)1; ((HorizontalOrVerticalLayoutGroup)component3).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)component3).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)component3).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component3).childControlHeight = true; val6.GetComponent<ContentSizeFitter>().verticalFit = (FitMode)2; component.content = component2; Transform transform = val6.transform; Scrollbar val7 = Object.Instantiate<Scrollbar>(((Component)val).GetComponentsInChildren<Scrollbar>().First(), val4.transform); val7.direction = (Direction)2; component.verticalScrollbar = val7; GameObject val8 = Object.Instantiate<GameObject>(((Component)optionsMenu.Find("Text")).gameObject, transform); ((Object)val8).name = "Title"; pageTitle = val8.GetComponent<TextMeshProUGUI>(); ((TMP_Text)pageTitle).alignment = (TextAlignmentOptions)514; ((TMP_Text)pageTitle).fontSize = 24f; ((TMP_Text)pageTitle).rectTransform.sizeDelta = new Vector2(400f, 50f); foreach (KeyValuePair<string, Lang> allLanguage in LanguageManager.allLanguages) { string languageId = allLanguage.Key; BuildButton(buttonTemplate, transform, languageId, allLanguage.Value.DisplayName, (UnityAction)delegate { SelectLanguage(languageId); }, resizeText: true, (Vector2?)new Vector2(160f, 40f)); } string empty = string.Empty; object obj = <>c.<>9__6_0; if (obj == null) { UnityAction val9 = delegate { Application.OpenURL(ConfigPaths.LanguagesDirectory); }; <>c.<>9__6_0 = val9; obj = (object)val9; } openFolderLabel = BuildButton(buttonTemplate, transform, "openLangFolder", empty, (UnityAction)obj).GetComponentInChildren<TextMeshProUGUI>(); return val4; } private static GameObject BuildButton(GameObject template, Transform parent, string name, string label, UnityAction onClick = null, bool resizeText = true, Vector2? size = null) { //IL_0019: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(template, parent); ((Object)val).name = name; RectTransform component = val.GetComponent<RectTransform>(); ((Transform)component).localPosition = Vector3.zero; ((Transform)component).localRotation = Quaternion.identity; ((Transform)component).localScale = Vector3.one; if (size.HasValue) { component.sizeDelta = size.Value; } Button component2 = val.GetComponent<Button>(); component2.onClick = new ButtonClickedEvent(); if (onClick != null) { ((UnityEvent)component2.onClick).AddListener(onClick); } ((Selectable)component2).interactable = true; Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)component2).navigation = navigation; TextMeshProUGUI componentInChildren = val.GetComponentInChildren<TextMeshProUGUI>(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).text = label; ((TMP_Text)componentInChildren).richText = true; ((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)514; ((TMP_Text)componentInChildren).enableAutoSizing = resizeText; if (resizeText) { ((TMP_Text)componentInChildren).fontSizeMin = 10f; ((TMP_Text)componentInChildren).fontSizeMax = 36f; } } val.SetActive(true); return val; } private static void SelectLanguage(string languageId) { Logging.Message("Selected language: " + languageId, addCallerFilePath: false, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Menus\\LanguageOptions.cs"); LanguageManager.TrySwitchLanguage(languageId); } private static void ConfigureNavigationButton(SettingsMenu settingsMenu, GameObject languagePage, GameObject navigationButton) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown ButtonHighlightParent highlight = ((Component)settingsMenu.navigationRail).GetComponent<ButtonHighlightParent>(); Button component = navigationButton.GetComponent<Button>(); Image image = navigationButton.GetComponent<Image>(); ((UnityEvent)component.onClick).AddListener((UnityAction)delegate { ButtonHighlightParent obj = highlight; if (obj != null) { obj.ChangeButton(image); } settingsMenu.SetActivePage(languagePage); }); RegisterNavigationButton(highlight, image); } private static void RegisterNavigationButton(ButtonHighlightParent highlight, Image image) { if (highlight?.buttons != null && !highlight.buttons.Contains(image)) { TMP_Text componentInChildren = ((Component)image).GetComponentInChildren<TMP_Text>(); int num = highlight.buttons.Length; Array.Resize(ref highlight.buttons, num + 1); Array.Resize(ref highlight.buttonTexts, num + 1); Array.Resize(ref highlight.buttonSprites, num + 1); highlight.buttons[num] = image; highlight.buttonTexts[num] = componentInChildren; highlight.buttonSprites[num] = image.sprite; } } private static void CopyRect(RectTransform source, RectTransform target) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) target.anchorMin = source.anchorMin; target.anchorMax = source.anchorMax; target.pivot = source.pivot; target.anchoredPosition = source.anchoredPosition; target.sizeDelta = source.sizeDelta; } } public static class Settings { public static ConfigEntry<string> lastLanguage; public static ConfigEntry<bool> activeDubbing; public static ConfigFile configFile; public static void InitializeConfig() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown configFile = new ConfigFile(ConfigPaths.SettingsFile, true); lastLanguage = configFile.Bind<string>("General", "LastLanguage", "en-GB", (ConfigDescription)null); activeDubbing = configFile.Bind<bool>("General", "activeDubbing", false, (ConfigDescription)null); } } internal static class StaticBatchAtlasSwapper { private readonly struct Template { public readonly string Name; public readonly int Width; public readonly int Height; public readonly Color32[] Pixels; public Template(string name, int width, int height, Color32[] pixels) { Name = name; Width = width; Height = height; Pixels = pixels; } } private sealed class AtlasState(Texture2D texture, Color32[] originalPixels) { public readonly Texture2D Texture = texture; public readonly Color32[] OriginalPixels = originalPixels; public readonly List<(string Name, RectInt Rect)> Regions = new List<(string, RectInt)>(); } private readonly struct FeaturePoint { public readonly int X; public readonly int Y; public readonly Color32 Color; public readonly int Contrast; public FeaturePoint(int x, int y, Color32 color, int contrast) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) X = x; Y = y; Color = color; Contrast = contrast; } } private static readonly List<Template> pendingTemplates = new List<Template>(); private static readonly Dictionary<Texture2D, AtlasState> atlases = new Dictionary<Texture2D, AtlasState>(); private static readonly HashSet<string> diagnostics = new HashSet<string>(); private static int optimizerId; private static bool resolving; internal static void Capture(StaticSceneOptimizer optimizer) { ReleaseAtlases(); pendingTemplates.Clear(); TextureSwapper.RestoreMutatedTextures(); optimizerId = (((Object)(object)optimizer != (Object)null) ? ((Object)optimizer).GetInstanceID() : 0); if ((Object)(object)optimizer == (Object)null || LanguageManager.IsEnglish) { return; } HashSet<Texture2D> seenBefore = new HashSet<Texture2D>(); List<MeshRenderer> staticMRends = optimizer.staticMRends; if (staticMRends == null) { WarnOnce("no-renderers", "StaticSceneOptimizer.staticMRends is null; cannot capture pre-batch texture names."); return; } foreach (MeshRenderer item2 in staticMRends) { CaptureRenderer(item2); } Logging.Message($"Captured {pendingTemplates.Count} named " + $"pre-batch template(s) from {staticMRends.Count} renderer(s).", addCallerFilePath: true, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Textures\\AtlasBatchSwapper.cs"); void CaptureMaterial(Material material) { foreach (var mainTexture in GetMainTextures(material)) { Texture2D item = mainTexture.Texture; Texture2D originalTexture = TextureSwapper.GetOriginalTexture(item); Texture2D val = (((Object)(object)originalTexture != (Object)null) ? originalTexture : item); string text = (((Object)(object)originalTexture != (Object)null) ? ((Object)originalTexture).name : ((Object)item).name); if (seenBefore.Add(item) && TextureSwapper.TryGetFile(text, out var _)) { Color32[] array = ReadPixels((Texture)(object)val); if (array == null) { WarnOnce($"template-read:{((Object)item).GetInstanceID()}", "Cannot read source template " + $"'{text}' ({((Texture)val).width}x{((Texture)val).height})."); } else { pendingTemplates.Add(new Template(text, ((Texture)val).width, ((Texture)val).height, array)); } } } } void CaptureRenderer(MeshRenderer renderer) { if (!((Object)(object)renderer == (Object)null)) { Material[] sharedMaterials = ((Renderer)renderer).sharedMaterials; foreach (Material material in sharedMaterials) { CaptureMaterial(material); } } } } private static void ReleaseAtlases() { foreach (AtlasState value in atlases.Values) { if ((Object)(object)value.texture != (Object)null && value.originalPixels.Length == ((Texture)value.texture).width * ((Texture)value.texture).height) { value.texture.SetPixels32(value.originalPixels); value.texture.Apply(false, false); } } atlases.Clear(); } internal static IEnumerator Resolve(StaticSceneOptimizer optimizer) { if (resolving || (Object)(object)optimizer == (Object)null || ((Object)optimizer).GetInstanceID() != optimizerId || pendingTemplates.Count == 0 || LanguageManager.IsEnglish) { yield break; } resolving = true; List<AtlasState> sceneAtlases = GetOrCreateAtlases(optimizer); if (sceneAtlases.Count == 0) { WarnOnce($"missing-atlas:{optimizerId}", "Optimizer finished, but no runtime Texture2D was found on its batch materials."); resolving = false; yield break; } Template[] pending = pendingTemplates.ToArray(); HashSet<string> found = new HashSet<string>(); foreach (AtlasState sceneAtlas in sceneAtlases) { Color32[] source = sceneAtlas.originalPixels; int sourceWidth = ((Texture)sceneAtlas.texture).width; int sourceHeight = ((Texture)sceneAtlas.texture).height; Task<List<(Template Template, RectInt Rect)>> task = Task.Run(() => FindRegions(source, sourceWidth, sourceHeight, pending)); while (!task.IsCompleted) { yield return null; } if (task.IsFaulted) { Logging.Error("Region search failed for " + $"{sourceWidth}x{sourceHeight} atlas: {task.Exception}", addCallerFilePath: true, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Textures\\AtlasBatchSwapper.cs"); continue; } foreach (var result in task.Result) { RectInt item = result.Rect; if (((RectInt)(ref item)).width > 0) { sceneAtlas.Regions.Add((result.Template.Name, result.Rect)); found.Add(result.Template.Name); string[] obj = new string[5] { "Located '", result.Template.Name, "' at ", null, null }; item = result.Rect; object arg = ((RectInt)(ref item)).x; item = result.Rect; object arg2 = ((RectInt)(ref item)).y; item = result.Rect; obj[3] = $"({arg},{arg2}) {((RectInt)(ref item)).width}x"; item = result.Rect; obj[4] = $"{((RectInt)(ref item)).height}."; Logging.Message(string.Concat(obj), addCallerFilePath: true, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Textures\\AtlasBatchSwapper.cs"); } } } Template[] array = pending; for (int num = 0; num < array.Length; num++) { Template template = array[num]; if (!found.Contains(template.Name)) { WarnOnce($"not-found:{optimizerId}:{template.Name}", "'" + template.Name + "' was not found in " + $"{sceneAtlases.Count} runtime atlas(es)."); } } pendingTemplates.Clear(); resolving = false; Apply(); } internal static void Apply() { //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0193: 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_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) int num = 0; foreach (AtlasState value in atlases.Values) { if ((Object)(object)value.texture == (Object)null || value.originalPixels.Length != ((Texture)value.texture).width * ((Texture)value.texture).height) { continue; } value.texture.SetPixels32(value.originalPixels); if (LanguageManager.IsEnglish) { value.texture.Apply(false, false); continue; } foreach (var region in value.Regions) { if (!TextureSwapper.TryGetFile(region.Name, out var file)) { continue; } int width = file.Width; RectInt item = region.Rect; if (width == ((RectInt)(ref item)).width) { int height = file.Height; item = region.Rect; if (height == ((RectInt)(ref item)).height) { try { Color32[] array = DecodePixels(file.Bytes); if (array != null) { Texture2D texture = value.texture; item = region.Rect; int x = ((RectInt)(ref item)).x; item = region.Rect; int y = ((RectInt)(ref item)).y; item = region.Rect; int width2 = ((RectInt)(ref item)).width; item = region.Rect; texture.SetPixels32(x, y, width2, ((RectInt)(ref item)).height, array); num++; } } catch (Exception ex) { WarnOnce("apply:" + region.Name + ":" + file.Path, "Failed to apply '" + file.Path + "': " + ex.Message); } continue; } } string key = "size:" + region.Name + ":" + file.Path; string[] obj = new string[6] { "Cannot put '", file.Path, "' into cached ", $"region '{region.Name}': PNG is {file.Width}x{file.Height}, region is ", null, null }; item = region.Rect; object arg = ((RectInt)(ref item)).width; item = region.Rect; obj[4] = $"{arg}x{((RectInt)(ref item)).height}. Atlas layout "; obj[5] = "must not be resized."; WarnOnce(key, string.Concat(obj)); } value.texture.Apply(false, false); } if (num > 0) { Logging.Message($"Applied {num} cached region(s) to " + $"{atlases.Count} atlas(es) without rescanning.", addCallerFilePath: true, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Textures\\AtlasBatchSwapper.cs"); } } private static List<AtlasState> GetOrCreateAtlases(StaticSceneOptimizer optimizer) { List<AtlasState> list = new List<AtlasState>(); HashSet<Texture2D> hashSet = new HashSet<Texture2D>(); Material[] array = (Material[])(object)new Material[2] { optimizer.batchMaterialOutdoors, optimizer.batchMaterialEnvironment }; Material[] array2 = array; foreach (Material val in array2) { foreach (var mainTexture in GetMainTextures(val)) { if (!hashSet.Add(mainTexture.Texture)) { continue; } if (atlases.TryGetValue(mainTexture.Texture, out var value)) { list.Add(value); continue; } Color32[] array3 = ReadPixels((Texture)(object)mainTexture.Texture); if (array3 != null) { AtlasState atlasState = new AtlasState(mainTexture.Texture, array3); atlases[mainTexture.Texture] = atlasState; list.Add(atlasState); Logging.Message("Runtime atlas candidate: material '" + ((Object)val).name + "', " + mainTexture.Property + ", texture '" + ((Object)mainTexture.Texture).name + "' " + $"{((Texture)mainTexture.Texture).width}x{((Texture)mainTexture.Texture).height}.", addCallerFilePath: true, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Textures\\AtlasBatchSwapper.cs"); } } } return list; } private static IEnumerable<(string Property, Texture2D Texture)> GetMainTextures(Material material) { if ((Object)(object)material == (Object)null || (Object)(object)material.shader == (Object)null) { yield break; } int count = material.shader.GetPropertyCount(); for (int i = 0; i < count; i++) { if ((int)material.shader.GetPropertyType(i) != 4) { continue; } string property = material.shader.GetPropertyName(i); if (!(property != "_MainTex") || !(property != "_BaseMap") || !(property != "_MainTexture")) { Texture texture = material.GetTexture(property); Texture2D texture2 = (Texture2D)(object)((texture is Texture2D) ? texture : null); if (texture2 != null) { yield return (Property: property, Texture: texture2); } } } } private static List<(Template Template, RectInt Rect)> FindRegions(Color32[] source, int sourceWidth, int sourceHeight, Template[] templates) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) List<(Template, RectInt)> list = new List<(Template, RectInt)>(templates.Length); foreach (Template template in templates) { list.Add((template, FindRegion(source, sourceWidth, sourceHeight, template))); } return list; } private static RectInt FindRegion(Color32[] source, int sourceWidth, int sourceHeight, Template template) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) if (template.Width > sourceWidth || template.Height > sourceHeight) { return default(RectInt); } FeaturePoint[] featurePoints = GetFeaturePoints(template); int num = sourceWidth - template.Width; int num2 = sourceHeight - template.Height; for (int i = 0; i <= num2; i++) { for (int j = 0; j <= num; j++) { if (MatchesFeatures(source, sourceWidth, j, i, featurePoints) && MatchesImage(source, sourceWidth, j, i, template)) { return new RectInt(j, i, template.Width, template.Height); } } } return default(RectInt); } private static FeaturePoint[] GetFeaturePoints(Template template) { //IL_001f: 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) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) long num = 0L; long num2 = 0L; long num3 = 0L; long num4 = 0L; Color32[] pixels = template.Pixels; foreach (Color32 val in pixels) { num += val.r; num2 += val.g; num3 += val.b; num4 += val.a; } int num5 = template.Pixels.Length; int num6 = (int)(num / num5); int num7 = (int)(num2 / num5); int num8 = (int)(num3 / num5); int num9 = (int)(num4 / num5); List<FeaturePoint> list = new List<FeaturePoint>(25); for (int j = 0; j < 5; j++) { for (int k = 0; k < 5; k++) { int num10 = k * (template.Width - 1) / 4; int num11 = j * (template.Height - 1) / 4; Color32 val2 = template.Pixels[num11 * template.Width + num10]; int contrast = Math.Abs(val2.r - num6) + Math.Abs(val2.g - num7) + Math.Abs(val2.b - num8) + Math.Abs(val2.a - num9); list.Add(new FeaturePoint(num10, num11, val2, contrast)); } } list.Sort((FeaturePoint a, FeaturePoint b) => b.Contrast.CompareTo(a.Contrast)); return list.GetRange(0, Math.Min(8, list.Count)).ToArray(); } private static bool MatchesFeatures(Color32[] source, int sourceWidth, int x, int y, FeaturePoint[] features) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_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_0036: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < features.Length; i++) { FeaturePoint featurePoint = features[i]; Color32 val = source[(y + featurePoint.Y) * sourceWidth + x + featurePoint.X]; if (!((object)featurePoint.Color/*cast due to .constrained prefix*/).Equals((object?)val)) { return false; } } return true; } private unsafe static bool MatchesImage(Color32[] source, int sourceWidth, int x, int y, Template template) { //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) //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_0035: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < template.Height; i++) { for (int j = 0; j < template.Width; j++) { Color32 val = template.Pixels[i * template.Width + j]; Color32 val2 = source[(y + i) * sourceWidth + x + j]; if (!((object)(*(Color32*)(&val))/*cast due to .constrained prefix*/).Equals((object?)val2)) { return false; } } } return true; } private static Color32[] DecodePixels(byte[] png) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); try { return ImageConversion.LoadImage(val, png, false) ? val.GetPixels32() : null; } catch (Exception) { return null; } finally { Object.Destroy((Object)(object)val); } } private static Color32[] ReadPixels(Texture source) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_005d: Unknown result type (might be due to invalid IL or missing references) RenderTexture val = null; RenderTexture active = RenderTexture.active; Texture2D val2 = null; try { val = RenderTexture.GetTemporary(source.width, source.height, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)0); Graphics.Blit(source, val); RenderTexture.active = val; val2 = new Texture2D(source.width, source.height, (TextureFormat)4, false); val2.ReadPixels(new Rect(0f, 0f, (float)source.width, (float)source.height), 0, 0); val2.Apply(); return val2.GetPixels32(); } catch (Exception ex) { WarnOnce($"read:{((Object)source).GetInstanceID()}", "Failed to read '" + ((Object)source).name + "': " + ex.Message); return null; } finally { RenderTexture.active = active; if ((Object)(object)val != (Object)null) { RenderTexture.ReleaseTemporary(val); } if ((Object)(object)val2 != (Object)null) { Object.Destroy((Object)(object)val2); } } } private static void WarnOnce(string key, string message) { if (diagnostics.Add(key)) { Logging.Warn(message, addCallerFilePath: true, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Textures\\AtlasBatchSwapper.cs"); } } } [HarmonyPatch(typeof(StaticSceneOptimizer))] internal static class StaticSceneOptimizerPatch { [HarmonyPatch("Start")] [HarmonyPrefix] private static void Prefix(StaticSceneOptimizer __instance) { StaticBatchAtlasSwapper.Capture(__instance); } [HarmonyPatch("Start")] [HarmonyPostfix] private static void Postfix(StaticSceneOptimizer __instance) { if ((Object)(object)MainPatch.Instance != (Object)null) { ((MonoBehaviour)MainPatch.Instance).StartCoroutine(ApplyAfterOptimizer(__instance)); } } private static IEnumerator ApplyAfterOptimizer(StaticSceneOptimizer optimizer) { yield return null; yield return null; yield return StaticBatchAtlasSwapper.Resolve(optimizer); } } internal sealed class ReplacementCache<T> where T : Object { private readonly struct Replacement { public readonly T Original; public readonly T Current; public Replacement(T original, T current) { Original = original; Current = current; } } private readonly Dictionary<T, Replacement> replacements = new Dictionary<T, Replacement>(); private readonly HashSet<T> active = new HashSet<T>(); private readonly List<T> stale = new List<T>(); public T GetOriginal(T value) { Replacement value2; return ((Object)(object)value != (Object)null && replacements.TryGetValue(value, out value2)) ? value2.Original : value; } public bool TryGetCurrent(T original, out T current) { Replacement value; bool flag = replacements.TryGetValue(original, out value); current = (flag ? value.Current : default(T)); return (Object)(object)current != (Object)null; } public bool IsReplacement(T value) { Replacement value2; return (Object)(object)value != (Object)null && replacements.TryGetValue(value, out value2) && (Object)(object)value2.Current == (Object)(object)value; } public void Add(T original, T replacement, bool cache = true) { Replacement value = new Replacement(original, replacement); if (cache) { replacements[original] = value; } replacements[replacement] = value; active.Add(replacement); } public void QueueCleanup() { foreach (T item in active) { Replacement replacement = replacements[item]; if (replacements.TryGetValue(replacement.Original, out var value) && (Object)(object)value.Current == (Object)(object)item) { replacements.Remove(replacement.Original); } } stale.AddRange(active); active.Clear(); } public void Cleanup(Action<T> destroy) { foreach (T item in stale) { replacements.Remove(item); if (!((Object)(object)item == (Object)null)) { destroy(item); } } stale.Clear(); } } public static class TextureSwapper { internal readonly struct ReplacementFile { public readonly string Path; public readonly byte[] Bytes; public readonly int Width; public readonly int Height; public ReplacementFile(string path, byte[] bytes, int width, int height) { Path = path; Bytes = bytes; Width = width; Height = height; } } private readonly struct MutatedTexture { public readonly byte[] OriginalPng; public readonly FilterMode FilterMode; public readonly TextureWrapMode WrapMode; public readonly int AnisoLevel; public MutatedTexture(byte[] originalPng, FilterMode filterMode, TextureWrapMode wrapMode, int anisoLevel) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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_0010: Unknown result type (might be due to invalid IL or missing references) OriginalPng = originalPng; FilterMode = filterMode; WrapMode = wrapMode; AnisoLevel = anisoLevel; } } private static readonly ReplacementCache<Sprite> spriteReplacementCache = new ReplacementCache<Sprite>(); private static readonly ReplacementCache<Texture> textureReplacementCache = new ReplacementCache<Texture>(); private static readonly Dictionary<(Material, string), Texture2D> materialTextureCache = new Dictionary<(Material, string), Texture2D>(); private static readonly HashSet<string> diagnostics = new HashSet<string>(); private static readonly Dictionary<string, int> replacementCount = new Dictionary<string, int> { ["Image slot(s)"] = 0, ["StyleHUD rank sprite(s)"] = 0, ["RawImage(s)"] = 0, ["SpriteRenderer(s)"] = 0, ["SpriteMask(s)"] = 0, ["Renderer property block slot(s)"] = 0, ["material property(s)"] = 0, ["loaded Texture2D object(s)"] = 0 }; private static bool scanInProgress; private static readonly Dictionary<string, ReplacementFile> files = new Dictionary<string, ReplacementFile>(); private static readonly Dictionary<Texture2D, MutatedTexture> mutatedTextures = new Dictionary<Texture2D, MutatedTexture>(); private static Sprite GetDesiredSprite(Sprite value) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)value == (Object)null) { return null; } Sprite original = spriteReplacementCache.GetOriginal(value); if (LanguageManager.IsEnglish || !TryGetFile(((Object)original).name, out var file)) { return original; } if (spriteReplacementCache.TryGetCurrent(original, out var current)) { return current; } Rect rect = original.rect; int num = Mathf.RoundToInt(((Rect)(ref rect)).width); int num2 = Mathf.RoundToInt(((Rect)(ref rect)).height); if (num <= 0 || num2 <= 0) { WarnOnce($"sprite-empty:{((Object)original).GetInstanceID()}", $"Invalid sprite rect '{((Object)original).name}': {num}x{num2}."); return original; } float num3 = (float)file.Width / ((Rect)(ref rect)).width; float num4 = (float)file.Height / ((Rect)(ref rect)).height; float num5 = ((Rect)(ref rect)).width / ((Rect)(ref rect)).height; float num6 = (float)file.Width / (float)file.Height; if (!Mathf.Approximately(num5, num6)) { WarnOnce($"sprite-aspect:{((Object)original).GetInstanceID()}:{file.Path}", $"Aspect change '{((Object)original).name}': {num5:0.###} -> " + $"{num6:0.###} ({num}x{num2} -> {file.Width}x{file.Height})."); } Texture2D val = CreateTexture(file, (Texture)(object)original.texture); if ((Object)(object)val == (Object)null) { return original; } Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(original.pivot.x / ((Rect)(ref rect)).width, original.pivot.y / ((Rect)(ref rect)).height); Vector4 border = original.border; ((Vector4)(ref border))..ctor(border.x * num3, border.y * num4, border.z * num3, border.w * num4); Sprite val3; try { val3 = Sprite.Create(val, new Rect(0f, 0f, (float)file.Width, (float)file.Height), val2, original.pixelsPerUnit, 0u, (SpriteMeshType)0, border); } catch (Exception ex) { Object.Destroy((Object)(object)val); WarnOnce($"sprite-create:{((Object)original).GetInstanceID()}:{file.Path}", "Cannot create sprite '" + ((Object)original).name + "': " + ex.Message); return original; } ((Object)val3).name = ((Object)original).name; spriteReplacementCache.Add(original, val3); return val3; } private static Texture GetDesiredTexture(Texture value) { if ((Object)(object)value == (Object)null) { return null; } Texture original = textureReplacementCache.GetOriginal(value); if (!LanguageManager.IsEnglish) { Texture2D val = (Texture2D)(object)((original is Texture2D) ? original : null); if (val != null && TryGetFile(((Object)original).name, out var file)) { if (textureReplacementCache.TryGetCurrent((Texture)(object)val, out var current)) { return current; } Texture2D val2 = CreateTexture(file, (Texture)(object)val); if ((Object)(object)val2 == (Object)null) { return original; } textureReplacementCache.Add(original, (Texture)(object)val2); return (Texture)(object)val2; } } return original; } private static Texture GetCanonicalTexture(Texture value) { return textureReplacementCache.GetOriginal(value); } internal static Texture2D GetOriginalTexture(Texture2D current) { Texture original = textureReplacementCache.GetOriginal((Texture)(object)current); Texture2D val = (Texture2D)(object)((original is Texture2D) ? original : null); return ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)current) ? val : null; } private static Texture GetDesiredMaterialTexture(Material material, string property, Texture original, ReplacementFile file) { if (LanguageManager.IsEnglish) { return original; } (Material, string) key = (material, property); if (materialTextureCache.TryGetValue(key, out var value) && (Object)(object)value != (Object)null) { return (Texture)(object)value; } Texture2D val = CreateTexture(file, original); if ((Object)(object)val == (Object)null) { return original; } materialTextureCache[key] = val; textureReplacementCache.Add(original, (Texture)(object)val, cache: false); return (Texture)(object)val; } private static bool IsUnusableAssetName(string name) { return string.IsNullOrWhiteSpace(name) || string.Equals(name.Trim(), "untitled"); } private static bool IsMainTextureProperty(string property) { return string.Equals(property, "_MainTex") || string.Equals(property, "_BaseMap") || string.Equals(property, "_MainTexture"); } private static Texture2D CreateTexture(ReplacementFile file, Texture source) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, file.Bytes, false)) { Object.Destroy((Object)(object)val); WarnOnce("decode:" + file.Path, "Cannot decode '" + file.Path + "'."); return null; } ((Object)val).name = "UltrakULL:" + LanguageManager.Current.Name + ":" + ((Object)source).name; ApplySampling((Texture)(object)val, source.filterMode, source.wrapMode, source.anisoLevel); return val; } private static void ApplySampling(Texture texture, FilterMode filter, TextureWrapMode wrap, int aniso) { //IL_0002: 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) texture.filterMode = filter; texture.wrapMode = wrap; texture.anisoLevel = aniso; } private static void QueueGeneratedAssetsForCleanup() { spriteReplacementCache.QueueCleanup(); textureReplacementCache.QueueCleanup(); materialTextureCache.Clear(); } private static void CleanupStaleAssets() { spriteReplacementCache.Cleanup(delegate(Sprite sprite) { Texture texture = (Texture)(object)sprite.texture; Object.Destroy((Object)(object)sprite); if ((Object)(object)texture != (Object)null) { Object.Destroy((Object)(object)texture); } }); textureReplacementCache.Cleanup((Action<Texture>)Object.Destroy); } private static void LogReplacement(Component component, string slot, Object original, Object replacement) { Logging.Info(component.transform.GetPath() + " | " + slot + " | '" + (((original != null) ? original.name : null) ?? "<null>") + "' -> '" + (((replacement != null) ? replacement.name : null) ?? "<null>") + "'" + GetSourceSuffix(original), addCallerFilePath: true, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Textures\\Swapper\\TextureSwapper.Assets.cs"); } private static void LogReplacement(Material material, string property, Texture original, Texture replacement) { Logging.Info("Material '" + ((Object)material).name + "' (" + ((Object)material.shader).name + ") | " + property + " | '" + (((original != null) ? ((Object)original).name : null) ?? "<null>") + "' -> '" + (((replacement != null) ? ((Object)replacement).name : null) ?? "<null>") + "'" + GetSourceSuffix((Object)(object)original), addCallerFilePath: true, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Textures\\Swapper\\TextureSwapper.Assets.cs"); } private static string GetSourceSuffix(Object value) { Object val = value; Sprite val2 = (Sprite)(object)((value is Sprite) ? value : null); if (val2 != null) { val = (Object)(object)spriteReplacementCache.GetOriginal(val2); } else { Texture val3 = (Texture)(object)((value is Texture) ? value : null); if (val3 != null) { val = (Object)(object)textureReplacementCache.GetOriginal(val3); } } ReplacementFile file; return (val != (Object)null && TryGetFile(val.name, out file)) ? (" | PNG: '" + file.Path + "'") : (LanguageManager.IsEnglish ? " | restoring original" : ""); } private static void ReplaceImages() { Image[] array = Resources.FindObjectsOfTypeAll<Image>(); foreach (Image image in array) { ReplaceSpriteSlot((Component)(object)image, "Image.sprite", image.sprite, delegate(Sprite value) { image.sprite = value; }, "Image slot(s)"); ReplaceSpriteSlot((Component)(object)image, "Image.overrideSprite", image.overrideSprite, delegate(Sprite value) { image.overrideSprite = value; }, "Image slot(s)"); } } private static void ReplaceRawImages() { RawImage[] array = Resources.FindObjectsOfTypeAll<RawImage>(); foreach (RawImage image in array) { ReplaceTextureSlot((Component)(object)image, "RawImage.texture", image.texture, delegate(Texture value) { image.texture = value; }, "RawImage(s)"); } } private static void ReplaceSpriteRenderers() { SpriteRenderer[] array = Resources.FindObjectsOfTypeAll<SpriteRenderer>(); foreach (SpriteRenderer renderer in array) { ReplaceSpriteSlot((Component)(object)renderer, "SpriteRenderer.sprite", renderer.sprite, delegate(Sprite value) { renderer.sprite = value; }, "SpriteRenderer(s)"); } } private static void ReplaceSpriteMasks() { SpriteMask[] array = Resources.FindObjectsOfTypeAll<SpriteMask>(); foreach (SpriteMask mask in array) { ReplaceSpriteSlot((Component)(object)mask, "SpriteMask.sprite", mask.sprite, delegate(Sprite value) { mask.sprite = value; }, "SpriteMask(s)"); } } private static void ReplaceSpriteSlot(Component owner, string slot, Sprite current, Action<Sprite> assign, string category) { Sprite desiredSprite = GetDesiredSprite(current); if (!((Object)(object)desiredSprite == (Object)(object)current)) { LogReplacement(owner, slot, (Object)(object)current, (Object)(object)desiredSprite); assign(desiredSprite); replacementCount[category]++; } } private static void ReplaceTextureSlot(Component owner, string slot, Texture current, Action<Texture> assign, string category) { Texture desiredTexture = GetDesiredTexture(current); if (!((Object)(object)desiredTexture == (Object)(object)current)) { LogReplacement(owner, slot, (Object)(object)current, (Object)(object)desiredTexture); assign(desiredTexture); replacementCount[category]++; } } internal static void Initialize() { LanguageManager.OnLanguageChanged += delegate(ValueChangedEvent<Lang> change) { LoadFiles(change.NewValue); }; } public static void Apply() { Scan(); StaticBatchAtlasSwapper.Apply(); } private static void Scan() { if (scanInProgress) { return; } scanInProgress = true; try { string[] array = replacementCount.Keys.ToArray(); foreach (string key in array) { replacementCount[key] = 0; } ReplaceImages(); ReplaceStyleHud(); ReplaceRawImages(); ReplaceSpriteRenderers(); ReplaceSpriteMasks(); ReplaceRendererPropertyBlocks(); ReplaceMaterialTextures(); ReplaceLoadedTextures(); CleanupStaleAssets(); if (replacementCount.Values.Sum() > 0) { Logging.Message("Applied: " + string.Join(", ", replacementCount.Select((KeyValuePair<string, int> item) => $"{item.Value} {item.Key}")) + ".", addCallerFilePath: true, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Textures\\Swapper\\TextureSwapper.cs"); } } catch (Exception arg) { Logging.Error($"Scan failed: {arg}", addCallerFilePath: true, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Textures\\Swapper\\TextureSwapper.cs"); } finally { scanInProgress = false; } } private static void WarnOnce(string key, string message) { if (diagnostics.Add(key)) { Logging.Warn(message, addCallerFilePath: true, "H:\\C#Project\\UltrakULL-1\\src\\Core\\Textures\\Swapper\\TextureSwapper.cs"); } } private static void LoadFiles(Lang language) { RestoreMutatedTextures(); QueueGeneratedAssetsForCleanup(); files.Clear(); diagnostics.Clear(); if (language == null || languag