using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using REPOLib; using REPOLib.Modules; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Xuaun")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Registers MoreHead .hhh cosmetics into the vanilla REPO cosmetics system via REPOLib.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+a791020af3b3c6b26cc77cb7230db084ccc414a5")] [assembly: AssemblyProduct("MoreHeadBridge")] [assembly: AssemblyTitle("MoreHeadBridge")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.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; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace MoreHeadBridge { internal static class BceConsole { private static readonly MethodInfo? _writeLine; private static readonly MethodInfo? _write; internal static bool IsAvailable => _writeLine != null; static BceConsole() { Type type = Type.GetType("BCE.console, BCE"); if (!(type == null)) { _writeLine = type.GetMethod("WriteLine", new Type[2] { typeof(string), typeof(ConsoleColor) }); _write = type.GetMethod("Write", new Type[2] { typeof(string), typeof(ConsoleColor) }); } } internal static void WriteLine(string msg, ConsoleColor color) { _writeLine?.Invoke(null, new object[2] { msg, color }); } internal static void Write(string msg, ConsoleColor color) { _write?.Invoke(null, new object[2] { msg, color }); } } internal static class BridgeIds { internal const string Prefix = "morehead-bridge:"; internal static bool IsBridgeAsset(string? assetId) { return !string.IsNullOrEmpty(assetId) && assetId.StartsWith("morehead-bridge:", StringComparison.Ordinal); } internal static bool IsBridgeAsset(CosmeticAsset? asset) { return (Object)(object)asset != (Object)null && IsBridgeAsset(asset.assetId); } } internal static class HhhCosmeticLoader { internal static readonly List RegisteredAssetIds = new List(); internal static readonly Dictionary BridgeIconTextures = new Dictionary(); private static readonly Dictionary TagToType = new Dictionary { ["head"] = (CosmeticType)0, ["neck"] = (CosmeticType)30, ["body"] = (CosmeticType)20, ["hip"] = (CosmeticType)21, ["rightarm"] = (CosmeticType)1, ["leftarm"] = (CosmeticType)2, ["rightleg"] = (CosmeticType)3, ["leftleg"] = (CosmeticType)4 }; private static readonly HashSet ValidTags; private static readonly HashSet _usedPrefabIds; private static readonly HashSet _usedInternalNames; public static void LoadAll() { string pluginPath = Paths.PluginPath; string[] array = Directory.GetFiles(pluginPath, "*.hhh", SearchOption.AllDirectories); string text = Plugin.SpecificFolders.Value ?? ""; if (!string.IsNullOrWhiteSpace(text)) { string[] allowed = (from s in text.Split(',') select s.Trim() into s where s.Length > 0 select s).ToArray(); int num = array.Length; array = array.Where((string f) => allowed.Any((string a) => f.IndexOf(a, StringComparison.OrdinalIgnoreCase) >= 0)).ToArray(); LogInfo(string.Format("SpecificFolders filter active ({0}) — kept {1}/{2} files.", string.Join(", ", allowed), array.Length, num)); } LogInfo($"Found {array.Length} .hhh file(s). Translating cosmetics from MoreHead to Vanilla REPO..."); int num2 = 0; List list = new List(); string[] array2 = array; foreach (string path in array2) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); ParseFileName(fileNameWithoutExtension, out string _, out string tag); if (tag == "world") { list.Add(Path.GetFileName(path)); Plugin.Logger.LogDebug((object)("Skipped (world tag): " + fileNameWithoutExtension)); } else if (TryRegister(path)) { num2++; } } if (list.Count > 0) { Plugin.Logger.LogWarning((object)$"Skipped {list.Count} 'world' cosmetic(s) — no vanilla equivalent (run with Debug log level to see names)."); } int num3 = array.Length; int num4 = num3 - num2 - list.Count; LogInfo($"Done — {num2}/{num3} registered. " + $"{list.Count} world-tag skipped, {num4} other error(s)."); } private static bool TryRegister(string path) { //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) FileInfo fileInfo = new FileInfo(path); if (!fileInfo.Exists || fileInfo.Length < 1024) { Plugin.Logger.LogWarning((object)("Skipped (too small/missing): " + Path.GetFileName(path))); return false; } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); ParseFileName(fileNameWithoutExtension, out string name, out string tag); if (!TagToType.TryGetValue(tag, out var value)) { return false; } AssetBundle val = AssetBundle.LoadFromFile(path); if ((Object)(object)val == (Object)null) { Plugin.Logger.LogError((object)("Failed to load bundle: " + fileNameWithoutExtension)); return false; } GameObject val2 = null; string[] allAssetNames = val.GetAllAssetNames(); foreach (string text in allAssetNames) { GameObject val3 = val.LoadAsset(text); if ((Object)(object)val3 != (Object)null) { val2 = val3; break; } } val.Unload(false); if ((Object)(object)val2 == (Object)null) { Plugin.Logger.LogError((object)("No GameObject in bundle: " + fileNameWithoutExtension)); return false; } string name2 = ((Object)val2).name; ((Object)val2).name = EnsureUniqueId(name2, _usedPrefabIds); if (((Object)val2).name != name2) { Plugin.Logger.LogWarning((object)("Duplicate prefab name '" + name2 + "' → renamed to '" + ((Object)val2).name + "'")); } string text2 = name; name = EnsureUniqueId(name, _usedInternalNames); if (name != text2) { Plugin.Logger.LogWarning((object)("Duplicate internal name '" + text2 + "' → renamed to '" + name + "'")); } if (!Object.op_Implicit((Object)(object)val2.GetComponent())) { Cosmetic val4 = val2.AddComponent(); val4.type = value; } PrefabRef val5 = NetworkPrefabs.RegisterNetworkPrefab("Cosmetics/" + ((Object)val2).name, val2); if (val5 == null) { Plugin.Logger.LogError((object)("Failed to register network prefab: " + name)); return false; } string text3 = "morehead-bridge:" + name.ToLowerInvariant(); CosmeticAsset val6 = ScriptableObject.CreateInstance(); ((Object)val6).name = name; val6.assetName = ((Object)val2).name; val6.type = value; val6.prefab = val5; val6.assetId = text3; val6.rarity = Plugin.DefaultRarity.Value; val6.customTypeList = new List(); val6.tintable = false; Cosmetics.RegisterCosmetic(val6); RegisteredAssetIds.Add(text3); Texture2D val7 = TryExtractIconTexture(val2); if ((Object)(object)val7 != (Object)null) { BridgeIconTextures[text3] = val7; } return true; } private static Texture2D? TryExtractIconTexture(GameObject prefab) { Renderer[] componentsInChildren = prefab.GetComponentsInChildren(true); string[] array = new string[5] { "_MainTex", "_BaseMap", "_BaseColorMap", "_Albedo", "_AlbedoMap" }; Renderer[] array2 = componentsInChildren; foreach (Renderer val in array2) { if ((Object)(object)val == (Object)null) { continue; } Material[] sharedMaterials = val.sharedMaterials; foreach (Material val2 in sharedMaterials) { if ((Object)(object)val2 == (Object)null) { continue; } string[] array3 = array; foreach (string text in array3) { if (val2.HasProperty(text)) { Texture texture = val2.GetTexture(text); Texture2D val3 = (Texture2D)(object)((texture is Texture2D) ? texture : null); if (val3 != null && (Object)(object)val3 != (Object)null) { return val3; } } } } } return null; } private static void ParseFileName(string fileName, out string name, out string tag) { int num = fileName.LastIndexOf('_'); if (num >= 0) { int num2 = num + 1; string text = fileName.Substring(num2, fileName.Length - num2).ToLowerInvariant(); if (ValidTags.Contains(text)) { name = fileName.Substring(0, num); tag = text; return; } } name = fileName; tag = "head"; } private static string EnsureUniqueId(string baseName, HashSet used) { string text = baseName; int num = 1; while (!used.Add(text)) { text = $"{baseName}({num})"; num++; } return text; } private static void LogInfo(string msg) { if (BceConsole.IsAvailable) { BceConsole.WriteLine("[Info : MoreHead Bridge] " + msg, ConsoleColor.Cyan); } else { Plugin.Logger.LogInfo((object)(msg ?? "")); } } static HhhCosmeticLoader() { HashSet hashSet = new HashSet(); foreach (string key in TagToType.Keys) { hashSet.Add(key); } hashSet.Add("world"); ValidTags = hashSet; _usedPrefabIds = new HashSet(); _usedInternalNames = new HashSet(); } } internal static class BatchIconGenerator { [CompilerGenerated] private sealed class d__2 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private List 5__1; private List 5__2; private int 5__3; private int 5__4; private int[] 5__5; private List.Enumerator <>s__6; private CosmeticAsset 5__7; private List.Enumerator <>s__8; private CosmeticAsset 5__9; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__2(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || (uint)(num - 2) <= 3u) { try { } finally { <>m__Finally1(); } } 5__1 = null; 5__2 = null; 5__5 = null; <>s__6 = default(List.Enumerator); 5__7 = null; <>s__8 = default(List.Enumerator); 5__9 = null; <>1__state = -2; } private bool MoveNext() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Expected O, but got Unknown //IL_02f9: Unknown result type (might be due to invalid IL or missing references) try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(0.5f); <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)MetaManager.instance == (Object)null) { return false; } _ranThisSession = true; 5__1 = MetaManager.instance.cosmeticEquippedPreview?.ToList() ?? new List(); 5__2 = new List(); <>s__6 = MetaManager.instance.cosmeticAssets.GetEnumerator(); try { while (<>s__6.MoveNext()) { 5__7 = <>s__6.Current; if (!((Object)(object)5__7 == (Object)null) && 5__7.assetId != null && BridgeIds.IsBridgeAsset(5__7) && !IconCapture.HasCache(5__7)) { 5__2.Add(5__7); 5__7 = null; } } } finally { ((IDisposable)<>s__6).Dispose(); } <>s__6 = default(List.Enumerator); Plugin.Logger.LogInfo((object)$"GenerateAllIcons: {5__2.Count} icon(s) to generate."); 5__3 = 0; 5__4 = 0; 5__5 = ((MetaManager.instance.colorsEquippedPreview != null) ? ((int[])MetaManager.instance.colorsEquippedPreview.Clone()) : null); <>s__8 = 5__2.GetEnumerator(); <>1__state = -3; break; case 2: <>1__state = -3; <>2__current = null; <>1__state = 3; return true; case 3: <>1__state = -3; <>2__current = null; <>1__state = 4; return true; case 4: <>1__state = -3; <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 5; return true; case 5: <>1__state = -3; if (IconCapture.TryCapture(5__9, 5__9.type)) { 5__3++; } else { 5__4++; } MetaManager.instance.CosmeticUnequip(5__9, true, false, false); if ((5__3 + 5__4) % 50 == 0) { Plugin.Logger.LogInfo((object)$"Batch progress: {5__3 + 5__4}/{5__2.Count} ({5__3} ok, {5__4} failed)"); } 5__9 = null; break; } if (<>s__8.MoveNext()) { 5__9 = <>s__8.Current; MetaManager.instance.cosmeticEquippedPreview = MetaManager.instance.cosmeticEquipped.ToList(); MetaManager.instance.colorsEquippedPreview = (int[])MetaManager.instance.colorsEquipped.Clone(); MetaManager.instance.CosmeticEquip(5__9, true, true); MetaManager.instance.CosmeticPreviewSet(true); MetaManager.instance.CosmeticPlayerUpdateLocal(false, false); <>2__current = null; <>1__state = 2; return true; } <>m__Finally1(); <>s__8 = default(List.Enumerator); MetaManager.instance.cosmeticEquippedPreview = 5__1; if (5__5 != null) { MetaManager.instance.colorsEquippedPreview = 5__5; } MetaManager.instance.CosmeticPreviewSet(false); MetaManager.instance.CosmeticPlayerUpdateLocal(false, false); Plugin.GenerateAllIcons.Value = false; ((BaseUnityPlugin)Plugin.Instance).Config.Save(); Plugin.Logger.LogInfo((object)$"GenerateAllIcons done — {5__3} captured, {5__4} failed. Flag reset to false."); return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>s__8).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static bool _ranThisSession; internal static void TryStart(MonoBehaviour host) { if (!_ranThisSession && Plugin.GenerateAllIcons.Value) { host.StartCoroutine(Run()); } } [IteratorStateMachine(typeof(d__2))] private static IEnumerator Run() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__2(0); } } [HarmonyPatch(typeof(MenuPageCosmetics), "Start")] internal static class BatchIconGeneratorTrigger { [HarmonyPostfix] private static void Postfix(MenuPageCosmetics __instance) { BatchIconGenerator.TryStart((MonoBehaviour)(object)__instance); } } internal static class IconCacheCleaner { internal static void Run() { if (!Plugin.DeleteIconCache.Value) { return; } try { string cacheDir = IconCapture.CacheDir; if (!Directory.Exists(cacheDir)) { Plugin.Logger.LogInfo((object)"DeleteIconCache: no cache directory, nothing to do."); ResetFlag(); return; } string text = Plugin.DeleteIconsMatching.Value ?? ""; string[] array = (from s in text.Split(',') select s.Trim().ToLowerInvariant() into s where s.Length > 0 select s).ToArray(); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (string registeredAssetId in HhhCosmeticLoader.RegisteredAssetIds) { int num = registeredAssetId.IndexOf(':'); if (num >= 0 && num + 1 < registeredAssetId.Length) { string text2 = registeredAssetId; int num2 = num + 1; hashSet.Add(text2.Substring(num2, text2.Length - num2)); } } int num3 = 0; int num4 = 0; string[] files = Directory.GetFiles(cacheDir, "*.png"); foreach (string text3 in files) { string name = Path.GetFileNameWithoutExtension(text3).ToLowerInvariant(); if (!hashSet.Contains(name)) { num4++; continue; } if (array.Length != 0 && !array.Any((string f) => name.Contains(f))) { num4++; continue; } try { File.Delete(text3); num3++; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Failed to delete '" + text3 + "': " + ex.Message)); } } Plugin.Logger.LogInfo((object)($"DeleteIconCache: removed {num3} bridge icon(s), kept {num4}. " + "Filter: " + ((array.Length == 0) ? "(all bridge icons)" : string.Join(",", array)))); } catch (Exception ex2) { Plugin.Logger.LogError((object)("DeleteIconCache failed: " + ex2.Message)); } finally { ResetFlag(); } } private static void ResetFlag() { Plugin.DeleteIconCache.Value = false; ((BaseUnityPlugin)Plugin.Instance).Config.Save(); } } internal static class IconCapture { private const int OutSize = 128; internal static string CacheDir => Path.Combine(Application.persistentDataPath, "MoreHeadBridge_Icons"); internal static string CachePathFor(CosmeticAsset asset) { string text = ((Object)asset).name.Replace("(Clone)", "").ToLowerInvariant(); return Path.Combine(CacheDir, text + ".png"); } internal static bool HasCache(CosmeticAsset asset) { return File.Exists(CachePathFor(asset)); } private static RenderTexture? FindActiveAvatarRT() { PlayerAvatarMenuHover val = Object.FindObjectOfType(); if ((Object)(object)val == (Object)null) { return null; } if ((Object)(object)val.renderTextureInstance != (Object)null) { return val.renderTextureInstance; } RawImage component = ((Component)val).GetComponent(); return (RenderTexture?)(((Object)(object)component != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); } internal static bool TryCapture(CosmeticAsset asset) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got I4 //IL_000d->IL000d: Incompatible stack types: O vs I4 //IL_0007->IL000d: Incompatible stack types: I4 vs O //IL_0007->IL000d: Incompatible stack types: O vs I4 object obj = asset; int num; if (asset != null) { obj = asset.type; num = (int)obj; } else { num = 0; obj = num; num = (int)obj; } return TryCapture((CosmeticAsset)(object)num, (CosmeticType)obj); } internal static bool TryCapture(CosmeticAsset asset, CosmeticType type) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_0098: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Expected O, but got Unknown if ((Object)(object)asset == (Object)null) { return false; } if (HasCache(asset)) { return false; } Texture2D val = null; Texture2D val2 = null; Texture2D val3 = null; RenderTexture active = RenderTexture.active; try { RenderTexture val4 = FindActiveAvatarRT(); if ((Object)(object)val4 == (Object)null) { return false; } Directory.CreateDirectory(CacheDir); RenderTexture.active = val4; val = new Texture2D(((Texture)val4).width, ((Texture)val4).height, (TextureFormat)4, false); val.ReadPixels(new Rect(0f, 0f, (float)((Texture)val4).width, (float)((Texture)val4).height), 0, 0); val.Apply(); Rect cropRect = GetCropRect(type); int num = Mathf.RoundToInt(((Rect)(ref cropRect)).x * (float)((Texture)val4).width); int num2 = Mathf.RoundToInt(((Rect)(ref cropRect)).y * (float)((Texture)val4).height); int num3 = Mathf.RoundToInt(((Rect)(ref cropRect)).width * (float)((Texture)val4).width); int num4 = Mathf.RoundToInt(((Rect)(ref cropRect)).height * (float)((Texture)val4).height); num3 = Mathf.Max(1, Mathf.Min(num3, ((Texture)val4).width - num)); num4 = Mathf.Max(1, Mathf.Min(num4, ((Texture)val4).height - num2)); Color[] pixels = val.GetPixels(num, num2, num3, num4); val2 = new Texture2D(num3, num4, (TextureFormat)4, false); val2.SetPixels(pixels); val2.Apply(); val3 = ResizeBilinear(val2, 128, 128); File.WriteAllBytes(CachePathFor(asset), ImageConversion.EncodeToPNG(val3)); asset.icon = null; RefreshVisibleButtons(asset); return true; } catch (Exception ex) { Plugin.Logger.LogDebug((object)("[MoreHeadBridge] Icon capture failed for '" + ((Object)asset).name + "': " + ex.Message)); return false; } finally { RenderTexture.active = active; if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } if ((Object)(object)val2 != (Object)null) { Object.Destroy((Object)(object)val2); } if ((Object)(object)val3 != (Object)null) { Object.Destroy((Object)(object)val3); } } } private static Rect GetCropRect(CosmeticType type) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected I4, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_011e: 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_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) switch ((int)type) { case 0: case 5: case 6: case 14: case 15: case 17: case 18: case 24: case 25: case 30: case 31: case 32: return new Rect(0.22f, 0.62f, 0.56f, 0.35f); case 7: case 8: case 16: case 20: case 21: case 23: return new Rect(0.18f, 0.34f, 0.64f, 0.36f); case 1: case 9: case 13: case 26: return new Rect(0.05f, 0.3f, 0.5f, 0.4f); case 2: case 10: case 27: return new Rect(0.45f, 0.3f, 0.5f, 0.4f); case 3: case 11: case 19: case 28: return new Rect(0.1f, 0f, 0.45f, 0.45f); case 4: case 12: case 22: case 29: return new Rect(0.45f, 0f, 0.45f, 0.45f); default: return new Rect(0f, 0f, 1f, 1f); } } private static void RefreshVisibleButtons(CosmeticAsset asset) { try { MenuElementCosmeticButton[] array = Object.FindObjectsOfType(); MenuElementCosmeticButton[] array2 = array; foreach (MenuElementCosmeticButton val in array2) { if ((Object)(object)val != (Object)null && (Object)(object)val.cosmeticAsset == (Object)(object)asset) { val.UpdateIcon(false); } } } catch (Exception ex) { Plugin.Logger.LogDebug((object)("[MoreHeadBridge] Button refresh failed: " + ex.Message)); } } private static Texture2D ResizeBilinear(Texture2D src, int w, int h) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) RenderTexture temporary = RenderTexture.GetTemporary(w, h); try { Graphics.Blit((Texture)(object)src, temporary); RenderTexture active = RenderTexture.active; RenderTexture.active = temporary; Texture2D val = new Texture2D(w, h, (TextureFormat)4, false); val.ReadPixels(new Rect(0f, 0f, (float)w, (float)h), 0, 0); val.Apply(); RenderTexture.active = active; return val; } finally { RenderTexture.ReleaseTemporary(temporary); } } } internal static class PlaceholderIcon { private const int Size = 64; private static Sprite? _cached; internal static Sprite Get() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0117: 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) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_cached != (Object)null) { return _cached; } Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false); ((Object)val).name = "MoreHeadBridge_Placeholder"; ((Texture)val).filterMode = (FilterMode)0; Color val2 = default(Color); ((Color)(ref val2))..ctor(1f, 0.8f, 0f, 1f); Color val3 = default(Color); ((Color)(ref val3))..ctor(0.13f, 0.13f, 0.18f, 1f); Color val4 = default(Color); ((Color)(ref val4))..ctor(0.22f, 0.22f, 0.28f, 1f); Color val5 = default(Color); ((Color)(ref val5))..ctor(1f, 0.8f, 0f, 0.35f); Color[] array = (Color[])(object)new Color[4096]; for (int i = 0; i < 64; i++) { for (int j = 0; j < 64; j++) { bool flag = j < 3 || j >= 61 || i < 3 || i >= 61; bool flag2 = (j + i) / 4 % 2 == 0; Color val6 = ((!flag) ? ((!flag2) ? val4 : Color.Lerp(val3, val5, 0.5f)) : val2); array[i * 64 + j] = val6; } } DrawM(array, 64, val2); val.SetPixels(array); val.Apply(); _cached = Sprite.Create(val, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f), 64f); ((Object)_cached).name = "MoreHeadBridge_Placeholder"; return _cached; } private static void DrawM(Color[] pixels, int size, Color color) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) for (int i = 14; i <= 50; i++) { Plot(pixels, size, 18, i, color); Plot(pixels, size, 19, i, color); Plot(pixels, size, 45, i, color); Plot(pixels, size, 44, i, color); } int num = 24; for (int j = 0; j <= num; j++) { int y = 50 - j; int num2 = 18 + j * 13 / num; int num3 = 45 - j * 14 / num; Plot(pixels, size, num2, y, color); Plot(pixels, size, num2 + 1, y, color); Plot(pixels, size, num3, y, color); Plot(pixels, size, num3 - 1, y, color); } } private static void Plot(Color[] pixels, int size, int x, int y, Color color) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (x >= 0 && y >= 0 && x < size && y < size) { pixels[y * size + x] = color; } } } internal static class ModdedRpcRetrigger { private static FieldInfo? _cosmeticEquippedField; internal static void TryApply(Harmony harmony) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("REPOLib.Objects.PlayerCosmeticsModded"); if (type == null) { Plugin.Logger.LogDebug((object)"PlayerCosmeticsModded not found — multiplayer bridge sync fix skipped."); return; } MethodInfo methodInfo = AccessTools.Method(type, "SetupCosmeticsModdedRPC", (Type[])null, (Type[])null); if (!(methodInfo == null)) { _cosmeticEquippedField = AccessTools.Field(type, "cosmeticEquipped"); if (!(_cosmeticEquippedField == null)) { MethodInfo method = typeof(ModdedRpcRetrigger).GetMethod("Postfix", BindingFlags.Static | BindingFlags.NonPublic); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Logger.LogDebug((object)"Multiplayer bridge sync fix applied."); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Could not apply multiplayer bridge sync fix: " + ex.Message)); } } private static void Postfix(MonoBehaviourPun __instance) { if ((Object)(object)__instance.photonView == (Object)null || __instance.photonView.IsMine || !(_cosmeticEquippedField?.GetValue(__instance) is List list) || list.Count == 0) { return; } bool flag = false; foreach (string item in list) { if (BridgeIds.IsBridgeAsset(item)) { flag = true; break; } } if (flag) { PlayerCosmetics component = ((Component)__instance).GetComponent(); if (component != null) { component.SetupCosmeticsLogic(Array.Empty(), false); } } } } internal static class MyPluginInfo { public const string PLUGIN_GUID = "Xuaun.MoreHeadBridge"; public const string PLUGIN_NAME = "MoreHead Bridge"; public const string PLUGIN_VERSION = "1.0.0"; } internal static class PartShrinkerBridge { private static bool _initialized; private static bool _available; private static Type? _shrinkerType; private static Type? _hiddenType; private static FieldInfo? _partField; private static FieldInfo? _hideChildrenField; private static MethodInfo? _addMethod; private static MethodInfo? _removeMethod; private static void EnsureInit() { if (_initialized) { return; } _initialized = true; try { _shrinkerType = AccessTools.TypeByName("MoreHeadUtilities.PartShrinker"); _hiddenType = AccessTools.TypeByName("MoreHeadUtilities.HiddenParts"); if (_shrinkerType == null || _hiddenType == null) { Plugin.Logger.LogDebug((object)"MoreHeadUtilities not loaded — PartShrinker bridge inactive."); return; } _partField = AccessTools.Field(_shrinkerType, "partToHide"); _hideChildrenField = AccessTools.Field(_shrinkerType, "hideChildren"); _addMethod = AccessTools.Method(_hiddenType, "AddHiddenPart", (Type[])null, (Type[])null); _removeMethod = AccessTools.Method(_hiddenType, "RemoveHiddenPart", (Type[])null, (Type[])null); _available = _partField != null && _hideChildrenField != null && _addMethod != null && _removeMethod != null; if (_available) { Plugin.Logger.LogInfo((object)"PartShrinker bridge installed."); } else { Plugin.Logger.LogWarning((object)"PartShrinker types found but reflection failed — disabled."); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("PartShrinker bridge init error: " + ex.Message)); } } internal static void OnSpawn(GameObject cosmetic, PlayerAvatarVisuals avatar) { Apply(cosmetic, avatar, isAdd: true); } internal static void OnRemove(GameObject cosmetic, PlayerAvatarVisuals avatar) { Apply(cosmetic, avatar, isAdd: false); } private static void Apply(GameObject cosmetic, PlayerAvatarVisuals avatar, bool isAdd) { EnsureInit(); if (!_available || (Object)(object)cosmetic == (Object)null || (Object)(object)avatar == (Object)null) { return; } Component[] componentsInChildren; try { componentsInChildren = cosmetic.GetComponentsInChildren(_shrinkerType, true); } catch { return; } if (componentsInChildren == null || componentsInChildren.Length == 0) { return; } Component val = ((Component)avatar).GetComponent(_hiddenType); if ((Object)(object)val == (Object)null) { try { val = ((Component)avatar).gameObject.AddComponent(_hiddenType); } catch (Exception ex) { Plugin.Logger.LogDebug((object)("Could not add HiddenParts: " + ex.Message)); return; } } MethodInfo methodInfo = (isAdd ? _addMethod : _removeMethod); Component[] array = componentsInChildren; foreach (Component val2 in array) { if ((Object)(object)val2 == (Object)null) { continue; } try { object value = _partField.GetValue(val2); bool flag = (bool)_hideChildrenField.GetValue(val2); methodInfo.Invoke(val, new object[3] { value, flag, true }); if (isAdd) { MonoBehaviour val3 = (MonoBehaviour)(object)((val2 is MonoBehaviour) ? val2 : null); if (val3 != null) { ((Behaviour)val3).enabled = false; } } } catch (Exception ex2) { Plugin.Logger.LogDebug((object)("PartShrinker " + (isAdd ? "Add" : "Remove") + " failed: " + ex2.Message)); } } } } [HarmonyPatch(typeof(PlayerCosmetics), "InstantiateCosmetic")] internal static class PartShrinkerBridge_SpawnPatch { [HarmonyPostfix] private static void Postfix(PlayerCosmetics __instance, CosmeticAsset _cosmeticAsset, GameObject __result) { if (!((Object)(object)__result == (Object)null) && BridgeIds.IsBridgeAsset(_cosmeticAsset) && !((Object)(object)__instance?.playerAvatarVisuals == (Object)null)) { PartShrinkerBridge.OnSpawn(__result, __instance.playerAvatarVisuals); } } } [HarmonyPatch(typeof(Cosmetic), "Remove")] internal static class PartShrinkerBridge_RemovePatch { [HarmonyPrefix] private static void Prefix(Cosmetic __instance) { if (!((Object)(object)__instance == (Object)null) && BridgeIds.IsBridgeAsset(__instance.cosmeticAsset) && !((Object)(object)__instance.playerCosmetics?.playerAvatarVisuals == (Object)null)) { PartShrinkerBridge.OnRemove(((Component)__instance).gameObject, __instance.playerCosmetics.playerAvatarVisuals); } } } internal static class PartShrinkerSuppressor { internal static void TryApply(Harmony harmony) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("MoreHeadUtilities.PartShrinker"); if (type == null) { Plugin.Logger.LogDebug((object)"MoreHeadUtilities not loaded — PartShrinker suppressor skipped."); return; } MethodInfo methodInfo = AccessTools.Method(type, "OnDisable", (Type[])null, (Type[])null); if (!(methodInfo == null)) { MethodInfo method = typeof(PartShrinkerSuppressor).GetMethod("Finalizer", BindingFlags.Static | BindingFlags.NonPublic); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null); Plugin.Logger.LogDebug((object)"PartShrinker.OnDisable NPE suppressor installed."); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Could not install PartShrinker suppressor: " + ex.Message)); } } private static Exception? Finalizer(Exception? __exception) { if (__exception is NullReferenceException) { return null; } return __exception; } } [HarmonyPatch(typeof(PlayerCosmetics), "InstantiateCosmetic")] internal static class ArmRotationPatch { [HarmonyPostfix] private static void Postfix(PlayerCosmetics __instance, CosmeticAsset _cosmeticAsset, GameObject __result) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Invalid comparison between Unknown and I4 //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__result == (Object)null || !BridgeIds.IsBridgeAsset(_cosmeticAsset) || (Object)(object)__instance == (Object)null || (Object)(object)__instance.playerAvatarVisuals == (Object)null) { return; } string name; if ((int)_cosmeticAsset.type == 1) { name = "ANIM ARM R SCALE"; } else { if ((int)_cosmeticAsset.type != 2) { return; } name = "code_arm_l"; } Transform val = FindByName(((Component)__instance.playerAvatarVisuals).transform, name); PrefabRef prefab = _cosmeticAsset.prefab; GameObject val2 = ((prefab != null) ? prefab.Prefab : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null)) { __result.transform.SetParent(val, false); __result.transform.localPosition = val2.transform.localPosition; __result.transform.localRotation = val2.transform.localRotation; __result.transform.localScale = val2.transform.localScale; } } private static Transform? FindByName(Transform root, string name) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown if (((Object)root).name == name) { return root; } foreach (Transform item in root) { Transform root2 = item; Transform val = FindByName(root2, name); if ((Object)(object)val != (Object)null) { return val; } } return null; } } [HarmonyPatch(typeof(MenuElementCosmeticButton), "Update")] internal static class CosmeticHoverPatch { [CompilerGenerated] private sealed class d__2 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public CosmeticAsset asset; private bool 5__1; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__2(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; <>2__current = null; <>1__state = 2; return true; case 2: <>1__state = -1; <>2__current = null; <>1__state = 3; return true; case 3: <>1__state = -1; <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 4; return true; case 4: <>1__state = -1; 5__1 = IconCapture.TryCapture(asset); if (!5__1) { _scheduled.Remove(asset.assetId); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static readonly HashSet _scheduled = new HashSet(); [HarmonyPostfix] private static void Postfix(MenuElementCosmeticButton __instance) { if (Plugin.AutoCaptureIcons.Value) { CosmeticAsset cosmeticAsset = __instance.cosmeticAsset; if (!((Object)(object)cosmeticAsset == (Object)null) && BridgeIds.IsBridgeAsset(cosmeticAsset) && __instance.wasHovering && !IconCapture.HasCache(cosmeticAsset) && _scheduled.Add(cosmeticAsset.assetId)) { ((MonoBehaviour)__instance).StartCoroutine(CaptureAfterDelay(cosmeticAsset)); } } } [IteratorStateMachine(typeof(d__2))] private static IEnumerator CaptureAfterDelay(CosmeticAsset asset) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__2(0) { asset = asset }; } } [HarmonyPatch(typeof(Cosmetic), "Setup")] internal static class CosmeticSetupPatch { [HarmonyPrefix] private static void Prefix(Cosmetic __instance) { //IL_0023: 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_0031: Invalid comparison between Unknown and I4 if (!((Object)(object)__instance == (Object)null) && BridgeIds.IsBridgeAsset(__instance.cosmeticAsset) && ((int)__instance.type == 0 || (int)__instance.type == 5) && (Object)(object)((Component)__instance).GetComponentInChildren(true) == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } } } [HarmonyPatch(typeof(Cosmetic), "CustomTypesLogic")] internal static class CosmeticUpdatePatch { [HarmonyFinalizer] private static Exception? Finalizer(Cosmetic __instance, Exception? __exception) { if (!(__exception is NullReferenceException)) { return __exception; } if (!BridgeIds.IsBridgeAsset(__instance?.cosmeticAsset)) { return __exception; } return Plugin.ShowBridgeDebugLogs.Value ? __exception : null; } } [HarmonyPatch(typeof(MetaManager), "GetCosmeticsToUnequip")] internal static class GetCosmeticsToUnequipPatch { [HarmonyPrefix] private static bool Prefix(MetaManager __instance, List _cosmeticEquipped, CosmeticAsset _cosmeticAssetNew, ref List __result) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) __result = new List(); if (!Object.op_Implicit((Object)(object)_cosmeticAssetNew)) { return false; } CosmeticTypeAsset typeAsset = GetTypeAsset(__instance, _cosmeticAssetNew.type); foreach (int item in _cosmeticEquipped) { if (item < 0 || item >= __instance.cosmeticAssets.Count) { continue; } CosmeticAsset val = __instance.cosmeticAssets[item]; if (!Object.op_Implicit((Object)(object)val) || (Object)(object)val == (Object)(object)_cosmeticAssetNew) { continue; } CosmeticTypeAsset typeAsset2 = GetTypeAsset(__instance, val.type); bool flag = val.type == _cosmeticAssetNew.type && !(typeAsset?.canEquipMultiple ?? false); bool flag2 = ContainsType(typeAsset?.disabledTypeList, typeAsset2) || ContainsType(typeAsset2?.disabledTypeList, typeAsset); if (flag || flag2) { __result.Add(val); continue; } foreach (Type item2 in typeAsset?.disabledCustomTypeList ?? new List()) { if (ContainsCustomType(val.customTypeList, item2) || ContainsCustomType(typeAsset2?.customTypeList, item2)) { __result.Add(val); break; } } } return false; } private static CosmeticTypeAsset? GetTypeAsset(MetaManager metaManager, CosmeticType type) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Expected I4, but got Unknown int num = (int)type; if (num < 0 || num >= metaManager.cosmeticTypeAssets.Count) { return null; } return metaManager.cosmeticTypeAssets[num]; } private static bool ContainsType(List? list, CosmeticTypeAsset? asset) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) return list != null && (Object)(object)asset != (Object)null && list.Contains(asset.type); } private static bool ContainsCustomType(List? list, Type value) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) return list?.Contains(value) ?? false; } } [HarmonyPatch(typeof(CosmeticAsset), "GetIcon")] internal static class GetIconPatch { [HarmonyPrefix] private static bool Prefix(CosmeticAsset __instance, ref Sprite? __result) { //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance.icon != (Object)null) { __result = __instance.icon; return false; } PrefabRef prefab = __instance.prefab; if ((Object)(object)((prefab != null) ? prefab.Prefab : null) == (Object)null) { return true; } if ((Object)(object)__instance.prefab.Prefab.GetComponentInChildren(true) != (Object)null) { return true; } string text = IconCapture.CachePathFor(__instance); if (File.Exists(text)) { __result = SemiFunc.LoadSpriteFromFile(text); __instance.icon = __result; return false; } if (BridgeIds.IsBridgeAsset(__instance)) { if (Plugin.UseTextureAsPlaceholder.Value && HhhCosmeticLoader.BridgeIconTextures.TryGetValue(__instance.assetId, out Texture2D value) && (Object)(object)value != (Object)null) { __result = Sprite.Create(value, new Rect(0f, 0f, (float)((Texture)value).width, (float)((Texture)value).height), new Vector2(0.5f, 0.5f), 100f); ((Object)__result).name = "BridgeIcon_" + ((Object)__instance).name; } else { __result = PlaceholderIcon.Get(); } __instance.icon = __result; } else { __result = null; } return false; } } internal static class HideMoreHeadUIPatch { internal static bool SkipInitialize() { return false; } } [HarmonyPatch(typeof(MenuElementCosmeticButton), "UpdateIcon")] internal static class MenuIconNpeGuardPatch { [HarmonyFinalizer] private static Exception? Finalizer(MenuElementCosmeticButton __instance, Exception? __exception) { if (!(__exception is NullReferenceException)) { return __exception; } if (!BridgeIds.IsBridgeAsset(__instance?.cosmeticAsset)) { return __exception; } return Plugin.ShowBridgeDebugLogs.Value ? __exception : null; } } [HarmonyPatch(typeof(MetaManager), "Load")] internal static class UnlockPatch { private static readonly bool _resetRequestedAtStartup = Plugin.ResetUnlocks.Value; private static bool _resetDone; [HarmonyPostfix] private static void Postfix(MetaManager __instance) { if (_resetRequestedAtStartup && !_resetDone) { TryReset(__instance); BundleLoader.OnAllBundlesLoaded += OnBundlesLoaded; } else if (!Plugin.UnlockAll.Value) { Plugin.Logger.LogDebug((object)"UnlockAll=false, skipping auto-unlock."); } else { TryUnlock(__instance); BundleLoader.OnAllBundlesLoaded += OnBundlesLoaded; } } private static void OnBundlesLoaded() { BundleLoader.OnAllBundlesLoaded -= OnBundlesLoaded; if ((Object)(object)MetaManager.instance == (Object)null) { Plugin.Logger.LogWarning((object)"MetaManager.instance is null in deferred path — skipping."); return; } if (_resetRequestedAtStartup && !_resetDone) { TryReset(MetaManager.instance); if (!_resetDone) { return; } } if (Plugin.UnlockAll.Value) { TryUnlock(MetaManager.instance); } } private static void TryUnlock(MetaManager instance) { int num = 0; foreach (string assetId in HhhCosmeticLoader.RegisteredAssetIds) { int num2 = instance.cosmeticAssets.FindIndex((CosmeticAsset a) => (Object)(object)a != (Object)null && a.assetId == assetId); if (num2 >= 0 && !instance.cosmeticUnlocks.Contains(num2)) { instance.cosmeticUnlocks.Add(num2); num++; } } if (num > 0) { Plugin.Logger.LogInfo((object)$"Auto-unlocked {num} bridge cosmetic(s) (UnlockAll=true)."); } } private static void TryReset(MetaManager instance) { HashSet hashSet = new HashSet(HhhCosmeticLoader.RegisteredAssetIds); if (hashSet.Count == 0) { return; } HashSet hashSet2 = new HashSet(); for (int i = 0; i < instance.cosmeticAssets.Count; i++) { CosmeticAsset val = instance.cosmeticAssets[i]; if ((Object)(object)val != (Object)null && hashSet.Contains(val.assetId)) { hashSet2.Add(i); } } if (hashSet2.Count == 0) { Plugin.Logger.LogDebug((object)"ResetUnlocks: no bridge cosmetics in cosmeticAssets yet, deferring."); return; } int num = instance.cosmeticUnlocks.RemoveAll(hashSet2.Contains); int num2 = instance.cosmeticEquipped.RemoveAll(hashSet2.Contains); int num3 = instance.cosmeticHistory.RemoveAll(hashSet2.Contains); instance.Save(true); Plugin.ResetUnlocks.Value = false; ((BaseUnityPlugin)Plugin.Instance).Config.Save(); _resetDone = true; Plugin.Logger.LogInfo((object)($"ResetUnlocks: cleared {num} unlock(s), " + $"{num2} equipped, {num3} history entry. Flag reset to false.")); } } [BepInPlugin("Xuaun.MoreHeadBridge", "MoreHead Bridge", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private readonly Harmony _harmony = new Harmony("Xuaun.MoreHeadBridge"); public static Plugin Instance { get; private set; } public static ManualLogSource Logger { get; private set; } public static ConfigEntry UnlockAll { get; private set; } public static ConfigEntry ResetUnlocks { get; private set; } public static ConfigEntry UseTextureAsPlaceholder { get; private set; } public static ConfigEntry AutoCaptureIcons { get; private set; } public static ConfigEntry GenerateAllIcons { get; private set; } public static ConfigEntry DeleteIconCache { get; private set; } public static ConfigEntry DeleteIconsMatching { get; private set; } public static ConfigEntry HideMoreHeadButton { get; private set; } public static ConfigEntry DefaultRarity { get; private set; } public static ConfigEntry SpecificFolders { get; private set; } public static ConfigEntry ShowBridgeDebugLogs { get; private set; } private void Awake() { Instance = this; Logger = ((BaseUnityPlugin)this).Logger; UnlockAll = ((BaseUnityPlugin)this).Config.Bind("General", "UnlockAll", true, "Auto-unlock NEW bridge cosmetics on every load.\n\nWhen TRUE — every bridge cosmetic gets added to your inventory\n on game start, so you never have to grind for them.\nWhen FALSE — bridge cosmetics behave like vanilla ones:\n you have to earn them in-game.\n\nIMPORTANT: this flag only controls what happens going FORWARD.\nCosmetics unlocked while UnlockAll was TRUE get saved permanently to\nthe REPOLib modded save file. Flipping this to FALSE later does NOT\nremove them — REPOLib re-reads the save on every launch.\nIf you want to wipe existing unlocks, see the [Reset] section below."); HideMoreHeadButton = ((BaseUnityPlugin)this).Config.Bind("General", "HideMoreHeadButton", false, "If true, removes the MoreHead button from all menus so you can use only the vanilla cosmetics UI. Requires restart."); DefaultRarity = ((BaseUnityPlugin)this).Config.Bind("General", "DefaultRarity", (Rarity)0, "Rarity tier assigned to bridge cosmetics in the vanilla shop. Values: Common, Uncommon, Rare, UltraRare."); SpecificFolders = ((BaseUnityPlugin)this).Config.Bind("General", "SpecificFolders", "", "Comma-separated subfolder names under BepInEx/plugins to scan for .hhh files. Empty = scan all. Example: 'Some-MoreHeadPack,Another-CosmeticsPack'. Matching is case-insensitive and uses path contains."); UseTextureAsPlaceholder = ((BaseUnityPlugin)this).Config.Bind("Icons", "UseTextureAsPlaceholder", true, "When TRUE (default) — the cosmetic's texture is used as the icon, overlaid on the placeholder background.\nWhen FALSE — the texture is NOT applied to the placeholder; the slot keeps the plain placeholder icon\n until a captured icon (AutoCaptureIcons / GenerateAllIcons) replaces it."); AutoCaptureIcons = ((BaseUnityPlugin)this).Config.Bind("Icons", "AutoCaptureIcons", true, "Reactively capture icons while you browse the cosmetics menu.\n\nWhen TRUE — every time you HOVER a bridge cosmetic in the menu,\n the game's existing avatar preview is snapshotted and\n saved as a PNG icon for that cosmetic. Next time the UI\n asks for that icon it loads the PNG (instant).\n Icons fill in gradually as you explore the menu.\nWhen FALSE — no captures. Bridge cosmetics keep the texture/placeholder\n fallback icons.\n\nPNG cache lives in:\n %userprofile%\\AppData\\LocalLow\\semiwork\\REPO\\MoreHeadBridge_Icons\\\nDelete that folder to wipe all generated icons.\n(We store icons OUTSIDE the vanilla cosmetics cache because REPOLib\n wipes that one on every launch for any non-vanilla cosmetic.)"); GenerateAllIcons = ((BaseUnityPlugin)this).Config.Bind("Icons", "GenerateAllIcons", false, "ONE-SHOT trigger. When TRUE, the next time you open the cosmetics menu\nthe mod will cycle through EVERY bridge cosmetic without a cached icon,\npreview-equipping each one, snapshotting the avatar, and saving the PNG.\n\nEffects while running:\n * The avatar will visibly rotate through cosmetics — that IS the progress.\n * Console logs progress every 50 items.\n * Expect ~1-3 minutes for 1600+ cosmetics.\n * Whatever you had previewing/equipped is restored at the end.\n * This flag auto-resets to FALSE so it doesn't fire again.\n\nUse this if you want all icons generated in one go instead of as you browse.\nRequires AutoCaptureIcons logic — keeps working even if AutoCaptureIcons=false."); DeleteIconCache = ((BaseUnityPlugin)this).Config.Bind("Icons", "DeleteIconCache", false, "ONE-SHOT trigger. When TRUE on launch, delete cached bridge icon PNGs from:\n %userprofile%\\AppData\\LocalLow\\semiwork\\REPO\\MoreHeadBridge_Icons\\\nUse DeleteIconsMatching to filter which ones to delete.\nAuto-resets to FALSE after running."); DeleteIconsMatching = ((BaseUnityPlugin)this).Config.Bind("Icons", "DeleteIconsMatching", "", "Optional comma-separated filter for DeleteIconCache. Case-insensitive\nsubstring match against the icon filename (which is the cosmetic's internal name).\nEmpty = delete ALL bridge icons.\nExample: 'PirateHat,Waluigi' deletes only icons whose name contains either."); ResetUnlocks = ((BaseUnityPlugin)this).Config.Bind("Reset", "ResetUnlocks", false, "⚠ DESTRUCTIVE ONE-SHOT TRIGGER ⚠\n\nSetting this to TRUE causes the NEXT game launch to:\n 1. Remove EVERY bridge cosmetic from your unlocks list\n 2. Remove them from any saved outfit/preset you have equipped\n 3. Remove them from your history\n 4. Rewrite the REPOLib modded save file\n 5. Auto-flip this flag back to FALSE so it doesn't fire again\n\nUse this if you want to start over with bridge cosmetics.\nIf UnlockAll=true, cosmetics are wiped and immediately re-unlocked on the same launch.\nSet UnlockAll=false FIRST if you want to keep them locked after the reset.\n\nThis does NOT touch vanilla cosmetics or cosmetics from other mods.\nThis does NOT delete the .hhh files — only the unlock state."); ShowBridgeDebugLogs = ((BaseUnityPlugin)this).Config.Bind("Debug", "ShowBridgeDebugLogs", false, "If true, do NOT suppress NullReferenceExceptions for bridge cosmetics.\nUse this to diagnose bridge-only issues (will spam logs if the base game is noisy)."); PrintBanner(); HhhCosmeticLoader.LoadAll(); IconCacheCleaner.Run(); _harmony.PatchAll(); if (HideMoreHeadButton.Value) { TryHideMoreHeadUI(); } PartShrinkerSuppressor.TryApply(_harmony); ModdedRpcRetrigger.TryApply(_harmony); } private void TryHideMoreHeadUI() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("MoreHead.MoreHeadUI"); if (type == null) { Logger.LogDebug((object)"HideMoreHeadButton=true but MoreHead is not loaded — skipping."); return; } MethodInfo methodInfo = AccessTools.Method(type, "Initialize", (Type[])null, (Type[])null); if (!(methodInfo == null)) { MethodInfo method = typeof(HideMoreHeadUIPatch).GetMethod("SkipInitialize", BindingFlags.Static | BindingFlags.NonPublic); _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Logger.LogInfo((object)"MoreHead UI hidden (HideMoreHeadButton=true)."); } } catch (Exception ex) { Logger.LogWarning((object)("Could not hide MoreHead UI: " + ex.Message)); } } private static void PrintBanner() { if (BceConsole.IsAvailable) { BceConsole.WriteLine("══════════════════════════════════════════════════════════════════════════════════", ConsoleColor.DarkCyan); BceConsole.Write("[Info : MoreHead Bridge] ", ConsoleColor.Cyan); BceConsole.WriteLine("► MoreHead Bridge v1.0.0 by Xuaun", ConsoleColor.DarkCyan); BceConsole.Write("[Info : MoreHead Bridge] ", ConsoleColor.Cyan); BceConsole.WriteLine(" Translating .hhh cosmetics into vanilla REPO", ConsoleColor.DarkCyan); BceConsole.WriteLine("══════════════════════════════════════════════════════════════════════════════════", ConsoleColor.DarkCyan); } else { Logger.LogInfo((object)"MoreHead Bridge v1.0.0 by Xuaun"); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }