Decompiled source of NPC Cards v1.1.2

NpcCards.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading.Tasks;
using DooDesch;
using DooDesch.Localization;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NpcCards;
using NpcCards.Host;
using NpcCards.Menu;
using Personnel;
using Personnel.Cards;
using SideHustle;
using Sideload.Api;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "NPC Cards", "1.1.2", "DooDesch", "https://github.com/DooDesch-Mods/ScheduleOne-NpcCards")]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: MelonOptionalDependencies(new string[] { "SideHustle" })]
[assembly: AssemblyMetadata("SourceNotice", "NpcCards by DooDesch, MIT License. The source code is public at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards. Please read, change and reuse the source code, not decompiled code from this file. Text and data mining reserved under Art. 4(3) Directive (EU) 2019/790 and § 44b(3) UrhG.")]
[assembly: AssemblyMetadata("License", "https://github.com/DooDesch-Mods/ScheduleOne-NpcCards/blob/main/LICENSE.md")]
[assembly: AssemblyMetadata("SourceCode", "https://github.com/DooDesch-Mods/ScheduleOne-NpcCards")]
[assembly: AssemblyMetadata("tdm-reservation", "1")]
[assembly: AssemblyMetadata("tdm-policy", "https://github.com/DooDesch-Mods/ScheduleOne-NpcCards/blob/main/LICENSE.md")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("DooDesch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © DooDesch. MIT License. Source code: https://github.com/DooDesch-Mods/ScheduleOne-NpcCards")]
[assembly: AssemblyDescription("Add new NPCs in one click in Schedule I.")]
[assembly: AssemblyFileVersion("1.1.2.0")]
[assembly: AssemblyInformationalVersion("1.1.2+43eef085decb29889f4337764e15a578dea0e129")]
[assembly: AssemblyProduct("NPC Cards")]
[assembly: AssemblyTitle("NPC Cards")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially created with the assistance of Generative AI.")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic/OpenAI")]
[assembly: AssemblyVersion("1.1.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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]
	[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 DooDesch
{
	[AttributeUsage(AttributeTargets.All, Inherited = false)]
	internal sealed class SourceNoticeAttribute : Attribute
	{
		public string Notice { get; }

		public SourceNoticeAttribute(string notice)
		{
			Notice = notice;
		}
	}
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	internal static class SourceNotice
	{
		internal const string Text = "NpcCards by DooDesch, MIT License. The source code is public at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards. Please read, change and reuse the source code, not decompiled code from this file. Text and data mining reserved under Art. 4(3) Directive (EU) 2019/790 and § 44b(3) UrhG.";

		internal const string Short = "NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.";
	}
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	internal static class ModVersion
	{
		internal const string Current = "1.1.2";
	}
}
namespace DooDesch.Localization
{
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	internal static class L10n
	{
		private static readonly Dictionary<string, Dictionary<string, string>> _tables = new Dictionary<string, Dictionary<string, string>>();

		private static Dictionary<string, string> _active;

		private static string _lang;

		internal static string Language => _lang ?? (_lang = Detect());

		private static string ModName => typeof(L10n).Assembly.GetName().Name;

		internal static void Register(string lang, Dictionary<string, string> table)
		{
			_tables[lang] = table;
			_active = null;
		}

		internal static string T(string en)
		{
			if (_active == null)
			{
				_active = BuildActiveTable();
			}
			if (!_active.TryGetValue(en, out var value))
			{
				return en;
			}
			return value;
		}

		internal static string T(string en, params object[] args)
		{
			return string.Format(T(en), args);
		}

		private static Dictionary<string, string> BuildActiveTable()
		{
			Dictionary<string, string> value;
			Dictionary<string, string> dictionary = (_tables.TryGetValue(Language, out value) ? new Dictionary<string, string>(value) : new Dictionary<string, string>());
			try
			{
				ExportTemplate();
				Overlay(dictionary, Path.Combine(PackDir(), Language + ".json"));
				Overlay(dictionary, Path.Combine(UserDir(), Language + ".json"));
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[L10n] user translations failed: " + ex.Message);
			}
			return dictionary;
		}

		private static void Overlay(Dictionary<string, string> table, string file)
		{
			if (!File.Exists(file))
			{
				return;
			}
			Dictionary<string, string> dictionary = ParseJsonObject(File.ReadAllText(file));
			if (dictionary == null)
			{
				MelonLogger.Warning("[L10n] ignoring invalid translation file (flat JSON object of \"english\": \"translated\" strings expected): " + file);
				return;
			}
			foreach (KeyValuePair<string, string> item in dictionary)
			{
				table[item.Key] = item.Value;
			}
		}

		private static string PackDir()
		{
			return Path.Combine(MelonEnvironment.ModsDirectory, "Localization", ModName);
		}

		private static string UserDir()
		{
			return Path.Combine(MelonEnvironment.UserDataDirectory, "DooDesch", "Localization", ModName);
		}

		private static void ExportTemplate()
		{
			if (_tables.Count == 0)
			{
				return;
			}
			SortedSet<string> sortedSet = new SortedSet<string>(StringComparer.Ordinal);
			foreach (Dictionary<string, string> value in _tables.Values)
			{
				foreach (string key in value.Keys)
				{
					sortedSet.Add(key);
				}
			}
			if (sortedSet.Count == 0)
			{
				return;
			}
			Directory.CreateDirectory(UserDir());
			WriteTemplate("en", sortedSet, null);
			foreach (string key2 in _tables.Keys)
			{
				if (key2 != "en")
				{
					WriteTemplate(key2, sortedSet, _tables[key2]);
				}
			}
		}

		private static void WriteTemplate(string lang, SortedSet<string> keys, Dictionary<string, string> table)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("{\n  ").Append(Quote("_readme")).Append(": ")
				.Append(Quote("This file is regenerated on every game start - do not edit it. To change or add a translation, copy it to <language code>.json (for example " + lang + ".json), keep only the lines you want to change, and translate the VALUES only - the keys are the mod's English source strings and must stay unchanged. Keep placeholders like {0}. Set Language in MelonPreferences.cfg under [DooDesch] to force a language (auto = OS language). To share your translation as an installable mod, see https://github.com/DooDesch-Mods/ScheduleOne-L10n/wiki"))
				.Append(",\n");
			foreach (string key in keys)
			{
				string value;
				string s = ((table != null && table.TryGetValue(key, out value)) ? value : key);
				stringBuilder.Append("  ").Append(Quote(key)).Append(": ")
					.Append(Quote(s))
					.Append(",\n");
			}
			stringBuilder.Length -= 2;
			stringBuilder.Append("\n}\n");
			File.WriteAllText(Path.Combine(UserDir(), "_template." + lang + ".json"), stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}

		private static string Detect()
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Invalid comparison between Unknown and I4
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Invalid comparison between Unknown and I4
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Invalid comparison between Unknown and I4
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Invalid comparison between Unknown and I4
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Expected I4, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Invalid comparison between Unknown and I4
			//IL_00dc: 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_00e1: Invalid comparison between Unknown and I4
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Invalid comparison between Unknown and I4
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Invalid comparison between Unknown and I4
			string text = "auto";
			try
			{
				MelonPreferences_Category val = MelonPreferences.CreateCategory("DooDesch", "DooDesch Mods");
				text = ((val.GetEntry<string>("Language") ?? val.CreateEntry<string>("Language", "auto", "Language", "Language for all DooDesch mods: auto (= OS language), en, de, ...", false, false, (ValueValidator)null, (string)null)).Value ?? "auto").Trim().ToLowerInvariant();
			}
			catch
			{
			}
			if (text.Length > 0 && text != "auto")
			{
				return text;
			}
			SystemLanguage systemLanguage = Application.systemLanguage;
			if ((int)systemLanguage <= 15)
			{
				if ((int)systemLanguage == 6)
				{
					goto IL_0121;
				}
				if ((int)systemLanguage == 14)
				{
					return "fr";
				}
				if ((int)systemLanguage == 15)
				{
					return "de";
				}
			}
			else if ((int)systemLanguage <= 34)
			{
				switch (systemLanguage - 21)
				{
				default:
					if ((int)systemLanguage != 34)
					{
						break;
					}
					return "es";
				case 0:
					return "it";
				case 7:
					return "pt";
				case 6:
					return "pl";
				case 9:
					return "ru";
				case 1:
					return "ja";
				case 2:
					return "ko";
				case 3:
				case 4:
				case 5:
				case 8:
					break;
				}
			}
			else
			{
				if ((int)systemLanguage == 37)
				{
					return "tr";
				}
				if (systemLanguage - 40 <= 1)
				{
					goto IL_0121;
				}
			}
			return "en";
			IL_0121:
			return "zh";
		}

		private static Dictionary<string, string> ParseJsonObject(string json)
		{
			if (json == null)
			{
				return null;
			}
			int i = 0;
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			SkipWs(json, ref i);
			if (i >= json.Length || json[i] != '{')
			{
				return null;
			}
			i++;
			SkipWs(json, ref i);
			if (i < json.Length && json[i] == '}')
			{
				return dictionary;
			}
			while (true)
			{
				string text = ParseString(json, ref i);
				if (text == null)
				{
					return null;
				}
				SkipWs(json, ref i);
				if (i >= json.Length || json[i] != ':')
				{
					return null;
				}
				i++;
				SkipWs(json, ref i);
				string text2 = ParseString(json, ref i);
				if (text2 == null)
				{
					return null;
				}
				dictionary[text] = text2;
				SkipWs(json, ref i);
				if (i >= json.Length)
				{
					return null;
				}
				if (json[i] != ',')
				{
					break;
				}
				i++;
				SkipWs(json, ref i);
				if (i < json.Length && json[i] == '}')
				{
					return dictionary;
				}
			}
			if (json[i] != '}')
			{
				return null;
			}
			return dictionary;
		}

		private static void SkipWs(string s, ref int i)
		{
			while (i < s.Length && (char.IsWhiteSpace(s[i]) || s[i] == '\ufeff'))
			{
				i++;
			}
		}

		private static string ParseString(string s, ref int i)
		{
			SkipWs(s, ref i);
			if (i >= s.Length || s[i] != '"')
			{
				return null;
			}
			i++;
			StringBuilder stringBuilder = new StringBuilder();
			while (i < s.Length)
			{
				char c = s[i++];
				switch (c)
				{
				case '"':
					return stringBuilder.ToString();
				default:
					stringBuilder.Append(c);
					break;
				case '\\':
					if (i >= s.Length)
					{
						return null;
					}
					switch (s[i++])
					{
					case '"':
						stringBuilder.Append('"');
						break;
					case '\\':
						stringBuilder.Append('\\');
						break;
					case '/':
						stringBuilder.Append('/');
						break;
					case 'b':
						stringBuilder.Append('\b');
						break;
					case 'f':
						stringBuilder.Append('\f');
						break;
					case 'n':
						stringBuilder.Append('\n');
						break;
					case 'r':
						stringBuilder.Append('\r');
						break;
					case 't':
						stringBuilder.Append('\t');
						break;
					case 'u':
						if (i + 4 > s.Length)
						{
							return null;
						}
						try
						{
							stringBuilder.Append((char)Convert.ToInt32(s.Substring(i, 4), 16));
						}
						catch
						{
							return null;
						}
						i += 4;
						break;
					default:
						return null;
					}
					break;
				}
			}
			return null;
		}

		private static string Quote(string s)
		{
			StringBuilder stringBuilder = new StringBuilder(s.Length + 2);
			stringBuilder.Append('"');
			foreach (char c in s)
			{
				switch (c)
				{
				case '"':
					stringBuilder.Append("\\\"");
					continue;
				case '\\':
					stringBuilder.Append("\\\\");
					continue;
				case '\n':
					stringBuilder.Append("\\n");
					continue;
				case '\r':
					stringBuilder.Append("\\r");
					continue;
				case '\t':
					stringBuilder.Append("\\t");
					continue;
				}
				if (c < ' ')
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("x4"));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			stringBuilder.Append('"');
			return stringBuilder.ToString();
		}
	}
}
namespace Sideload.Api
{
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	public static class Apps
	{
		private static bool _bound;

		private static int _probeAttempts;

		private static readonly List<Action> _pending = new List<Action>();

		private static Action<string, string, string, string, Assembly> _registerApp;

		private static Action<string, string, Func<string, string, string>> _handle;

		private static Action<string, string, string> _emit;

		private static Action<string, string> _allowHost;

		private static Action<string, string> _declareOrientations;

		private static Action<string, int> _setBadge;

		private static Action<string, string, string> _notify;

		private static Action<string, string, string, float> _notifyFor;

		private static Func<string, bool> _isOnScreen;

		private static Action<string, string, byte[]> _setImage;

		private static Action<string, bool> _setIconHidden;

		private static Action<string, bool> _setAppOpen;

		private static Func<string, bool> _isAppOpen;

		private static Func<bool, bool> _setPhoneRaised;

		private static Func<bool> _isPhoneRaised;

		private static Action<string, string, Func<string, string, bool>> _claimKeys;

		private static Func<object, string, string, Assembly, float, bool> _mountSurface;

		private static Action<string> _unmountSurface;

		private static Func<string, bool> _isSurfaceMounted;

		private static Action<string> _reloadApp;

		private static Action<string> _unregisterApp;

		private static Action<string, string> _allowGameWrites;

		public static bool Available
		{
			get
			{
				EnsureBound();
				return _bound;
			}
		}

		internal static bool HasLiveEditing
		{
			get
			{
				EnsureBound();
				if (_reloadApp != null)
				{
					return _unregisterApp != null;
				}
				return false;
			}
		}

		internal static bool HasSurfaces
		{
			get
			{
				EnsureBound();
				return _mountSurface != null;
			}
		}

		internal static bool HasKeys
		{
			get
			{
				EnsureBound();
				return _claimKeys != null;
			}
		}

		internal static bool HasPhone
		{
			get
			{
				EnsureBound();
				return _setPhoneRaised != null;
			}
		}

		internal static bool HasOpen
		{
			get
			{
				EnsureBound();
				if (_setAppOpen != null)
				{
					return _setIconHidden != null;
				}
				return false;
			}
		}

		public static AppHandle Register(string id, string bundlePrefix, string title = null, string iconLabel = null, Assembly hostAssembly = null)
		{
			AppHandle result = new AppHandle(id);
			if (string.IsNullOrEmpty(id))
			{
				return result;
			}
			Assembly asm = hostAssembly ?? Assembly.GetCallingAssembly();
			string t = title;
			string il = iconLabel;
			string prefix = bundlePrefix;
			EnsureBound();
			if (_registerApp != null)
			{
				_registerApp(id, t, il, prefix, asm);
			}
			else
			{
				_pending.Add(delegate
				{
					_registerApp?.Invoke(id, t, il, prefix, asm);
				});
			}
			return result;
		}

		internal static void WhenBound(Action work)
		{
			if (work != null)
			{
				EnsureBound();
				if (_bound)
				{
					work();
				}
				else
				{
					_pending.Add(work);
				}
			}
		}

		internal static void HandleCall(string appId, string name, Func<string, string> handler)
		{
			if (_handle != null && handler != null)
			{
				_handle(appId, name, (string app, string argument) => handler(argument));
			}
		}

		internal static void EmitEvent(string appId, string name, string payload)
		{
			_emit?.Invoke(appId, name, payload);
		}

		internal static void AllowNetHost(string appId, string host)
		{
			_allowHost?.Invoke(appId, host);
		}

		internal static void Orient(string appId, string orientations)
		{
			_declareOrientations?.Invoke(appId, orientations);
		}

		internal static void Badge(string appId, int count)
		{
			_setBadge?.Invoke(appId, count);
		}

		internal static void Notify(string appId, string title, string subtitle, float seconds)
		{
			if (_notifyFor != null)
			{
				_notifyFor(appId, title, subtitle, seconds);
			}
			else if (_notify != null)
			{
				_notify(appId, title, subtitle);
			}
		}

		internal static bool OnScreen(string appId)
		{
			if (_isOnScreen != null)
			{
				return _isOnScreen(appId);
			}
			return false;
		}

		internal static void SetImage(string appId, string name, byte[] png)
		{
			_setImage?.Invoke(appId, name, png);
		}

		internal static void HideIcon(string appId, bool hidden)
		{
			_setIconHidden?.Invoke(appId, hidden);
		}

		internal static void OpenApp(string appId, bool open)
		{
			_setAppOpen?.Invoke(appId, open);
		}

		internal static bool AppIsOpen(string appId)
		{
			if (_isAppOpen != null)
			{
				return _isAppOpen(appId);
			}
			return false;
		}

		internal static bool RaisePhone(bool raised)
		{
			if (_setPhoneRaised != null)
			{
				return _setPhoneRaised(raised);
			}
			return false;
		}

		internal static bool PhoneIsRaised()
		{
			if (_isPhoneRaised != null)
			{
				return _isPhoneRaised();
			}
			return false;
		}

		internal static void ClaimKeys(string appId, string keys, Func<string, string, bool> handler)
		{
			_claimKeys?.Invoke(appId, keys, handler);
		}

		internal static bool MountSurface(object rect, string id, string prefix, Assembly host, float shortSide)
		{
			if (_mountSurface != null)
			{
				return _mountSurface(rect, id, prefix, host, shortSide);
			}
			return false;
		}

		internal static void UnmountSurface(string id)
		{
			_unmountSurface?.Invoke(id);
		}

		internal static bool SurfaceIsMounted(string id)
		{
			if (_isSurfaceMounted != null)
			{
				return _isSurfaceMounted(id);
			}
			return false;
		}

		internal static void ReloadApp(string appId)
		{
			_reloadApp?.Invoke(appId);
		}

		internal static void UnregisterApp(string appId)
		{
			_unregisterApp?.Invoke(appId);
		}

		internal static void AllowGameWrites(string appId, string scope)
		{
			_allowGameWrites?.Invoke(appId, scope);
		}

		private static void EnsureBound()
		{
			if (_bound)
			{
				return;
			}
			try
			{
				Type type = FindBridge(_probeAttempts++ % 30 == 0);
				if (type == null || (type.GetField("AbiVersion", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) is int num && num < 1))
				{
					return;
				}
				_registerApp = Get<Action<string, string, string, string, Assembly>>(type, "RegisterApp");
				if (_registerApp == null)
				{
					return;
				}
				_handle = Get<Action<string, string, Func<string, string, string>>>(type, "Handle");
				_emit = Get<Action<string, string, string>>(type, "Emit");
				_allowHost = Get<Action<string, string>>(type, "AllowHost");
				_declareOrientations = Get<Action<string, string>>(type, "DeclareOrientations");
				_setBadge = Get<Action<string, int>>(type, "SetBadge");
				_notify = Get<Action<string, string, string>>(type, "Notify");
				_notifyFor = Get<Action<string, string, string, float>>(type, "NotifyFor");
				_isOnScreen = Get<Func<string, bool>>(type, "IsAppOnScreen");
				_setImage = Get<Action<string, string, byte[]>>(type, "SetImage");
				_setIconHidden = Get<Action<string, bool>>(type, "SetIconHidden");
				_setAppOpen = Get<Action<string, bool>>(type, "SetAppOpen");
				_isAppOpen = Get<Func<string, bool>>(type, "IsAppOpen");
				_setPhoneRaised = Get<Func<bool, bool>>(type, "SetPhoneRaised");
				_isPhoneRaised = Get<Func<bool>>(type, "IsPhoneRaised");
				_claimKeys = Get<Action<string, string, Func<string, string, bool>>>(type, "ClaimKeys");
				_mountSurface = Get<Func<object, string, string, Assembly, float, bool>>(type, "MountSurface");
				_unmountSurface = Get<Action<string>>(type, "UnmountSurface");
				_isSurfaceMounted = Get<Func<string, bool>>(type, "IsSurfaceMounted");
				_reloadApp = Get<Action<string>>(type, "ReloadApp");
				_unregisterApp = Get<Action<string>>(type, "UnregisterApp");
				_allowGameWrites = Get<Action<string, string>>(type, "AllowGameWrites");
				_bound = true;
				for (int i = 0; i < _pending.Count; i++)
				{
					try
					{
						_pending[i]();
					}
					catch
					{
					}
				}
				_pending.Clear();
			}
			catch
			{
			}
		}

		private static T Get<T>(Type t, string field) where T : class
		{
			return t.GetField(field, BindingFlags.Static | BindingFlags.Public)?.GetValue(null) as T;
		}

		private static Type FindBridge(bool scan)
		{
			Type type = Type.GetType("Sideload.Bridge.SideloadBridge, Sideload", throwOnError: false);
			if (type != null || !scan)
			{
				return type;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					type = assembly.GetType("Sideload.Bridge.SideloadBridge", throwOnError: false);
					if (type != null)
					{
						return type;
					}
				}
				catch
				{
				}
			}
			return null;
		}
	}
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	public sealed class AppHandle
	{
		private readonly string _id;

		public string Id => _id;

		public bool IsOnScreen
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.OnScreen(_id);
				}
				return false;
			}
		}

		public bool IsOpen
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.AppIsOpen(_id);
				}
				return false;
			}
		}

		public static bool CanOpenProgrammatically
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.HasOpen;
				}
				return false;
			}
		}

		public static bool CanClaimKeys
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.HasKeys;
				}
				return false;
			}
		}

		public static bool CanEditLive
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.HasLiveEditing;
				}
				return false;
			}
		}

		internal AppHandle(string id)
		{
			_id = id ?? "";
		}

		public AppHandle OnCall(string name, Func<string, string> handler)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.HandleCall(id, name, handler);
			});
			return this;
		}

		public void Emit(string name, string payload = "")
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.EmitEvent(id, name, payload);
			});
		}

		public AppHandle AllowHost(string host)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.AllowNetHost(id, host);
			});
			return this;
		}

		public AppHandle Orientation(params string[] supported)
		{
			string id = _id;
			string list = ((supported == null) ? "" : string.Join(",", supported));
			Apps.WhenBound(delegate
			{
				Apps.Orient(id, list);
			});
			return this;
		}

		public AppHandle Badge(int count)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.Badge(id, count);
			});
			return this;
		}

		public AppHandle Notify(string title, string subtitle = "", float seconds = 0f)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.Notify(id, title, subtitle, seconds);
			});
			return this;
		}

		public AppHandle Image(string name, byte[] png)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.SetImage(id, name, png);
			});
			return this;
		}

		public AppHandle Icon(bool visible)
		{
			Apps.HideIcon(_id, !visible);
			return this;
		}

		public AppHandle NoIcon()
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.HideIcon(id, hidden: true);
			});
			return this;
		}

		public AppHandle Open()
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.OpenApp(id, open: true);
			});
			return this;
		}

		public AppHandle Close()
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.OpenApp(id, open: false);
			});
			return this;
		}

		public bool Show()
		{
			if (!Apps.RaisePhone(raised: true))
			{
				return false;
			}
			Apps.OpenApp(_id, open: true);
			return true;
		}

		public AppHandle OnKey(string keys, Func<string, bool> handler)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.ClaimKeys(id, keys, (handler == null) ? null : ((Func<string, string, bool>)((string _, string key) => handler(key))));
			});
			return this;
		}

		public AppHandle Reload()
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.ReloadApp(id);
			});
			return this;
		}

		public void Unregister()
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.UnregisterApp(id);
			});
		}

		public AppHandle AllowGameWrites(string scope)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.AllowGameWrites(id, scope);
			});
			return this;
		}

		public AppHandle Hide()
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.OpenApp(id, open: false);
				Apps.RaisePhone(raised: false);
			});
			return this;
		}
	}
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	public static class Surfaces
	{
		public static bool Available
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.HasSurfaces;
				}
				return false;
			}
		}

		public static SurfaceHandle Mount(object hostRect, string id, string bundlePrefix, float designShortSide = 0f, Assembly hostAssembly = null)
		{
			SurfaceHandle result = new SurfaceHandle(id);
			if (string.IsNullOrEmpty(id) || hostRect == null)
			{
				return result;
			}
			Assembly host = hostAssembly ?? Assembly.GetCallingAssembly();
			Apps.MountSurface(hostRect, id, bundlePrefix, host, designShortSide);
			return result;
		}

		public static void Unmount(string id)
		{
			Apps.UnmountSurface(id);
		}

		public static bool IsMounted(string id)
		{
			return Apps.SurfaceIsMounted(id);
		}
	}
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	public sealed class SurfaceHandle
	{
		private readonly string _id;

		public string Id => _id;

		internal SurfaceHandle(string id)
		{
			_id = id;
		}

		public SurfaceHandle OnCall(string name, Func<string, string> handler)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.HandleCall(id, name, handler);
			});
			return this;
		}

		public SurfaceHandle Emit(string name, string payload = "")
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.EmitEvent(id, name, payload);
			});
			return this;
		}

		public SurfaceHandle AllowHost(string host)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.AllowNetHost(id, host);
			});
			return this;
		}

		public SurfaceHandle Image(string name, byte[] png)
		{
			string id = _id;
			Apps.WhenBound(delegate
			{
				Apps.SetImage(id, name, png);
			});
			return this;
		}

		public void Unmount()
		{
			Apps.UnmountSurface(_id);
		}
	}
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	public static class PhoneScreen
	{
		public static bool IsRaised
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.PhoneIsRaised();
				}
				return false;
			}
		}

		public static bool Available
		{
			get
			{
				if (Apps.Available)
				{
					return Apps.HasPhone;
				}
				return false;
			}
		}

		public static bool Raise()
		{
			return Apps.RaisePhone(raised: true);
		}

		public static bool Lower()
		{
			return Apps.RaisePhone(raised: false);
		}
	}
}
namespace NpcCards
{
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	public sealed class Core : MelonMod
	{
		public static Core Instance { get; private set; }

		public static Instance Log { get; private set; }

		public override void OnInitializeMelon()
		{
			Instance = this;
			Log = ((MelonBase)this).LoggerInstance;
			Strings.Register();
			bool num = PersonnelCheck.Detect();
			if (num)
			{
				StartCards();
			}
			Calls.RegisterPhone();
			MenuEntry.TryRegister();
			if (num)
			{
				LogReady();
			}
			else
			{
				Log.Msg("NPC Cards " + ((MelonBase)this).Info.Version + " ready without Personnel: the app only shows the notice.");
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void StartCards()
		{
			Library.Start();
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private void LogReady()
		{
			Log.Msg($"NPC Cards {((MelonBase)this).Info.Version} ready (Personnel cards API, vault {Vault.BaseUrl}).");
		}

		public override void OnUpdate()
		{
			MainThread.Drain();
			MenuSurface.Tick();
		}

		public override void OnSceneWasUnloaded(int buildIndex, string sceneName)
		{
			if (sceneName == "Menu")
			{
				MenuSurface.Close("the menu scene unloaded");
			}
		}
	}
}
namespace NpcCards.Menu
{
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	internal static class MenuEntry
	{
		internal const string GamemodeId = "doodesch.npccards";

		private static LaunchContext _ctx;

		internal static void TryRegister()
		{
			if (!AppDomain.CurrentDomain.GetAssemblies().Any((Assembly a) => a.GetName().Name == "SideHustle"))
			{
				Core.Log.Msg("[menu] Side Hustle is not installed - NPC Cards is a phone app only.");
				return;
			}
			try
			{
				Register();
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[menu] registering with Side Hustle failed (" + ex.Message + ") - NPC Cards is a phone app only.");
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void Register()
		{
			//IL_0000: 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_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0036: 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_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			API.Register(new GamemodeDescriptor
			{
				Id = "doodesch.npccards",
				DisplayName = "NPC Cards",
				Description = Strings.T("Add new NPCs in one click."),
				Author = "DooDesch",
				Support = (GamemodeSupport)0,
				Surface = (GamemodeSurface)0,
				OnLaunchSingleplayer = OnLaunch,
				OnExitToHub = OnExit
			});
			Core.Log.Msg("[menu] registered with Side Hustle as a main-menu gamemode.");
		}

		private static void OnLaunch(LaunchContext ctx)
		{
			_ctx = ctx;
			MenuSurface.Open(ReturnToHub);
		}

		private static void OnExit(LaunchContext ctx)
		{
			_ctx = null;
			MenuSurface.Close("Side Hustle left the gamemode");
		}

		private static void ReturnToHub()
		{
			LaunchContext ctx = _ctx;
			if (ctx == null)
			{
				Core.Log.Warning("[menu] back to the hub asked without a launch context - closing the page only.");
				MenuSurface.Close("no launch context");
				return;
			}
			try
			{
				ctx.ReturnToHub();
			}
			catch (Exception ex)
			{
				Core.Log.Error("[menu] returning to the Side Hustle hub failed: " + ex.Message);
				MenuSurface.Close("returning to the hub failed");
			}
		}
	}
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	internal static class MenuSurface
	{
		private const string SurfaceId = "npccards-menu";

		private static GameObject _root;

		private static Action _returnToHub;

		private static bool _closeRequested;

		internal static bool IsOpen => (Object)(object)_root != (Object)null;

		internal static void Open(Action returnToHub)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			_returnToHub = returnToHub;
			if (!Surfaces.Available)
			{
				Core.Log.Error("[menu] Sideload 1.36 or newer is needed for the menu page - it was not found.");
				returnToHub?.Invoke();
				return;
			}
			if ((Object)(object)_root != (Object)null)
			{
				Close("opened again");
			}
			_root = new GameObject("NpcCards_Menu");
			Object.DontDestroyOnLoad((Object)(object)_root);
			Canvas obj = _root.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = 30000;
			_root.AddComponent<GraphicRaycaster>();
			SurfaceHandle surface = Surfaces.Mount(_root.GetComponent<RectTransform>(), "npccards-menu", "NpcCards.Assets.app", 720f);
			if (!Surfaces.IsMounted("npccards-menu"))
			{
				Core.Log.Error("[menu] the page could not be mounted - see the Sideload log lines above.");
				Close("mount failed");
				returnToHub?.Invoke();
			}
			else
			{
				Calls.RegisterMenu(surface);
				Pages.AttachMenu(surface);
				Core.Log.Msg("[menu] page mounted over the main menu.");
			}
		}

		internal static void Close(string why)
		{
			if (!((Object)(object)_root == (Object)null))
			{
				Pages.DetachMenu();
				try
				{
					Surfaces.Unmount("npccards-menu");
				}
				catch (Exception ex)
				{
					Core.Log.Warning("[menu] unmounting the page failed: " + ex.Message);
				}
				Object.Destroy((Object)(object)_root);
				_root = null;
				Core.Log.Msg("[menu] page closed (" + why + ").");
			}
		}

		internal static JObject RequestClose(string where)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			if (where != "menu" || (Object)(object)_root == (Object)null)
			{
				Core.Log.Warning($"[menu] close asked from the {where} page while the menu page is {(IsOpen ? "open" : "closed")} - ignored.");
				return Calls.Fail("Something went wrong. See the log.");
			}
			_closeRequested = true;
			return new JObject { ["ok"] = JToken.op_Implicit(true) };
		}

		internal static void Tick()
		{
			if ((Object)(object)_root == (Object)null)
			{
				return;
			}
			if (_closeRequested)
			{
				_closeRequested = false;
				Action returnToHub = _returnToHub;
				if (returnToHub != null)
				{
					returnToHub();
				}
				else
				{
					Close("closed from the page");
				}
			}
			else if (Input.GetKeyDown((KeyCode)27) || Input.GetMouseButtonDown(1))
			{
				Pages.EmitMenu("nav.back", null);
			}
		}
	}
}
namespace NpcCards.Host
{
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	internal static class Calls
	{
		internal const string AppId = "npccards";

		internal const string Bundle = "NpcCards.Assets.app";

		internal const string PersonifyUrl = "https://thunderstore.io/c/schedule-i/p/DooDesch/Personify/";

		private static readonly Dictionary<string, Func<JObject, string, JObject>> Handlers = new Dictionary<string, Func<JObject, string, JObject>>(StringComparer.Ordinal)
		{
			["init"] = (JObject a, string where) => Init(where),
			["vault.state"] = (JObject a, string _) => Vault.State(),
			["vault.get"] = (JObject a, string _) => Vault.Get((string)a["id"], (string)a["path"]),
			["vault.install"] = (JObject a, string _) => Vault.Install((string)a["id"], (string)a["code"]),
			["vault.thumb"] = (JObject a, string _) => Vault.Thumb((string)a["code"]),
			["vault.connect"] = (JObject a, string _) => Vault.Connect(),
			["vault.disconnect"] = (JObject a, string _) => Vault.Disconnect(),
			["vault.upload"] = (JObject a, string _) => Vault.Upload((string)a["id"], (string)a["file"]),
			["cards.installed"] = (JObject a, string _) => Library.List(),
			["cards.remove"] = (JObject a, string _) => Library.Remove((string)a["code"]),
			["cards.importFiles"] = (JObject a, string _) => Library.ImportFiles(),
			["cards.importFile"] = (JObject a, string _) => Library.ImportFile((string)a["file"]),
			["cards.openFolder"] = (JObject a, string _) => Library.OpenFolder(),
			["cards.inbox"] = (JObject a, string _) => new JObject
			{
				["ok"] = JToken.op_Implicit(true),
				["cards"] = (JToken)(object)Library.Arrivals()
			},
			["cards.uploadFiles"] = (JObject a, string _) => Library.UploadFiles(),
			["open.url"] = (JObject a, string _) => Vault.OpenUrl((string)a["url"]),
			["copy"] = (JObject a, string _) => Vault.Copy((string)a["text"]),
			["siesta.hint"] = (JObject a, string _) => SiestaHint.State(),
			["siesta.close"] = (JObject a, string _) => SiestaHint.Close(),
			["menu.close"] = (JObject a, string where) => MenuSurface.RequestClose(where)
		};

		private static readonly HashSet<string> WithoutPersonnel = new HashSet<string>(StringComparer.Ordinal) { "init", "menu.close" };

		internal static void RegisterPhone()
		{
			try
			{
				AppHandle appHandle = Apps.Register("npccards", "NpcCards.Assets.app", "NPC Cards", "NPC Cards");
				appHandle.Orientation("landscape", "portrait");
				foreach (KeyValuePair<string, Func<JObject, string, JObject>> handler in Handlers)
				{
					KeyValuePair<string, Func<JObject, string, JObject>> h = handler;
					appHandle.OnCall(h.Key, (string arg) => Run(h.Key, arg, "phone", h.Value));
				}
				Pages.Phone = appHandle;
				Core.Log.Msg(Apps.Available ? "[phone] app registered with Sideload." : "[phone] Sideload is not loaded yet - the app registration is queued.");
			}
			catch (Exception value)
			{
				Core.Log.Error($"[phone] registering the phone app failed: {value}");
			}
		}

		internal static void RegisterMenu(SurfaceHandle surface)
		{
			foreach (KeyValuePair<string, Func<JObject, string, JObject>> handler in Handlers)
			{
				KeyValuePair<string, Func<JObject, string, JObject>> h = handler;
				surface.OnCall(h.Key, (string arg) => Run(h.Key, arg, "menu", h.Value));
			}
		}

		private static string Run(string name, string arg, string where, Func<JObject, string, JObject> handler)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!PersonnelCheck.Loaded && !WithoutPersonnel.Contains(name))
				{
					Core.Log.Warning($"[{where}] {name} refused: Personnel is not loaded.");
					return ((JToken)Fail("Personnel is not loaded.")).ToString((Formatting)0);
				}
				JObject arg2 = (JObject)((!string.IsNullOrWhiteSpace(arg)) ? ((object)JObject.Parse(arg)) : ((object)new JObject()));
				return ((JToken)(handler(arg2, where) ?? Fail("Something went wrong. See the log."))).ToString((Formatting)0);
			}
			catch (Exception value)
			{
				Core.Log.Error($"[{where}] {name}({arg}) failed: {value}");
				return ((JToken)Fail("Something went wrong. See the log.")).ToString((Formatting)0);
			}
		}

		internal static JObject Fail(string en)
		{
			//IL_0000: 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_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			return new JObject
			{
				["ok"] = JToken.op_Implicit(false),
				["msg"] = JToken.op_Implicit(Strings.T(en))
			};
		}

		private static JObject Init(string where)
		{
			//IL_0007: 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_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			if (!PersonnelCheck.Loaded)
			{
				return new JObject
				{
					["ok"] = JToken.op_Implicit(true),
					["where"] = JToken.op_Implicit(where),
					["version"] = JToken.op_Implicit("1.1.2"),
					["strings"] = (JToken)(object)Strings.ForPage(),
					["personnelMissing"] = JToken.op_Implicit(true)
				};
			}
			return FullInit(where);
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static JObject FullInit(string where)
		{
			//IL_0000: 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_0016: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			return new JObject
			{
				["ok"] = JToken.op_Implicit(true),
				["where"] = JToken.op_Implicit(where),
				["version"] = JToken.op_Implicit("1.1.2"),
				["strings"] = (JToken)(object)Strings.ForPage(),
				["vault"] = (JToken)(object)Vault.State(),
				["personifyUrl"] = JToken.op_Implicit("https://thunderstore.io/c/schedule-i/p/DooDesch/Personify/"),
				["importFolder"] = JToken.op_Implicit(Library.ImportDir)
			};
		}
	}
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	internal static class Library
	{
		private sealed class Arrival
		{
			public string Code;

			public string Name;

			public DateTime At;
		}

		private static Dictionary<string, string> _loaded;

		private static readonly Dictionary<string, string> Names = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly List<Arrival> Inbox = new List<Arrival>();

		private static HashSet<string> _known;

		private static readonly Dictionary<string, JObject> FileCache = new Dictionary<string, JObject>(StringComparer.OrdinalIgnoreCase);

		internal static string ImportDir => CardsApi.ImportFolder();

		private static string DoneDir => Path.Combine(ImportDir, "Done");

		internal static void Start()
		{
			List<JObject> list = Installed();
			_loaded = list.ToDictionary<JObject, string, string>((JObject c) => (string)c["code"], (JObject c) => ((string)c["contentHash"]) ?? "", StringComparer.Ordinal);
			_known = new HashSet<string>(_loaded.Keys, StringComparer.Ordinal);
			foreach (JObject item in list)
			{
				Names[(string)item["code"]] = (string)item["name"];
			}
			if (CardsApi.PendingRestart())
			{
				Core.Log.Warning("[cards] Personnel already reports changed cards at our start; the 'after the next start' count may miss some.");
			}
			CardLibrary.Changed += delegate
			{
				MainThread.Post("cards changed", OnChanged);
			};
			Core.Log.Msg($"[cards] {_loaded.Count} card(s) loaded this game; import folder {ImportDir}.");
		}

		private static List<JObject> Installed()
		{
			try
			{
				return (from c in ((IEnumerable)JArray.Parse(CardsApi.List())).OfType<JObject>()
					where !string.IsNullOrEmpty((string)c["code"])
					select c).ToList();
			}
			catch (Exception ex)
			{
				Core.Log.Error("[cards] reading the installed cards from Personnel failed: " + ex.Message);
				return new List<JObject>();
			}
		}

		private static void OnChanged()
		{
			foreach (JObject item in Installed())
			{
				string text = (string)item["code"];
				Names[text] = (string)item["name"];
				if (_known.Add(text) && (((string)item["source"]) ?? "").StartsWith("inbox ", StringComparison.Ordinal))
				{
					Inbox.Insert(0, new Arrival
					{
						Code = text,
						Name = (((string)item["name"]) ?? text),
						At = DateTime.Now
					});
					Core.Log.Msg($"[cards] '{(string)item["name"]}' ({text}) arrived from the website.");
				}
			}
			Pages.Emit("cards.changed", (JToken)(object)Summary());
		}

		internal static void AfterInstall(JObject r)
		{
			if ((bool?)r["ok"] == true && (bool?)r["alreadyInstalled"] != true)
			{
				Pages.Notify(((string)r["name"]) ?? ((string)r["code"]), Strings.T("Arrives after the next game start."));
			}
		}

		internal static JObject Summary()
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Expected O, but got Unknown
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Expected O, but got Unknown
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: 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_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Expected O, but got Unknown
			List<JObject> source = Installed();
			Dictionary<string, string> nowCodes = source.ToDictionary<JObject, string, string>((JObject c) => (string)c["code"], (JObject c) => ((string)c["contentHash"]) ?? "", StringComparer.Ordinal);
			JArray val = new JArray((object)(from c in source
				where !_loaded.ContainsKey((string)c["code"])
				select (string)c["name"]));
			string value;
			JArray val2 = new JArray((object)(from c in source
				where _loaded.TryGetValue((string)c["code"], out value) && value != (((string)c["contentHash"]) ?? "")
				select (string)c["name"]));
			JArray val3 = new JArray((object)(from k in _loaded.Keys
				where !nowCodes.ContainsKey(k)
				select (!Names.TryGetValue(k, out value) || value == null) ? k : value));
			return new JObject
			{
				["arriving"] = (JToken)(object)val,
				["leaving"] = (JToken)(object)val3,
				["changed"] = (JToken)(object)val2
			};
		}

		internal static JObject List()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Expected O, but got Unknown
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Expected O, but got Unknown
			JArray val = new JArray();
			foreach (JObject item in Installed())
			{
				string text = (string)item["code"];
				val.Add((JToken)new JObject
				{
					["code"] = JToken.op_Implicit(text),
					["name"] = JToken.op_Implicit(((string)item["name"]) ?? text),
					["role"] = JToken.op_Implicit((string)item["role"]),
					["creator"] = JToken.op_Implicit((string)item["creator"]),
					["importedAt"] = item["importedAt"],
					["basedOn"] = JToken.op_Implicit((string)item["basedOn"]),
					["loaded"] = JToken.op_Implicit(_loaded.ContainsKey(text)),
					["picture"] = JToken.op_Implicit(Portrait(text))
				});
			}
			Core.Log.Msg($"[cards] listed {((JContainer)val).Count} installed card(s).");
			return new JObject
			{
				["ok"] = JToken.op_Implicit(true),
				["cards"] = (JToken)(object)val,
				["next"] = (JToken)(object)Summary()
			};
		}

		private static string Portrait(string code)
		{
			string text = "portrait-" + code;
			if (Pages.HasPicture(text))
			{
				return "s1://" + text;
			}
			string path = Path.Combine(CardLibrary.PackDir, code, "portrait.png");
			if (!File.Exists(path))
			{
				return null;
			}
			try
			{
				return Pages.Picture(text, File.ReadAllBytes(path));
			}
			catch (Exception ex)
			{
				Core.Log.Warning($"[cards] reading the picture of {code} failed ({ex.Message}); the row shows none.");
				return null;
			}
		}

		internal static JObject Remove(string code)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			if (!CardLibrary.IsValidCode(code))
			{
				Core.Log.Warning("[cards] remove refused: '" + code + "' is not a card code.");
				return Calls.Fail("Not a card code.");
			}
			bool flag = CardsApi.Remove(code);
			Core.Log.Msg($"[cards] remove {code}: {(flag ? "removed" : "no such card")}.");
			if (!flag)
			{
				return Calls.Fail("This card is not installed.");
			}
			return new JObject
			{
				["ok"] = JToken.op_Implicit(true),
				["next"] = (JToken)(object)Summary()
			};
		}

		internal static JObject ImportFiles()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: 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_0139: Expected O, but got Unknown
			JArray val = new JArray();
			string importDir = ImportDir;
			try
			{
				Directory.CreateDirectory(importDir);
				foreach (FileInfo item in new DirectoryInfo(importDir).GetFiles("*.png").OrderBy<FileInfo, string>((FileInfo f) => f.Name, StringComparer.OrdinalIgnoreCase))
				{
					val.Add((JToken)(object)Describe(item));
				}
			}
			catch (Exception ex)
			{
				Core.Log.Error("[cards] listing " + importDir + " failed: " + ex.Message);
				return Calls.Fail("The import folder could not be read.");
			}
			Core.Log.Msg($"[cards] import folder: {((JContainer)val).Count} file(s) in {importDir}.");
			return new JObject
			{
				["ok"] = JToken.op_Implicit(true),
				["folder"] = JToken.op_Implicit(importDir),
				["files"] = (JToken)(object)val
			};
		}

		private static string AuthorOf(JObject meta)
		{
			object obj;
			if (meta == null)
			{
				obj = null;
			}
			else
			{
				JToken obj2 = meta["credit"];
				obj = ((obj2 != null) ? obj2[(object)"author"] : null);
			}
			string text = ((string)(JToken)obj)?.Trim();
			if (((meta != null) ? meta["basedOn"] : null) is JObject || string.IsNullOrEmpty(text))
			{
				return (string)((meta != null) ? meta["creator"] : null);
			}
			return text;
		}

		private static JObject Describe(FileInfo fi)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			string text = fi.FullName + "|" + fi.LastWriteTimeUtc.Ticks + "|" + fi.Length;
			if (FileCache.TryGetValue(text, out var value))
			{
				return value;
			}
			JObject val = new JObject
			{
				["file"] = JToken.op_Implicit(fi.Name),
				["sizeKb"] = JToken.op_Implicit(fi.Length / 1024)
			};
			try
			{
				byte[] array = File.ReadAllBytes(fi.FullName);
				JObject val2 = JObject.Parse(CardsApi.ReadCard(array));
				if ((bool?)val2["ok"] == true)
				{
					JToken obj = val2["npc"];
					JObject val3 = (JObject)(object)((obj is JObject) ? obj : null);
					val["card"] = JToken.op_Implicit(true);
					val["name"] = JToken.op_Implicit(((string)((val3 != null) ? val3["name"] : null)) ?? Path.GetFileNameWithoutExtension(fi.Name));
					val["role"] = JToken.op_Implicit(CardLibrary.RoleOf(val3));
					? val4 = val;
					JToken obj2 = val2["meta"];
					((JObject)val4)["creator"] = JToken.op_Implicit(AuthorOf((JObject)(object)((obj2 is JObject) ? obj2 : null)));
					val["picture"] = JToken.op_Implicit(Pages.Picture("file-" + Fnv(text), array));
				}
				else
				{
					val["card"] = JToken.op_Implicit(false);
					val["message"] = JToken.op_Implicit((string)val2["message"]);
					Core.Log.Msg($"[cards] {fi.Name} in the import folder is not a card: {(string)val2["error"]} {(string)val2["message"]}");
				}
			}
			catch (Exception ex)
			{
				val["card"] = JToken.op_Implicit(false);
				val["message"] = JToken.op_Implicit(ex.Message);
				Core.Log.Warning("[cards] reading " + fi.Name + " from the import folder failed: " + ex.Message);
			}
			FileCache[text] = val;
			return val;
		}

		internal static JObject ImportFile(string file)
		{
			string text = InFolder(ImportDir, file);
			if (text == null)
			{
				return Calls.Fail("File not found.");
			}
			JObject val = JObject.Parse(CardsApi.ImportFile(text));
			if ((bool?)val["ok"] != true)
			{
				Core.Log.Error($"[cards] installing {file} from the import folder failed: {(string)val["error"]} {(string)val["message"]}");
				val["message"] = JToken.op_Implicit(((string)val["message"]) ?? Strings.T("This file is not a card."));
				return val;
			}
			Core.Log.Msg($"[cards] {file} installed as '{(string)val["name"]}' ({(string)val["code"]}){(((bool?)val["alreadyInstalled"] == true) ? ", already installed" : "")}.");
			MoveToDone(text);
			AfterInstall(val);
			return val;
		}

		private static void MoveToDone(string full)
		{
			try
			{
				Directory.CreateDirectory(DoneDir);
				string text = Path.Combine(DoneDir, Path.GetFileName(full));
				if (File.Exists(text))
				{
					text = Path.Combine(DoneDir, Path.GetFileNameWithoutExtension(full) + "-" + DateTime.Now.ToString("yyyyMMdd-HHmmss") + ".png");
				}
				File.Move(full, text);
				Core.Log.Msg($"[cards] moved {Path.GetFileName(full)} to {text}.");
			}
			catch (Exception ex)
			{
				Core.Log.Warning($"[cards] moving {full} to {DoneDir} failed ({ex.Message}); Personnel installs it again at the next start and finds it already there.");
			}
		}

		internal static JObject OpenFolder()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_005f: Expected O, but got Unknown
			string importDir = ImportDir;
			try
			{
				Directory.CreateDirectory(importDir);
				Application.OpenURL(new Uri(importDir).AbsoluteUri);
				Core.Log.Msg("[cards] opened " + importDir + " in the file browser.");
				return new JObject
				{
					["ok"] = JToken.op_Implicit(true),
					["folder"] = JToken.op_Implicit(importDir)
				};
			}
			catch (Exception ex)
			{
				Core.Log.Error("[cards] opening " + importDir + " failed: " + ex.Message);
				return Calls.Fail("The folder could not be opened.");
			}
		}

		internal static JObject UploadFiles()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			JArray val = new JArray();
			string[] array = new string[2] { ImportDir, DoneDir };
			foreach (string text in array)
			{
				if (!Directory.Exists(text))
				{
					continue;
				}
				try
				{
					foreach (FileInfo item in from f in new DirectoryInfo(text).GetFiles("*.png")
						orderby f.LastWriteTimeUtc descending
						select f)
					{
						JObject val2 = Describe(item);
						if ((bool?)val2["card"] == true)
						{
							JObject val3 = (JObject)((JToken)val2).DeepClone();
							val3["file"] = JToken.op_Implicit((text == DoneDir) ? ("Done/" + item.Name) : item.Name);
							val.Add((JToken)(object)val3);
						}
					}
				}
				catch (Exception ex)
				{
					Core.Log.Error("[cards] listing " + text + " for uploads failed: " + ex.Message);
				}
			}
			Core.Log.Msg($"[cards] {((JContainer)val).Count} card file(s) can be uploaded.");
			return new JObject
			{
				["ok"] = JToken.op_Implicit(true),
				["files"] = (JToken)(object)val
			};
		}

		internal static string UploadPath(string file)
		{
			if (file != null && file.StartsWith("Done/", StringComparison.Ordinal))
			{
				return InFolder(DoneDir, file.Substring(5));
			}
			return InFolder(ImportDir, file);
		}

		private static string InFolder(string dir, string file)
		{
			if (string.IsNullOrEmpty(file) || file.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0 || file.Contains(".."))
			{
				Core.Log.Warning($"[cards] '{file}' is not a file name in {dir}.");
				return null;
			}
			string text = Path.Combine(dir, file);
			if (!File.Exists(text))
			{
				Core.Log.Warning("[cards] " + text + " does not exist.");
				return null;
			}
			return text;
		}

		internal static JArray Arrivals()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			return new JArray((object)((IEnumerable<Arrival>)Inbox).Select((Func<Arrival, JObject>)((Arrival a) => new JObject
			{
				["code"] = JToken.op_Implicit(a.Code),
				["name"] = JToken.op_Implicit(a.Name),
				["at"] = JToken.op_Implicit(a.At.ToString("HH:mm")),
				["picture"] = JToken.op_Implicit(Portrait(a.Code))
			})));
		}

		private static string Fnv(string s)
		{
			uint num = 2166136261u;
			byte[] bytes = Encoding.UTF8.GetBytes(s);
			foreach (byte b in bytes)
			{
				num ^= b;
				num *= 16777619;
			}
			return num.ToString("x8");
		}
	}
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	internal static class MainThread
	{
		private static readonly ConcurrentQueue<(string what, Action run)> Queue = new ConcurrentQueue<(string, Action)>();

		internal static void Post(string what, Action run)
		{
			if (run != null)
			{
				Queue.Enqueue((what, run));
			}
		}

		internal static void Drain()
		{
			int num = 0;
			(string, Action) result;
			while (num++ < 32 && Queue.TryDequeue(out result))
			{
				try
				{
					result.Item2();
				}
				catch (Exception value)
				{
					Core.Log.Error($"[main] {result.Item1} failed on the main thread: {value}");
				}
			}
		}
	}
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	internal static class Pages
	{
		internal static AppHandle Phone;

		private static SurfaceHandle _menu;

		private static readonly Dictionary<string, byte[]> Pictures = new Dictionary<string, byte[]>(StringComparer.Ordinal);

		internal static void AttachMenu(SurfaceHandle surface)
		{
			_menu = surface;
			foreach (KeyValuePair<string, byte[]> picture in Pictures)
			{
				surface.Image(picture.Key, picture.Value);
			}
			Core.Log.Msg($"[pages] menu page attached, {Pictures.Count} picture(s) handed over.");
		}

		internal static void DetachMenu()
		{
			_menu = null;
		}

		internal static bool HasPicture(string name)
		{
			return Pictures.ContainsKey(name);
		}

		internal static void Emit(string name, JToken payload)
		{
			string payload2 = ((payload != null) ? payload.ToString((Formatting)0) : null) ?? "";
			if (Phone == null && _menu == null)
			{
				Core.Log.Warning("[pages] event '" + name + "' dropped: no page is registered.");
				return;
			}
			Phone?.Emit(name, payload2);
			_menu?.Emit(name, payload2);
		}

		internal static void EmitMenu(string name, JToken payload)
		{
			_menu?.Emit(name, ((payload != null) ? payload.ToString((Formatting)0) : null) ?? "");
		}

		internal static string Picture(string name, byte[] png)
		{
			Pictures[name] = png;
			Phone?.Image(name, png);
			_menu?.Image(name, png);
			return "s1://" + name;
		}

		internal static void Notify(string title, string text)
		{
			if (Phone == null)
			{
				Core.Log.Warning($"[pages] notification '{title}: {text}' dropped: the phone app is not registered.");
			}
			else
			{
				Phone.Notify(title, text, 6f);
				Core.Log.Msg("[pages] notification: " + title + ": " + text);
			}
		}
	}
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	internal static class PersonnelCheck
	{
		internal const string Notice = "Personnel is not loaded.";

		private const string ProbeType = "Personnel.Cards.CardsApi, Personnel";

		internal static bool Loaded { get; private set; }

		internal static bool Detect()
		{
			try
			{
				Loaded = Type.GetType("Personnel.Cards.CardsApi, Personnel", throwOnError: false) != null;
			}
			catch (Exception ex)
			{
				Core.Log.Error("[personnel] looking for Personnel.Cards.CardsApi, Personnel failed: " + ex.Message);
				Loaded = false;
			}
			if (Loaded)
			{
				Core.Log.Msg("[personnel] found.");
			}
			else
			{
				Core.Log.Error("[personnel] not loaded: the app only shows a notice. Personnel.dll has to sit directly in Mods.");
			}
			return Loaded;
		}
	}
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	internal static class SiestaHint
	{
		internal const string Url = "https://thunderstore.io/c/schedule-i/p/DooDesch/Siesta/";

		private const string MelonName = "Siesta";

		private const string DllName = "Siesta.dll";

		private const int Threshold = 40;

		private static bool _closed;

		internal static JObject State()
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: 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_00fc: 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_0124: Expected O, but got Unknown
			int num = -1;
			try
			{
				num = API.All.Count;
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[siesta hint] reading the NPC count from Personnel failed (" + ex.Message + ") - no hint.");
			}
			bool flag = IsInstalled();
			Core.Log.Msg($"[siesta hint] {num} custom NPC(s), threshold {40}, Siesta installed: {flag}, closed: {_closed}.");
			return new JObject
			{
				["ok"] = JToken.op_Implicit(true),
				["offer"] = JToken.op_Implicit(num >= 0 && !flag && !_closed),
				["npcs"] = JToken.op_Implicit(num),
				["threshold"] = JToken.op_Implicit(40),
				["url"] = JToken.op_Implicit("https://thunderstore.io/c/schedule-i/p/DooDesch/Siesta/")
			};
		}

		internal static JObject Close()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			_closed = true;
			Core.Log.Msg("[siesta hint] closed by the player until the game is closed.");
			return new JObject { ["ok"] = JToken.op_Implicit(true) };
		}

		private static bool IsInstalled()
		{
			try
			{
				if (MelonBase.RegisteredMelons.Any((MelonBase m) => m.Info.Name == "Siesta"))
				{
					return true;
				}
				return File.Exists(Path.Combine(MelonEnvironment.ModsDirectory, "Siesta.dll"));
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[siesta hint] checking for Siesta failed (" + ex.Message + ") - treated as installed, no hint.");
				return true;
			}
		}
	}
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	internal static class Strings
	{
		private const string Resource = "NpcCards.Assets.app.de.json";

		private static readonly Dictionary<string, string> German = new Dictionary<string, string>(StringComparer.Ordinal);

		internal static void Register()
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Invalid comparison between Unknown and I4
			try
			{
				using Stream stream = typeof(Strings).Assembly.GetManifestResourceStream("NpcCards.Assets.app.de.json");
				if (stream == null)
				{
					Core.Log.Warning("[l10n] NpcCards.Assets.app.de.json is not embedded - the app shows English only.");
					return;
				}
				using StreamReader streamReader = new StreamReader(stream);
				foreach (JProperty item in JObject.Parse(streamReader.ReadToEnd()).Properties())
				{
					if ((int)item.Value.Type == 8)
					{
						German[item.Name] = (string)item.Value;
					}
				}
				L10n.Register("de", German);
				Core.Log.Msg($"[l10n] {German.Count} German strings registered; language {L10n.Language}.");
			}
			catch (Exception ex)
			{
				Core.Log.Error($"[l10n] reading {"NpcCards.Assets.app.de.json"} failed ({ex.Message}) - the app shows English only.");
			}
		}

		internal static string T(string en)
		{
			return L10n.T(en);
		}

		internal static string T(string en, params object[] args)
		{
			try
			{
				return string.Format(T(en), args);
			}
			catch (FormatException ex)
			{
				Core.Log.Warning($"[l10n] translation of '{en}' has broken placeholders ({ex.Message}) - English is shown.");
				return string.Format(en, args);
			}
		}

		internal static JObject ForPage()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			JObject val = new JObject();
			foreach (string key in German.Keys)
			{
				val[key] = JToken.op_Implicit(L10n.T(key));
			}
			return val;
		}
	}
	[SourceNotice("NpcCards by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-NpcCards, not decompiled code.")]
	internal static class Vault
	{
		private static readonly HttpClient Http = MakeClient();

		private static readonly HashSet<string> Loading = new HashSet<string>(StringComparer.Ordinal);

		internal static string BaseUrl
		{
			get
			{
				try
				{
					return (string)JObject.Parse(CardsApi.VaultState())["baseUrl"];
				}
				catch (Exception ex)
				{
					Core.Log.Error("[vault] reading the vault address from Personnel failed: " + ex.Message);
					return "https://personify.doomods.com";
				}
			}
		}

		internal static string Caller => "NPCCards/1.1.2";

		private static HttpClient MakeClient()
		{
			HttpClient httpClient = new HttpClient();
			httpClient.Timeout = TimeSpan.FromSeconds(20.0);
			httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(Caller);
			return httpClient;
		}

		internal static JObject State()
		{
			JObject obj = JObject.Parse(CardsApi.VaultState());
			obj["ok"] = JToken.op_Implicit(true);
			return obj;
		}

		private static JObject Pending()
		{
			//IL_0000: 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_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			return new JObject
			{
				["ok"] = JToken.op_Implicit(true),
				["pending"] = JToken.op_Implicit(true)
			};
		}

		private static void Reply(string id, bool ok, JToken body)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			Pages.Emit("vault.reply", (JToken)new JObject
			{
				["id"] = JToken.op_Implicit(id),
				["ok"] = JToken.op_Implicit(ok),
				["body"] = body
			});
		}

		internal static JObject Get(string id, string path)
		{
			if (string.IsNullOrEmpty(path) || !path.StartsWith("/api/v1/", StringComparison.Ordinal))
			{
				Core.Log.Warning("[vault] path '" + path + "' refused: not under /api/v1/.");
				return Calls.Fail("Something went wrong. See the log.");
			}
			Core.Log.Msg("[vault] GET " + path);
			using (CardsApi.Caller(Caller))
			{
				CardsApi.VaultGet(path, (Action<bool, string>)delegate(bool ok, string body)
				{
					//IL_0041: Expected O, but got Unknown
					if (!ok)
					{
						Core.Log.Error("[vault] GET " + path + " failed: " + body);
						Reply(id, ok: false, JToken.op_Implicit(body));
					}
					else
					{
						JToken val;
						try
						{
							val = JToken.Parse(body);
						}
						catch (JsonException ex)
						{
							JsonException ex2 = ex;
							Core.Log.Error($"[vault] GET {path} answered something that is not JSON ({((Exception)(object)ex2).Message}).");
							Reply(id, ok: false, JToken.op_Implicit("The vault answered something unreadable."));
							return;
						}
						JToken obj = val[(object)"cards"];
						JToken obj2 = ((obj is JArray) ? obj : null);
						int num = ((obj2 != null) ? ((JContainer)obj2).Count : (-1));
						Core.Log.Msg((num >= 0) ? $"[vault] GET {path}: {num} card(s), next {((string)val[(object)"next"]) ?? "none"}." : ("[vault] GET " + path + ": ok."));
						Reply(id, ok: true, val);
					}
				});
				return Pending();
			}
		}

		internal static JObject Install(string id, string code)
		{
			if (!CardLibrary.IsValidCode(code))
			{
				Core.Log.Warning("[vault] install refused: '" + code + "' is not a card code.");
				return Calls.Fail("Not a card code.");
			}
			Core.Log.Msg("[vault] installing card " + code + ".");
			using (CardsApi.Caller(Caller))
			{
				CardsApi.VaultInstall(code, (Action<string>)delegate(string json)
				{
					JObject val = Parse(json, "install " + code);
					if ((bool?)val["ok"] == true)
					{
						Core.Log.Msg($"[vault] card {code} installed as '{(string)val["name"]}'{(((bool?)val["alreadyInstalled"] == true) ? " (already installed)" : "")}.");
						Library.AfterInstall(val);
					}
					else
					{
						Core.Log.Error($"[vault] installing card {code} failed: {(string)val["error"]} {(string)val["message"]}");
					}
					Reply(id, ok: true, (JToken)(object)val);
				});
				return Pending();
			}
		}

		internal static JObject Connect()
		{
			Core.Log.Msg("[vault] connect started.");
			using (CardsApi.Caller(Caller))
			{
				CardsApi.VaultConnect((Action<string>)delegate(string json)
				{
					JObject val = Parse(json, "connect");
					Instance log = Core.Log;
					string text = (string)val["state"];
					string text2 = (string)val["message"];
					log.Msg("[vault] connect: " + text + ((text2 != null && text2.Length > 0) ? (" - " + text2) : "") + ".");
					Pages.Emit("vault.connect", (JToken)(object)val);
					if ((string)val["state"] == "connected")
					{
						Pages.Emit("vault.state", (JToken)(object)State());
					}
				});
				return Pending();
			}
		}

		internal static JObject Disconnect()
		{
			Core.Log.Msg("[vault] disconnect asked.");
			using (CardsApi.Caller(Caller))
			{
				CardsApi.VaultDisconnect((Action<bool>)delegate(bool ok)
				{
					Core.Log.Msg($"[vault] disconnected; token revoked on the vault: {ok}.");
					Pages.Emit("vault.state", (JToken)(object)State());
				});
				return State();
			}
		}

		internal static JObject Upload(string id, string file)
		{
			string text = Library.UploadPath(file);
			if (text == null)
			{
				return Calls.Fail("File not found.");
			}
			if (!VaultClient.IsConnected)
			{
				Core.Log.Warning("[vault] upload of " + file + " refused: not connected.");
				return Calls.Fail("Connect this game to NPC Vault first.");
			}
			byte[] array;
			try
			{
				array = File.ReadAllBytes(text);
			}
			catch (Exception ex)
			{
				Core.Log.Error("[vault] reading " + text + " for the upload failed: " + ex.Message);
				return Calls.Fail("The file could not be read.");
			}
			Core.Log.Msg($"[vault] uploading {Path.GetFileName(text)} ({array.Length / 1024} KB).");
			using (CardsApi.Caller(Caller))
			{
				CardsApi.VaultUpload(array, (Action<string>)delegate(string json)
				{
					JObject val = Parse(json, "upload " + file);
					if ((bool?)val["ok"] == true)
					{
						Core.Log.Msg($"[vault] {file} published as {(string)val["code"]} ({(string)val["url"]}).");
					}
					else if ((bool?)val["duplicate"] == true)
					{
						Core.Log.Msg($"[vault] {file} is already on the vault as {(string)val["code"]}.");
					}
					else
					{
						Core.Log.Error("[vault] upload of " + file + " failed: " + (string)val["message"]);
					}
					Reply(id, ok: true, (JToken)(object)val);
				});
				return Pending();
			}
		}

		internal static JObject Thumb(string code)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			if (!CardLibrary.IsValidCode(code))
			{
				Core.Log.Warning("[vault] thumb refused: '" + code + "' is not a card code.");
				return Calls.Fail("Not a card code.");
			}
			string name = "vault-" + code;
			if (Pages.HasPicture(name))
			{
				return new JObject
				{
					["ok"] = JToken.op_Implicit(true),
					["url"] = JToken.op_Implicit("s1://" + name)
				};
			}
			if (!Loading.Add(code))
			{
				return Pending();
			}
			string url = BaseUrl.TrimEnd('/') + "/c/" + code + "/thumb.png";
			Task.Run(async delegate
			{
				byte[] png = null;
				string error = null;
				try
				{
					using HttpResponseMessage res = await Http.GetAsync(url).ConfigureAwait(continueOnCapturedContext: false);
					if (res.IsSuccessStatusCode)
					{
						png = await res.Content.ReadAsByteArrayAsync().ConfigureAwait(continueOnCapturedContext: false);
					}
					else
					{
						error = "HTTP " + (int)res.StatusCode;
					}
				}
				catch (Exception ex)
				{
					error = ex.GetType().Name + ": " + ex.GetBaseException().Message;
				}
				MainThread.Post("thumb " + code, delegate
				{
					//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
					//IL_00c0: 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_0106: Expected O, but got Unknown
					//IL_0084: Unknown result type (might be due to invalid IL or missing references)
					//IL_0089: 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_00b5: Expected O, but got Unknown
					Loading.Remove(code);
					if (png == null)
					{
						Core.Log.Error($"[vault] picture {url} failed ({error}).");
						Pages.Emit("vault.thumb", (JToken)new JObject
						{
							["code"] = JToken.op_Implicit(code),
							["url"] = null
						});
					}
					else
					{
						Pages.Emit("vault.thumb", (JToken)new JObject
						{
							["code"] = JToken.op_Implicit(code),
							["url"] = JToken.op_Implicit(Pages.Picture(name, png))
						});
					}
				});
			});
			return Pending();
		}

		internal static JObject OpenUrl(string url)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected O, but got Unknown
			string value = BaseUrl.TrimEnd('/') + "/";
			if (string.IsNullOrEmpty(url) || (!url.StartsWith(value, StringComparison.OrdinalIgnoreCase) && !(url == "https://thunderstore.io/c/schedule-i/p/DooDesch/Personify/") && !(url == "https://thunderstore.io/c/schedule-i/p/DooDesch/Siesta/")))
			{
				Core.Log.Warning("[vault] opening '" + url + "' refused: not a vault page.");
				return Calls.Fail("Something went wrong. See the log.");
			}
			Application.OpenURL(url);
			Core.Log.Msg("[vault] opened " + url + " in the browser.");
			return new JObject { ["ok"] = JToken.op_Implicit(true) };
		}

		internal static JObject Copy(string text)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			if (string.IsNullOrEmpty(text) || text.Length > 64)
			{
				Core.Log.Warning("[vault] copy refused: empty or too long.");
				return Calls.Fail("Something went wrong. See the log.");
			}
			GUIUtility.systemCopyBuffer = text;
			Core.Log.Msg("[vault] '" + text + "' copied to the clipboard.");
			return new JObject { ["ok"] = JToken.op_Implicit(true) };
		}

		private static JObject Parse(string json, string what)
		{
			//IL_005c: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_0022: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			if (string.IsNullOrEmpty(json))
			{
				Core.Log.Error("[vault] " + what + ": Personnel answered nothing.");
				return new JObject
				{
					["ok"] = JToken.op_Implicit(false),
					["message"] = JToken.op_Implicit("Personnel answered nothing.")
				};
			}
			try
			{
				return JObject.Parse(json);
			}
			catch (JsonException ex)
			{
				JsonException ex2 = ex;
				Core.Log.Error($"[vault] {what}: Personnel's answer is not JSON ({((Exception)(object)ex2).Message}).");
				return new JObject
				{
					["ok"] = JToken.op_Implicit(false),
					["message"] = JToken.op_Implicit("Personnel answered something unreadable.")
				};
			}
		}
	}
}