Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of MoreHeadBridge v1.0.0
MoreHeadBridge.dll
Decompiled a day agousing 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<string> RegisteredAssetIds = new List<string>(); internal static readonly Dictionary<string, Texture2D> BridgeIconTextures = new Dictionary<string, Texture2D>(); private static readonly Dictionary<string, CosmeticType> TagToType = new Dictionary<string, CosmeticType> { ["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<string> ValidTags; private static readonly HashSet<string> _usedPrefabIds; private static readonly HashSet<string> _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<string> list = new List<string>(); 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<GameObject>(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>())) { Cosmetic val4 = val2.AddComponent<Cosmetic>(); 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<CosmeticAsset>(); ((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<Type>(); 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<Renderer>(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<string> 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<string> hashSet = new HashSet<string>(); foreach (string key in TagToType.Keys) { hashSet.Add(key); } hashSet.Add("world"); ValidTags = hashSet; _usedPrefabIds = new HashSet<string>(); _usedInternalNames = new HashSet<string>(); } } internal static class BatchIconGenerator { [CompilerGenerated] private sealed class <Run>d__2 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private List<int> <savedPreview>5__1; private List<CosmeticAsset> <work>5__2; private int <done>5__3; private int <failed>5__4; private int[] <savedColorsPreview>5__5; private List<CosmeticAsset>.Enumerator <>s__6; private CosmeticAsset <asset>5__7; private List<CosmeticAsset>.Enumerator <>s__8; private CosmeticAsset <asset>5__9; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <Run>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(); } } <savedPreview>5__1 = null; <work>5__2 = null; <savedColorsPreview>5__5 = null; <>s__6 = default(List<CosmeticAsset>.Enumerator); <asset>5__7 = null; <>s__8 = default(List<CosmeticAsset>.Enumerator); <asset>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; <savedPreview>5__1 = MetaManager.instance.cosmeticEquippedPreview?.ToList() ?? new List<int>(); <work>5__2 = new List<CosmeticAsset>(); <>s__6 = MetaManager.instance.cosmeticAssets.GetEnumerator(); try { while (<>s__6.MoveNext()) { <asset>5__7 = <>s__6.Current; if (!((Object)(object)<asset>5__7 == (Object)null) && <asset>5__7.assetId != null && BridgeIds.IsBridgeAsset(<asset>5__7) && !IconCapture.HasCache(<asset>5__7)) { <work>5__2.Add(<asset>5__7); <asset>5__7 = null; } } } finally { ((IDisposable)<>s__6).Dispose(); } <>s__6 = default(List<CosmeticAsset>.Enumerator); Plugin.Logger.LogInfo((object)$"GenerateAllIcons: {<work>5__2.Count} icon(s) to generate."); <done>5__3 = 0; <failed>5__4 = 0; <savedColorsPreview>5__5 = ((MetaManager.instance.colorsEquippedPreview != null) ? ((int[])MetaManager.instance.colorsEquippedPreview.Clone()) : null); <>s__8 = <work>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(<asset>5__9, <asset>5__9.type)) { <done>5__3++; } else { <failed>5__4++; } MetaManager.instance.CosmeticUnequip(<asset>5__9, true, false, false); if ((<done>5__3 + <failed>5__4) % 50 == 0) { Plugin.Logger.LogInfo((object)$"Batch progress: {<done>5__3 + <failed>5__4}/{<work>5__2.Count} ({<done>5__3} ok, {<failed>5__4} failed)"); } <asset>5__9 = null; break; } if (<>s__8.MoveNext()) { <asset>5__9 = <>s__8.Current; MetaManager.instance.cosmeticEquippedPreview = MetaManager.instance.cosmeticEquipped.ToList(); MetaManager.instance.colorsEquippedPreview = (int[])MetaManager.instance.colorsEquipped.Clone(); MetaManager.instance.CosmeticEquip(<asset>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<CosmeticAsset>.Enumerator); MetaManager.instance.cosmeticEquippedPreview = <savedPreview>5__1; if (<savedColorsPreview>5__5 != null) { MetaManager.instance.colorsEquippedPreview = <savedColorsPreview>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 — {<done>5__3} captured, {<failed>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(<Run>d__2))] private static IEnumerator Run() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <Run>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<string> hashSet = new HashSet<string>(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<PlayerAvatarMenuHover>(); if ((Object)(object)val == (Object)null) { return null; } if ((Object)(object)val.renderTextureInstance != (Object)null) { return val.renderTextureInstance; } RawImage component = ((Component)val).GetComponent<RawImage>(); 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>(); 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<string> 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<PlayerCosmetics>(); if (component != null) { component.SetupCosmeticsLogic(Array.Empty<int>(), 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 <CaptureAfterDelay>d__2 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public CosmeticAsset asset; private bool <ok>5__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <CaptureAfterDelay>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; <ok>5__1 = IconCapture.TryCapture(asset); if (!<ok>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<string> _scheduled = new HashSet<string>(); [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(<CaptureAfterDelay>d__2))] private static IEnumerator CaptureAfterDelay(CosmeticAsset asset) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <CaptureAfterDelay>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<CosmeticPlayerCrown>(true) == (Object)null) { ((Component)__instance).gameObject.AddComponent<CosmeticPlayerCrown>(); } } } [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<int> _cosmeticEquipped, CosmeticAsset _cosmeticAssetNew, ref List<CosmeticAsset> __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<CosmeticAsset>(); 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<Type>()) { 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<CosmeticType>? 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<Type>? 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<SemiIconMaker>(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<string> hashSet = new HashSet<string>(HhhCosmeticLoader.RegisteredAssetIds); if (hashSet.Count == 0) { return; } HashSet<int> hashSet2 = new HashSet<int>(); 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<bool> UnlockAll { get; private set; } public static ConfigEntry<bool> ResetUnlocks { get; private set; } public static ConfigEntry<bool> UseTextureAsPlaceholder { get; private set; } public static ConfigEntry<bool> AutoCaptureIcons { get; private set; } public static ConfigEntry<bool> GenerateAllIcons { get; private set; } public static ConfigEntry<bool> DeleteIconCache { get; private set; } public static ConfigEntry<string> DeleteIconsMatching { get; private set; } public static ConfigEntry<bool> HideMoreHeadButton { get; private set; } public static ConfigEntry<Rarity> DefaultRarity { get; private set; } public static ConfigEntry<string> SpecificFolders { get; private set; } public static ConfigEntry<bool> ShowBridgeDebugLogs { get; private set; } private void Awake() { Instance = this; Logger = ((BaseUnityPlugin)this).Logger; UnlockAll = ((BaseUnityPlugin)this).Config.Bind<bool>("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<bool>("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<Rarity>("General", "DefaultRarity", (Rarity)0, "Rarity tier assigned to bridge cosmetics in the vanilla shop. Values: Common, Uncommon, Rare, UltraRare."); SpecificFolders = ((BaseUnityPlugin)this).Config.Bind<string>("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<bool>("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<bool>("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<bool>("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<bool>("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<string>("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<bool>("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<bool>("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) { } } }