Decompiled source of CustomAchievements v0.0.0

CustomAchievements.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using CatAPI;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace CustomAchievements
{
	public sealed class AchIcon
	{
		internal readonly string Ref;

		internal readonly byte[] Png;

		private AchIcon(string reference, byte[] png)
		{
			Ref = reference;
			Png = png;
		}

		public static AchIcon Item(int itemId)
		{
			return new AchIcon("item:" + itemId, null);
		}

		public static AchIcon VanillaAchievement(int achievementId)
		{
			return new AchIcon("ach:" + achievementId, null);
		}

		public static AchIcon Resource(string path)
		{
			if (string.IsNullOrEmpty(path))
			{
				throw new ArgumentException("Resource path is required", "path");
			}
			return new AchIcon("res:" + path, null);
		}

		public static AchIcon FromPng(byte[] png)
		{
			if (png == null || png.Length < 8 || png[0] != 137 || png[1] != 80 || png[2] != 78 || png[3] != 71)
			{
				throw new ArgumentException("Not a PNG image", "png");
			}
			using SHA1 sHA = SHA1.Create();
			StringBuilder stringBuilder = new StringBuilder();
			byte[] array = sHA.ComputeHash(png);
			foreach (byte b in array)
			{
				stringBuilder.Append(b.ToString("x2"));
			}
			return new AchIcon("png:" + stringBuilder, (byte[])png.Clone());
		}

		public static AchIcon FromEmbeddedPng(Assembly assembly, string resourceName)
		{
			using Stream stream = assembly.GetManifestResourceStream(resourceName);
			if (stream == null)
			{
				throw new ArgumentException("Embedded resource not found: " + resourceName, "resourceName");
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return FromPng(memoryStream.ToArray());
		}
	}
	public static class AchievementRegistry
	{
		public static AchievementMod RegisterMod(string githubUrl, AchText name)
		{
			if (!GitHubId.TryNormalize(githubUrl, out var id))
			{
				throw new ArgumentException("Expected a GitHub repository URL like https://github.com/owner/repo, got: " + githubUrl, "githubUrl");
			}
			if (name == null)
			{
				throw new ArgumentNullException("name");
			}
			return new AchievementMod(Store.GetOrAddMod(id, name));
		}
	}
	public sealed class AchievementMod
	{
		private readonly ModRecord record;

		public string Id => record.Id;

		internal AchievementMod(ModRecord record)
		{
			this.record = record;
		}

		public Achievement Register(string key, AchText name, AchText description, long target = 1L, AchIcon icon = null, bool allowSandbox = false)
		{
			if (!GitHubId.IsKey(key))
			{
				throw new ArgumentException("Achievement key must use letters, digits, '-', '_' or '.', got: " + key, "key");
			}
			if (name == null)
			{
				throw new ArgumentNullException("name");
			}
			if (target < 1)
			{
				throw new ArgumentOutOfRangeException("target", "target must be at least 1");
			}
			return new Achievement(Store.GetOrAddAchievement(record, key.ToLowerInvariant(), name, description ?? AchText.Empty(), target, icon, allowSandbox));
		}
	}
	public sealed class Achievement
	{
		private readonly AchRecord rec;

		public string Id => rec.FullId;

		public long Target => rec.Target;

		public long Progress
		{
			get
			{
				lock (Store.Sync)
				{
					return rec.Progress;
				}
			}
		}

		public bool Unlocked
		{
			get
			{
				lock (Store.Sync)
				{
					return rec.Unlocked;
				}
			}
		}

		public bool CanProgress
		{
			get
			{
				lock (Store.Sync)
				{
					return Store.CanProgress(rec);
				}
			}
		}

		internal Achievement(AchRecord rec)
		{
			this.rec = rec;
		}

		public void Unlock()
		{
			Store.Unlock(rec);
		}

		public void SetProgress(long value)
		{
			Store.SetProgress(rec, value);
		}

		public void AddProgress(long delta)
		{
			Store.AddProgress(rec, delta);
		}
	}
	public sealed class AchText
	{
		private readonly string en;

		private readonly Dictionary<int, string> text = new Dictionary<int, string>();

		public string English => en;

		private AchText(string en)
		{
			this.en = en ?? "";
		}

		public static AchText En(string en)
		{
			if (string.IsNullOrEmpty(en))
			{
				throw new ArgumentException("English text is required / 必须提供英文", "en");
			}
			return new AchText(en);
		}

		internal static AchText Empty()
		{
			return new AchText("");
		}

		public static implicit operator AchText(string en)
		{
			return En(en);
		}

		public AchText ZhCN(string value)
		{
			return Set(2052, value);
		}

		public AchText FrFR(string value)
		{
			return Set(1036, value);
		}

		public AchText DeDE(string value)
		{
			return Set(1031, value);
		}

		public AchText EsES(string value)
		{
			return Set(3082, value);
		}

		public AchText JaJP(string value)
		{
			return Set(1041, value);
		}

		public AchText KoKR(string value)
		{
			return Set(1042, value);
		}

		public AchText Set(int lcid, string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				text[lcid] = value;
			}
			return this;
		}

		public override string ToString()
		{
			int lcid = Lang.Lcid;
			if (text.TryGetValue(lcid, out var value))
			{
				return value;
			}
			int num = lcid & 0x3FF;
			foreach (KeyValuePair<int, string> item in text)
			{
				if ((item.Key & 0x3FF) == num)
				{
					return item.Value;
				}
			}
			return en;
		}

		internal void Write(BinaryWriter w)
		{
			w.Write(en);
			w.Write(text.Count);
			foreach (KeyValuePair<int, string> item in text)
			{
				w.Write(item.Key);
				w.Write(item.Value);
			}
		}

		internal static AchText Read(BinaryReader r)
		{
			AchText achText = new AchText(r.ReadString());
			int num = r.ReadInt32();
			for (int i = 0; i < num; i++)
			{
				int lcid = r.ReadInt32();
				achText.Set(lcid, r.ReadString());
			}
			return achText;
		}
	}
	internal static class GitHubId
	{
		internal static bool TryNormalize(string url, out string id)
		{
			id = null;
			if (string.IsNullOrWhiteSpace(url))
			{
				return false;
			}
			string text = url.Trim();
			int num = text.IndexOfAny(new char[2] { '?', '#' });
			if (num >= 0)
			{
				text = text.Substring(0, num);
			}
			int num2 = text.IndexOf("://", StringComparison.Ordinal);
			if (num2 >= 0)
			{
				text = text.Substring(num2 + 3);
			}
			if (text.StartsWith("www.", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(4);
			}
			string[] array = text.Split(new char[1] { '/' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length < 3 || !array[0].Equals("github.com", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			string text2 = array[1];
			string text3 = array[2];
			if (text3.EndsWith(".git", StringComparison.OrdinalIgnoreCase))
			{
				text3 = text3.Substring(0, text3.Length - 4);
			}
			if (!IsName(text2) || !IsName(text3))
			{
				return false;
			}
			id = ("github.com/" + text2 + "/" + text3).ToLowerInvariant();
			return true;
		}

		internal static bool IsKey(string key)
		{
			return IsName(key);
		}

		private static bool IsName(string s)
		{
			if (string.IsNullOrEmpty(s) || s.Length > 100)
			{
				return false;
			}
			foreach (char c in s)
			{
				if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') && c != '-' && c != '_' && c != '.')
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class IconCache
	{
		private const string DefaultIconResource = "CustomAchievements.res.default_icon.png";

		private static readonly Dictionary<string, Sprite> pngSprites = new Dictionary<string, Sprite>();

		private static Sprite fallback;

		internal static Sprite Get(string reference, byte[] png)
		{
			Sprite val = null;
			try
			{
				val = Resolve(reference, png);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Icon " + reference + " failed: " + ex.Message));
				}
			}
			if (!((Object)(object)val != (Object)null))
			{
				return Fallback();
			}
			return val;
		}

		private static Sprite Resolve(string reference, byte[] png)
		{
			if (string.IsNullOrEmpty(reference))
			{
				return null;
			}
			int num = reference.IndexOf(':');
			if (num < 0)
			{
				return null;
			}
			string text = reference.Substring(0, num);
			string text2 = reference.Substring(num + 1);
			switch (text)
			{
			case "item":
			{
				if (!int.TryParse(text2, out var result2))
				{
					return null;
				}
				ItemProto obj2 = ((ProtoSet<ItemProto>)(object)LDB.items).Select(result2);
				if (obj2 == null)
				{
					return null;
				}
				return obj2.iconSprite;
			}
			case "ach":
			{
				if (!int.TryParse(text2, out var result))
				{
					return null;
				}
				AchievementProto obj = ((ProtoSet<AchievementProto>)(object)LDB.achievements).Select(result);
				if (obj == null)
				{
					return null;
				}
				return obj.iconSprite;
			}
			case "res":
				return Resources.Load<Sprite>(text2);
			case "png":
				return PngSprite(text2, png);
			default:
				return null;
			}
		}

		private static Sprite PngSprite(string hash, byte[] png)
		{
			if (pngSprites.TryGetValue(hash, out var value))
			{
				return value;
			}
			byte[] array = png ?? Store.ReadIcon(hash);
			Sprite val = ((array == null) ? null : ToSprite(array));
			pngSprites[hash] = val;
			return val;
		}

		private static Sprite Fallback()
		{
			if ((Object)(object)fallback == (Object)null)
			{
				using Stream stream = typeof(IconCache).Assembly.GetManifestResourceStream("CustomAchievements.res.default_icon.png");
				using MemoryStream memoryStream = new MemoryStream();
				stream.CopyTo(memoryStream);
				fallback = ToSprite(memoryStream.ToArray());
			}
			return fallback;
		}

		private static Sprite ToSprite(byte[] png)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Expected O, but got Unknown
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//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)
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			try
			{
				if (!ImageConversion.LoadImage(val, png))
				{
					return null;
				}
				Texture2D val2 = new Texture2D(((Texture)val).width, ((Texture)val).height, (TextureFormat)4, true)
				{
					wrapMode = (TextureWrapMode)1,
					filterMode = (FilterMode)2,
					anisoLevel = 4
				};
				val2.SetPixels32(BleedTransparent(val.GetPixels32()));
				val2.Apply(true, true);
				return Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.5f));
			}
			finally
			{
				Object.Destroy((Object)(object)val);
			}
		}

		private static Color32[] BleedTransparent(Color32[] pixels)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			long num = 0L;
			long num2 = 0L;
			long num3 = 0L;
			long num4 = 0L;
			foreach (Color32 val in pixels)
			{
				num += val.r * val.a;
				num2 += val.g * val.a;
				num3 += val.b * val.a;
				num4 += val.a;
			}
			if (num4 == 0L)
			{
				return pixels;
			}
			Color32 val2 = default(Color32);
			((Color32)(ref val2))..ctor((byte)(num / num4), (byte)(num2 / num4), (byte)(num3 / num4), (byte)0);
			for (int j = 0; j < pixels.Length; j++)
			{
				if (pixels[j].a == 0)
				{
					pixels[j] = val2;
				}
			}
			return pixels;
		}
	}
	internal static class PanelView
	{
		private struct Row
		{
			public string FullId;

			public string Name;

			public string Description;

			public string Source;

			public long Target;

			public long Progress;

			public bool Unlocked;

			public long UnlockedUtcTicks;

			public string IconRef;

			public byte[] IconPng;
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UnityAction <>9__31_0;

			public static Comparison<Row> <>9__36_0;

			public static Comparison<Row> <>9__36_1;

			internal void <CreateToggle>b__31_0()
			{
				SetMode(!showMods);
			}

			internal int <Rebuild>b__36_0(Row a, Row b)
			{
				return a.UnlockedUtcTicks.CompareTo(b.UnlockedUtcTicks);
			}

			internal int <Rebuild>b__36_1(Row a, Row b)
			{
				return ((double)b.Progress / (double)b.Target).CompareTo((double)a.Progress / (double)a.Target);
			}
		}

		private const int Columns = 4;

		private const int EntryWidth = 270;

		private const int EntryHeight = 310;

		private const int EntryLeft = 8;

		private const int EntryTop = 41;

		private const int ContainerBottom = 8;

		private const int ContainerGap = 16;

		private const float RebuildInterval = 0.5f;

		private static UIAchievementPanel panel;

		private static RectTransform unlockedBox;

		private static RectTransform progressBox;

		private static RectTransform lockedBox;

		private static Text toggleText;

		private static readonly List<UIAchievementEntry> entries = new List<UIAchievementEntry>();

		private static readonly Dictionary<string, UIAchievementEntry> entriesById = new Dictionary<string, UIAchievementEntry>();

		private static bool showMods;

		private static string pendingReveal;

		private static int builtVersion = -1;

		private static int builtLcid = -1;

		private static int toggleLcid = -1;

		private static float lastRebuildTime = -100f;

		private static float contentHeight;

		private static int total;

		private static int unlockedCount;

		internal static void LateUpdate()
		{
			UIAchievementPanel val = FindPanel();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if ((Object)(object)val != (Object)(object)panel || (Object)(object)unlockedBox == (Object)null || (Object)(object)toggleText == (Object)null)
			{
				Attach(val);
			}
			if (!((Component)panel).gameObject.activeInHierarchy)
			{
				return;
			}
			if (Lang.Lcid != toggleLcid)
			{
				UpdateToggleText();
			}
			if (pendingReveal != null)
			{
				RevealNow();
			}
			else if (showMods && panel.highlightingEntries.Count > 0)
			{
				SetMode(mods: false);
			}
			if (showMods)
			{
				if ((Store.Version != builtVersion || Lang.Lcid != builtLcid) && Time.unscaledTime - lastRebuildTime >= 0.5f)
				{
					Rebuild();
				}
				Enforce();
			}
		}

		internal static void Reveal(string fullId)
		{
			UIGame uiGame = UIRoot.instance.uiGame;
			if (!((ManualBehaviour)uiGame.statWindow).active)
			{
				uiGame.ShutAllFunctionWindow();
				uiGame.ShutPlayerInventory();
				uiGame.OpenProductionWindow();
			}
			uiGame.statWindow.OnTabButtonClick(5);
			pendingReveal = fullId;
		}

		private static void RevealNow()
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			string text = pendingReveal;
			pendingReveal = null;
			if (!showMods)
			{
				SetMode(mods: true);
			}
			else if (Store.Version != builtVersion)
			{
				Rebuild();
			}
			if (text != null && entriesById.TryGetValue(text, out var value) && (Object)(object)value != (Object)null)
			{
				float num = 0f - (((RectTransform)((Transform)value.selfRect).parent).anchoredPosition.y + value.selfRect.anchoredPosition.y) - 30f;
				panel.scrollViewConent.anchoredPosition = new Vector2(panel.scrollViewConent.anchoredPosition.x, Mathf.Max(0f, num));
			}
		}

		private static UIAchievementPanel FindPanel()
		{
			UIRoot instance = UIRoot.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.uiGame == (Object)null || (Object)(object)instance.uiGame.statWindow == (Object)null)
			{
				return null;
			}
			return instance.uiGame.statWindow.achievementPanelUI;
		}

		private static void Attach(UIAchievementPanel target)
		{
			panel = target;
			entries.Clear();
			entriesById.Clear();
			showMods = false;
			builtVersion = -1;
			unlockedBox = CloneContainer(panel.unlockedContainerRect);
			progressBox = CloneContainer(panel.inProgressContainerRect);
			lockedBox = CloneContainer(panel.lockedContainerRect);
			CreateToggle();
			UpdateToggleText();
		}

		private static RectTransform CloneContainer(RectTransform source)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			GameObject val = Object.Instantiate<GameObject>(((Component)source).gameObject, ((Transform)source).parent, false);
			((Object)val).name = ((Object)source).name + "_Mod";
			UIAchievementEntry[] componentsInChildren = val.GetComponentsInChildren<UIAchievementEntry>(true);
			foreach (UIAchievementEntry obj in componentsInChildren)
			{
				((Component)obj).gameObject.SetActive(false);
				Object.Destroy((Object)(object)((Component)obj).gameObject);
			}
			val.SetActive(false);
			return (RectTransform)val.transform;
		}

		private static void CreateToggle()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: 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_0124: 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_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: 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)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Expected O, but got Unknown
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = (RectTransform)((Component)panel).transform;
			GameObject val2 = Object.Instantiate<GameObject>(((Component)panel.enableAchBtn).gameObject, (Transform)(object)val, false);
			((Object)val2).name = "ModAchievementToggle";
			Localizer[] componentsInChildren = val2.GetComponentsInChildren<Localizer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.Destroy((Object)(object)componentsInChildren[i]);
			}
			Button component = val2.GetComponent<Button>();
			component.onClick = new ButtonClickedEvent();
			ButtonClickedEvent onClick = component.onClick;
			object obj = <>c.<>9__31_0;
			if (obj == null)
			{
				UnityAction val3 = delegate
				{
					SetMode(!showMods);
				};
				<>c.<>9__31_0 = val3;
				obj = (object)val3;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj);
			toggleText = val2.GetComponentInChildren<Text>(true);
			RectTransform val4 = (RectTransform)val2.transform;
			val4.anchorMin = new Vector2(0.5f, 0.5f);
			val4.anchorMax = new Vector2(0.5f, 0.5f);
			val4.pivot = new Vector2(0.5f, 0.5f);
			val4.sizeDelta = new Vector2(160f, 32f);
			RectTransform rectTransform = ((Graphic)panel.overallProgressImg).rectTransform;
			Rect rect = rectTransform.rect;
			Vector3 val5 = ((Transform)val).InverseTransformPoint(((Transform)rectTransform).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect)).center)));
			rect = rectTransform.rect;
			float x = ((Transform)val).InverseTransformPoint(((Transform)rectTransform).TransformPoint(new Vector3(((Rect)(ref rect)).xMin, 0f, 0f))).x;
			float num = ProgressLabelLeft(val, val5.y, x) ?? (x - 110f);
			((Transform)val4).localPosition = new Vector3(num - 16f - val4.sizeDelta.x / 2f, val5.y, 0f);
			val2.SetActive(true);
		}

		private static float? ProgressLabelLeft(RectTransform panelRect, float rowY, float barLeft)
		{
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: 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_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: 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_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Expected I4, but got Unknown
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			float? result = null;
			float num = float.MinValue;
			Text[] componentsInChildren = ((Component)panelRect).GetComponentsInChildren<Text>(true);
			foreach (Text val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)(object)toggleText || (Object)(object)val == (Object)(object)panel.topTipText || (Object)(object)val == (Object)(object)panel.overallProgressText || (Object)(object)val == (Object)(object)panel.detailText || string.IsNullOrEmpty(val.text) || (Object)(object)((Component)val).GetComponentInParent<UIAchievementEntry>() != (Object)null || ((Component)val).transform.IsChildOf(panel.enableDetectionTip.transform))
				{
					continue;
				}
				RectTransform rectTransform = ((Graphic)val).rectTransform;
				Rect rect = rectTransform.rect;
				float xMin = ((Rect)(ref rect)).xMin;
				rect = rectTransform.rect;
				Vector3 val2 = ((Transform)panelRect).InverseTransformPoint(((Transform)rectTransform).TransformPoint(new Vector3(xMin, ((Rect)(ref rect)).yMin, 0f)));
				rect = rectTransform.rect;
				float xMax = ((Rect)(ref rect)).xMax;
				rect = rectTransform.rect;
				Vector3 val3 = ((Transform)panelRect).InverseTransformPoint(((Transform)rectTransform).TransformPoint(new Vector3(xMax, ((Rect)(ref rect)).yMax, 0f)));
				if (!(rowY < val2.y - 4f) && !(rowY > val3.y + 4f) && !((val2.x + val3.x) / 2f >= barLeft) && !(val3.x <= num))
				{
					float num2 = val3.x - val2.x;
					rect = rectTransform.rect;
					float num3 = num2 / Mathf.Max(((Rect)(ref rect)).width, 1f);
					float num4 = Mathf.Min(val.preferredWidth * num3, val3.x - val2.x);
					TextAnchor alignment = val.alignment;
					switch (alignment - 1)
					{
					case 1:
					case 4:
					case 7:
						result = val3.x - num4;
						break;
					case 0:
					case 3:
					case 6:
						result = (val2.x + val3.x - num4) / 2f;
						break;
					default:
						result = val2.x;
						break;
					}
					num = val3.x;
				}
			}
			return result;
		}

		private static void UpdateToggleText()
		{
			toggleLcid = Lang.Lcid;
			if ((Object)(object)toggleText != (Object)null)
			{
				toggleText.text = Line.op_Implicit(showMods ? Lang.En("Official Achievements").ZhCN("官方成就") : Lang.En("Mod Achievements").ZhCN("MOD成就"));
			}
		}

		private static void SetMode(bool mods)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			showMods = mods;
			Vector2 anchoredPosition = panel.scrollViewConent.anchoredPosition;
			panel.scrollViewConent.anchoredPosition = new Vector2(anchoredPosition.x, 0f);
			if (mods)
			{
				Rebuild();
				Enforce();
			}
			else
			{
				((Component)unlockedBox).gameObject.SetActive(false);
				((Component)progressBox).gameObject.SetActive(false);
				((Component)lockedBox).gameObject.SetActive(false);
				panel.SetLayout();
				panel.SetOverallProgress();
				AccessTools.Method(typeof(UIAchievementPanel), "_OnOpen", (Type[])null, (Type[])null).Invoke(panel, null);
			}
			UpdateToggleText();
		}

		private static void Enforce()
		{
			//IL_0078: 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_00db: Unknown result type (might be due to invalid IL or missing references)
			((Component)panel.unlockedContainerRect).gameObject.SetActive(false);
			((Component)panel.inProgressContainerRect).gameObject.SetActive(false);
			((Component)panel.lockedContainerRect).gameObject.SetActive(false);
			((Component)panel.topTipText).gameObject.SetActive(false);
			panel.enableDetectionTip.SetActive(false);
			panel.scrollViewConent.sizeDelta = new Vector2(0f, contentHeight);
			float num = ((total == 0) ? 0f : ((float)unlockedCount / (float)total));
			panel.overallProgressImg.fillAmount = num;
			RectTransform progressPointRect = panel.progressPointRect;
			Rect rect = ((Graphic)panel.overallProgressImg).rectTransform.rect;
			progressPointRect.anchoredPosition = new Vector2(((Rect)(ref rect)).width * num, 0f);
			panel.overallProgressText.text = unlockedCount + " / " + total;
		}

		private static void Rebuild()
		{
			lastRebuildTime = Time.unscaledTime;
			builtVersion = Store.Version;
			builtLcid = Lang.Lcid;
			foreach (UIAchievementEntry entry in entries)
			{
				if ((Object)(object)entry != (Object)null)
				{
					((ManualBehaviour)entry)._Destroy();
				}
			}
			entries.Clear();
			entriesById.Clear();
			List<Row> list = new List<Row>();
			List<Row> list2 = new List<Row>();
			List<Row> list3 = new List<Row>();
			foreach (ModRecord item2 in Store.SnapshotMods())
			{
				lock (Store.Sync)
				{
					string text = Line.op_Implicit(Lang.En("From: ").ZhCN("源自:")) + item2.Name;
					if (!item2.Installed)
					{
						text += Line.op_Implicit(Lang.En(" (not installed)").ZhCN("(未安装)"));
					}
					foreach (AchRecord achievement in item2.Achievements)
					{
						Row item = new Row
						{
							FullId = achievement.FullId,
							Name = achievement.Name.ToString(),
							Description = achievement.Description.ToString(),
							Source = text,
							Target = achievement.Target,
							Progress = achievement.Progress,
							Unlocked = achievement.Unlocked,
							UnlockedUtcTicks = achievement.UnlockedUtcTicks,
							IconRef = achievement.IconRef,
							IconPng = achievement.IconPng
						};
						if (item.Unlocked)
						{
							list.Add(item);
						}
						else if (item.Target > 1 && item.Progress > 0)
						{
							list2.Add(item);
						}
						else
						{
							list3.Add(item);
						}
					}
				}
			}
			list.Sort((Row a, Row b) => a.UnlockedUtcTicks.CompareTo(b.UnlockedUtcTicks));
			list2.Sort((Row a, Row b) => ((double)b.Progress / (double)b.Target).CompareTo((double)a.Progress / (double)a.Target));
			total = list.Count + list2.Count + list3.Count;
			unlockedCount = list.Count;
			Fill(unlockedBox, list);
			Fill(progressBox, list2);
			Fill(lockedBox, list3);
			Layout(list.Count, list2.Count, list3.Count);
		}

		private static void Fill(RectTransform box, List<Row> rows)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < rows.Count; i++)
			{
				UIAchievementEntry val = Object.Instantiate<UIAchievementEntry>(panel.entryPrefab, (Transform)(object)box);
				((ManualBehaviour)val)._Create();
				((ManualBehaviour)val)._Init((object)null);
				((ManualBehaviour)val)._Open();
				val.selfRect.anchoredPosition = new Vector2((float)(i % 4 * 270 + 8), (float)(-(i / 4 * 310 + 41)));
				Show(val, rows[i]);
				entries.Add(val);
				entriesById[rows[i].FullId] = val;
			}
		}

		private static void Show(UIAchievementEntry card, Row row)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_0326: Unknown result type (might be due to invalid IL or missing references)
			//IL_032c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0342: Unknown result type (might be due to invalid IL or missing references)
			//IL_0348: Unknown result type (might be due to invalid IL or missing references)
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0364: Unknown result type (might be due to invalid IL or missing references)
			card.icon.sprite = IconCache.Get(row.IconRef, row.IconPng);
			card.titleText.text = row.Name;
			card.descText.supportRichText = true;
			card.descText.text = row.Description + "\n<color=#ffffff80>" + row.Source + "</color>";
			((Component)card.propertyTipRect).gameObject.SetActive(false);
			float num;
			if (row.Unlocked)
			{
				card.iconRect.anchoredPosition = new Vector2(0f, -35f);
				card.titleRect.anchoredPosition = new Vector2(0f, -130f);
				card.descRect.anchoredPosition = new Vector2(0f, -165f);
				((Component)card.progressRect).gameObject.SetActive(false);
				((Component)card.lockedRect).gameObject.SetActive(false);
				((Component)card.unlockedBelt).gameObject.SetActive(true);
				((Component)card.unlockedBeltText).gameObject.SetActive(true);
				num = 1f;
			}
			else if (row.Target > 1 && row.Progress > 0)
			{
				card.iconRect.anchoredPosition = new Vector2(0f, -28f);
				card.titleRect.anchoredPosition = new Vector2(0f, -108f);
				card.descRect.anchoredPosition = new Vector2(0f, -130f);
				((Component)card.progressRect).gameObject.SetActive(true);
				((Component)card.lockedRect).gameObject.SetActive(false);
				((Component)card.unlockedBelt).gameObject.SetActive(false);
				((Component)card.unlockedBeltText).gameObject.SetActive(false);
				card.progress = (double)row.Progress / (double)row.Target;
				card.progressBar.fillAmount = (float)card.progress;
				RectTransform progressPointRect = card.progressPointRect;
				Rect rect = ((Graphic)card.progressBar).rectTransform.rect;
				progressPointRect.anchoredPosition = new Vector2(((Rect)(ref rect)).width * (float)card.progress, 0f);
				card.progressText.text = row.Progress + " / " + row.Target;
				num = -1f;
			}
			else
			{
				card.iconRect.anchoredPosition = new Vector2(0f, -35f);
				card.titleRect.anchoredPosition = new Vector2(0f, -116f);
				card.descRect.anchoredPosition = new Vector2(0f, -140f);
				((Component)card.progressRect).gameObject.SetActive(false);
				((Component)card.lockedRect).gameObject.SetActive(true);
				((Component)card.unlockedBelt).gameObject.SetActive(false);
				((Component)card.unlockedBeltText).gameObject.SetActive(false);
				num = -1f;
			}
			float a = ((num > 0f) ? 1f : (3f / 85f));
			float a2 = ((num > 0f) ? 1f : 0.2509804f);
			((Graphic)card.icon).color = WithAlpha(((Graphic)card.icon).color, a);
			((Graphic)card.titleText).color = WithAlpha(((Graphic)card.titleText).color, a2);
			((Graphic)card.descText).color = WithAlpha(((Graphic)card.descText).color, a2);
		}

		private static Color WithAlpha(Color c, float a)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			return new Color(c.r, c.g, c.b, a);
		}

		private static void Layout(int unlocked, int inProgress, int locked)
		{
			float y = 0f;
			float height = 0f;
			y = Place(unlockedBox, unlocked, y, ref height);
			y = Place(progressBox, inProgress, y, ref height);
			Place(lockedBox, locked, y, ref height);
			contentHeight = height;
		}

		private static float Place(RectTransform box, int count, float y, ref float height)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			if (count == 0)
			{
				box.sizeDelta = Vector2.zero;
				((Component)box).gameObject.SetActive(false);
				return y;
			}
			if (height > 0f)
			{
				y -= 16f;
				height += 16f;
			}
			float num = Mathf.CeilToInt((float)count / 4f) * 310 + 41 + 8;
			box.anchoredPosition = new Vector2(0f, y);
			box.sizeDelta = new Vector2(0f, num);
			((Component)box).gameObject.SetActive(true);
			height += num;
			return y - num;
		}
	}
	[BepInPlugin("com.yummi.dsp.customachievements", "Custom Achievements", "0.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.yummi.dsp.customachievements";

		public const string PluginName = "Custom Achievements";

		public const string PluginVersion = "0.0.0";

		public const string GitHubUrl = "https://github.com/cat-yummi-dspmod/DSP-CustomAchievements";

		internal static ManualLogSource Log;

		private bool panelFailed;

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"Custom Achievements v0.0.0 loaded");
		}

		private void Update()
		{
			if (!Store.Loaded && VFPreload.done)
			{
				Store.Load();
			}
			Store.SaveIfDue();
			ShowUnlocks();
		}

		private void LateUpdate()
		{
			if (panelFailed || !Store.Loaded)
			{
				return;
			}
			try
			{
				PanelView.LateUpdate();
			}
			catch (Exception ex)
			{
				panelFailed = true;
				Log.LogError((object)("Achievement panel integration disabled: " + ex));
				Harness.Report((NoticeLevel)2, "Custom Achievements", "自定义成就无法接入成就面板,成就仍会正常记录。详情见日志。", "Custom achievements could not be added to the achievement panel; progress is still recorded. See the log.", (string)null, (string)null);
			}
		}

		private void OnApplicationQuit()
		{
			Store.Save();
		}

		private void OnDestroy()
		{
			Store.Save();
		}

		private static void ShowUnlocks()
		{
			foreach (AchRecord item in Store.TakeJustUnlocked())
			{
				try
				{
					if (!UnlockPopup.Show(item))
					{
						UIRealtimeTip.Popup(Line.op_Implicit(Lang.En("Achievement unlocked: ").ZhCN("成就解锁:")) + item.Name, true, 0);
					}
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("Unlock popup failed: " + ex.Message));
				}
			}
		}
	}
	internal sealed class ModRecord
	{
		internal string Id;

		internal AchText Name;

		internal bool Installed;

		internal readonly List<AchRecord> Achievements = new List<AchRecord>();

		internal AchRecord Find(string key)
		{
			foreach (AchRecord achievement in Achievements)
			{
				if (achievement.Key == key)
				{
					return achievement;
				}
			}
			return null;
		}
	}
	internal sealed class AchRecord
	{
		internal ModRecord Mod;

		internal string Key;

		internal AchText Name;

		internal AchText Description;

		internal long Target = 1L;

		internal long Progress;

		internal bool Unlocked;

		internal long UnlockedUtcTicks;

		internal bool Registered;

		internal bool AllowSandbox;

		internal string IconRef;

		internal byte[] IconPng;

		internal string FullId => Mod.Id + "#" + Key;
	}
	internal static class Store
	{
		private const int Magic = 1751335235;

		private const int FormatVersion = 2;

		private const double SaveDelaySeconds = 2.0;

		internal static readonly object Sync = new object();

		private static readonly List<ModRecord> mods = new List<ModRecord>();

		private static readonly Queue<AchRecord> justUnlocked = new Queue<AchRecord>();

		private static string path;

		private static bool dirty;

		private static DateTime lastChangeUtc;

		internal static bool Loaded { get; private set; }

		internal static int Version { get; private set; }

		private static string IconDir => Path.Combine(Path.GetDirectoryName(path), "icons");

		internal static ModRecord GetOrAddMod(string id, AchText name)
		{
			lock (Sync)
			{
				ModRecord modRecord = FindMod(id);
				if (modRecord == null)
				{
					modRecord = new ModRecord
					{
						Id = id
					};
					mods.Add(modRecord);
				}
				modRecord.Name = name;
				modRecord.Installed = true;
				MarkDirty();
				return modRecord;
			}
		}

		internal static AchRecord GetOrAddAchievement(ModRecord mod, string key, AchText name, AchText description, long target, AchIcon icon, bool allowSandbox)
		{
			lock (Sync)
			{
				AchRecord achRecord = mod.Find(key);
				if (achRecord == null)
				{
					achRecord = new AchRecord
					{
						Mod = mod,
						Key = key
					};
					mod.Achievements.Add(achRecord);
				}
				achRecord.Name = name;
				achRecord.Description = description;
				achRecord.Target = target;
				achRecord.AllowSandbox = allowSandbox;
				achRecord.IconRef = icon?.Ref;
				achRecord.IconPng = icon?.Png;
				achRecord.Registered = true;
				MarkDirty();
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("Registered achievement " + achRecord.FullId));
				}
				return achRecord;
			}
		}

		internal static bool CanProgress(AchRecord rec)
		{
			if (!Loaded || rec.Unlocked || !rec.Registered)
			{
				return false;
			}
			GameData data = GameMain.data;
			if (!rec.AllowSandbox && data != null)
			{
				return !data.gameDesc.isSandboxMode;
			}
			return true;
		}

		internal static void SetProgress(AchRecord rec, long value)
		{
			lock (Sync)
			{
				if (CanProgress(rec))
				{
					if (value < 0)
					{
						value = 0L;
					}
					if (value >= rec.Target)
					{
						UnlockLocked(rec);
					}
					else if (value != rec.Progress)
					{
						rec.Progress = value;
						MarkDirty();
					}
				}
			}
		}

		internal static void AddProgress(AchRecord rec, long delta)
		{
			lock (Sync)
			{
				SetProgress(rec, rec.Progress + delta);
			}
		}

		internal static void Unlock(AchRecord rec)
		{
			lock (Sync)
			{
				if (CanProgress(rec))
				{
					UnlockLocked(rec);
				}
			}
		}

		private static void UnlockLocked(AchRecord rec)
		{
			rec.Progress = rec.Target;
			rec.Unlocked = true;
			rec.UnlockedUtcTicks = DateTime.UtcNow.Ticks;
			justUnlocked.Enqueue(rec);
			MarkDirty();
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("Achievement unlocked: " + rec.FullId));
			}
		}

		internal static List<AchRecord> TakeJustUnlocked()
		{
			lock (Sync)
			{
				List<AchRecord> result = new List<AchRecord>(justUnlocked);
				justUnlocked.Clear();
				return result;
			}
		}

		internal static List<ModRecord> SnapshotMods()
		{
			lock (Sync)
			{
				List<ModRecord> list = new List<ModRecord>(mods);
				list.Sort((ModRecord a, ModRecord b) => (a.Installed != b.Installed) ? ((!a.Installed) ? 1 : (-1)) : string.Compare(a.Name.ToString(), b.Name.ToString(), StringComparison.CurrentCultureIgnoreCase));
				return list;
			}
		}

		private static ModRecord FindMod(string id)
		{
			foreach (ModRecord mod in mods)
			{
				if (mod.Id == id)
				{
					return mod;
				}
			}
			return null;
		}

		private static void MarkDirty()
		{
			dirty = true;
			lastChangeUtc = DateTime.UtcNow;
			Version++;
		}

		internal static byte[] ReadIcon(string hash)
		{
			if (!Loaded || !IsHash(hash))
			{
				return null;
			}
			string text = Path.Combine(IconDir, hash + ".png");
			if (!File.Exists(text))
			{
				return null;
			}
			return File.ReadAllBytes(text);
		}

		private static bool IsHash(string s)
		{
			if (s == null || s.Length != 40)
			{
				return false;
			}
			foreach (char c in s)
			{
				if ((c < '0' || c > '9') && (c < 'a' || c > 'f'))
				{
					return false;
				}
			}
			return true;
		}

		private static void WriteIcons()
		{
			foreach (ModRecord mod in mods)
			{
				foreach (AchRecord achievement in mod.Achievements)
				{
					if (achievement.IconPng == null || achievement.IconRef == null || !achievement.IconRef.StartsWith("png:", StringComparison.Ordinal))
					{
						continue;
					}
					string text = achievement.IconRef.Substring(4);
					if (IsHash(text))
					{
						string text2 = Path.Combine(IconDir, text + ".png");
						if (!File.Exists(text2))
						{
							Directory.CreateDirectory(IconDir);
							File.WriteAllBytes(text2, achievement.IconPng);
						}
					}
				}
			}
		}

		private static string ResolvePath()
		{
			string directoryName = Path.GetDirectoryName(GameConfig.achievementFolder.TrimEnd('/', '\\'));
			string text = "local";
			try
			{
				if (!((AccountData)(ref AccountData.me)).isNull)
				{
					text = ((object)Unsafe.As<ESalePlatform, ESalePlatform>(ref AccountData.me.platform)/*cast due to .constrained prefix*/).ToString() + "_" + AccountData.me.userId;
				}
			}
			catch
			{
			}
			return Path.Combine(Path.Combine(directoryName, "CustomAchievements"), text + ".dat");
		}

		internal static void Load()
		{
			lock (Sync)
			{
				if (Loaded)
				{
					return;
				}
				path = ResolvePath();
				if (File.Exists(path))
				{
					try
					{
						using BinaryReader r = new BinaryReader(File.OpenRead(path));
						Read(r);
					}
					catch (Exception ex)
					{
						string text = path + ".bad-" + DateTime.Now.ToString("yyyyMMdd-HHmmss");
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogError((object)("Achievement file is unreadable, kept a copy at " + text + "\n" + ex));
						}
						try
						{
							File.Copy(path, text, overwrite: true);
						}
						catch (Exception ex2)
						{
							ManualLogSource log2 = Plugin.Log;
							if (log2 != null)
							{
								log2.LogError((object)("Backup failed: " + ex2.Message));
							}
						}
					}
				}
				Loaded = true;
				dirty = true;
				ManualLogSource log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogInfo((object)("Achievement file: " + path));
				}
				foreach (ModRecord mod in mods)
				{
					int num = 0;
					foreach (AchRecord achievement in mod.Achievements)
					{
						if (achievement.Unlocked)
						{
							num++;
						}
					}
					string text2 = (mod.Installed ? "installed" : "not installed, frozen");
					ManualLogSource log4 = Plugin.Log;
					if (log4 != null)
					{
						log4.LogInfo((object)$"  {mod.Id} ({mod.Name.English}, {text2}): {num}/{mod.Achievements.Count} unlocked");
					}
				}
			}
		}

		internal static void SaveIfDue()
		{
			if (dirty && Loaded && (DateTime.UtcNow - lastChangeUtc).TotalSeconds >= 2.0)
			{
				Save();
			}
		}

		internal static void Save()
		{
			lock (Sync)
			{
				if (!Loaded || !dirty)
				{
					return;
				}
				try
				{
					Directory.CreateDirectory(Path.GetDirectoryName(path));
					WriteIcons();
					string sourceFileName = path + ".tmp";
					using (BinaryWriter w = new BinaryWriter(File.Create(sourceFileName)))
					{
						Write(w);
					}
					if (File.Exists(path))
					{
						File.Copy(path, path + ".bak", overwrite: true);
						File.Delete(path);
					}
					File.Move(sourceFileName, path);
					dirty = false;
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogDebug((object)"Achievement file saved");
					}
				}
				catch (Exception ex)
				{
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogError((object)("Saving achievements failed: " + ex));
					}
					lastChangeUtc = DateTime.UtcNow;
				}
			}
		}

		private static void Write(BinaryWriter w)
		{
			w.Write(1751335235);
			w.Write(2);
			w.Write(mods.Count);
			foreach (ModRecord mod in mods)
			{
				w.Write(mod.Id);
				mod.Name.Write(w);
				w.Write(mod.Achievements.Count);
				foreach (AchRecord achievement in mod.Achievements)
				{
					w.Write(achievement.Key);
					achievement.Name.Write(w);
					achievement.Description.Write(w);
					w.Write(achievement.Target);
					w.Write(achievement.Progress);
					w.Write(achievement.Unlocked);
					w.Write(achievement.UnlockedUtcTicks);
					w.Write(achievement.IconRef ?? "");
				}
			}
		}

		private static void Read(BinaryReader r)
		{
			if (r.ReadInt32() != 1751335235)
			{
				throw new InvalidDataException("not a custom achievement file");
			}
			int num = r.ReadInt32();
			if (num > 2)
			{
				throw new InvalidDataException("file version " + num + " is newer than this mod");
			}
			int num2 = r.ReadInt32();
			for (int i = 0; i < num2; i++)
			{
				string id = r.ReadString();
				AchText name = AchText.Read(r);
				ModRecord modRecord = FindMod(id);
				if (modRecord == null)
				{
					modRecord = new ModRecord
					{
						Id = id,
						Name = name,
						Installed = false
					};
					mods.Add(modRecord);
				}
				int num3 = r.ReadInt32();
				for (int j = 0; j < num3; j++)
				{
					string key = r.ReadString();
					AchText name2 = AchText.Read(r);
					AchText description = AchText.Read(r);
					long target = r.ReadInt64();
					long progress = r.ReadInt64();
					bool unlocked = r.ReadBoolean();
					long unlockedUtcTicks = r.ReadInt64();
					string iconRef = ((num >= 2) ? r.ReadString() : "");
					AchRecord achRecord = modRecord.Find(key);
					if (achRecord == null)
					{
						achRecord = new AchRecord
						{
							Mod = modRecord,
							Key = key,
							Name = name2,
							Description = description,
							Target = target,
							IconRef = iconRef
						};
						modRecord.Achievements.Add(achRecord);
					}
					achRecord.Progress = progress;
					achRecord.Unlocked = unlocked;
					achRecord.UnlockedUtcTicks = unlockedUtcTicks;
					if (!achRecord.Unlocked && achRecord.Progress >= achRecord.Target)
					{
						if (achRecord.Registered)
						{
							UnlockLocked(achRecord);
						}
						else
						{
							achRecord.Progress = achRecord.Target;
						}
					}
				}
			}
		}
	}
	internal static class UnlockPopup
	{
		private static readonly FieldInfo cardsField = AccessTools.Field(typeof(UIVariousPopupGroup), "cards");

		private static readonly MethodInfo refreshLayout = AccessTools.Method(typeof(UIVariousPopupGroup), "RefreshCardsLayout", (Type[])null, (Type[])null);

		private static readonly FieldInfo iconField = AccessTools.Field(typeof(AchievementProto), "_iconSprite");

		private static readonly MethodInfo dataSetter = AccessTools.PropertySetter(typeof(ManualBehaviour), "data");

		internal static bool Show(AchRecord rec)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			UIRoot instance = UIRoot.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.uiGame == (Object)null)
			{
				return false;
			}
			UIVariousPopupGroup variousPopupGroup = instance.uiGame.variousPopupGroup;
			if ((Object)(object)variousPopupGroup == (Object)null || !((ManualBehaviour)variousPopupGroup).inited || !(cardsField?.GetValue(variousPopupGroup) is List<UIVariousPopupCard> list) || refreshLayout == null || iconField == null || dataSetter == null)
			{
				return false;
			}
			AchievementProto val = new AchievementProto
			{
				name = rec.Name.ToString(),
				description = rec.Description.ToString()
			};
			iconField.SetValue(val, IconCache.Get(rec.IconRef, rec.IconPng));
			UIVariousPopupCard val2 = Object.Instantiate<UIVariousPopupCard>(variousPopupGroup.achievementCardPrefab, ((Transform)variousPopupGroup.achievementCardPrefab.rectTrans).parent);
			((ManualBehaviour)val2)._Create();
			((ManualBehaviour)val2)._Init((object)val);
			((ManualBehaviour)val2)._Open();
			dataSetter.Invoke(val2, new object[1]);
			((Component)val2).gameObject.AddComponent<PopupClick>().FullId = rec.FullId;
			list.Insert(0, val2);
			refreshLayout.Invoke(variousPopupGroup, null);
			((Component)variousPopupGroup).transform.SetAsLastSibling();
			variousPopupGroup.scrollBox.verticalNormalizedPosition = 1f;
			return true;
		}
	}
	internal sealed class PopupClick : MonoBehaviour, IPointerUpHandler, IEventSystemHandler
	{
		internal string FullId;

		public void OnPointerUp(PointerEventData eventData)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			if (eventData != null && !eventData.dragging && (int)eventData.button == 0)
			{
				PanelView.Reveal(FullId);
			}
		}
	}
}