using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using Zorro.Core;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace PEAKSkinCatalog
{
[BepInPlugin("alexs.peak.skin.catalog", "PEAK Skin Catalog", "1.0.0")]
[DefaultExecutionOrder(31000)]
public sealed class CatalogPlugin : BaseUnityPlugin
{
public const string Guid = "alexs.peak.skin.catalog";
private ConfigEntry<string> selected;
private ConfigEntry<bool> catalogEnabled;
private PassportSkinPicker picker;
public static CatalogPlugin Instance { get; private set; }
public SkinRegistry Registry { get; private set; }
private void Awake()
{
selected = ((BaseUnityPlugin)this).Config.Bind<string>("Appearance", "SelectedSkinId", "", "Persistent skin ID; empty selects standard appearance. Missing skins fall back to standard until reinstalled.");
catalogEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable the shared skin catalog in Passport clothing.");
StartCatalog();
}
private void StartCatalog()
{
if (selected != null && (Registry == null || Registry.IsDisposed))
{
Instance = this;
Registry = new SkinRegistry(selected.Value, delegate(string id)
{
selected.Value = id;
((BaseUnityPlugin)this).Config.Save();
}, delegate(string message)
{
((BaseUnityPlugin)this).Logger.LogError((object)message);
});
Registry.Enabled = catalogEnabled.Value;
picker = new PassportSkinPicker(Registry);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Shared skin catalog ready. Providers register by stable ID; no fixed skin count.");
}
}
private void OnEnable()
{
StartCatalog();
}
private void LateUpdate()
{
if (Registry != null && !Registry.IsDisposed)
{
Registry.Enabled = catalogEnabled.Value;
Registry.Tick();
PassportManager instance = PassportManager.instance;
if (Object.op_Implicit((Object)(object)instance) && ((MenuWindow)instance).isOpen)
{
picker.Refresh(instance);
}
}
}
private void OnDisable()
{
if (Registry != null)
{
Registry.Dispose();
}
if (picker != null)
{
picker.Dispose();
picker = null;
}
if ((Object)(object)Instance == (Object)(object)this)
{
Instance = null;
}
}
}
public sealed class PassportSkinPicker : IDisposable
{
private static PassportSkinPicker current;
private readonly SkinRegistry registry;
private readonly Harmony harmony;
private readonly Dictionary<PassportButton, GameObject> labels = new Dictionary<PassportButton, GameObject>();
private PassportManager lastManager;
private int lastRevision = -1;
private int lastCount = -1;
public PassportSkinPicker(SkinRegistry registry)
{
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Expected O, but got Unknown
if (current != null)
{
throw new InvalidOperationException("Only one shared Passport catalog may be installed");
}
this.registry = registry;
harmony = new Harmony("alexs.peak.skin.catalog.passport");
current = this;
try
{
harmony.Patch((MethodBase)AccessTools.Method(typeof(PassportManager), "SetButtons", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, Patch("GridListTranspiler"), (HarmonyMethod)null, (HarmonyMethod)null);
harmony.Patch((MethodBase)AccessTools.Method(typeof(PassportManager), "SetActiveButton", (Type[])null, (Type[])null), Patch("ActivePrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
harmony.Patch((MethodBase)AccessTools.Method(typeof(PassportManager), "SetOption", (Type[])null, (Type[])null), Patch("OptionPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
harmony.Patch((MethodBase)AccessTools.Method(typeof(PassportButton), "SetButton", (Type[])null, (Type[])null), (HarmonyMethod)null, Patch("ButtonPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
catch
{
Dispose();
throw;
}
}
private static HarmonyMethod Patch(string name)
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Expected O, but got Unknown
return new HarmonyMethod(typeof(PassportSkinPicker), name, (Type[])null);
}
private static IEnumerable<CodeInstruction> GridListTranspiler(IEnumerable<CodeInstruction> instructions)
{
MethodInfo original = AccessTools.Method(typeof(Customization), "GetList", (Type[])null, (Type[])null);
MethodInfo replacement = AccessTools.Method(typeof(PassportSkinPicker), "GetPassportOptions", (Type[])null, (Type[])null);
int count = 0;
foreach (CodeInstruction instruction in instructions)
{
if (CodeInstructionExtensions.Calls(instruction, original))
{
instruction.opcode = OpCodes.Call;
instruction.operand = replacement;
count++;
}
yield return instruction;
}
if (count != 1)
{
throw new InvalidOperationException("Unsupported Passport grid: expected exactly one GetList call; found " + count);
}
}
private static CustomizationOption[] GetPassportOptions(Customization customization, Type type)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Invalid comparison between Unknown and I4
CustomizationOption[] list = customization.GetList(type);
if (current == null || (int)type != 40)
{
return list;
}
SkinRegistration[] visibleEntries = current.registry.VisibleEntries;
if (visibleEntries.Length == 0)
{
return list;
}
CustomizationOption[] array = (CustomizationOption[])(object)new CustomizationOption[list.Length + visibleEntries.Length];
Array.Copy(list, array, list.Length);
for (int i = 0; i < visibleEntries.Length; i++)
{
array[list.Length + i] = visibleEntries[i].Option;
}
return array;
}
private static bool ActivePrefix(PassportManager __instance, bool fromTabChange, int ___page, int ___buttonsPerPage, ref int __result)
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Invalid comparison between Unknown and I4
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
if (current == null || (int)__instance.activeType != 40)
{
return true;
}
int num = Array.FindIndex(current.registry.VisibleEntries, (SkinRegistration e) => e.IsSelected);
if (num < 0)
{
return true;
}
__result = Singleton<Customization>.Instance.GetList((Type)40).Length + num;
int num2 = (fromTabChange ? (__result / ___buttonsPerPage) : ___page);
for (int num3 = 0; num3 < __instance.buttons.Length; num3++)
{
((Graphic)__instance.buttons[num3].border).color = ((num3 + num2 * ___buttonsPerPage == __result) ? __instance.activeBorderColor : __instance.inactiveBorderColor);
}
return false;
}
private static bool OptionPrefix(PassportManager __instance, CustomizationOption option)
{
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Invalid comparison between Unknown and I4
if (current == null)
{
return true;
}
if (current.registry.IsCustomOption(option))
{
current.registry.SelectOption(option);
__instance.SetButtons(false);
return false;
}
if (Object.op_Implicit((Object)(object)option) && (int)option.type == 40 && (!option.IsLocked || __instance.testUnlockAll))
{
current.registry.Select("");
}
return true;
}
private static void ButtonPostfix(PassportButton __instance, CustomizationOption option)
{
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: Expected O, but got Unknown
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00c1: Expected O, but got Unknown
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
//IL_0109: Unknown result type (might be due to invalid IL or missing references)
//IL_011f: Unknown result type (might be due to invalid IL or missing references)
//IL_0145: Unknown result type (might be due to invalid IL or missing references)
//IL_017a: Unknown result type (might be due to invalid IL or missing references)
//IL_017f: Unknown result type (might be due to invalid IL or missing references)
//IL_018c: Unknown result type (might be due to invalid IL or missing references)
//IL_0192: Unknown result type (might be due to invalid IL or missing references)
//IL_0197: Unknown result type (might be due to invalid IL or missing references)
//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
//IL_0235: Unknown result type (might be due to invalid IL or missing references)
PassportSkinPicker passportSkinPicker = current;
if (passportSkinPicker == null)
{
return;
}
SkinRegistration skinRegistration = passportSkinPicker.registry.VisibleEntries.FirstOrDefault((SkinRegistration e) => (Object)(object)e.Option == (Object)(object)option);
bool flag = skinRegistration != null;
if (passportSkinPicker.labels.TryGetValue(__instance, out var value) && Object.op_Implicit((Object)(object)value))
{
value.SetActive(flag);
if (flag)
{
((TMP_Text)value.GetComponentInChildren<TextMeshProUGUI>(true)).text = skinRegistration.Name;
}
}
else if (flag)
{
value = new GameObject("__PEAKSkinCatalog_Label", new Type[2]
{
typeof(RectTransform),
typeof(Image)
});
value.layer = ((Component)__instance).gameObject.layer;
RectTransform val = (RectTransform)value.transform;
((Transform)val).SetParent(((Component)__instance).transform, false);
val.anchorMin = Vector2.zero;
val.anchorMax = new Vector2(1f, 0f);
val.pivot = new Vector2(0.5f, 0f);
val.anchoredPosition = Vector2.zero;
val.sizeDelta = new Vector2(0f, 15f);
Image component = value.GetComponent<Image>();
((Graphic)component).color = new Color(0.08f, 0.07f, 0.06f, 0.85f);
((Graphic)component).raycastTarget = false;
GameObject val2 = new GameObject("Text", new Type[2]
{
typeof(RectTransform),
typeof(TextMeshProUGUI)
})
{
layer = value.layer
};
RectTransform val3 = (RectTransform)val2.transform;
((Transform)val3).SetParent((Transform)(object)val, false);
val3.anchorMin = Vector2.zero;
val3.anchorMax = Vector2.one;
Vector2 offsetMin = (val3.offsetMax = Vector2.zero);
val3.offsetMin = offsetMin;
TextMeshProUGUI component2 = val2.GetComponent<TextMeshProUGUI>();
PassportManager manager = __instance.manager;
if (Object.op_Implicit((Object)(object)manager.nameText) && Object.op_Implicit((Object)(object)((TMP_Text)manager.nameText).font))
{
((TMP_Text)component2).font = ((TMP_Text)manager.nameText).font;
}
((TMP_Text)component2).text = skinRegistration.Name;
((TMP_Text)component2).fontSize = 12f;
((TMP_Text)component2).alignment = (TextAlignmentOptions)514;
((Graphic)component2).color = Color.white;
((Graphic)component2).raycastTarget = false;
passportSkinPicker.labels[__instance] = value;
}
}
public void Refresh(PassportManager manager)
{
if (!Object.op_Implicit((Object)(object)manager) || !((MenuWindow)manager).isOpen)
{
return;
}
int num = Singleton<Customization>.Instance.GetList((Type)40).Length;
if (!((Object)(object)lastManager == (Object)(object)manager) || lastRevision != registry.Revision || lastCount != num)
{
lastManager = manager;
lastRevision = registry.Revision;
lastCount = num;
manager.SetButtons(true);
PassportButton[] array = labels.Keys.Where((PassportButton k) => !Object.op_Implicit((Object)(object)k)).ToArray();
foreach (PassportButton key in array)
{
labels.Remove(key);
}
}
}
public void Dispose()
{
if (current == this)
{
current = null;
}
harmony.UnpatchSelf();
foreach (GameObject value in labels.Values)
{
if (Object.op_Implicit((Object)(object)value))
{
value.SetActive(false);
if (Application.isPlaying)
{
Object.Destroy((Object)(object)value);
}
else
{
Object.DestroyImmediate((Object)(object)value);
}
}
}
labels.Clear();
if (Object.op_Implicit((Object)(object)lastManager) && ((MenuWindow)lastManager).isOpen)
{
lastManager.SetButtons(true);
}
lastManager = null;
}
}
public sealed class SkinRegistration : IDisposable
{
internal SkinRegistry Registry;
internal readonly Func<bool> Available;
internal readonly Action<bool> SelectionChanged;
internal Texture2D Icon;
internal bool Visible;
internal bool Faulted;
public string Id { get; internal set; }
public string Name { get; internal set; }
public CustomizationOption Option { get; internal set; }
public bool IsSelected { get; internal set; }
public bool IsDisposed
{
get
{
if (Registry != null)
{
return Registry.IsDisposed;
}
return true;
}
}
internal SkinRegistration(Func<bool> available, Action<bool> changed)
{
Available = available;
SelectionChanged = changed;
}
public void Dispose()
{
if (Registry != null)
{
Registry.Remove(this);
}
}
}
public sealed class SkinRegistry : IDisposable
{
private readonly SortedDictionary<string, SkinRegistration> entries = new SortedDictionary<string, SkinRegistration>(StringComparer.Ordinal);
private readonly List<SkinRegistration> owned = new List<SkinRegistration>();
private readonly Action<string> persist;
private readonly Action<string> error;
private readonly HashSet<string> reported = new HashSet<string>();
private SkinRegistration active;
public bool Enabled = true;
public string RequestedId { get; private set; }
public string ActiveId
{
get
{
if (active != null)
{
return active.Id;
}
return "";
}
}
public bool IsDisposed { get; private set; }
public int Revision { get; private set; }
public SkinRegistration[] VisibleEntries => entries.Values.Where((SkinRegistration e) => e.Visible).ToArray();
public SkinRegistry(string selectedId, Action<string> persist, Action<string> error)
{
RequestedId = selectedId ?? "";
this.persist = persist ?? ((Action<string>)delegate
{
});
this.error = error ?? ((Action<string>)delegate
{
});
}
public SkinRegistration Register(string id, string name, byte[] iconPng, Func<bool> available, Action<bool> changed)
{
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: Expected O, but got Unknown
//IL_010d: Unknown result type (might be due to invalid IL or missing references)
//IL_0129: Unknown result type (might be due to invalid IL or missing references)
//IL_012e: 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_0146: Unknown result type (might be due to invalid IL or missing references)
if (IsDisposed)
{
throw new ObjectDisposedException("SkinRegistry");
}
if (string.IsNullOrWhiteSpace(id) || id.Any((char c) => !char.IsLetterOrDigit(c) && c != '.' && c != '_' && c != '-'))
{
throw new ArgumentException("Use a stable nonempty skin ID containing letters, digits, dot, underscore or hyphen.");
}
if (entries.ContainsKey(id))
{
throw new ArgumentException("Duplicate skin ID: " + id);
}
if (string.IsNullOrWhiteSpace(name) || available == null || changed == null)
{
throw new ArgumentException("Skin name and callbacks required");
}
SkinRegistration skinRegistration = new SkinRegistration(available, changed)
{
Registry = this,
Id = id,
Name = name
};
try
{
skinRegistration.Icon = new Texture2D(2, 2, (TextureFormat)4, false);
if (iconPng == null || !ImageConversion.LoadImage(skinRegistration.Icon, iconPng, true))
{
throw new ArgumentException("Invalid PNG for " + id);
}
((Object)skinRegistration.Icon).name = name + " thumbnail";
skinRegistration.Option = ScriptableObject.CreateInstance<CustomizationOption>();
((Object)skinRegistration.Option).name = name;
skinRegistration.Option.type = (Type)40;
skinRegistration.Option.texture = (Texture)(object)skinRegistration.Icon;
skinRegistration.Option.color = Color.white;
skinRegistration.Option.requiredAchievement = (ACHIEVEMENTTYPE)0;
skinRegistration.Option.requiredSteamStat = (STEAMSTATTYPE)0;
changed(obj: false);
entries.Add(id, skinRegistration);
owned.Add(skinRegistration);
Revision++;
Tick();
return skinRegistration;
}
catch
{
Destroy((Object)(object)skinRegistration.Option);
Destroy((Object)(object)skinRegistration.Icon);
skinRegistration.Registry = null;
throw;
}
}
public bool IsCustomOption(CustomizationOption option)
{
if (Object.op_Implicit((Object)(object)option))
{
return owned.Any((SkinRegistration e) => (Object)(object)e.Option == (Object)(object)option);
}
return false;
}
public void SelectOption(CustomizationOption option)
{
SkinRegistration skinRegistration = entries.Values.FirstOrDefault((SkinRegistration e) => (Object)(object)e.Option == (Object)(object)option && e.Visible);
if (skinRegistration != null)
{
Select(skinRegistration.Id);
}
}
public void Select(string id)
{
if (IsDisposed)
{
throw new ObjectDisposedException("SkinRegistry");
}
id = id ?? "";
if (id != "" && (!entries.ContainsKey(id) || !entries[id].Visible))
{
throw new ArgumentException("Skin unavailable: " + id);
}
if (RequestedId != id)
{
RequestedId = id;
persist(id);
Revision++;
}
Tick();
}
public void Tick()
{
if (IsDisposed)
{
return;
}
foreach (SkinRegistration value2 in entries.Values)
{
bool flag = false;
try
{
flag = Enabled && !value2.Faulted && value2.Available();
}
catch (Exception ex)
{
Report(value2.Id + ": availability: " + ex.Message);
}
if (flag != value2.Visible)
{
value2.Visible = flag;
Revision++;
}
}
if (!entries.TryGetValue(RequestedId, out var value) || !value.Visible)
{
value = null;
}
if (active == value || !Release() || value == null)
{
return;
}
active = value;
value.IsSelected = true;
try
{
value.SelectionChanged(obj: true);
Revision++;
}
catch (Exception ex2)
{
value.Faulted = true;
value.Visible = false;
Revision++;
Report(value.Id + ": activation: " + ex2.Message);
Release();
}
}
private bool Release()
{
if (active == null)
{
return true;
}
SkinRegistration skinRegistration = active;
skinRegistration.IsSelected = false;
try
{
skinRegistration.SelectionChanged(obj: false);
active = null;
Revision++;
return true;
}
catch (Exception ex)
{
skinRegistration.IsSelected = true;
Report(skinRegistration.Id + ": cleanup: " + ex.Message);
return false;
}
}
internal void Remove(SkinRegistration entry)
{
if (entry.Registry == this && (active != entry || Release()))
{
entries.Remove(entry.Id);
entry.Visible = false;
entry.Registry = null;
Revision++;
}
}
private void Report(string message)
{
if (reported.Add(message))
{
error(message);
}
}
private static void Destroy(Object value)
{
if (Object.op_Implicit(value))
{
if (Application.isPlaying)
{
Object.Destroy(value);
}
else
{
Object.DestroyImmediate(value);
}
}
}
public void Dispose()
{
if (IsDisposed)
{
return;
}
Release();
IsDisposed = true;
foreach (SkinRegistration item in owned)
{
item.IsSelected = false;
item.Registry = null;
Destroy((Object)(object)item.Option);
Destroy((Object)(object)item.Icon);
}
entries.Clear();
owned.Clear();
Revision++;
}
}
}