using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using CellMenu; using Clonesoft.Json; using Clonesoft.Json.Linq; using Clonesoft.Json.Serialization; using FluffyUnderware.DevTools.Extensions; using GameData; using Gear; using Globals; using HarmonyLib; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Localization; using MTFO.API; using Microsoft.CodeAnalysis; using Player; using Skins.Config; using Skins.Data; using Skins.Interop; using Skins.Localization; using Skins.Manifest; using Skins.Patches; using Skins.Target; using Skins.Textures; using Skins.UI; using Skins.UI.Menu; using Skins.Utils; using Skins.Utils.Ext; using TMPro; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("Skins")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Skins")] [assembly: AssemblyTitle("Skins")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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; } } } namespace Skins { [HarmonyPatch] public class Events { public static event Action? OnGameDataInitialized; public static event Action? OnGearSpawnComplete; [HarmonyPatch(typeof(GameDataInit), "Initialize")] [HarmonyPostfix] public static void Postfix__GameDataInit_Initialize() { Events.OnGameDataInitialized?.Invoke(); } [HarmonyPatch(typeof(Item), "OnGearSpawnComplete")] [HarmonyPostfix] public static void Postfix__Item_OnGearSpawnComplete(Item __instance) { Events.OnGearSpawnComplete?.Invoke(__instance); } [HarmonyPatch(typeof(ItemEquippable), "OnGearSpawnComplete")] [HarmonyPostfix] public static void Postfix__ItemEquippable_OnGearSpawnComplete(Item __instance) { Events.OnGearSpawnComplete?.Invoke(__instance); } } internal static class Logger { private static ManualLogSource _mLogSource; public static bool Ready => _mLogSource != null; public static void Setup() { _mLogSource = Logger.CreateLogSource("io.takina.gtfo.Skins"); } public static void SetupFromInit(ManualLogSource logSource) { _mLogSource = logSource; } private static string Format(object data) { return data.ToString(); } public static void Debug(object msg) { if (ConfigMgr.Debug) { _mLogSource.LogDebug((object)Format(msg)); } } public static void Debugf(string fmt, params object[] args) { if (ConfigMgr.Debug) { _mLogSource.LogDebug((object)Format(string.Format(fmt, args))); } } public static void Info(object msg) { _mLogSource.LogInfo((object)Format(msg)); } public static void Infof(string fmt, params object[] args) { _mLogSource.LogInfo((object)Format(string.Format(fmt, args))); } public static void Warn(object msg) { _mLogSource.LogWarning((object)Format(msg)); } public static void Warnf(string fmt, params object[] args) { _mLogSource.LogWarning((object)Format(string.Format(fmt, args))); } public static void Error(object msg) { _mLogSource.LogError((object)Format(msg)); } public static void Errorf(string fmt, params object[] args) { _mLogSource.LogError((object)Format(string.Format(fmt, args))); } public static void Fatal(object msg) { _mLogSource.LogFatal((object)Format(msg)); } public static void Fatalf(string fmt, params object[] args) { _mLogSource.LogFatal((object)Format(string.Format(fmt, args))); } } [BepInPlugin("io.takina.gtfo.Skins", "Skins", "0.5.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BasePlugin { public const string NAME = "Skins"; public const string GUID = "io.takina.gtfo.Skins"; public const string VERSION = "0.5.3"; internal const string GUID_MTFO = "com.dak.MTFO"; public static GameObject? PluginObject; public event Action? OnManagersSetup; public override void Load() { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown Logger.Setup(); Logger.Info("Skins [io.takina.gtfo.Skins @ 0.5.3]"); JObject.Parse("{}"); ConfigMgr.Process(); ModData.Initialize(); if (!PersistentConfig.Load()) { Logger.Fatal("Failed to load user config, aborting plugin load. Please ensure the config file is valid JSON"); return; } Events.OnGameDataInitialized += Loc.InsertToGtfoDB; GearDump.Load(); Application.quitting += Action.op_Implicit((Action)GearDump.FlushNow); ShaderRefs.Preload(); InternalData.Write(); SkinManager.Initialize(); RegisterIl2CppTypes(); OnManagersSetup += Initialize; Global.OnAllManagersSetup += Action.op_Implicit(this.OnManagersSetup); Logger.Info("Patching..."); ApplyPatches(new Harmony("io.takina.gtfo.Skins")); Logger.Info("Finished Patching"); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void ApplyPatches(Harmony h) { ApplyPatch(h); ApplyPatch(h); ApplyPatch(h); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void ApplyPatchIfLoaded(Harmony h, string guid) { if (((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey(guid)) { ApplyPatch(h); Logger.Info("Applied compat patches for plugin with GUID " + guid + "."); } else { Logger.Info("Plugin with GUID " + guid + " not found, skipping compat patches for it."); } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void ApplyPatch(Harmony h) { h.PatchAll(typeof(T)); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void RegisterIl2CppTypes() { Type[] types = Assembly.GetExecutingAssembly().GetTypes(); foreach (Type type in types) { if (type.GetCustomAttribute() != null) { ClassInjector.RegisterTypeInIl2Cpp(type); } } } private void Initialize() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown Logger.Debug("Initializing..."); _ = Loc.CurrentLocale; UIManager.Initialize(); PluginObject = new GameObject("io.takina.gtfo.Skins"); Object.DontDestroyOnLoad((Object)(object)PluginObject); PluginObject.AddComponent(); } } public static class SkinManager { private static readonly HashSet MaterialTargets = new HashSet(); private static readonly Dictionary SkinGroups = new Dictionary(); public static bool Initialized { get; private set; } = false; public static void Initialize() { if (Initialized) { Logger.Warn("[SkinManager] Already initialized, skipping initialization."); return; } if (!ConfigMgr.DoNotNormalizeFolders) { NormalizeFolder(ModData.SkinsFolder); NormalizeFolder(ModData.PerProfileSkinsFolder); } if (ConfigMgr.AutoMovePerProfileSkinsToGlobal) { MovePerProfileSkinsToGlobal(); } RegisterGroup(SkinGroup.Vanilla); LoadSkinsFolder(ModData.SkinsFolder); LoadSkinsFolder(ModData.PerProfileSkinsFolder); Initialized = true; } private static void LoadSkinsFolder(string path) { foreach (string item in FileUtils.FileInEachSubdirectory(path, "manifest.skingroup.json")) { SkinGroup skinGroup = JsonUtils.DeserializeFromFile(item); if (skinGroup == null) { Logger.Warn("[SkinManager] group manifest at '" + item + "' deserialized to null, skipping."); continue; } if (skinGroup.Initialize(item)) { RegisterGroup(skinGroup); continue; } Logger.Error($"[SkinManager] Failed to initialize skin group '{skinGroup.Name}' at '{item}', skipping."); } } private static void NormalizeFolder(string path) { if (!Directory.Exists(path)) { return; } string[] directories = Directory.GetDirectories(path); foreach (string text in directories) { string text2 = Path.Combine(text, "manifest.skingroup.json"); if (!File.Exists(text2)) { Logger.Debug("[SkinManager] NormalizeFolder: no manifest found in '" + text + "', skipping."); continue; } SkinGroup skinGroup = JsonUtils.DeserializeFromFile(text2); if (skinGroup == null || string.IsNullOrEmpty(skinGroup.Guid)) { Logger.Warn("[SkinManager] NormalizeFolder: could not read a valid GUID from '" + text2 + "', skipping."); continue; } string fileName = Path.GetFileName(text); if (string.Equals(fileName, skinGroup.Guid, StringComparison.OrdinalIgnoreCase)) { continue; } string text3 = Path.Combine(path, skinGroup.Guid); if (Directory.Exists(text3)) { Logger.Warn($"[SkinManager] NormalizeFolder: cannot rename '{fileName}' -> '{skinGroup.Guid}' -- a folder with that name already exists. Skipping."); continue; } try { Directory.Move(text, text3); Logger.Info($"[SkinManager] NormalizeFolder: renamed '{fileName}' -> '{skinGroup.Guid}'"); } catch (Exception ex) { Logger.Error($"[SkinManager] NormalizeFolder: failed to rename '{fileName}' -> '{skinGroup.Guid}': {ex.Message}"); } } } private static void MovePerProfileSkinsToGlobal() { if (!Directory.Exists(ModData.PerProfileSkinsFolder)) { return; } string[] directories = Directory.GetDirectories(ModData.PerProfileSkinsFolder); foreach (string text in directories) { string fileName = Path.GetFileName(text); string text2 = Path.Combine(ModData.SkinsFolder, fileName); if (Directory.Exists(text2)) { try { Directory.Delete(text2, recursive: true); Logger.Info("[SkinManager] AutoMove: removed outdated global copy of '" + fileName + "'."); } catch (Exception ex) { Logger.Error("[SkinManager] AutoMove: failed to remove existing '" + fileName + "' from global skins folder, skipping move: " + ex.Message); continue; } } try { Directory.Move(text, text2); Logger.Info("[SkinManager] AutoMove: moved '" + fileName + "' to global skins folder."); } catch (Exception ex2) { Logger.Error("[SkinManager] AutoMove: failed to move '" + fileName + "': " + ex2.Message); } } } public static void UpdateTargetsUnder(GameObject go) { foreach (ManagedMaterialTarget componentsInChild in go.GetComponentsInChildren()) { componentsInChild.UpdateMaterial(); } } public static void UpdateAllTargets() { foreach (ManagedMaterialTarget materialTarget in MaterialTargets) { if (!((Object)(object)materialTarget == (Object)null)) { materialTarget.UpdateMaterial(); } } } public static void Reload() { Unload(); Initialize(); UpdateAllTargets(); } public static void Unload() { SkinGroups.Clear(); TextureLibrary.UnloadAll(); TextureMacro.UnloadAll(); Initialized = false; } public static bool TryGetGroup(string guid, [NotNullWhen(true)] out SkinGroup? group) { return SkinGroups.TryGetValue(guid, out group); } public static List GetAllMatchingGroups(TargetContext ctx) { List list = new List(); foreach (SkinGroup value in SkinGroups.Values) { if (value.IsMatch(ctx)) { list.Add(value); } } return list; } public static SkinGroup? GetAnyMatchingGroup(TargetContext ctx) { foreach (SkinGroup value in SkinGroups.Values) { if (value.IsMatch(ctx)) { return value; } } return null; } private static bool RegisterGroup(SkinGroup group) { if (string.IsNullOrEmpty(group.Guid)) { Logger.Error($"Failed to register skin group '{group.Name}' because it has an empty guid."); return false; } if (!SkinGroups.TryAdd(group.Guid, group)) { Logger.Warn($"Unable to register skin group '{group.Name}' ({group.Guid}) because a group with the same guid already exists. Skipping."); return false; } return true; } internal static void RegisterMatTarget(ManagedMaterialTarget target) { MaterialTargets.Add(target); } internal static void UnregisterMatTarget(ManagedMaterialTarget target) { MaterialTargets.Remove(target); } } [RegisterIl2Cpp] public class SkinProcess : MonoBehaviour { private void Update() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (ConfigMgr.EnableHotReloadKey && Input.GetKeyDown(ConfigMgr.HotReloadKey)) { SkinManager.Reload(); } } } } namespace Skins.Utils { public static class FileUtils { public static IEnumerable FileInEachSubdirectory(string dir, string fileName) { foreach (string item in Directory.EnumerateDirectories(dir)) { string text = Path.Combine(item, fileName); if (File.Exists(text)) { yield return text; } } } public static IEnumerable FilesWithExtensionRecursive(string dir, string extension) { if (!Directory.Exists(dir)) { yield break; } string text = (extension.StartsWith(".") ? extension : ("." + extension)); foreach (string item in Directory.EnumerateFiles(dir, "*" + text, SearchOption.AllDirectories)) { yield return item; } } } public static class GearUtils { private const string GearIdPattern = "^OfflineGear_ID_(\\d+)$"; private static readonly Regex GearIdRe = new Regex("^OfflineGear_ID_(\\d+)$", RegexOptions.Compiled); public static int? ExtractOfflineGearId(string gearPartId) { Match match = GearIdRe.Match(gearPartId); if (!match.Success) { return null; } if (int.TryParse(match.Groups[1].Value, out var result)) { return result; } return null; } public static bool TryGetOfflineGearId(BackpackItem item, out int gearId) { if (item.GearIDRange != null) { return TryGetOfflineGearId(item.GearIDRange, out gearId); } gearId = -1; return false; } public static bool TryGetOfflineGearId(ItemEquippable item, out int gearId) { if (item.GearIDRange != null) { return TryGetOfflineGearId(item.GearIDRange, out gearId); } gearId = -1; return false; } public static bool TryGetOfflineGearId(GearIDRange idRange, out int gearId) { int? num = ExtractOfflineGearId(idRange.PlayfabItemInstanceId); if (num.HasValue) { gearId = num.Value; return true; } gearId = -1; return false; } } public static class GuiUtils { public static void SetItemColor(CM_Item item, Color spriteColor, Color textColorAll) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: 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_0065: 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_007d: 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) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) Color spriteColorOut = default(Color); ((Color)(ref spriteColorOut))..ctor(spriteColor.r, spriteColor.g, spriteColor.b, item.m_spriteAlphaOut); Color spriteColorOver = default(Color); ((Color)(ref spriteColorOver))..ctor(spriteColor.r, spriteColor.g, spriteColor.b, item.m_spriteAlphaOver); Color val = default(Color); ((Color)(ref val))..ctor(textColorAll.r, textColorAll.g, textColorAll.b, item.m_textAlphaOut); Color val2 = default(Color); ((Color)(ref val2))..ctor(textColorAll.r, textColorAll.g, textColorAll.b, item.m_textAlphaOver); item.m_spriteColorOrg = spriteColor; item.m_spriteColorOut = spriteColorOut; item.m_spriteColorOver = spriteColorOver; for (int i = 0; i < ((Il2CppArrayBase)(object)item.m_texts).Count; i++) { ((Il2CppArrayBase)(object)item.m_textColorOrg)[i] = textColorAll; ((Il2CppArrayBase)(object)item.m_textColorOut)[i] = val; ((Il2CppArrayBase)(object)item.m_textColorOver)[i] = val2; } item.OnHoverOut(); } public static void FixLobbyScrollItem(CM_LobbyScrollItem item, float? newX = null, float? newY = null, float leftLineThickness = 12f, float lineThickness = 4f) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: 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_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: 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_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) RectTransform obj = ((Il2CppObjectBase)((Component)item).transform).TryCast(); RectTransform component = ((Component)CustomExtensions.FindChildRecursive((Transform)(object)obj, "StretchLineT", true)).GetComponent(); RectTransform component2 = ((Component)CustomExtensions.FindChildRecursive((Transform)(object)obj, "StretchLineB", true)).GetComponent(); RectTransform component3 = ((Component)CustomExtensions.FindChildRecursive((Transform)(object)obj, "StretchLineL", true)).GetComponent(); RectTransform component4 = ((Component)CustomExtensions.FindChildRecursive((Transform)(object)obj, "StretchLineR", true)).GetComponent(); RectTransform component5 = ((Component)((Transform)component).parent).GetComponent(); RectTransform component6 = ((Component)CustomExtensions.FindChildRecursive((Transform)(object)component5, "Background", true)).GetComponent(); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(newX ?? component5.sizeDelta.x, newY ?? component5.sizeDelta.y); component5.sizeDelta = val; component6.anchorMin = Vector2.zero; component6.anchorMax = Vector2.one; component6.offsetMin = Vector2.zero; component6.offsetMax = Vector2.zero; component.anchorMin = new Vector2(0f, 1f); component.anchorMax = new Vector2(1f, 1f); component.offsetMin = new Vector2(0f, 0f - lineThickness); component.offsetMax = new Vector2(0f, 0f); component2.anchorMin = new Vector2(0f, 0f); component2.anchorMax = new Vector2(1f, 0f); component2.offsetMin = new Vector2(0f, 0f - lineThickness); component2.offsetMax = new Vector2(0f, 0f); component3.anchorMin = new Vector2(0f, 0f); component3.anchorMax = new Vector2(0f, 1f); component3.offsetMin = new Vector2(0f, 0f); component3.offsetMax = new Vector2(leftLineThickness, 0f); component4.anchorMin = new Vector2(1f, 0f); component4.anchorMax = new Vector2(1f, 1f); component4.offsetMin = new Vector2(0f, 0f); component4.offsetMax = new Vector2(lineThickness, 0f); foreach (UI_SpriteResizer componentsInChild in ((Component)item).GetComponentsInChildren()) { if (Object.op_Implicit((Object)(object)componentsInChild)) { componentsInChild.Resize(); } } ((CM_Item)item).m_collider.size = val; } } public static class JsonUtils { public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings { ContractResolver = (IContractResolver)(object)new SkinsContractResolver() }; public static T? Deserialize(string json) { return JsonConvert.DeserializeObject(json, Settings); } public static T? DeserializeFromFile(string path) { if (!File.Exists(path)) { Logger.Warn("[JsonUtils] File not found: '" + path + "'"); return default(T); } return Deserialize(File.ReadAllText(path)); } } public class SkinsContractResolver : DefaultContractResolver { protected override JsonConverter? ResolveContractConverter(Type objectType) { if (objectType == typeof(IMaterialProperty)) { return (JsonConverter?)(object)new MaterialPropertyConverter(); } return ((DefaultContractResolver)this).ResolveContractConverter(objectType); } } [RegisterIl2Cpp] public class LifecycleHook : MonoBehaviour { public event Action? OnStarted; public event Action? OnDestroyed; private void Start() { this.OnStarted?.Invoke(); } private void OnDestroy() { this.OnDestroyed?.Invoke(); } } public class LocaleUtils { public static readonly Dictionary LocaleKeyToDisplayName = new Dictionary { { "en", "English" }, { "fr", "Français" }, { "de", "Deutsch" }, { "es", "Español" }, { "it", "Italiano" }, { "ja", "日本語" }, { "ko", "한국어" }, { "pt-BR", "Português (Brasil)" }, { "ru", "Русский" }, { "pl", "Polski" }, { "zh-Hans", "简体中文" }, { "zh-Hant", "繁體中文" } }; public static string LocaleKeyFromEnum(Language key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected I4, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) return (key - 1) switch { 0 => "en", 1 => "fr", 3 => "de", 4 => "es", 2 => "it", 8 => "ja", 9 => "ko", 6 => "pt-BR", 5 => "ru", 7 => "pl", 11 => "zh-Hans", 10 => "zh-Hant", _ => throw new ArgumentOutOfRangeException("key", key, null), }; } public static void SetTranslation(TextDataBlock db, string locale, string text) { switch (locale) { case "en": db.English = text; break; case "fr": db.French = LanguageData.op_Implicit(text); break; case "de": db.German = LanguageData.op_Implicit(text); break; case "es": db.Spanish = LanguageData.op_Implicit(text); break; case "it": db.Italian = LanguageData.op_Implicit(text); break; case "ja": db.Japanese = LanguageData.op_Implicit(text); break; case "ko": db.Korean = LanguageData.op_Implicit(text); break; case "pt-BR": db.Portuguese_Brazil = LanguageData.op_Implicit(text); break; case "ru": db.Russian = LanguageData.op_Implicit(text); break; case "pl": db.Polish = LanguageData.op_Implicit(text); break; case "zh-Hans": db.Chinese_Simplified = LanguageData.op_Implicit(text); break; case "zh-Hant": db.Chinese_Traditional = LanguageData.op_Implicit(text); break; } } public static uint GetSlotGearSelectTextDbId(InventorySlot slot) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected I4, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 switch (slot - 1) { default: if ((int)slot == 10) { return 490u; } return 0u; case 0: return 487u; case 1: return 488u; case 2: return 489u; } } public static string GetSlotGearSelectLocalizedText(InventorySlot slot) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Text.Get(GetSlotGearSelectTextDbId(slot)); } } public static class LogDumper { private static readonly Dictionary LogStreams = new Dictionary(); private static readonly Dictionary> LoggedIds = new Dictionary>(); public static void Dump(string name, string content, bool append = false) { if (!LogStreams.TryGetValue(name, out StreamWriter value)) { value = new StreamWriter(Path.Combine(ModData.DumpFolder, name + ".log"), append); LogStreams[name] = value; LoggedIds[name] = new HashSet(); } value.WriteLine(content); value.Flush(); } public static void DumpUnique(string name, int id, string content, bool append = false) { if (!LogStreams.TryGetValue(name, out StreamWriter value)) { value = new StreamWriter(Path.Combine(ModData.DumpFolder, name + ".log"), append); LogStreams[name] = value; LoggedIds[name] = new HashSet(); } if (!LoggedIds[name].Contains(id)) { value.WriteLine(content); value.Flush(); LoggedIds[name].Add(id); } } } public static class NameUtils { private static readonly Regex CleanPattern = new Regex("\\s*\\((?:Clone|Instance)\\).*$", RegexOptions.Compiled); public static string GetCleanMaterialName(string name) { return CleanPattern.Replace(name, ""); } } public static class PluginUtils { public static bool IsPluginLoaded(string pluginGuid) { return ((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey(pluginGuid); } } public static class TextureUtils { public static Texture2D CreateBlankWhiteTexture() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, Color.white); val.Apply(); return val; } public static Texture2D CreateBlankTexture(Color color) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, color); val.Apply(); return val; } public static Sprite SpriteFromRenderTexture(RenderTexture rt) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(((Texture)rt).width, ((Texture)rt).height, (TextureFormat)4, false); RenderTexture active = RenderTexture.active; RenderTexture.active = rt; val.ReadPixels(new Rect(0f, 0f, (float)((Texture)rt).width, (float)((Texture)rt).height), 0, 0); val.Apply(); RenderTexture.active = active; return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); } public static Sprite SpriteFromTexture2D(Texture2D tex) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) float num = ((Texture)tex).width; float num2 = ((Texture)tex).height; return Sprite.Create(tex, new Rect(0f, 0f, num, num2), new Vector2(0.5f, 0.5f)); } public static TextureOptions InferOptionsFromSlot(string slot) { return new TextureOptions(slot switch { "_BumpMap" => true, "_MetallicGlossMap" => true, "_AOCurveTex" => true, "_SpecGlossMap" => true, "_HeightMap" => true, "_DistortionTex" => true, "_DistortionSignal" => true, _ => false, }, Mipmaps: false); } } } namespace Skins.Utils.Ext { public static class BackpackExtensions { public static TargetContext GetContextForSlot(this PlayerBackpack bkpk, InventorySlot slot) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 TargetContext targetContext = new TargetContext(); BackpackItem item = default(BackpackItem); if ((slot - 1 <= 2 || (int)slot == 10) && bkpk.TryGetBackpackItem(slot, ref item) && GearUtils.TryGetOfflineGearId(item, out var gearId)) { targetContext.OfflineGearId = gearId; } return targetContext; } } } namespace Skins.UI { [HarmonyPatch] public class UIManager { private static readonly Color AccentColor = Color32.op_Implicit(new Color32((byte)137, (byte)180, (byte)250, byte.MaxValue)); private static readonly Action OnOfflineGearShowSkinSelectPopup = OpenOfflineGearSkinSelect; private static readonly List GearSlots = new List { (InventorySlot)1, (InventorySlot)2, (InventorySlot)3, (InventorySlot)10 }; private static Sprite? _pluginIcon = null; public static void Initialize() { Text.UpdateAllTexts(); Texture2D val = TextureLibrary.Load(Path.Combine(ModData.PluginFolder, "icon.png"), default(TextureOptions), managed: false); if ((Object)(object)val != (Object)null) { _pluginIcon = TextureUtils.SpriteFromTexture2D(val); } } private static void OpenOfflineGearSkinSelect(int _) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) PlayerBackpack localBackpack = PlayerBackpackManager.LocalBackpack; SelectionPopupMenu selectionPopupMenu = new SelectionPopupMenu(); BackpackItem val = default(BackpackItem); foreach (InventorySlot gearSlot in GearSlots) { SelectionPopupHeader header = new SelectionPopupHeader(LocaleUtils.GetSlotGearSelectLocalizedText(gearSlot)) { SetActiveConfirmText = "" + Loc.T("gear.select.clickAgain") + "", IsActiveText = "" + Loc.T("gear.select.active") + "" }; TargetContext ctx = localBackpack.GetContextForSlot(gearSlot); BackpackItem bkpkItem = (localBackpack.TryGetBackpackItem(gearSlot, ref val) ? val : null); SelectionPopupHeader selectionPopupHeader = header; UserConfig? instance = PersistentConfig.Instance; selectionPopupHeader.ActiveItemID = ((instance != null && instance.CurrentSkinConfig.OfflineGear.TryGetValue(ctx.OfflineGearId ?? (-1), out string value)) ? value : "10chambers.vanilla"); AddGroup(SkinGroup.Vanilla); foreach (SkinGroup allMatchingGroup in SkinManager.GetAllMatchingGroups(ctx)) { AddGroup(allMatchingGroup); } selectionPopupMenu.AddHeader(header); void AddGroup(SkinGroup group) { SkinSelectionPopupItem obj = new SkinSelectionPopupItem { DisplayName = group.Name.Resolve(), ID = group.Guid, SubTitle = group.Author.Resolve(), Description = group.Description?.Resolve(), setActiveAction = OnOfflineGearSkinSelected, upperTextCustomization = AuthorTextDecoration, subtitleTextCustomization = DescriptionTextDecoration, Context = ctx }; BackpackItem obj2 = bkpkItem; object obj3; if (obj2 == null) { obj3 = null; } else { Item instance2 = obj2.Instance; obj3 = ((instance2 != null) ? ((Component)instance2).gameObject : null); } if (obj3 == null) { obj3 = null; } obj.ApplicationTarget = (GameObject?)obj3; obj.SkinGUID = group.Guid; obj.IsVanilla = group.Guid == SkinGroup.Vanilla.Guid; SkinSelectionPopupItem skinSelectionPopupItem = obj; if ((Object)(object)_pluginIcon != (Object)null) { skinSelectionPopupItem.SpriteLarge = _pluginIcon; skinSelectionPopupItem.SpriteSmall = _pluginIcon; } header.AddItem(skinSelectionPopupItem); } } selectionPopupMenu.Show(); } private static void OnOfflineGearSkinSelected(ISelectionPopupItem item) { if (!(item is SkinSelectionPopupItem skinSelectionPopupItem)) { Logger.Warn("[UIManager] OnSkinSelected called with non-skin item."); return; } TargetContext context = skinSelectionPopupItem.Context; if (PersistentConfig.Instance != null) { if (!context.OfflineGearId.HasValue) { Logger.Warn("[UIManager] Selected skin for item with no offline gear id - context: " + context); return; } PersistentConfig.Instance.CurrentSkinConfig.OfflineGear[context.OfflineGearId.Value] = (skinSelectionPopupItem.IsVanilla ? "10chambers.vanilla" : skinSelectionPopupItem.SkinGUID); PersistentConfig.Save(); } if ((Object)(object)skinSelectionPopupItem.ApplicationTarget != (Object)null) { SkinManager.UpdateTargetsUnder(skinSelectionPopupItem.ApplicationTarget); } } private static string AuthorTextDecoration(ISelectionPopupItem item, string text) { return text; } private static string DescriptionTextDecoration(ISelectionPopupItem item, string text) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) return "<" + ColorExt.ToHtml(AccentColor) + ">" + text + ""; } [HarmonyPatch(typeof(CM_PlayerLobbyBar), "SetupFromPage")] [HarmonyPostfix] private static void AddLobbyBarSelectSkinEntryPoint(CM_PlayerLobbyBar __instance) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) RectTransform val = ((Il2CppObjectBase)__instance.m_weaponsHeaderText.transform).TryCast(); CM_LobbyScrollItem val2 = Object.Instantiate(__instance.m_clothesButton, ((Component)__instance.m_clothesButton).transform.parent, true); ((RectTransformComp)val2).Setup(); ((Object)((Component)val2).gameObject).name = "Skins Select Button"; ((CM_Item)val2).SetOnBtnPressCallback(Action.op_Implicit(OnOfflineGearShowSkinSelectPopup)); RectTransform val3 = ((Il2CppObjectBase)((Component)val2).transform).TryCast(); if ((Object)(object)val3 != (Object)null && (Object)(object)val != (Object)null) { Vector2 anchoredPosition = val3.anchoredPosition; anchoredPosition.y = val.anchoredPosition.y + 15f; val3.anchoredPosition = anchoredPosition; Vector2 val4 = default(Vector2); ((Vector2)(ref val4))..ctor(1f, 0.5f); val3.anchorMax = val4; val3.anchorMin = val4; ((Transform)val3).Translate(new Vector3(32f, 0f, 0f)); GuiUtils.FixLobbyScrollItem(val2, val3.sizeDelta.x * 0.4f); GuiUtils.SetItemColor((CM_Item)(object)val2, AccentColor, Color.white); } Loc.TrySetTMP((Component)(object)val2, "ui.skinsButton"); GameStateCache.LobbyBar2LobbyScrollItem.Save((Component)(object)__instance, val2); } [HarmonyPatch(typeof(CM_PlayerLobbyBar), "PrepareForIntro")] [HarmonyPostfix] private static void Postfix__LobbyBar_PrepareForIntro(CM_PlayerLobbyBar __instance) { CM_LobbyScrollItem val = GameStateCache.LobbyBar2LobbyScrollItem.Get((Component)(object)__instance); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(false); } } [HarmonyPatch(typeof(CM_PlayerLobbyBar), "SpawnPlayerModel")] [HarmonyPostfix] private static void Postfix__LobbyBar_SpawnPlayerModel(CM_PlayerLobbyBar __instance) { CM_LobbyScrollItem val = GameStateCache.LobbyBar2LobbyScrollItem.Get((Component)(object)__instance); if ((Object)(object)val != (Object)null && (Object)(object)__instance.m_player != (Object)null && __instance.m_player.IsLocal) { CoroutineManager.BlinkIn((CM_Item)(object)val, 1.2f, (Transform)null); } } [HarmonyPatch(typeof(CM_PlayerLobbyBar), "ShowWeaponSelectionPopup")] [HarmonyPostfix] private static void AddWeaponSelectSkinEntryPoint(CM_PlayerLobbyBar __instance) { } [HarmonyPatch(typeof(CM_PageSettings), "Setup")] [HarmonyPostfix] [HarmonyPriority(800)] private static void AddHotReloadButton(CM_PageSettings __instance) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) Logger.Warn("Uhh... maybe TheArchive should find a better way to store offset, because I hate to have to take first priority ;)"); CM_Item val = __instance.CreateSingleButton(__instance.m_redMenuButtonPrefab, Loc.ID("settings.reload")); if ((Object)(object)val != (Object)null) { val.SetText("Reload Skins"); GuiUtils.SetItemColor(val, AccentColor, AccentColor); val.OnBtnPressCallback += Action.op_Implicit((Action)OnPageMenuSettingReloadButtonPressed); } } private static void OnPageMenuSettingReloadButtonPressed(int _) { SkinManager.Reload(); } } } namespace Skins.UI.Utils { [RegisterIl2Cpp] public class ColliderDebugDraw : MonoBehaviour { private BoxCollider2D _col; private Material _mat; private void Start() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown _col = ((Component)this).GetComponent(); _mat = new Material(Shader.Find("Hidden/Internal-Colored")); } private void OnRenderObject() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_col == (Object)null)) { Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(((Collider2D)_col).offset.x, ((Collider2D)_col).offset.y, 0f); float num = _col.size.x * 0.5f; float num2 = _col.size.y * 0.5f; Vector3 val2 = ((Component)this).transform.TransformPoint(val + new Vector3(0f - num, num2, 0f)); Vector3 val3 = ((Component)this).transform.TransformPoint(val + new Vector3(num, num2, 0f)); Vector3 val4 = ((Component)this).transform.TransformPoint(val + new Vector3(num, 0f - num2, 0f)); Vector3 val5 = ((Component)this).transform.TransformPoint(val + new Vector3(0f - num, 0f - num2, 0f)); _mat.SetPass(0); GL.Begin(1); GL.Color(Color.green); GL.Vertex(val2); GL.Vertex(val3); GL.Vertex(val3); GL.Vertex(val4); GL.Vertex(val4); GL.Vertex(val5); GL.Vertex(val5); GL.Vertex(val2); GL.End(); } } } } namespace Skins.UI.Menu { internal interface ISelectionPopupItem { string DisplayName { get; } string ID { get; } string SubTitle { get; } string Description { get; } Sprite SpriteSmall { get; } Sprite SpriteLarge { get; } bool WasDoubleClick { get; set; } bool IsActive { get; set; } bool IsSelected { get; set; } bool IsAllowedToSelect { get; set; } bool CloseMenu { get; set; } float ScrollPosition { get; set; } void OnClicked(); void OnSetActive(); void SubtitleTextCustomization(ref string suptitleText, ref string subtitleText) { } } internal sealed class SelectionPopupHeader { private Action _drawAction; private CM_PlayerLobbyBar _lobbyBar; public string HeaderText { get; set; } = "No Text Set"; public string IsActiveText { get; set; } = "Selected"; public string SetActiveConfirmText { get; set; } = "Click again to select."; public Func AllowedToSelectItem { get; set; } public List Items { get; } = new List(); public string ActiveItemID { get; set; } public string LastSelectedItemID { get; set; } public Vector2? SpriteLargeSize { get; set; } public Action PreDraw { get; set; } internal Action DrawAction => Draw; internal SelectionPopupMenu Parent { get; set; } public bool IsUpdating { get; private set; } public ISelectionPopupItem LastClickedItem { get; private set; } public SelectionPopupHeader(string headerText) { HeaderText = headerText; } public void Show() { Parent.Show(Parent.GetKey(this)); TrySetLargeSpriteSize(); } public void Hide() { Parent.Hide(); } public SelectionPopupHeader AddItem(ISelectionPopupItem item) { if (Items.Contains(item)) { return this; } Items.Add(item); return this; } internal void Setup(CM_PlayerLobbyBar lobbyBar) { _lobbyBar = lobbyBar; } internal void Draw(int key) { Draw(key, null); } internal void Draw(int key, float? scrollPosOffset) { if (!((Object)(object)_lobbyBar == (Object)null)) { IsUpdating = false; UpdateContent(key, scrollPosOffset); } } private void UpdateContent(int key, float? scrollPosOffset = null) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_039c: Unknown result type (might be due to invalid IL or missing references) PreDraw?.Invoke(this); List val = new List(); int num = 0; foreach (ISelectionPopupItem item in Items) { CM_LobbyScrollItem val2 = GOUtil.SpawnChildAndGetComp(_lobbyBar.m_clothesCardPrefab, ((Component)_lobbyBar).transform); val.Add(new iScrollWindowContent(((Il2CppObjectBase)val2).Pointer)); ((CM_Item)val2).TextMeshRoot = ((Component)_lobbyBar.m_parentPage).transform; val2.SetupFromLobby(((Component)_lobbyBar).transform, _lobbyBar, true); ((RectTransformComp)val2).ForcePopupLayer(true, (GameObject)null); ((Component)val2).transform.localScale = Vector3.one; ((TMP_Text)val2.m_nameText).SetText(item.DisplayName ?? "", true); bool flag = item.ID == LastSelectedItemID; item.IsSelected = flag; val2.IsSelected = flag; bool flag2 = item.ID == ActiveItemID; item.IsActive = flag2; ((Component)((Component)val2).transform.FindChild("EmptyIcon")).gameObject.SetActive(false); ((Component)((Component)val2).transform.FindChild("Background")).gameObject.SetActive(flag); string suptitleText = string.Empty; item.IsAllowedToSelect = AllowedToSelectItem?.Invoke(this, item) ?? true; if (flag) { _lobbyBar.m_popupScrollWindow.InfoBox.SetInfoBox(item.DisplayName, item.SubTitle, item.Description, string.Empty, string.Empty, (Sprite)null); _lobbyBar.m_popupScrollWindow.InfoBox.SetInfoBox(item.DisplayName, item.SubTitle, item.Description, string.Empty, string.Empty, item.SpriteLarge); TrySetLargeSpriteSize(); if (item.IsAllowedToSelect) { suptitleText = SetActiveConfirmText; } } if (flag2) { suptitleText = IsActiveText; item.OnSetActive(); } string subtitleText = item.ID; item.SubtitleTextCustomization(ref suptitleText, ref subtitleText); ((TMP_Text)val2.m_subTitleText).SetText(suptitleText, true); ((TMP_Text)val2.m_descText).SetText(" <#666>" + subtitleText + "", true); ((CM_Item)val2).OnBtnPressCallback = Action.op_Implicit((Action)delegate { bool flag3 = LastSelectedItemID == item.ID; item.WasDoubleClick = flag3; item.OnClicked(); if (item.CloseMenu) { item.CloseMenu = false; Hide(); } else { if (flag3) { ActiveItemID = item.ID; } LastSelectedItemID = item.ID; _lobbyBar.m_popupScrollWindow.InfoBox.SetInfoBox(item.DisplayName, item.SubTitle, item.Description, string.Empty, string.Empty, item.SpriteLarge); IsUpdating = true; LastClickedItem = item; UpdateContent(key, _lobbyBar.m_popupScrollWindow.m_posOffset); } }); if ((Object)(object)item.SpriteSmall != (Object)null) { val2.m_icon.sprite = item.SpriteSmall; } val2.PlayIntro((float)num * 0.1f, -1); ((CM_Item)val2).m_alphaSpriteOnHover = true; ((CM_Item)val2).m_alphaTextOnHover = true; num++; } _lobbyBar.m_popupScrollWindow.SetContentItems(val, 0f); for (int i = 0; i < val.Count; i++) { CM_LobbyScrollItem val3 = ((Il2CppObjectBase)val[i]).TryCast(); if (!((Object)(object)val3 == (Object)null)) { val3.UpdateSizesAndOffsets(); Items[i].ScrollPosition = val3.PosYStartEnd.x; } } if (scrollPosOffset.HasValue) { ScrollTo(scrollPosOffset.Value); } _lobbyBar.Select(); _lobbyBar.ShowPopup(); _lobbyBar.m_popupScrollWindow.SelectHeader(key); } private void TrySetLargeSpriteSize() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (SpriteLargeSize.HasValue) { _lobbyBar.m_popupScrollWindow.InfoBox.m_infoMainIcon.size = SpriteLargeSize.Value; } } public void ScrollTo(ISelectionPopupItem item) { ScrollTo(item.ScrollPosition); } public void ScrollTo(float posOffset) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) CM_ScrollWindow popupScrollWindow = _lobbyBar.m_popupScrollWindow; if (popupScrollWindow.m_scrollBar.Visible) { float num = popupScrollWindow.m_contentContainerHeight - popupScrollWindow.m_windowHeight; popupScrollWindow.m_posOffset = Mathf.Clamp(posOffset, 0f, num); Vector2 position = popupScrollWindow.m_contentContainer.GetPosition(); position.y = popupScrollWindow.m_posStart + popupScrollWindow.m_posOffset; popupScrollWindow.m_contentContainer.SetPosition(position); popupScrollWindow.UpdateContentItemVisibility(); popupScrollWindow.m_scrollBar.SetHandlePosition(Mathf.Clamp01(posOffset / num), popupScrollWindow.m_windowHeight); } } } internal class SelectionPopupItem : ISelectionPopupItem { public Action clickedAction; public Action setActiveAction; public Func upperTextCustomization; public Func subtitleTextCustomization; public string DisplayName { get; set; } public string ID { get; set; } public string SubTitle { get; set; } public string Description { get; set; } public Sprite SpriteSmall { get; set; } public Sprite SpriteLarge { get; set; } public bool WasDoubleClick { get; set; } public bool IsActive { get; set; } public bool IsSelected { get; set; } public bool IsAllowedToSelect { get; set; } public bool CloseMenu { get; set; } public float ScrollPosition { get; set; } public void OnClicked() { clickedAction?.Invoke(this); } public void OnSetActive() { setActiveAction?.Invoke(this); } public void SubtitleTextCustomization(ref string suptitleText, ref string subtitleText) { string text = upperTextCustomization?.Invoke(this, suptitleText); if (text != null) { suptitleText = text; } string text2 = subtitleTextCustomization?.Invoke(this, subtitleText); if (text2 != null) { subtitleText = text2; } } } internal sealed class SelectionPopupMenu { private readonly List _headers = new List(); private CM_PlayerLobbyBar _lobbyBar; public SelectionPopupMenu AddHeader(SelectionPopupHeader header) { if (_headers.Contains(header)) { return this; } _headers.Add(header); header.Parent = this; return this; } public void Show(int key = 0) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 FocusStateManager.EnterMenu((eCM_MenuPage)13, false); eGameStateName currentStateName = GameStateManager.CurrentStateName; if ((int)currentStateName == 6 || (int)currentStateName == 10) { FocusStateManager.Current.m_previousToggleState = (eFocusState)4; } _lobbyBar = ((IEnumerable)CM_PageLoadout.Current.m_playerLobbyBars).FirstOrDefault((Func)((CM_PlayerLobbyBar plb) => ((Behaviour)plb).isActiveAndEnabled)); if ((Object)(object)_lobbyBar == (Object)null) { _lobbyBar = ((Il2CppArrayBase)(object)CM_PageLoadout.Current.m_playerLobbyBars)[0]; } Transform popupAlign = CM_PageLoadout.Current.m_popupAlign; ShowSelectionPopup(_lobbyBar, popupAlign, key); } public void Hide() { if (!((Object)(object)_lobbyBar == (Object)null)) { _lobbyBar.HidePopup(); } } public int GetKey(SelectionPopupHeader header) { return _headers.IndexOf(header); } public bool TryGetHeader(int key, out SelectionPopupHeader header) { header = null; if (key < 0 || key >= _headers.Count) { return false; } header = _headers[key]; return header != null; } private void ShowSelectionPopup(CM_PlayerLobbyBar lobbyBar, Transform align, int keyToShow) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) lobbyBar.m_popupVisible = true; ((Component)lobbyBar.m_popupHolder).transform.position = align.position + new Vector3(300f, 0f, 0f); lobbyBar.m_popupScrollWindow.m_infoBoxWidth = 600f; ((Component)lobbyBar.m_popupHolder).gameObject.SetActive(true); ((Component)lobbyBar.m_popupItemHolder).gameObject.SetActive(true); ((RectTransformComp)lobbyBar.m_popupScrollWindow).SetSize(new Vector2(1600f, 760f)); lobbyBar.m_popupScrollWindow.ResetHeaders(); int num = 0; foreach (SelectionPopupHeader header in _headers) { header.Setup(lobbyBar); int keyLocal = num; lobbyBar.m_popupScrollWindow.AddHeader(header.HeaderText, num, Action.op_Implicit((Action)delegate { header.DrawAction(keyLocal); })); num++; } ((RectTransformComp)lobbyBar.m_popupScrollWindow).SetPosition(new Vector2(0f, 350f)); lobbyBar.m_popupScrollWindow.RespawnInfoBoxFromPrefab(lobbyBar.m_popupInfoBoxWeaponPrefab); if (TryGetHeader(keyToShow, out var header2)) { header2.Draw(keyToShow); } } } internal class SkinSelectionPopupItem : SelectionPopupItem { public GameObject? ApplicationTarget { get; set; } public TargetContext Context { get; set; } = new TargetContext(); public string SkinGUID { get; set; } = ""; public bool IsVanilla { get; set; } } } namespace Skins.Textures { public class ShaderRefs { public static readonly string[] ShadersToPreload = new string[18] { "Cell/Player/CustomGearShader", "Unlit/HolographicSight_3Layers", "GTFO/BlendHS", "GTFO/MeshDecal", "Cell/Player/GearShader", "FX/FX_ParticleAdditive", "Cell/Player/Display_GearShader", "Unlit/HolographicSight_Thermal", "Cell/Player/Display_Hologram", "GTFO/Standard", "GTFO/Glass", "GTFO/Glass_DoubleSided", "GTFO/RefractiveGlass", "GTFO/RefractiveGlass_V2", "GTFO/TankGlass", "GTFO/Persistence/Clothing", "GTFO/FleshBulb", "GTFO/CMS_WorldProp" }; public static readonly Dictionary ShaderReferences = new Dictionary(); public static void Preload() { string[] shadersToPreload = ShadersToPreload; foreach (string text in shadersToPreload) { Shader val = Shader.Find(text); if ((Object)(object)val == (Object)null) { Logger.Error("Failed to preload shader '" + text + "'"); } else { ShaderReferences[text] = val; } } } public static Shader? Get(string shaderName) { if (ShaderReferences.TryGetValue(shaderName, out Shader value)) { return value; } value = Shader.Find(shaderName); if ((Object)(object)value == (Object)null) { Logger.Error("Failed to load shader '" + shaderName + "'"); return null; } ShaderReferences[shaderName] = value; return value; } } public static class TextureLibrary { private static readonly Dictionary _textures = new Dictionary(); public static Texture2D? Load(string path, TextureOptions options = default(TextureOptions), bool managed = true) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown string key = CacheKey(path, options); if (_textures.TryGetValue(key, out Texture2D value)) { return value; } string path2 = NormalizePath(path); if (!File.Exists(path2)) { Logger.Warn("[TextureLibrary] Texture not found: " + path); return null; } byte[] array = File.ReadAllBytes(path2); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, options.Mipmaps, options.Linear); if (!ImageConversion.LoadImage(val, Il2CppStructArray.op_Implicit(array), !options.Mipmaps)) { Logger.Warn("[TextureLibrary] Failed to decode image: " + path); Object.Destroy((Object)(object)val); return null; } if (options.Mipmaps) { val.Apply(true, true); } ((Object)val).name = Path.GetFileNameWithoutExtension(path2); if (managed) { _textures[key] = val; } return val; } public static void Unload(string path, TextureOptions options = default(TextureOptions)) { string key = CacheKey(path, options); if (_textures.Remove(key, out Texture2D value)) { Object.Destroy((Object)(object)value); } } public static void UnloadAll() { foreach (Texture2D value in _textures.Values) { Object.Destroy((Object)(object)value); } _textures.Clear(); } private static string CacheKey(string path, TextureOptions options) { return $"{NormalizePath(path)}|l={options.Linear}|m={options.Mipmaps}"; } private static string NormalizePath(string path) { return Path.GetFullPath(path).ToLowerInvariant(); } } public enum MacroParamType { Float01, Float255, Degrees, Percent, PositiveInt } public record MacroParam(string Name, MacroParamType Type); public interface ITextureMacroImpl { string Name { get; } string Description { get; } IReadOnlyList Parameters { get; } string Signature { get { if (Parameters.Count != 0) { return $"@{Name}({string.Join(",", Parameters.Select((MacroParam p) => p.Name))})[width,height]"; } return "@" + Name + "[width,height]"; } } Texture2D? Evaluate(string[] args, Vector2Int size, TextureOptions options); } public static class TextureMacro { private sealed class WhiteMacro : ITextureMacroImpl { public string Name => "WHITE"; public string Description => "Solid white texture"; public IReadOnlyList Parameters => NoParams; public Texture2D? Evaluate(string[] args, Vector2Int size, TextureOptions opts) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Solid(Color.white, ((Vector2Int)(ref size)).x, ((Vector2Int)(ref size)).y, opts); } } private sealed class BlackMacro : ITextureMacroImpl { public string Name => "BLACK"; public string Description => "Solid black texture"; public IReadOnlyList Parameters => NoParams; public Texture2D? Evaluate(string[] args, Vector2Int size, TextureOptions opts) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Solid(Color.black, ((Vector2Int)(ref size)).x, ((Vector2Int)(ref size)).y, opts); } } private sealed class TransparentMacro : ITextureMacroImpl { public string Name => "TRANSPARENT"; public string Description => "Fully transparent texture"; public IReadOnlyList Parameters => NoParams; public Texture2D? Evaluate(string[] args, Vector2Int size, TextureOptions opts) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Solid(Color.clear, ((Vector2Int)(ref size)).x, ((Vector2Int)(ref size)).y, opts); } } private sealed class NormalMacro : ITextureMacroImpl { public string Name => "NORMAL"; public string Description => "Default normal map (0.5, 0.5, 1, 1)"; public IReadOnlyList Parameters => NoParams; public Texture2D? Evaluate(string[] args, Vector2Int size, TextureOptions opts) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) return Solid(new Color(0.5f, 0.5f, 1f, 1f), ((Vector2Int)(ref size)).x, ((Vector2Int)(ref size)).y, opts); } } private sealed class GreyMacro : ITextureMacroImpl { public string Name => "GREY"; public string Description => "Grey texture; value 0-255"; public IReadOnlyList Parameters { get; } = new MacroParam[1] { new MacroParam("value", MacroParamType.Float255) }; public Texture2D? Evaluate(string[] args, Vector2Int size, TextureOptions opts) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) float result; float num = ((args.Length != 0 && float.TryParse(args[0], out result)) ? (result / 255f) : 0.5f); return Solid(new Color(num, num, num, 1f), ((Vector2Int)(ref size)).x, ((Vector2Int)(ref size)).y, opts); } } private sealed class RgbaMacro : ITextureMacroImpl { public string Name => "RGBA"; public string Description => "Solid color, channels 0-1"; public IReadOnlyList Parameters => RgbaParams01; public Texture2D? Evaluate(string[] args, Vector2Int size, TextureOptions opts) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (args.Length < 4) { Logger.Warn("[TextureMacro] @RGBA requires 4 arguments (R,G,B,A) in 0-1 range"); return null; } return Solid(ParseColor01(args), ((Vector2Int)(ref size)).x, ((Vector2Int)(ref size)).y, opts); } } private sealed class Rgba32Macro : ITextureMacroImpl { public string Name => "RGBA32"; public string Description => "Solid color, channels 0-255"; public IReadOnlyList Parameters => RgbaParams255; public Texture2D? Evaluate(string[] args, Vector2Int size, TextureOptions opts) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (args.Length < 4) { Logger.Warn("[TextureMacro] @RGBA32 requires 4 arguments (R,G,B,A) in 0-255 range"); return null; } return Solid(ParseColor255(args), ((Vector2Int)(ref size)).x, ((Vector2Int)(ref size)).y, opts); } } private sealed class HslMacro : ITextureMacroImpl { public string Name => "HSL"; public string Description => "HSL color (H: 0-360, S/L: 0-100)"; public IReadOnlyList Parameters { get; } = new MacroParam[3] { new MacroParam("h", MacroParamType.Degrees), new MacroParam("s", MacroParamType.Percent), new MacroParam("l", MacroParamType.Percent) }; public Texture2D? Evaluate(string[] args, Vector2Int size, TextureOptions opts) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (args.Length < 3) { Logger.Warn("[TextureMacro] @HSL requires 3 arguments (H, S, L)"); return null; } return Solid(HslToColor(args), ((Vector2Int)(ref size)).x, ((Vector2Int)(ref size)).y, opts); } } private sealed class MetallicSmoothnessMacro : ITextureMacroImpl { public string Name => "METALLIC_SMOOTHNESS"; public string Description => "Metallic/smoothness map: R = metallic, G = smoothness, B = 0, A = 1"; public IReadOnlyList Parameters { get; } = new MacroParam[2] { new MacroParam("metallic", MacroParamType.Float01), new MacroParam("smoothness", MacroParamType.Float01) }; public Texture2D? Evaluate(string[] args, Vector2Int size, TextureOptions opts) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) float result; float num = ((args.Length != 0 && float.TryParse(args[0], out result)) ? result : 0f); float result2; float num2 = ((args.Length > 1 && float.TryParse(args[1], out result2)) ? result2 : 0f); return Solid(new Color(num, num2, 0f, 1f), ((Vector2Int)(ref size)).x, ((Vector2Int)(ref size)).y, opts); } } private sealed class CheckerMacro : ITextureMacroImpl { public string Name => "CHECKER"; public string Description => "Checkerboard with two RGBA colors"; public IReadOnlyList Parameters { get; } = new MacroParam[9] { new MacroParam("r1", MacroParamType.Float01), new MacroParam("g1", MacroParamType.Float01), new MacroParam("b1", MacroParamType.Float01), new MacroParam("a1", MacroParamType.Float01), new MacroParam("r2", MacroParamType.Float01), new MacroParam("g2", MacroParamType.Float01), new MacroParam("b2", MacroParamType.Float01), new MacroParam("a2", MacroParamType.Float01), new MacroParam("cells", MacroParamType.PositiveInt) }; public Texture2D? Evaluate(string[] args, Vector2Int size, TextureOptions opts) { //IL_000f: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0041: Unknown result type (might be due to invalid IL or missing references) Color a = ((args.Length >= 4) ? ParseColor01(args) : Color.white); Color b = ((args.Length >= 8) ? ParseColor01(args, 4) : Color.black); int result; int cells = ((args.Length >= 9 && int.TryParse(args[8], out result)) ? result : 2); return Checker(a, b, cells, ((Vector2Int)(ref size)).x, ((Vector2Int)(ref size)).y, opts); } } private sealed class GradientHMacro : ITextureMacroImpl { public string Name => "GRADIENT_H"; public string Description => "Horizontal gradient between two colors"; public IReadOnlyList Parameters => TwoColorsParams01; public Texture2D? Evaluate(string[] args, Vector2Int size, TextureOptions opts) { //IL_000f: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0029: Unknown result type (might be due to invalid IL or missing references) Color a = ((args.Length >= 4) ? ParseColor01(args) : Color.black); Color b = ((args.Length >= 8) ? ParseColor01(args, 4) : Color.white); return GradientH(a, b, ((Vector2Int)(ref size)).x, ((Vector2Int)(ref size)).y, opts); } } private sealed class GradientVMacro : ITextureMacroImpl { public string Name => "GRADIENT_V"; public string Description => "Vertical gradient between two colors"; public IReadOnlyList Parameters => TwoColorsParams01; public Texture2D? Evaluate(string[] args, Vector2Int size, TextureOptions opts) { //IL_000f: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0029: Unknown result type (might be due to invalid IL or missing references) Color a = ((args.Length >= 4) ? ParseColor01(args) : Color.black); Color b = ((args.Length >= 8) ? ParseColor01(args, 4) : Color.white); return GradientV(a, b, ((Vector2Int)(ref size)).x, ((Vector2Int)(ref size)).y, opts); } } private const int DefaultSize = 4; private static readonly Regex MacroRe = new Regex("@(\\w+)(?:\\(([^)]*)\\))?(?:\\[(\\d+),\\s*(\\d+)\\])?", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly Dictionary ByName = Registry.ToDictionary((ITextureMacroImpl m) => m.Name, (ITextureMacroImpl m) => m, StringComparer.OrdinalIgnoreCase); private static readonly Dictionary Cache = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly MacroParam[] NoParams = Array.Empty(); private static readonly MacroParam[] RgbaParams01 = new MacroParam[4] { new MacroParam("r", MacroParamType.Float01), new MacroParam("g", MacroParamType.Float01), new MacroParam("b", MacroParamType.Float01), new MacroParam("a", MacroParamType.Float01) }; private static readonly MacroParam[] RgbaParams255 = new MacroParam[4] { new MacroParam("r", MacroParamType.Float255), new MacroParam("g", MacroParamType.Float255), new MacroParam("b", MacroParamType.Float255), new MacroParam("a", MacroParamType.Float255) }; private static readonly MacroParam[] TwoColorsParams01 = new MacroParam[8] { new MacroParam("r1", MacroParamType.Float01), new MacroParam("g1", MacroParamType.Float01), new MacroParam("b1", MacroParamType.Float01), new MacroParam("a1", MacroParamType.Float01), new MacroParam("r2", MacroParamType.Float01), new MacroParam("g2", MacroParamType.Float01), new MacroParam("b2", MacroParamType.Float01), new MacroParam("a2", MacroParamType.Float01) }; public static IReadOnlyList Registry { get; } = new List { new WhiteMacro(), new BlackMacro(), new TransparentMacro(), new NormalMacro(), new GreyMacro(), new RgbaMacro(), new Rgba32Macro(), new HslMacro(), new MetallicSmoothnessMacro(), new CheckerMacro(), new GradientHMacro(), new GradientVMacro() }.AsReadOnly(); public static bool IsMacro(string value) { return MacroRe.IsMatch(value.Trim()); } public static Texture2D? Resolve(string value, TextureOptions options = default(TextureOptions)) { //IL_017a: Unknown result type (might be due to invalid IL or missing references) string text = value.Trim(); if (string.IsNullOrEmpty(text)) { return null; } string key = $"{text}|l={options.Linear}|m={options.Mipmaps}"; if (Cache.TryGetValue(key, out Texture2D value2)) { return value2; } Match match = MacroRe.Match(text); if (!match.Success) { Logger.Warn("[TextureMacro] Invalid macro format: '" + text + "'"); return null; } string value3 = match.Groups[1].Value; string[] args = (match.Groups[2].Success ? match.Groups[2].Value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) : Array.Empty()); int result; int num = ((match.Groups[3].Success && int.TryParse(match.Groups[3].Value, out result)) ? result : 4); int result2; int num2 = ((match.Groups[4].Success && int.TryParse(match.Groups[4].Value, out result2)) ? result2 : 4); if (!ByName.TryGetValue(value3, out ITextureMacroImpl value4)) { Logger.Warn("[TextureMacro] Unknown macro: '@" + value3 + "'"); return null; } Texture2D val = value4.Evaluate(args, new Vector2Int(num, num2), options); if ((Object)(object)val != (Object)null) { ((Object)val).name = "macro_" + text; Cache[key] = val; } return val; } public static void UnloadAll() { foreach (Texture2D value in Cache.Values) { Object.Destroy((Object)(object)value); } Cache.Clear(); } internal static Texture2D Solid(Color color, int w, int h, TextureOptions options) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown Texture2D val = new Texture2D(w, h, (TextureFormat)4, options.Mipmaps, options.Linear); Color[] array = (Color[])(object)new Color[w * h]; Array.Fill(array, color); val.SetPixels(Il2CppStructArray.op_Implicit(array)); val.Apply(options.Mipmaps, true); return val; } internal static Texture2D Checker(Color a, Color b, int cells, int w, int h, TextureOptions options) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(w, h, (TextureFormat)4, options.Mipmaps, options.Linear); Color[] array = (Color[])(object)new Color[w * h]; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int num = j * cells / w; int num2 = i * cells / h; array[i * w + j] = (((num + num2) % 2 == 0) ? a : b); } } val.SetPixels(Il2CppStructArray.op_Implicit(array)); val.Apply(options.Mipmaps, true); return val; } internal static Texture2D GradientH(Color a, Color b, int w, int h, TextureOptions options) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(w, h, (TextureFormat)4, options.Mipmaps, options.Linear); Color[] array = (Color[])(object)new Color[w * h]; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { array[i * w + j] = Color.Lerp(a, b, (float)j / (float)Mathf.Max(1, w - 1)); } } val.SetPixels(Il2CppStructArray.op_Implicit(array)); val.Apply(options.Mipmaps, true); return val; } internal static Texture2D GradientV(Color a, Color b, int w, int h, TextureOptions options) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(w, h, (TextureFormat)4, options.Mipmaps, options.Linear); Color[] array = (Color[])(object)new Color[w * h]; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { array[i * w + j] = Color.Lerp(a, b, (float)i / (float)Mathf.Max(1, h - 1)); } } val.SetPixels(Il2CppStructArray.op_Implicit(array)); val.Apply(options.Mipmaps, true); return val; } internal static Color ParseColor01(string[] args, int offset = 0) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) float result; float result2; float result3; float result4; return new Color(float.TryParse(args[offset], out result) ? result : 0f, float.TryParse(args[offset + 1], out result2) ? result2 : 0f, float.TryParse(args[offset + 2], out result3) ? result3 : 0f, float.TryParse(args[offset + 3], out result4) ? result4 : 1f); } internal static Color ParseColor255(string[] args, int offset = 0) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) float result; float result2; float result3; float result4; return new Color(float.TryParse(args[offset], out result) ? (result / 255f) : 0f, float.TryParse(args[offset + 1], out result2) ? (result2 / 255f) : 0f, float.TryParse(args[offset + 2], out result3) ? (result3 / 255f) : 0f, float.TryParse(args[offset + 3], out result4) ? (result4 / 255f) : 1f); } internal static Color HslToColor(string[] args) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) float result; float num = (float.TryParse(args[0], out result) ? (result / 360f) : 0f); float result2; float num2 = (float.TryParse(args[1], out result2) ? (result2 / 100f) : 0f); float result3; float num3 = (float.TryParse(args[2], out result3) ? (result3 / 100f) : 0f); float result4; float num4 = ((args.Length >= 4 && float.TryParse(args[3], out result4)) ? result4 : 1f); if (num2 == 0f) { return new Color(num3, num3, num3, num4); } float num5 = ((num3 < 0.5f) ? (num3 * (1f + num2)) : (num3 + num2 - num3 * num2)); float p = 2f * num3 - num5; return new Color(HueToRgb(p, num5, num + 1f / 3f), HueToRgb(p, num5, num), HueToRgb(p, num5, num - 1f / 3f), num4); } internal static float HueToRgb(float p, float q, float t) { if (t < 0f) { t += 1f; } if (t > 1f) { t -= 1f; } if (t < 1f / 6f) { return p + (q - p) * 6f * t; } if (t < 0.5f) { return q; } if (t < 2f / 3f) { return p + (q - p) * (2f / 3f - t) * 6f; } return p; } } [JsonConverter(typeof(TextureOptionsConverter))] public readonly record struct TextureOptions(bool Linear, bool Mipmaps) { public static readonly TextureOptions Albedo = new TextureOptions(Linear: false, Mipmaps: true); public static readonly TextureOptions Data = new TextureOptions(Linear: true, Mipmaps: true); public static readonly TextureOptions AlbedoNoMips = new TextureOptions(Linear: false, Mipmaps: false); public static readonly TextureOptions DataNoMips = new TextureOptions(Linear: true, Mipmaps: false); } public class TextureOptionsConverter : JsonConverter { public override void WriteJson(JsonWriter writer, TextureOptions value, JsonSerializer serializer) { string text = (value.Equals(TextureOptions.Albedo) ? "Albedo" : (value.Equals(TextureOptions.Data) ? "Data" : (value.Equals(TextureOptions.AlbedoNoMips) ? "AlbedoNoMips" : ((!value.Equals(TextureOptions.DataNoMips)) ? null : "DataNoMips")))); string text2 = text; if (text2 != null) { writer.WriteValue(text2); return; } writer.WriteStartObject(); writer.WritePropertyName("linear"); writer.WriteValue(value.Linear); writer.WritePropertyName("mipmaps"); writer.WriteValue(value.Mipmaps); writer.WriteEndObject(); } public override TextureOptions ReadJson(JsonReader reader, Type objectType, TextureOptions existingValue, bool hasExistingValue, JsonSerializer serializer) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Invalid comparison between Unknown and I4 //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Invalid comparison between Unknown and I4 //IL_0077: Unknown result type (might be due to invalid IL or missing references) if ((int)reader.TokenType == 9) { string text = (string)reader.Value; return text switch { "Albedo" => TextureOptions.Albedo, "Data" => TextureOptions.Data, "AlbedoNoMips" => TextureOptions.AlbedoNoMips, "DataNoMips" => TextureOptions.DataNoMips, _ => throw new JsonException("Unknown TextureOptions preset: " + text), }; } bool linear = false; bool mipmaps = true; while (reader.Read() && (int)reader.TokenType != 13) { if ((int)reader.TokenType != 4) { continue; } string text2 = (string)reader.Value; if (!(text2 == "linear")) { if (text2 == "mipmaps") { mipmaps = reader.ReadAsBoolean() ?? true; } } else { linear = reader.ReadAsBoolean().GetValueOrDefault(); } } return new TextureOptions(linear, mipmaps); } } } namespace Skins.Target { [RegisterIl2Cpp] public class ManagedItemTarget : MonoBehaviour { } [RegisterIl2Cpp] public class ManagedMaterialTarget : MonoBehaviour { private MeshRenderer _meshRenderer; private MeshFilter _meshFilter; private Material _material; private bool _clean = true; private MaterialSnapshot _originalMatSnapshot; private Dictionary _originalTextureNames = new Dictionary(); public MeshRenderer MeshRenderer => _meshRenderer; public MeshFilter MeshFilter => _meshFilter; public Material Material => _material; public Shader? OriginalShader => _originalMatSnapshot.Shader; public string? OriginalShaderName { get { Shader? shader = _originalMatSnapshot.Shader; if (shader == null) { return null; } return ((Object)shader).name; } } public int OwnerOfflineGearId { get; private set; } = -1; public string CleanMaterialName { get; private set; } = ""; public TargetContext Context { get; private set; } = new TargetContext(); private void Awake() { SkinManager.RegisterMatTarget(this); } private void OnDestroy() { SkinManager.UnregisterMatTarget(this); } private void Start() { if ((Object)(object)((Component)this).GetComponent() != (Object)null) { Logger.Error("[ManagedMaterialTarget] cannot be attached to a GameObject with a TMP_Text component."); Object.Destroy((Object)(object)this); return; } _meshRenderer = ((Component)this).GetComponent(); _meshFilter = ((Component)this).GetComponent(); if ((Object)(object)_meshRenderer == (Object)null || (Object)(object)_meshFilter == (Object)null) { Logger.Error("[ManagedMaterialTarget] cannot attach to target without MeshRenderer and MeshFilter components."); Object.Destroy((Object)(object)this); return; } _material = ((Renderer)_meshRenderer).material; CleanMaterialName = NameUtils.GetCleanMaterialName(((Object)_material).name); _originalMatSnapshot = MaterialSnapshot.Capture(_material); _originalTextureNames = _originalMatSnapshot.GetTextureNames(); UpdateContext(); UpdateMaterial(); } public bool UpdateMaterial(bool overrideGroup = false, string overrideGroupGuid = "", bool matchAny = false) { string value = null; Revert(); bool flag = false; if (overrideGroup) { value = overrideGroupGuid; } else if (PersistentConfig.Instance == null) { if (!matchAny) { return false; } } else { flag = PersistentConfig.Instance.CurrentSkinConfig.OfflineGear.TryGetValue(OwnerOfflineGearId, out value); if (!flag && !matchAny) { return false; } } SkinGroup group = null; if (value == null) { group = SkinManager.GetAnyMatchingGroup(Context); if (group == null) { Logger.Warn($"[ManagedMaterialTarget] failed to find any matching skin group for target with context: {Context}"); return false; } } else if (!SkinManager.TryGetGroup(value, out group)) { Logger.Error("[ManagedMaterialTarget] failed to find skin group with guid '" + value + "'"); if (flag) { PersistentConfig.Instance.CurrentSkinConfig.OfflineGear[OwnerOfflineGearId] = "10chambers.vanilla"; Logger.Warn($"[ManagedMaterialTarget] Reverted to vanilla skin group for OfflineGearID={OwnerOfflineGearId}"); } return false; } if (!group.TryGetMaterialTarget(CleanMaterialName, _originalTextureNames, out MaterialTarget targetOut)) { Logger.Debug($"[ManagedMaterialTarget] skin group '{group.Name}' does not have a matching material target for material '{CleanMaterialName}' with the required textures. This might be intentional."); return false; } if (targetOut.Shader != null && targetOut.Shader != ((Object)_material.shader).name) { Shader val = ShaderRefs.Get(targetOut.Shader); if ((Object)(object)val == (Object)null) { Logger.Warn($"[ManagedMaterialTarget] failed to load shader '{targetOut.Shader}' for material target '{targetOut.MaterialNameToMatch}' in skin group '{group.Name}'. Shader will not be substituted."); } else { _material.shader = val; } } _clean = false; foreach (var (propertyName, materialProperty2) in targetOut.Properties) { materialProperty2?.Apply(_material, propertyName); } Logger.Info($"[ManagedMaterialTarget] updated material '{CleanMaterialName}' ({((Object)_material).GetInstanceID()}) with skin group '{group.Name}'"); return true; } public void Revert() { if (!_clean) { _originalMatSnapshot.Restore(_material); _clean = true; } } internal void SetAssociatedOfflineGearId(int? id) { OwnerOfflineGearId = id ?? (-1); UpdateContext(); } private void UpdateContext() { Context = new TargetContext { OfflineGearId = OwnerOfflineGearId }; } } public class MaterialSnapshot { public Dictionary Floats { get; } = new Dictionary(); public Dictionary Colors { get; } = new Dictionary(); public Dictionary Vectors { get; } = new Dictionary(); public Dictionary Textures { get; } = new Dictionary(); public Shader? Shader { get; private set; } public List EnabledKeywords { get; } = new List(); public static MaterialSnapshot Capture(Material mat) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected I4, but got Unknown //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) MaterialSnapshot materialSnapshot = new MaterialSnapshot(); materialSnapshot.Shader = mat.shader; foreach (string item in (Il2CppArrayBase)(object)mat.GetShaderKeywords()) { if (mat.IsKeywordEnabled(item)) { materialSnapshot.EnabledKeywords.Add(item); } } Shader shader = mat.shader; for (int i = 0; i < shader.GetPropertyCount(); i++) { string propertyName = shader.GetPropertyName(i); ShaderPropertyType propertyType = shader.GetPropertyType(i); switch ((int)propertyType) { case 2: case 3: materialSnapshot.Floats[propertyName] = mat.GetFloat(propertyName); break; case 0: materialSnapshot.Colors[propertyName] = mat.GetColor(propertyName); break; case 1: materialSnapshot.Vectors[propertyName] = mat.GetVector(propertyName); break; case 4: materialSnapshot.Textures[propertyName] = mat.GetTexture(propertyName); break; } } return materialSnapshot; } public void Restore(Material mat) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_00d9: 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) if ((Object)(object)Shader != (Object)null) { mat.shader = Shader; } string key; foreach (KeyValuePair @float in Floats) { @float.Deconstruct(out key, out var value); string text = key; float num = value; mat.SetFloat(text, num); } foreach (KeyValuePair color in Colors) { color.Deconstruct(out key, out var value2); string text2 = key; Color val = value2; mat.SetColor(text2, val); } foreach (KeyValuePair vector in Vectors) { vector.Deconstruct(out key, out var value3); string text3 = key; Vector4 val2 = value3; mat.SetVector(text3, val2); } foreach (KeyValuePair texture in Textures) { texture.Deconstruct(out key, out var value4); string text4 = key; Texture val3 = value4; mat.SetTexture(text4, val3); } foreach (string item in (Il2CppArrayBase)(object)mat.shaderKeywords) { mat.DisableKeyword(item); } foreach (string enabledKeyword in EnabledKeywords) { mat.EnableKeyword(enabledKeyword); } } public Dictionary GetTextureNames() { Dictionary dictionary = new Dictionary(); foreach (KeyValuePair texture in Textures) { texture.Deconstruct(out var key, out var value); string key2 = key; Texture val = value; dictionary[key2] = ((val != null) ? ((Object)val).name : null); } return dictionary; } } public class TargetContext { public int? OfflineGearId; } } namespace Skins.Patches { [HarmonyPatch] public class GearPatches { [HarmonyPatch(typeof(GearPartHolder), "OnAllPartsSpawned")] [HarmonyPostfix] private static void Postfix__GearPartHolder_OnAllPartsSpawned(GearPartHolder __instance) { if (__instance.GearIDRange == null) { return; } string text = ((Object)__instance).name.Replace("GearPartHolder_", ""); int? num = GearUtils.ExtractOfflineGearId(__instance.GearIDRange.PlayfabItemInstanceId); string text2 = $"[Gear] '{text}' #{num}\n"; foreach (MeshRenderer componentsInChild in ((Component)__instance).GetComponentsInChildren()) { if (ConfigMgr.DumpGearTree) { text2 = text2 + " [Group] '" + ((Object)((Component)componentsInChild).gameObject).name + "'\n"; MeshFilter component = ((Component)componentsInChild).GetComponent(); if ((Object)(object)((Component)componentsInChild).gameObject.GetComponent() != (Object)null) { continue; } text2 += $" [MeshRenderer] '{((Object)componentsInChild).name}' ({((Object)componentsInChild).GetInstanceID()})\n [Material] '{NameUtils.GetCleanMaterialName(((Object)((Renderer)componentsInChild).material).name)}' ({((Object)((Renderer)componentsInChild).material).GetInstanceID()})\n [Shader] '{((Object)((Renderer)componentsInChild).material.shader).name}' ({((Object)((Renderer)componentsInChild).material.shader).GetInstanceID()})\n"; foreach (string item in (Il2CppArrayBase)(object)((Renderer)componentsInChild).material.GetTexturePropertyNames()) { Texture texture = ((Renderer)componentsInChild).material.GetTexture(item); text2 += $" [Texture] {item}: {(((Object)(object)texture == (Object)null) ? "null" : $"'{((Object)texture).name}' ({((Object)texture).GetInstanceID()})")}\n"; } text2 += $" [MeshFilter] '{((Object)component).name}' ({((Object)component).GetInstanceID()})\n [Mesh] '{((Object)component.mesh).name}' ({((Object)component.mesh).GetInstanceID()})\n"; } if ((Object)(object)((Component)componentsInChild).gameObject.GetComponent() == (Object)null) { Dictionary dictionary = new Dictionary(); foreach (string item2 in (Il2CppArrayBase)(object)((Renderer)componentsInChild).material.GetTexturePropertyNames()) { Texture texture2 = ((Renderer)componentsInChild).material.GetTexture(item2); dictionary[item2] = ((texture2 != null) ? ((Object)texture2).name : null); } GearDump.RecordMaterial(NameUtils.GetCleanMaterialName(((Object)((Renderer)componentsInChild).material).name), ((Object)((Renderer)componentsInChild).material.shader).name, dictionary, text, num ?? (-1)); } if (!((Object)(object)((Component)componentsInChild).gameObject.GetComponent() != (Object)null)) { ((Component)componentsInChild).gameObject.AddComponent().SetAssociatedOfflineGearId(num); } } LogDumper.DumpUnique("GearTree", num ?? (-1), text2); } } } namespace Skins.Manifest { public interface IMaterialProperty { void Initialize(string? dirBase, string propertyName) { } void Apply(Material material, string propertyName); } [JsonObject] public class Texture2DProperty : IMaterialProperty { [JsonIgnore] private Texture2D? _resolved; [JsonProperty("src")] public string Source { get; set; } = ""; [JsonProperty("options")] public TextureOptions? Options { get; set; } public void Initialize(string? dirBase, string propertyName) { if (string.IsNullOrWhiteSpace(Source)) { Logger.Warn("[Texture2DProperty] Empty source for property '" + propertyName + "'. This is interpreted as no substitution — please remove empty texture entries."); return; } if (TextureMacro.IsMacro(Source)) { _resolved = TextureMacro.Resolve(Source, TextureUtils.InferOptionsFromSlot(propertyName)); if ((Object)(object)_resolved == (Object)null) { Logger.Warn($"[Texture2DProperty] Bad texture macro '{Source}' for property '{propertyName}'."); } return; } if (dirBase == null) { Logger.Error("[Texture2DProperty] Cannot resolve relative path '" + Source + "' for property '" + propertyName + "': manifest directory is invalid."); return; } TextureOptions options = Options ?? TextureUtils.InferOptionsFromSlot(propertyName); _resolved = TextureLibrary.Load(Path.Combine(dirBase, Source), options); if ((Object)(object)_resolved == (Object)null) { Logger.Error($"[Texture2DProperty] Failed to load texture from '{Source}' for property '{propertyName}'."); } } public void Apply(Material material, string propertyName) { if (!((Object)(object)_resolved == (Object)null) && ShaderPropCheck.Verify(material, propertyName, "Texture2DProperty", (ShaderPropertyType)4)) { if (propertyName == "_EmissiveMap") { material.EnableKeyword("ENABLE_EMISSIVE"); } material.SetTexture(propertyName, (Texture)(object)_resolved); } } } [JsonObject] public class FloatProperty : IMaterialProperty { [JsonProperty("value")] public float Value { get; set; } public void Apply(Material material, string propertyName) { if (ShaderPropCheck.Verify(material, propertyName, "FloatProperty", (ShaderPropertyType)2, (ShaderPropertyType)3)) { material.SetFloat(propertyName, Value); } } } [JsonObject] public class ColorProperty : IMaterialProperty { [JsonProperty("r")] public float R { get; set; } [JsonProperty("g")] public float G { get; set; } [JsonProperty("b")] public float B { get; set; } [JsonProperty("a")] public float A { get; set; } = 1f; public void Apply(Material material, string propertyName) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (ShaderPropCheck.Verify(material, propertyName, "ColorProperty", (ShaderPropertyType)0)) { material.SetColor(propertyName, new Color(R, G, B, A)); } } } [JsonObject] public class VectorProperty : IMaterialProperty { [JsonProperty("x")] public float X { get; set; } [JsonProperty("y")] public float Y { get; set; } [JsonProperty("z")] public float Z { get; set; } [JsonProperty("w")] public float W { get; set; } public void Apply(Material material, string propertyName) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (ShaderPropCheck.Verify(material, propertyName, "VectorProperty", (ShaderPropertyType)1)) { material.SetVector(propertyName, new Vector4(X, Y, Z, W)); } } } public static class ShaderPropCheck { internal static bool Verify(Material material, string propertyName, string callerTag, ShaderPropertyType expected, ShaderPropertyType? expected2 = null) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) Shader shader = material.shader; int num = shader.FindPropertyIndex(propertyName); if (num < 0) { Logger.Warn($"[{callerTag}] Shader '{((Object)shader).name}' does not have property '{propertyName}'."); return false; } ShaderPropertyType propertyType = shader.GetPropertyType(num); if (propertyType != expected && (!expected2.HasValue || (ShaderPropertyType?)propertyType != expected2)) { string value = ((!expected2.HasValue) ? ((object)(ShaderPropertyType)(ref expected)).ToString() : $"{expected} or {expected2}"); Logger.Warn($"[{callerTag}] Shader '{((Object)shader).name}' property '{propertyName}' is type '{propertyType}', expected '{value}'. Property will not be applied."); return false; } return true; } } public class MaterialPropertyConverter : JsonConverter { public override bool CanWrite => false; public override void WriteJson(JsonWriter writer, IMaterialProperty? value, JsonSerializer serializer) { throw new NotSupportedException("MaterialPropertyConverter does not support writing."); } public override IMaterialProperty? ReadJson(JsonReader reader, Type objectType, IMaterialProperty? existingValue, bool hasExistingValue, JsonSerializer serializer) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 if ((int)reader.TokenType == 11) { return null; } JObject val = JObject.Load(reader); JToken obj = val["type"]; string text = ((obj != null) ? Extensions.Value((IEnumerable)obj) : null); if (text == null) { Logger.Error("[MaterialProperty] missing 'type' field in material property entry."); return null; } JToken val2 = val["data"]; if (val2 == null) { Logger.Error("[MaterialProperty] entry of type '" + text + "' is missing the required 'data' field."); return null; } IMaterialProperty materialProperty; switch (text) { case "Texture2D": materialProperty = val2.ToObject(serializer); if (materialProperty == null) { Logger.Error("[MaterialProperty] failed to deserialize Texture2D."); } break; case "Float": materialProperty = val2.ToObject(serializer); if (materialProperty == null) { Logger.Error("[MaterialProperty] failed to deserialize Float."); } break; case "Color": materialProperty = val2.ToObject(serializer); if (materialProperty == null) { Logger.Error("[MaterialProperty] failed to deserialize Color."); } break; case "Vector": materialProperty = val2.ToObject(serializer); if (materialProperty == null) { Logger.Error("[MaterialProperty] failed to deserialize Vector."); } break; default: materialProperty = null; Logger.Error("[MaterialProperty] unknown material property type '" + text + "'."); break; } return materialProperty; } } [JsonObject] public class MaterialTarget { [JsonIgnore] public const string ManifestExtension = ".mattarget.json"; [JsonIgnore] public bool Initialized { get; private set; } [JsonIgnore] public SkinGroup Group { get; private set; } [JsonProperty("material_name")] public string MaterialNameToMatch { get; set; } = ""; [JsonProperty("matchGroups")] public List MatchGroups { get; set; } = new List(); [JsonProperty("texture_names")] [Obsolete("Deprecated: use matchGroups instead")] private Dictionary? LegacyTextureNames { get; set; } [JsonProperty("shader")] public string? Shader { get; set; } [JsonProperty("properties")] public Dictionary Properties { get; set; } = new Dictionary(); public bool IsMatchVariant(Dictionary textureNames) { if (MatchGroups.Count == 0) { return true; } foreach (MaterialTargetMatchGroup matchGroup in MatchGroups) { if (matchGroup.IsMatch(textureNames)) { return true; } } return false; } public MaterialTarget Initialize(string manifestPath, SkinGroup group) { Group = group; string directoryName = Path.GetDirectoryName(manifestPath); Dictionary legacyTextureNames = LegacyTextureNames; if (legacyTextureNames != null && legacyTextureNames.Count > 0) { Logger.Warn("[MaterialTarget] '" + MaterialNameToMatch + "': 'texture_names' is deprecated, please migrate to 'matchGroups'."); MaterialTargetMatchGroup materialTargetMatchGroup = new MaterialTargetMatchGroup(); foreach (var (slot, value) in LegacyTextureNames) { materialTargetMatchGroup.Conditions.Add(new MaterialTargetMatchCondition { Slot = slot, Value = value }); } MatchGroups.Add(materialTargetMatchGroup); LegacyTextureNames = null; } string[] array = Properties.Keys.ToArray(); foreach (string text3 in array) { IMaterialProperty materialProperty = Properties[text3]; if (materialProperty == null) { Logger.Error($"[MaterialTarget] property '{text3}' in material target '{MaterialNameToMatch}' " + "is null or could not be deserialized."); Properties.Remove(text3); } else { materialProperty.Initialize(directoryName, text3); } } Logger.Info($"[MaterialTarget] Initialized '{MaterialNameToMatch}' with {Properties.Count} property(s) in '{Group.Name}' ({manifestPath})"); Initialized = true; return this; } } [JsonObject] public class MaterialTargetMatchCondition { [JsonProperty("slot")] public string Slot { get; set; } = ""; [JsonProperty("value")] public string Value { get; set; } = ""; public bool IsMatch(Dictionary textureNames) { if (textureNames.TryGetValue(Slot, out string value)) { return value == Value; } return false; } } [JsonObject] public class MaterialTargetMatchGroup { [JsonProperty("conditions")] public List Conditions { get; set; } = new List(); public bool IsMatch(Dictionary textureNames) { foreach (MaterialTargetMatchCondition condition in Conditions) { if (!condition.IsMatch(textureNames)) { return false; } } return true; } } [JsonObject] public class SkinGroup { [JsonIgnore] public const string VanillaGroupGuid = "10chambers.vanilla"; [JsonIgnore] public const string ManifestFileName = "manifest.skingroup.json"; [JsonIgnore] public static readonly SkinGroup Vanilla = new SkinGroup { Initialized = true, Name = LocalizedString.FromLanguageMap(new Dictionary { [(Language)1] = "Vanilla", [(Language)2] = "Vanilla", [(Language)4] = "Vanilla", [(Language)5] = "Vanilla", [(Language)3] = "Vanilla", [(Language)9] = "バニラ", [(Language)10] = "바닐라", [(Language)7] = "Vanilla", [(Language)6] = "Ванилла", [(Language)8] = "Vanilla", [(Language)12] = "原版", [(Language)11] = "原版" }), Author = "10 Chambers", Description = LocalizedString.FromLanguageMap(new Dictionary { [(Language)1] = "The default skins from the base game.", [(Language)2] = "Les skins par défaut du jeu de base.", [(Language)4] = "Die Standard-Skins des Basisspiels.", [(Language)5] = "Las skins predeterminadas del juego base.", [(Language)3] = "Le skin predefinite del gioco base.", [(Language)9] = "ベースゲームのデフォルトスキン。", [(Language)10] = "기본 게임의 기본 스킨.", [(Language)7] = "As skins padrão do jogo base.", [(Language)6] = "Стандартные скины базовой игры.", [(Language)8] = "Domyślne skórki z podstawowej gry.", [(Language)12] = "游戏的默认皮肤。", [(Language)11] = "遊戲的預設皮膚。" }), Guid = "10chambers.vanilla" }; [JsonIgnore] public bool Initialized { get; private set; } [JsonProperty("name")] public LocalizedString Name { get; set; } [JsonProperty("desc")] public LocalizedString? Description { get; set; } [JsonProperty("author")] public LocalizedString Author { get; set; } [JsonProperty("guid")] public string Guid { get; set; } = ""; [JsonProperty("filterGroups")] public List FilterGroups { get; set; } = new List(); [JsonProperty("filters")] [Obsolete("Deprecated: use filterGroups instead")] private List? LegacyFilters { get; set; } [JsonIgnore] private List MaterialTargets { get; set; } = new List(); public bool Initialize(string manifestPath) { Logger.Info($"[SkinGroup] Initializing '{Name}' by '{Author}'..."); List legacyFilters = LegacyFilters; if (legacyFilters != null && legacyFilters.Count > 0) { Logger.Warn($"[SkinGroup] '{Name}': 'filters' is deprecated, please migrate to 'filterGroups'."); FilterGroups.Add(new SkinGroupFilterGroup { Filters = LegacyFilters }); LegacyFilters = null; } if (Name.IsEmpty) { Logger.Error("[SkinGroup] Invalid manifest at '" + manifestPath + "': name is required"); return false; } if (Author.IsEmpty) { Logger.Error("[SkinGroup] Invalid manifest at '" + manifestPath + "': author is required"); return false; } if (string.IsNullOrEmpty(Guid)) { Logger.Error("[SkinGroup] Invalid manifest at '" + manifestPath + "': guid is required"); return false; } string directoryName = Path.GetDirectoryName(manifestPath); if (directoryName == null) { Logger.Error("[SkinGroup] Invalid manifest path: " + manifestPath); return false; } foreach (string item in FileUtils.FilesWithExtensionRecursive(directoryName, ".mattarget.json")) { MaterialTarget materialTarget = JsonUtils.DeserializeFromFile(item); if (materialTarget == null) { Logger.Error("[SkinGroup] Failed to parse MaterialTarget manifest at '" + item + "'"); return false; } MaterialTargets.Add(materialTarget.Initialize(item, this)); } return true; } public bool IsMatch(TargetContext ctx) { if (FilterGroups.Count == 0) { return false; } foreach (SkinGroupFilterGroup filterGroup in FilterGroups) { if (filterGroup.IsMatch(ctx)) { return true; } } return false; } public bool TryGetMaterialTarget(string materialName, Dictionary textureNames, [NotNullWhen(true)] out MaterialTarget? targetOut) { targetOut = null; foreach (MaterialTarget materialTarget in MaterialTargets) { if (!(materialTarget.MaterialNameToMatch != materialName) && materialTarget.IsMatchVariant(textureNames)) { targetOut = materialTarget; return true; } } return false; } } public enum SkinGroupFilterType { OfflineGearPersistentID, RundownName } [JsonObject] public class SkinGroupFilter { [JsonProperty("type")] public SkinGroupFilterType Type { get; set; } [JsonProperty("value")] public string Value { get; set; } = ""; [JsonProperty("invert")] public bool Invert { get; set; } public bool IsMatch(TargetContext ctx) { bool flag = false; switch (Type) { case SkinGroupFilterType.OfflineGearPersistentID: { if (int.TryParse(Value, out var result)) { flag = ctx.OfflineGearId == result; } else { Logger.Warn("[SkinGroupFilter] Invalid OfflineGearPersistentID value '" + Value + "'"); } break; } case SkinGroupFilterType.RundownName: if (Value == ModData.RundownName) { flag = true; } break; } if (!Invert) { return flag; } return !flag; } } [JsonObject] public class SkinGroupFilterGroup { [JsonProperty("filters")] public List Filters { get; set; } = new List(); public bool IsMatch(TargetContext ctx) { foreach (SkinGroupFilter filter in Filters) { if (!filter.IsMatch(ctx)) { return false; } } return true; } } } namespace Skins.Localization { public static class Loc { public const string FallbackLocale = "en"; public const uint BaseId = 67676767u; private static readonly Dictionary> Translations; private static readonly Dictionary DataBlockIds; public static string CurrentLocale { get { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) ITextLocalizationService textLocalizationService = Text.TextLocalizationService; Language? val = ((textLocalizationService != null) ? new Language?(textLocalizationService.CurrentLanguage) : null); if (!val.HasValue) { Logger.Warn("[Loc] TextLocalizationService or CurrentLanguage is null.Are we running before GTFO has initialized its localization system? Defaulting to English."); } return LocaleUtils.LocaleKeyFromEnum((Language)(((int?)val) ?? 1)); } } static Loc() { Translations = new Dictionary>(); DataBlockIds = new Dictionary(); Load(); } public static uint ID(string key) { return DataBlockIds.GetValueOrDefault(key, 0u); } public static string T(string key) { string text = CurrentLocale; if (!Translations.ContainsKey(text)) { Logger.Debug($"[Loc] no translations found for locale '{text}', falling back to '{"en"}'."); text = "en"; } if (!Translations.TryGetValue(text, out Dictionary value)) { Logger.Error("[Loc] no translations available for fallback locale 'en'!"); return "NO_LOCALE[" + key + "]"; } if (!value.TryGetValue(key, out var value2)) { Logger.Warn($"[Loc] no translation found for key '{key}' in locale '{text}'!"); return $"UNKNOWN[{text}:{key}]"; } return value2; } public static bool TrySet(Component target, string key) { ILocalizedTextSetter component = target.GetComponent(); if (component == null) { return false; } Text.SetTextSetter(component, ID(key)); return true; } public static bool TrySetTMP(Component target, string key) { TMP_Localizer componentInChildren = target.GetComponentInChildren(); if ((Object)(object)componentInChildren == (Object)null) { return false; } componentInChildren.m_blockId = ID(key); return true; } private static void Load() { foreach (string item in FileUtils.FilesWithExtensionRecursive(ModData.LocalizationFolder, ".json")) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item); Dictionary dictionary = JsonUtils.DeserializeFromFile>(item); if (dictionary != null) { Translations[fileNameWithoutExtension] = dictionary; } } } internal static void InsertToGtfoDB() { //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: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_008a: Expected O, but got Unknown uint id = 67676767u; if (!Translations.TryGetValue("en", out Dictionary value)) { Logger.Error("[Loc] no translations found for fallback locale 'en', cannot insert to TextDataBlock!"); return; } List list = value.Keys.ToList(); List list2 = Translations.Keys.ToList(); foreach (string item in list) { uint num = GetNextId(); TextDataBlock val = new TextDataBlock(); ((GameDataBlockBase)val).name = "io.takina.gtfo.Skins." + item; ((GameDataBlockBase)val).internalEnabled = true; ((GameDataBlockBase)val).persistentID = num; TextDataBlock val2 = val; foreach (string item2 in list2) { if (Translations[item2].TryGetValue(item, out string value2)) { LocaleUtils.SetTranslation(val2, item2, value2); } } GameDataBlockBase.AddBlock(val2, -1); DataBlockIds[item] = num; } uint GetNextId() { id++; for (; GameDataBlockBase.HasBlock(id); id++) { } return id; } } } [JsonConverter(typeof(LocalizedStringConverter))] public readonly struct LocalizedString { private readonly string? _plain; private readonly IReadOnlyDictionary? _map; public bool IsEmpty { get { if (_plain != null) { return _plain.Length == 0; } if (_map != null) { return _map.Count == 0; } return true; } } public LocalizedString(string plain) { _plain = plain; _map = null; } public LocalizedString(Dictionary map) { _plain = null; _map = map; } public static implicit operator LocalizedString(string s) { return new LocalizedString(s); } public static LocalizedString FromLanguageMap(Dictionary map) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(map.Count); foreach (var (key, value) in map) { dictionary[LocaleUtils.LocaleKeyFromEnum(key)] = value; } return new LocalizedString(dictionary); } public string Resolve() { if (_plain != null) { return _plain; } if (_map == null || _map.Count == 0) { return ""; } string currentLocale = Loc.CurrentLocale; if (_map.TryGetValue(currentLocale, out string value)) { return value; } return _map.Values.First(); } public override string ToString() { return Resolve(); } } public class LocalizedStringConverter : JsonConverter { public override LocalizedString ReadJson(JsonReader reader, Type objectType, LocalizedString existingValue, bool hasExistingValue, JsonSerializer serializer) { //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Invalid comparison between Unknown and I4 //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Invalid comparison between Unknown and I4 JsonToken tokenType = reader.TokenType; if ((int)tokenType != 1) { if ((int)tokenType == 9) { return new LocalizedString((string)reader.Value); } return new LocalizedString(""); } Dictionary dictionary = new Dictionary(); while (reader.Read() && (int)reader.TokenType != 13) { if ((int)reader.TokenType == 4) { string key = (string)reader.Value; string value = reader.ReadAsString() ?? ""; dictionary[key] = value; } } return new LocalizedString(dictionary); } public override void WriteJson(JsonWriter writer, LocalizedString value, JsonSerializer serializer) { writer.WriteValue(value.Resolve()); } } } namespace Skins.Interop { [AttributeUsage(AttributeTargets.Class, Inherited = false)] public class RegisterIl2CppAttribute : Attribute { } } namespace Skins.Data { public static class GameStateCache { public static CacheMapping LobbyBar2LobbyScrollItem = new CacheMapping(); } public class CacheMapping where T : Component { private readonly Dictionary _cache = new Dictionary(); public Dictionary Cache => _cache; public bool Save(GameObject keyObj, T item, bool autoManageLifecycle = true) { return Save(((Object)keyObj).GetInstanceID(), item, autoManageLifecycle); } public bool Save(Component keyComp, T item, bool autoManageLifecycle = true) { return Save(((Object)keyComp).GetInstanceID(), item, autoManageLifecycle); } public bool Save(int instanceID, T item, bool autoManageLifecycle = true) { if (autoManageLifecycle) { ((Component)item).gameObject.AddComponent().OnDestroyed += delegate { Remove(instanceID); }; } return _cache.TryAdd(instanceID, item); } public bool Remove(GameObject keyObj) { return Remove(((Object)keyObj).GetInstanceID()); } public bool Remove(Component keyComp) { return Remove(((Object)keyComp).GetInstanceID()); } public bool Remove(int instanceID) { return _cache.Remove(instanceID); } public T? Get(GameObject keyObj) { return Get(((Object)keyObj).GetInstanceID()); } public T? Get(Component keyComp) { return Get(((Object)keyComp).GetInstanceID()); } public T? Get(int instanceID) { return _cache.GetValueOrDefault(instanceID); } } public static class GearDump { public const string FileName = "internal_data_gear_dump.json"; private static GearDumpFile _current = new GearDumpFile(); private static bool _dirty = false; private static DateTime _nextAllowedFlush = DateTime.MinValue; private static readonly TimeSpan FlushCooldown = TimeSpan.FromSeconds(15.0); private static readonly Dictionary _index = new Dictionary(); private static string MakeKey(string materialName, Dictionary textures) { string text = string.Join(",", from kvp in textures where kvp.Value != null orderby kvp.Key select kvp.Key + "=" + kvp.Value); return materialName + "|" + text; } public static void Load() { _index.Clear(); string text = Path.Combine(ModData.DataFolder, "internal_data_gear_dump.json"); if (!File.Exists(text)) { _current = new GearDumpFile(); return; } try { _current = JsonUtils.DeserializeFromFile(text) ?? new GearDumpFile(); foreach (GearDumpMaterial material in _current.Materials) { string key = MakeKey(material.MaterialName, material.Textures); _index.TryAdd(key, material); } Logger.Info($"[GearDump] Loaded {_current.Materials.Count} material(s) from '{text}'"); } catch (Exception ex) { Logger.Error("[GearDump] Failed to load: " + ex.Message); _current = new GearDumpFile(); } } public static void RecordMaterial(string materialName, string shaderName, Dictionary textures, string gearName, int offlineGearId) { string gearName2 = gearName; string key = MakeKey(materialName, textures); bool flag = false; if (!_index.TryGetValue(key, out GearDumpMaterial value)) { value = new GearDumpMaterial { MaterialName = materialName, ShaderName = shaderName, Textures = textures }; _current.Materials.Add(value); _index[key] = value; flag = true; } if ((offlineGearId == -1 || !value.SeenOnGears.Any((GearDumpGearRef g) => g.OfflineGearId == offlineGearId)) && (offlineGearId != -1 || !value.SeenOnGears.Any((GearDumpGearRef g) => g.Name == gearName2)) && !string.IsNullOrEmpty(gearName2)) { value.SeenOnGears.Add(new GearDumpGearRef { Name = gearName2, OfflineGearId = offlineGearId }); flag = true; } if (flag) { _dirty = true; TryFlush(); } } private static void TryFlush(bool force = false) { if (!_dirty) { return; } DateTime utcNow = DateTime.UtcNow; if (!force && utcNow < _nextAllowedFlush) { return; } _current.UpdatedAt = utcNow.ToString("o"); string path = Path.Combine(ModData.DataFolder, "internal_data_gear_dump.json"); try { File.WriteAllText(path, JsonConvert.SerializeObject((object)_current, (Formatting)1)); _dirty = false; _nextAllowedFlush = utcNow + FlushCooldown; Logger.Info($"[GearDump] Saved {_current.Materials.Count} material(s)"); } catch (Exception ex) { Logger.Error("[GearDump] Failed to write: " + ex.Message); } } public static void FlushNow() { TryFlush(force: true); } } public class GearDumpFile { [JsonProperty("version")] public string Version { get; set; } = "0.5.3"; [JsonProperty("updatedAt")] public string UpdatedAt { get; set; } = DateTime.UtcNow.ToString("o"); [JsonProperty("materials")] public List Materials { get; set; } = new List(); } public class GearDumpMaterial { [JsonProperty("materialName")] public string MaterialName { get; set; } = ""; [JsonProperty("shaderName")] public string ShaderName { get; set; } = ""; [JsonProperty("textures")] public Dictionary Textures { get; set; } = new Dictionary(); [JsonProperty("seenOnGears")] public List SeenOnGears { get; set; } = new List(); } public class GearDumpGearRef { [JsonProperty("name")] public string Name { get; set; } = ""; [JsonProperty("offlineGearId")] public int OfflineGearId { get; set; } } public class InternalData { public const string FileName = "internal_data.json"; [JsonProperty("version")] public string Version { get; set; } = "0.5.3"; [JsonProperty("generatedAt")] public string GeneratedAt { get; set; } = DateTime.UtcNow.ToString("o"); [JsonProperty("textureMacros")] public List TextureMacros { get; set; } = new List(); [JsonProperty("filterTypes")] public List FilterTypes { get; set; } = new List(); [JsonProperty("locales")] public List Locales { get; set; } = new List(); [JsonProperty("textureOptionPresets")] public List TextureOptionPresets { get; set; } = new List(); [JsonProperty("gearDumpFile")] public string GearDumpFile { get; set; } = "internal_data_gear_dump.json"; [JsonProperty("shaderProperties")] public Dictionary> ShaderProperties { get; set; } = new Dictionary>(); public static void Write() { InternalData internalData = new InternalData { TextureMacros = TextureMacro.Registry.Select((ITextureMacroImpl m) => new TextureMacroDescriptor { Name = m.Name, Signature = m.Signature, Description = m.Description, Params = m.Parameters.Select((MacroParam p) => new MacroParamDescriptor { Name = p.Name, Type = p.Type.ToString() }).ToList() }).ToList(), FilterTypes = Enum.GetNames(typeof(SkinGroupFilterType)).ToList(), Locales = LocaleUtils.LocaleKeyToDisplayName.Select, LocaleDescriptor>((KeyValuePair kv) => new LocaleDescriptor { Key = kv.Key, DisplayName = kv.Value }).ToList(), TextureOptionPresets = { new TextureOptionsPresetDescriptor { Name = "Albedo", Linear = false, Mipmaps = true }, new TextureOptionsPresetDescriptor { Name = "Data", Linear = true, Mipmaps = true }, new TextureOptionsPresetDescriptor { Name = "AlbedoNoMips", Linear = false, Mipmaps = false }, new TextureOptionsPresetDescriptor { Name = "DataNoMips", Linear = true, Mipmaps = false } }, GearDumpFile = "internal_data_gear_dump.json", ShaderProperties = MakeShaderProperties() }; string text = Path.Combine(ModData.DataFolder, "internal_data.json"); try { File.WriteAllText(text, JsonConvert.SerializeObject((object)internalData, (Formatting)1)); Logger.Info("[InternalData] Written to '" + text + "'"); } catch (Exception ex) { Logger.Error("[InternalData] Failed to write: " + ex.Message); } } private static Dictionary> MakeShaderProperties() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected I4, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Invalid comparison between Unknown and I4 Dictionary> dictionary = new Dictionary>(); foreach (KeyValuePair shaderReference in ShaderRefs.ShaderReferences) { shaderReference.Deconstruct(out var key, out var value); string key2 = key; Shader val = value; List list = new List(); int propertyCount = val.GetPropertyCount(); for (int i = 0; i < propertyCount; i++) { ShaderPropertyType propertyType = val.GetPropertyType(i); key = (int)propertyType switch { 4 => ((int)val.GetPropertyTextureDimension(i) == 2) ? "Texture2D" : null, 2 => "Float", 3 => "Float", 0 => "Color", 1 => "Vector", _ => null, }; string text = key; if (text != null) { list.Add(new ShaderPropertyDescriptor { Name = val.GetPropertyName(i), Type = text }); } } if (list.Count > 0) { dictionary[key2] = list; } } return dictionary; } } public class LocaleDescriptor { [JsonProperty("key")] public string Key { get; set; } = ""; [JsonProperty("displayName")] public string DisplayName { get; set; } = ""; } public class TextureMacroDescriptor { [JsonProperty("name")] public string Name { get; set; } = ""; [JsonProperty("signature")] public string Signature { get; set; } = ""; [JsonProperty("description")] public string Description { get; set; } = ""; [JsonProperty("params")] public List Params { get; set; } = new List(); } public class MacroParamDescriptor { [JsonProperty("name")] public string Name { get; set; } = ""; [JsonProperty("type")] public string Type { get; set; } = ""; } public class TextureOptionsPresetDescriptor { [JsonProperty("name")] public string Name { get; set; } = ""; [JsonProperty("linear")] public bool Linear { get; set; } [JsonProperty("mipmaps")] public bool Mipmaps { get; set; } } public class ShaderPropertyDescriptor { [JsonProperty("name")] public string Name { get; set; } = ""; [JsonProperty("type")] public string Type { get; set; } = ""; } public static class ModData { public const string VanillaRundownKey = "__vanilla__"; public static readonly string DataFolder; public static readonly string SkinsFolder; public static readonly string PerProfileSkinsFolder; public static readonly string DumpFolder; public static readonly string PluginFolder; public static readonly string ResourcesFolder; public static readonly string LocalizationFolder; public static bool IsCustomRundown; public static string RundownName; static ModData() { IsCustomRundown = false; RundownName = "__vanilla__"; DataFolder = Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "io.takina.gtfo.Skins")); SkinsFolder = Path.Combine(DataFolder, "skins"); PerProfileSkinsFolder = Path.Combine(Paths.BepInExRootPath, "Assets", "Skins"); DumpFolder = Path.Combine(DataFolder, "dumps"); PluginFolder = Path.GetDirectoryName(((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins["io.takina.gtfo.Skins"].Location); ResourcesFolder = Path.Combine(PluginFolder, "Resources"); LocalizationFolder = Path.Combine(ResourcesFolder, "Localization"); } public static void Initialize() { SetupEnvironment(); if (PluginUtils.IsPluginLoaded("com.dak.MTFO")) { LoadMtfoInfo(); } } private static void LoadMtfoInfo() { IsCustomRundown = MTFOPathAPI.HasRundownPath; if (IsCustomRundown) { RundownName = Path.GetFileName(MTFOPathAPI.RundownPath); } } private static void SetupEnvironment() { Directory.CreateDirectory(DataFolder); Directory.CreateDirectory(SkinsFolder); Directory.CreateDirectory(PerProfileSkinsFolder); Directory.CreateDirectory(DumpFolder); } } [JsonObject] public abstract class PersistentConfig where T : PersistentConfig, new() { private static T? _instance; private static bool _inUnsafeState; private static bool quittingHandlerAdded; [JsonIgnore] public static T? Instance { get { if (_instance == null) { Load(); } return _instance; } } [JsonIgnore] public static bool Loaded => _instance != null; [JsonIgnore] public abstract string ConfigPath { get; } [JsonIgnore] public virtual bool SaveOnApplicationQuit => true; public static bool Load() { T val = new T(); string configPath = val.ConfigPath; if (!File.Exists(configPath)) { _instance = val; return Save(); } try { _instance = JToken.Parse(File.ReadAllText(configPath)).ToObject() ?? new T(); _inUnsafeState = false; } catch (Exception ex) { Logger.Fatal("Error while parsing config at '" + configPath + "': " + ex.Message); _inUnsafeState = true; _instance = val; return false; } if (!_instance.SaveOnApplicationQuit || quittingHandlerAdded) { return true; } Application.quitting += Action.op_Implicit((Action)delegate { Save(); }); quittingHandlerAdded = true; return true; } public static bool Save() { if (_inUnsafeState) { Logger.Warn("[PersistentConfig] Attempted to save config while in unsafe state, skipping save to prevent potential data loss or errors."); return false; } if (_instance == null) { _instance = new T(); } string contents; try { contents = JsonConvert.SerializeObject((object)_instance, (Formatting)1); } catch (Exception ex) { Logger.Fatal("[PersistentConfig] Error while serializing config: " + ex.Message); return false; } string configPath = _instance.ConfigPath; string directoryName = Path.GetDirectoryName(configPath); if (directoryName == null) { throw new InvalidDataException("[PersistentConfig] Invalid config path: " + configPath); } if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } try { File.WriteAllText(configPath, contents); } catch (Exception ex2) { Logger.Fatal("[PersistentConfig] Error while saving config at '" + configPath + "': " + ex2.Message); return false; } if (!_instance.SaveOnApplicationQuit || quittingHandlerAdded) { return true; } Application.quitting += Action.op_Implicit((Action)delegate { Save(); }); quittingHandlerAdded = true; return true; } } public class UserConfig : PersistentConfig { public override string ConfigPath => Path.Combine(ModData.DataFolder, "config.json"); [JsonIgnore] public SkinConfig CurrentSkinConfig => GetOrCreate(ModData.RundownName); [JsonProperty("skins_config")] internal Dictionary SkinConfigs { get; set; } = new Dictionary(); private SkinConfig GetOrCreate(string rundownName) { if (!SkinConfigs.TryGetValue(rundownName, out SkinConfig value)) { value = new SkinConfig(); SkinConfigs[rundownName] = value; PersistentConfig.Save(); } return value; } } [JsonObject] public class SkinConfig { [JsonProperty("offline_gear")] public Dictionary OfflineGear { get; set; } = new Dictionary(); } } namespace Skins.Config { public enum ConfigEntryRule { Min, Max } public class ConfigEntryExtended { private Dictionary _rules = new Dictionary(); private ConfigEntry _entry; public T Value { get { return _entry.Value; } set { _entry.Value = Enforce(value); } } public object BoxedValue { get { return ((ConfigEntryBase)_entry).BoxedValue; } set { ((ConfigEntryBase)_entry).BoxedValue = Enforce((T)value); } } public ConfigEntryExtended(ConfigEntry entry) { _entry = entry; } public bool AddRule(ConfigEntryRule rule, T ruleValue) { if (_rules.TryAdd(rule, ruleValue)) { Enforce(rule); return true; } return false; } private void Enforce() { T val = Enforce(_entry.Value); if (val != null && !val.Equals(_entry.Value)) { _entry.Value = val; } } private T Enforce(T val) { foreach (var (configEntryRule2, val3) in _rules) { switch (configEntryRule2) { case ConfigEntryRule.Min: if (Comparer.Default.Compare(val, val3) < 0) { val = val3; } break; case ConfigEntryRule.Max: if (Comparer.Default.Compare(val, val3) > 0) { val = val3; } break; } } return val; } private void Enforce(ConfigEntryRule rule) { T value = _entry.Value; value = Enforce(value, rule); if (value != null && !value.Equals(_entry.Value)) { _entry.Value = value; } } private T Enforce(T val, ConfigEntryRule rule) { if (!_rules.TryGetValue(rule, out var value)) { return val; } switch (rule) { case ConfigEntryRule.Min: if (Comparer.Default.Compare(val, value) < 0) { val = value; } break; case ConfigEntryRule.Max: if (Comparer.Default.Compare(val, value) > 0) { val = value; } break; } return val; } public static implicit operator ConfigEntryExtended(ConfigEntry entry) { return new ConfigEntryExtended(entry); } } internal static class ConfigMgr { private static readonly ConfigFile Conf; private static readonly FileSystemWatcher? ConfigWatcher; private static readonly ConfigEntryExtended AutoMovePerProfileSkinsToGlobalConf; private static readonly ConfigEntryExtended DoNotNormalizeFoldersConf; private static readonly ConfigEntryExtended EnableHotReloadKeyConf; private static readonly ConfigEntryExtended HotReloadKeyConf; private static readonly ConfigEntryExtended DumpGearTreeConf; private static readonly ConfigEntryExtended DebugConf; public static bool Processed { get; private set; } public static bool AutoMovePerProfileSkinsToGlobal => AutoMovePerProfileSkinsToGlobalConf.Value; public static bool DoNotNormalizeFolders => DoNotNormalizeFoldersConf.Value; public static bool EnableHotReloadKey => EnableHotReloadKeyConf.Value; public static KeyCode HotReloadKey => HotReloadKeyConf.Value; public static bool DumpGearTree => DumpGearTreeConf.Value; public static bool Debug => DebugConf.Value; public static void Process() { Logger.Info($"debug={Debug}"); Processed = true; } static ConfigMgr() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown string text = "Skins.cfg"; string text2 = Path.Combine(Paths.ConfigPath, text); Logger.Info("cfgPath = " + text2); Conf = new ConfigFile(text2, true); ConfigWatcher = new FileSystemWatcher(Paths.ConfigPath, text) { NotifyFilter = NotifyFilters.LastWrite, EnableRaisingEvents = true }; ConfigWatcher.Changed += async delegate { ConfigWatcher.EnableRaisingEvents = false; await Task.Delay(500); Logger.Debug("Reloading config..."); Conf.Reload(); await Task.Delay(250); Process(); await Task.Delay(250); ConfigWatcher.EnableRaisingEvents = true; }; int num = 0; string text3 = $"({++num}) Skin Management"; AutoMovePerProfileSkinsToGlobalConf = Conf.Bind(text3, "Move Per-Profile Skins to Global Folder on Game Launch", true, "When true, on plugin initialization,automatically move any skins found in the per-profile skins folder " + $"('{ModData.PerProfileSkinsFolder}') to the global skins folder ('{ModData.SkinsFolder}')."); DoNotNormalizeFoldersConf = Conf.Bind(text3, "Do Not Normalize Skin Folder Names", false, "When true, don't perform skin folder renames on launch. This is mostly for development purposes, as this can hide duplicate GUID issues on the fs level or cause folder name collisions across different authors."); text3 = "(Y) Data"; DumpGearTreeConf = Conf.Bind(text3, "Dump Player Gear Tree", false, "Dump player gear tree for debugging and development purposes (in '" + ModData.DataFolder + "')"); text3 = "(Z) Dev"; EnableHotReloadKeyConf = Conf.Bind(text3, "Enable Hot Reload Key", false, "Use a key to hot reload skins (this can also be done by pressing the 'Reload Skins' button in game settings!)"); HotReloadKeyConf = Conf.Bind(text3, "Hot Reload Key", (KeyCode)288, "When 'Enable Hot Reload Key' is true, use this key to reload skins."); DebugConf = Conf.Bind(text3, "Enable Debug Logs", false, "debug logging"); } } } namespace Skins.Assets { public static class AssetBundleLoader { private static readonly Dictionary Cache = new Dictionary(); public static AssetBundle? Load(string bundlePath) { if (Cache.TryGetValue(bundlePath, out AssetBundle value)) { return value; } string text = Path.Combine(Paths.BepInExRootPath, "Assets", "Skins", bundlePath); Logger.Debug($"Loading bundle at '{bundlePath}' ==resolved=> '{text}'"); AssetBundle val = AssetBundle.LoadFromFile(text); if ((Object)(object)val == (Object)null) { Logger.Error("Failed to load bundle at " + bundlePath); return null; } Cache[bundlePath] = val; return val; } public static T? LoadAsset(string bundlePath, string assetPath) where T : Il2CppObjectBase { AssetBundle? obj = Load(bundlePath); if (obj == null) { return default(T); } Object obj2 = obj.LoadAsset(assetPath); if (obj2 == null) { return default(T); } return ((Il2CppObjectBase)obj2).TryCast(); } public static void Unload(string bundlePath, bool unloadAllObjects = false) { if (Cache.TryGetValue(bundlePath, out AssetBundle value)) { value.Unload(unloadAllObjects); Cache.Remove(bundlePath); } } public static void UnloadAll(bool unloadAllObjects = false) { foreach (AssetBundle value in Cache.Values) { value.Unload(unloadAllObjects); } Cache.Clear(); } } }