Decompiled source of Valheim Moments v0.23.2

BepInEx/plugins/ValheimMoments/ValheimMoments.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Rendering;
using ValheimMoments.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ValheimMoments")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.23.2.0")]
[assembly: AssemblyInformationalVersion("0.23.2+843a04a0cdca497472f3afcc175d172032bdee69")]
[assembly: AssemblyProduct("ValheimMoments")]
[assembly: AssemblyTitle("ValheimMoments")]
[assembly: AssemblyVersion("0.23.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValheimMoments
{
	internal sealed class AcquisitionHighlights
	{
		private sealed class Batch
		{
			internal string Kind;

			internal double Due;

			internal BossKill Event;
		}

		private readonly List<Batch> batches = new List<Batch>();

		internal void Add(string kind, LootItem item, string player, double now)
		{
			if ((!(kind != "c") || !(kind != "w")) && item != null && item.Quantity > 0)
			{
				Batch batch = batches.Find((Batch b) => b.Kind == kind);
				if (batch == null)
				{
					batch = new Batch
					{
						Kind = kind,
						Due = now + 0.25,
						Event = new BossKill
						{
							Acquired = true,
							EnemyKey = ((kind == "c") ? "a treasure chest" : "the world"),
							PlayerName = player,
							Loot = new BossLoot()
						}
					};
					batches.Add(batch);
				}
				batch.Event.Loot.AddEpic(item);
			}
		}

		internal void Poll(double now, string minimum, Func<string, bool> enabled, Action<BossKill> accept)
		{
			int num = 0;
			while (num < batches.Count)
			{
				Batch batch = batches[num];
				if (now < batch.Due)
				{
					num++;
					continue;
				}
				batches.RemoveAt(num);
				if (enabled(batch.Kind) && BossLootFilter.TryRank(minimum, out var rank) && batch.Event.Loot.Items.Exists((LootItem item) => item.Rank >= rank))
				{
					accept(batch.Event);
				}
			}
		}

		internal void Clear()
		{
			batches.Clear();
		}
	}
	internal enum BossNameMode
	{
		KillCredit,
		FinalBlow,
		Both
	}
	internal static class BossAttribution
	{
		private sealed class Context
		{
			internal string Enemy;

			internal string Name;

			internal string Credits;

			internal string EventId;

			internal string Subject;
		}

		private const string RpcName = "ValheimMoments_BossFinalBlow_v1";

		private const string CreditRpcName = "ValheimMoments_KillCredits_v1";

		private const string EventRpcName = "ValheimMoments_KillEvent_v1";

		private static readonly FieldInfo LastHit = typeof(Character).GetField("m_lastHit", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo NetworkView = AccessTools.Field(typeof(Character), "m_nview");

		private const string SubjectRpcName = "ValheimMoments_CinematicSubject_v1";

		private static readonly AttributionInbox subjectInbox = new AttributionInbox(80);

		internal static Func<Character, string> CinematicSubject;

		[ThreadStatic]
		private static Context current;

		private static ZRoutedRpc registered;

		private static readonly AttributionInbox inbox = new AttributionInbox();

		private static readonly AttributionInbox creditInbox = new AttributionInbox(1024);

		private static readonly AttributionInbox eventInbox = new AttributionInbox(32);

		private static readonly Stopwatch clock = Stopwatch.StartNew();

		internal static Action<string> OnDiagnostic;

		internal static string ResolveCredits(Character victim)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				object? obj = NetworkView?.GetValue(victim);
				object? obj2 = ((obj is ZNetView) ? obj : null);
				ZDO val = ((obj2 != null) ? ((ZNetView)obj2).GetZDO() : null);
				if (val == null || (Object)(object)ZNet.instance == (Object)null)
				{
					return null;
				}
				List<string> list = new List<string>();
				string text = ZDOVars.s_attackers.ToString();
				foreach (PlayerInfo player in ZNet.instance.GetPlayerList())
				{
					if (!string.IsNullOrEmpty(player.m_name) && val.GetBool(text + player.m_name, false))
					{
						list.Add(player.m_name);
					}
				}
				return FormatCredits(list);
			}
			catch
			{
				return null;
			}
		}

		internal static string FormatCredits(IEnumerable<string> players)
		{
			List<string> list = new List<string>();
			foreach (string player in players)
			{
				string text = (player ?? "").Replace("\r", " ").Replace("\n", " ").Replace("*", "")
					.Replace("`", "")
					.Trim();
				if (text.Length > 80)
				{
					text = text.Substring(0, char.IsHighSurrogate(text[79]) ? 79 : 80);
				}
				if (text.Length != 0 && !list.Contains(text))
				{
					list.Add(text);
				}
			}
			list.Sort(StringComparer.OrdinalIgnoreCase);
			string text2 = "";
			for (int i = 0; i < list.Count; i++)
			{
				if (text2.Length + list[i].Length + 2 > 980)
				{
					return text2 + " (+" + (list.Count - i) + " more)";
				}
				text2 = text2 + ((text2.Length == 0) ? "" : ", ") + list[i];
			}
			if (text2.Length != 0)
			{
				return text2;
			}
			return null;
		}

		internal static string CreditLabel(string credits, string local)
		{
			if (!string.IsNullOrWhiteSpace(credits))
			{
				return credits;
			}
			return (string.IsNullOrWhiteSpace(local) ? "A player" : local) + " (full list unavailable)";
		}

		internal static string Resolve(HitData hit, out string reason)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: 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_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			if (hit == null)
			{
				reason = "no recorded damage";
				return null;
			}
			if (((ZDOID)(ref hit.m_attacker)).IsNone())
			{
				string text = PeriodicAttribution.Resolve(hit);
				if (text != null)
				{
					reason = "resolved single-source periodic damage";
					return text;
				}
			}
			Character attacker = hit.GetAttacker();
			Player val = (Player)(object)((attacker is Player) ? attacker : null);
			if ((Object)(object)val != (Object)null)
			{
				reason = "resolved player object";
				return val.GetPlayerName();
			}
			if ((Object)(object)attacker == (Object)null && !((ZDOID)(ref hit.m_attacker)).IsNone() && (Object)(object)ZNet.instance != (Object)null)
			{
				foreach (PlayerInfo player in ZNet.instance.GetPlayerList())
				{
					if (player.m_characterID == hit.m_attacker)
					{
						reason = "resolved exact player network ID";
						return player.m_name;
					}
				}
			}
			reason = "hit=" + ((object)Unsafe.As<HitType, HitType>(ref hit.m_hitType)/*cast due to .constrained prefix*/).ToString() + ", " + (((Object)(object)attacker != (Object)null) ? "non-player attacker" : (((ZDOID)(ref hit.m_attacker)).IsNone() ? "no attacker ID" : "attacker ID not in player list"));
			return null;
		}

		internal static void Install(Harmony harmony)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_004d: Expected O, but got Unknown
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Expected O, but got Unknown
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Expected O, but got Unknown
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Character), "OnDeath", Type.EmptyTypes, (Type[])null), new HarmonyMethod(typeof(BossAttribution), "BeforeDeath", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(BossAttribution), "AfterDeath", (Type[])null), (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Game), "RegisterKill", new Type[6]
			{
				typeof(long),
				typeof(string),
				typeof(int),
				typeof(KillModifiers),
				typeof(int),
				typeof(bool)
			}, (Type[])null), new HarmonyMethod(typeof(BossAttribution), "BeforeSendCredit", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			foreach (ConstructorInfo declaredConstructor in AccessTools.GetDeclaredConstructors(typeof(ZRoutedRpc), (bool?)null))
			{
				harmony.Patch((MethodBase)declaredConstructor, (HarmonyMethod)null, new HarmonyMethod(typeof(BossAttribution), "AfterRouterCreated", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			EnsureRegistered(ZRoutedRpc.instance);
		}

		private static void AfterRouterCreated(ZRoutedRpc __instance)
		{
			EnsureRegistered(__instance);
		}

		private static void EnsureRegistered(ZRoutedRpc router)
		{
			try
			{
				if (router == null || registered == router)
				{
					return;
				}
				router.Register<string, string>("ValheimMoments_BossFinalBlow_v1", (Action<long, string, string>)Receive);
				router.Register<string, string>("ValheimMoments_KillCredits_v1", (Action<long, string, string>)delegate(long sender, string enemy, string names)
				{
					creditInbox.Add(sender, enemy, names, clock.Elapsed.TotalSeconds);
				});
				router.Register<string, string>("ValheimMoments_CinematicSubject_v1", (Action<long, string, string>)delegate(long sender, string enemy, string id)
				{
					subjectInbox.Add(sender, enemy, id, clock.Elapsed.TotalSeconds);
				});
				router.Register<string, string>("ValheimMoments_KillEvent_v1", (Action<long, string, string>)delegate(long sender, string enemy, string id)
				{
					if (HighlightDirector.ValidId(id))
					{
						eventInbox.Add(sender, enemy, id, clock.Elapsed.TotalSeconds);
					}
				});
				registered = router;
				inbox.Clear();
				creditInbox.Clear();
				eventInbox.Clear();
				subjectInbox.Clear();
			}
			catch
			{
			}
		}

		private static void Receive(long sender, string enemy, string name)
		{
			inbox.Add(sender, enemy, name, clock.Elapsed.TotalSeconds);
		}

		private static void BeforeDeath(Character __instance, out Context __state)
		{
			__state = current;
			current = null;
			try
			{
				if (!__instance.IsOwner() || __instance is Player)
				{
					return;
				}
				current = new Context
				{
					Enemy = __instance.m_name,
					EventId = Guid.NewGuid().ToString("N")
				};
				current.Subject = CinematicSubject?.Invoke(__instance);
				object? obj = LastHit?.GetValue(__instance);
				string reason;
				string name = Resolve((HitData)((obj is HitData) ? obj : null), out reason);
				current.Name = name;
				current.Credits = ResolveCredits(__instance);
				try
				{
					OnDiagnostic?.Invoke(reason);
				}
				catch
				{
				}
			}
			catch
			{
			}
		}

		private static void AfterDeath(Context __state)
		{
			current = __state;
		}

		private static void BeforeSendCredit(long playerPeerID, string enemyName, int bossNumber)
		{
			try
			{
				if (current != null && !(current.Enemy != enemyName))
				{
					try
					{
						ZRoutedRpc.instance.InvokeRoutedRPC(playerPeerID, "ValheimMoments_KillEvent_v1", new object[2] { enemyName, current.EventId });
					}
					catch
					{
					}
					if (current.Subject != null)
					{
						ZRoutedRpc.instance.InvokeRoutedRPC(playerPeerID, "ValheimMoments_CinematicSubject_v1", new object[2] { enemyName, current.Subject });
					}
					ZRoutedRpc.instance.InvokeRoutedRPC(playerPeerID, "ValheimMoments_BossFinalBlow_v1", new object[2]
					{
						enemyName,
						current.Name ?? ""
					});
					ZRoutedRpc.instance.InvokeRoutedRPC(playerPeerID, "ValheimMoments_KillCredits_v1", new object[2]
					{
						enemyName,
						current.Credits ?? ""
					});
				}
			}
			catch
			{
			}
		}

		internal static string Take(long sender, string enemy)
		{
			if (sender == 0L && current != null && current.Enemy == enemy)
			{
				return current.Name;
			}
			string text = inbox.Take(sender, enemy, clock.Elapsed.TotalSeconds);
			try
			{
				OnDiagnostic?.Invoke((text == null) ? "owner metadata missing or expired" : ((text.Length == 0) ? "owner reported no player attacker" : "received owner attribution"));
			}
			catch
			{
			}
			return text;
		}

		internal static string TakeCredits(long sender, string enemy)
		{
			if (sender == 0L && current != null && current.Enemy == enemy)
			{
				return current.Credits;
			}
			return creditInbox.Take(sender, enemy, clock.Elapsed.TotalSeconds);
		}

		internal static string TakeEvent(long sender, string enemy)
		{
			if (sender == 0L && current != null && current.Enemy == enemy)
			{
				return current.EventId;
			}
			return eventInbox.Take(sender, enemy, clock.Elapsed.TotalSeconds);
		}

		internal static string TakeSubject(long sender, string enemy)
		{
			if (sender != 0L || current == null || !(current.Enemy == enemy))
			{
				return subjectInbox.Take(sender, enemy, clock.Elapsed.TotalSeconds);
			}
			return current.Subject;
		}

		internal static void Clear()
		{
			current = null;
			inbox.Clear();
			creditInbox.Clear();
			eventInbox.Clear();
			subjectInbox.Clear();
			OnDiagnostic = null;
			CinematicSubject = null;
		}
	}
	internal sealed class AttributionInbox
	{
		private sealed class Entry
		{
			internal long Sender;

			internal string Enemy;

			internal string Name;

			internal double Time;
		}

		private readonly List<Entry> entries = new List<Entry>();

		private readonly int maximumName;

		internal AttributionInbox(int maximumName = 256)
		{
			this.maximumName = maximumName;
		}

		internal void Add(long sender, string enemy, string name, double now)
		{
			if (!string.IsNullOrEmpty(enemy) && enemy.Length <= 256 && name != null && name.Length <= maximumName)
			{
				entries.RemoveAll((Entry e) => now - e.Time > 5.0 || (e.Sender == sender && e.Enemy == enemy));
				if (entries.Count >= 64)
				{
					entries.RemoveAt(0);
				}
				entries.Add(new Entry
				{
					Sender = sender,
					Enemy = enemy,
					Name = name,
					Time = now
				});
			}
		}

		internal string Take(long sender, string enemy, double now)
		{
			entries.RemoveAll((Entry e) => now - e.Time > 5.0);
			int num = entries.FindIndex((Entry e) => e.Sender == sender && e.Enemy == enemy);
			if (num < 0)
			{
				return null;
			}
			string name = entries[num].Name;
			entries.RemoveAt(num);
			return name;
		}

		internal void Clear()
		{
			entries.Clear();
		}
	}
	internal sealed class BossKill
	{
		internal string EnemyKey;

		internal string PlayerName;

		internal string FinalBlowName;

		internal string CreditNames;

		internal string EventId;

		internal string CinematicSubject;

		internal int BossNumber;

		internal bool FirstKill;

		internal bool Acquired;

		internal bool Special;

		internal BossLoot Loot;
	}
	internal static class BossKillDetector
	{
		private sealed class State
		{
			internal PlayerProfile Profile;

			internal string EnemyKey;

			internal int BossNumber;

			internal float Count;

			internal string FinalBlowName;

			internal string CreditNames;

			internal string EventId;

			internal string Subject;

			internal BossLoot Loot;
		}

		internal static Action<BossKill> OnKill;

		internal static Action<BossKill> OnLootKill;

		internal static Func<bool> ObserveOrdinary;

		internal static Action OnError;

		private const BindingFlags Fields = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly FieldInfo Stats = typeof(PlayerProfile).GetField("m_playerStats", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		internal static bool TryCount(PlayerProfile profile, string key, out float count)
		{
			count = 0f;
			try
			{
				if (!(Stats?.GetValue(profile) is Array { Length: not 0 } array))
				{
					return false;
				}
				object value = array.GetValue(0);
				if (!((value?.GetType().GetField("m_enemyStats", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(value) is Dictionary<string, float>[] array2) || array2.Length == 0 || array2[0] == null || string.IsNullOrEmpty(key))
				{
					return false;
				}
				array2[0].TryGetValue(key, out count);
				return !float.IsNaN(count) && !float.IsInfinity(count) && count >= 0f;
			}
			catch
			{
				return false;
			}
		}

		internal static void Install(Harmony harmony)
		{
			//IL_00aa: 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_00cc: Expected O, but got Unknown
			//IL_00cc: Expected O, but got Unknown
			MethodInfo method = typeof(Game).GetMethod("RPC_RegisterKill", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[6]
			{
				typeof(long),
				typeof(string),
				typeof(int),
				typeof(int),
				typeof(int),
				typeof(bool)
			}, null);
			if (method == null || method.ReturnType != typeof(void))
			{
				throw new MissingMethodException("Expected Game.RPC_RegisterKill signature not found.");
			}
			harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(BossKillDetector), "Prefix", (Type[])null), new HarmonyMethod(typeof(BossKillDetector), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static void Prefix(Game __instance, long sender, string enemyName, int bossNumber, out State __state)
		{
			__state = null;
			try
			{
				string eventId = BossAttribution.TakeEvent(sender, enemyName);
				string subject = BossAttribution.TakeSubject(sender, enemyName);
				string finalBlowName = BossAttribution.Take(sender, enemyName);
				string creditNames = BossAttribution.TakeCredits(sender, enemyName);
				if (bossNumber <= 0)
				{
					Func<bool> observeOrdinary = ObserveOrdinary;
					if (observeOrdinary == null || !observeOrdinary())
					{
						return;
					}
				}
				BossLoot loot = BossLootDetector.Take(sender, enemyName);
				PlayerProfile playerProfile = __instance.GetPlayerProfile();
				if (!TryCount(playerProfile, enemyName, out var count))
				{
					ReportError();
					return;
				}
				__state = new State
				{
					Profile = playerProfile,
					EnemyKey = enemyName,
					BossNumber = bossNumber,
					Count = count,
					FinalBlowName = finalBlowName,
					CreditNames = creditNames,
					Loot = loot,
					EventId = eventId,
					Subject = subject
				};
			}
			catch
			{
				ReportError();
			}
		}

		private static void Postfix(State __state)
		{
			if (__state == null)
			{
				return;
			}
			try
			{
				if (!TryCount(__state.Profile, __state.EnemyKey, out var count))
				{
					ReportError();
				}
				else if (!(count <= __state.Count))
				{
					((__state.BossNumber > 0) ? OnKill : OnLootKill)?.Invoke(new BossKill
					{
						EnemyKey = __state.EnemyKey,
						BossNumber = __state.BossNumber,
						PlayerName = __state.Profile.GetName(),
						FirstKill = (__state.Count == 0f),
						FinalBlowName = __state.FinalBlowName,
						CreditNames = __state.CreditNames,
						Loot = __state.Loot,
						EventId = __state.EventId,
						CinematicSubject = __state.Subject
					});
				}
			}
			catch
			{
				ReportError();
			}
		}

		private static void ReportError()
		{
			try
			{
				OnError?.Invoke();
			}
			catch
			{
			}
		}
	}
	internal sealed class LootItem
	{
		internal string Id;

		internal string Name;

		internal int Quantity;

		internal int Rank = -1;

		internal int Sockets;

		internal string Rarity = "";

		internal string Color = "";

		internal string Modifiers = "";

		internal bool Unidentified;
	}
	internal sealed class BossLoot
	{
		internal readonly string Id = Guid.NewGuid().ToString("N");

		internal readonly List<LootItem> Items = new List<LootItem>();

		internal bool Observed;

		internal bool Incomplete;

		internal bool Pending;

		internal int Revision;

		internal void AddEpic(LootItem item)
		{
			if (item == null || string.IsNullOrEmpty(item.Name) || string.IsNullOrEmpty(item.Id) || item.Quantity <= 0 || Items.Count >= 64)
			{
				Incomplete = true;
				return;
			}
			Items.Add(item);
			Observed = true;
		}

		internal void Add(string id, string name, long quantity)
		{
			if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(name) || quantity <= 0)
			{
				Incomplete = true;
				return;
			}
			if (id.Length > 128 || name.Length > 256 || quantity > int.MaxValue)
			{
				Incomplete = true;
				return;
			}
			LootItem lootItem = Items.Find((LootItem i) => i.Id == id && i.Name == name);
			if (lootItem != null)
			{
				if (quantity > int.MaxValue - lootItem.Quantity)
				{
					Incomplete = true;
				}
				else
				{
					lootItem.Quantity += (int)quantity;
				}
			}
			else if (Items.Count < 64)
			{
				Items.Add(new LootItem
				{
					Id = id,
					Name = name,
					Quantity = (int)quantity
				});
			}
			else
			{
				Incomplete = true;
			}
		}

		internal string Display(int maximum, bool quantity, Func<string, string> localize, bool showRarity = true, bool showModifiers = true, bool showSockets = true, bool showUnidentified = true)
		{
			if (!Observed)
			{
				return "unavailable";
			}
			if (Items.Count == 0)
			{
				if (!Incomplete)
				{
					return "No items generated.";
				}
				return "unavailable";
			}
			List<LootItem> list = new List<LootItem>();
			foreach (LootItem item in Items)
			{
				list.Add(new LootItem
				{
					Id = item.Id,
					Name = localize(item.Name),
					Quantity = item.Quantity,
					Rank = item.Rank,
					Rarity = localize(item.Rarity),
					Color = item.Color,
					Modifiers = localize(item.Modifiers),
					Sockets = item.Sockets,
					Unidentified = item.Unidentified
				});
			}
			list.Sort(delegate(LootItem a, LootItem b)
			{
				int num3 = b.Rank.CompareTo(a.Rank);
				if (num3 != 0)
				{
					return num3;
				}
				num3 = StringComparer.OrdinalIgnoreCase.Compare(a.Name, b.Name);
				return (num3 == 0) ? StringComparer.Ordinal.Compare(a.Id, b.Id) : num3;
			});
			maximum = Math.Max(1, Math.Min(20, maximum));
			List<string> list2 = new List<string>();
			for (int num = 0; num < Math.Min(maximum, list.Count); num++)
			{
				LootItem lootItem = list[num];
				string text = ((showRarity && lootItem.Rank >= 0) ? (Marker(lootItem.Color) + " " + WithRarity(lootItem.Name, lootItem.Rarity)) : lootItem.Name);
				list2.Add("* " + text + (quantity ? (" ×" + lootItem.Quantity) : "") + ((showUnidentified && lootItem.Unidentified) ? " (unidentified)" : ""));
				if (!lootItem.Unidentified && showModifiers && !string.IsNullOrEmpty(lootItem.Modifiers))
				{
					string[] array = lootItem.Modifiers.Replace("\r\n", "\n").Replace('\r', '\n').Split('\n');
					foreach (string text2 in array)
					{
						if (!string.IsNullOrWhiteSpace(text2))
						{
							list2.Add("  * -# " + text2.Trim());
						}
					}
				}
				if (!lootItem.Unidentified && showSockets && lootItem.Sockets > 0)
				{
					list2.Add("  * -# Sockets: " + lootItem.Sockets);
				}
			}
			if (list.Count > maximum)
			{
				list2.Add("+" + (list.Count - maximum) + " more item types");
			}
			if (Incomplete)
			{
				list2.Add("Some loot details unavailable.");
			}
			if (Pending)
			{
				list2.Add("Additional Epic Loot details unavailable before upload.");
			}
			return string.Join("\n", list2);
		}

		private static string WithRarity(string name, string rarity)
		{
			string text = (rarity ?? "").Trim();
			if (text.Length == 0)
			{
				return name;
			}
			string text2 = (name ?? "").TrimStart();
			while (text2.StartsWith(text, StringComparison.OrdinalIgnoreCase))
			{
				if (text2.Length == text.Length)
				{
					return text;
				}
				if (!char.IsWhiteSpace(text2[text.Length]))
				{
					break;
				}
				text2 = text2.Substring(text.Length).TrimStart();
			}
			return text + ((text2.Length == 0) ? "" : (" " + text2));
		}

		private static string Marker(string color)
		{
			if (color == null || color.Length != 7 || color[0] != '#' || !int.TryParse(color.Substring(1), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result))
			{
				return "◆";
			}
			int[] array = new int[7] { 4359668, 10181046, 15965202, 15022389, 4431943, 16635957, 15658734 };
			string[] array2 = new string[7] { "\ud83d\udd35", "\ud83d\udfe3", "\ud83d\udfe0", "\ud83d\udd34", "\ud83d\udfe2", "\ud83d\udfe1", "⚪" };
			int num = 0;
			int num2 = int.MaxValue;
			for (int i = 0; i < array.Length; i++)
			{
				int num3 = (result >> 16) - (array[i] >> 16);
				int num4 = ((result >> 8) & 0xFF) - ((array[i] >> 8) & 0xFF);
				int num5 = (result & 0xFF) - (array[i] & 0xFF);
				int num6 = num3 * num3 + num4 * num4 + num5 * num5;
				if (num6 < num2)
				{
					num2 = num6;
					num = i;
				}
			}
			return array2[num];
		}

		internal string Encode()
		{
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true))
			{
				binaryWriter.Write(2);
				binaryWriter.Write(Observed);
				binaryWriter.Write(Incomplete);
				binaryWriter.Write(Pending);
				binaryWriter.Write(Revision);
				binaryWriter.Write(Items.Count);
				foreach (LootItem item in Items)
				{
					binaryWriter.Write(item.Id);
					binaryWriter.Write(item.Name);
					binaryWriter.Write(item.Quantity);
					binaryWriter.Write(item.Rank);
					binaryWriter.Write(item.Rarity);
					binaryWriter.Write(item.Color);
					binaryWriter.Write(item.Modifiers);
					binaryWriter.Write(item.Sockets);
					binaryWriter.Write(item.Unidentified);
				}
			}
			return Convert.ToBase64String(memoryStream.ToArray());
		}

		internal static BossLoot Decode(string encoded)
		{
			if (encoded == null || encoded.Length > 524288)
			{
				return null;
			}
			try
			{
				using MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(encoded));
				using BinaryReader binaryReader = new BinaryReader(memoryStream, Encoding.UTF8);
				if (binaryReader.ReadInt32() != 2)
				{
					return null;
				}
				BossLoot bossLoot = new BossLoot
				{
					Observed = binaryReader.ReadBoolean(),
					Incomplete = binaryReader.ReadBoolean(),
					Pending = binaryReader.ReadBoolean(),
					Revision = binaryReader.ReadInt32()
				};
				if (bossLoot.Revision < 0)
				{
					return null;
				}
				int num = binaryReader.ReadInt32();
				if (num < 0 || num > 64)
				{
					return null;
				}
				for (int i = 0; i < num; i++)
				{
					string text = binaryReader.ReadString();
					string text2 = binaryReader.ReadString();
					int num2 = binaryReader.ReadInt32();
					if (text.Length == 0 || text.Length > 128 || text2.Length == 0 || text2.Length > 256 || num2 <= 0)
					{
						return null;
					}
					LootItem lootItem = new LootItem
					{
						Id = text,
						Name = text2,
						Quantity = num2,
						Rank = binaryReader.ReadInt32(),
						Rarity = binaryReader.ReadString(),
						Color = binaryReader.ReadString(),
						Modifiers = binaryReader.ReadString(),
						Sockets = binaryReader.ReadInt32(),
						Unidentified = binaryReader.ReadBoolean()
					};
					if (lootItem.Rank < -1 || lootItem.Rank > 100 || lootItem.Rarity.Length > 64 || lootItem.Color.Length > 16 || lootItem.Modifiers.Length > 1024 || lootItem.Sockets < 0 || lootItem.Sockets > 64)
					{
						return null;
					}
					bossLoot.Items.Add(lootItem);
				}
				return (memoryStream.Position == memoryStream.Length) ? bossLoot : null;
			}
			catch
			{
				return null;
			}
		}
	}
	internal sealed class LootInbox
	{
		private sealed class Entry
		{
			internal long Sender;

			internal string Enemy;

			internal string Token;

			internal double Time;

			internal bool Taken;

			internal bool Completed;

			internal BossLoot Loot = new BossLoot();
		}

		private readonly List<Entry> entries = new List<Entry>();

		private void Prune(double now)
		{
			entries.RemoveAll((Entry e) => now - e.Time > 30.0);
		}

		internal void Announce(long sender, string enemy, string token, double now)
		{
			if (string.IsNullOrEmpty(enemy) || enemy.Length > 256 || token == null || token.Length != 32 || !Guid.TryParseExact(token, "N", out var _))
			{
				return;
			}
			Prune(now);
			if (!entries.Exists((Entry e) => e.Sender == sender && e.Token == token))
			{
				if (entries.Count >= 64)
				{
					entries.RemoveAt(0);
				}
				entries.Add(new Entry
				{
					Sender = sender,
					Enemy = enemy,
					Token = token,
					Time = now
				});
			}
		}

		internal BossLoot Take(long sender, string enemy, double now)
		{
			Prune(now);
			Entry entry = entries.FindLast((Entry e) => !e.Taken && e.Sender == sender && e.Enemy == enemy && now - e.Time <= 5.0);
			if (entry == null)
			{
				return null;
			}
			entry.Taken = true;
			return entry.Loot;
		}

		internal void Complete(long sender, string token, string payload, double now)
		{
			Prune(now);
			Entry entry = entries.Find((Entry e) => e.Sender == sender && e.Token == token);
			if (entry != null)
			{
				BossLoot bossLoot = BossLoot.Decode(payload);
				if (bossLoot != null && (!entry.Completed || bossLoot.Revision > entry.Loot.Revision))
				{
					entry.Loot.Items.Clear();
					entry.Loot.Items.AddRange(bossLoot.Items);
					entry.Loot.Observed = bossLoot.Observed;
					entry.Loot.Incomplete = bossLoot.Incomplete;
					entry.Loot.Pending = bossLoot.Pending;
					entry.Loot.Revision = bossLoot.Revision;
					entry.Completed = true;
				}
			}
		}

		internal void Clear()
		{
			entries.Clear();
		}
	}
	internal static class BossLootDetector
	{
		private sealed class Context
		{
			internal Character Character;

			internal BossLoot Loot = new BossLoot();

			internal HashSet<long> Recipients = new HashSet<long>();

			internal HashSet<ItemData> EpicItems = new HashSet<ItemData>();

			internal int PendingRagdolls;
		}

		private const string CreditRpc = "ValheimMoments_BossLootCredit_v2";

		private const string ResultRpc = "ValheimMoments_BossLootResult_v2";

		private static readonly FieldInfo DropCharacter = typeof(CharacterDrop).GetField("m_character", BindingFlags.Instance | BindingFlags.NonPublic);

		[ThreadStatic]
		private static Context current;

		private static readonly LootInbox inbox = new LootInbox();

		private static readonly Stopwatch clock = Stopwatch.StartNew();

		private static ZRoutedRpc registered;

		private static bool enabled;

		private static ConditionalWeakTable<object, Context> ragdolls = new ConditionalWeakTable<object, Context>();

		internal static Action<int> OnObserved;

		internal static Action OnError;

		internal static void EnableEpic(Harmony harmony)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Expected O, but got Unknown
			//IL_007f: Expected O, but got Unknown
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Ragdoll), "Setup", (Type[])null, (Type[])null), new HarmonyMethod(typeof(BossLootDetector), "BeforeRagdollSetup", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Ragdoll), "SpawnLoot", (Type[])null, (Type[])null), new HarmonyMethod(typeof(BossLootDetector), "BeforeRagdollLoot", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(BossLootDetector), "AfterRagdollLoot", (Type[])null), (HarmonyMethod)null);
		}

		private static void BeforeRagdollSetup(object __instance, CharacterDrop characterDrop)
		{
			try
			{
				if (current != null && DropCharacter.GetValue(characterDrop) == current.Character)
				{
					ragdolls.Remove(__instance);
					ragdolls.Add(__instance, current);
					current.PendingRagdolls++;
					current.Loot.Pending = true;
				}
			}
			catch
			{
				Error();
			}
		}

		private static void BeforeRagdollLoot(object __instance, out Context __state)
		{
			__state = current;
			current = ((enabled && ragdolls.TryGetValue(__instance, out var value)) ? value : null);
		}

		private static void AfterRagdollLoot(object __instance, Context __state, Exception __exception)
		{
			Context context = current;
			current = __state;
			try
			{
				if (context != null)
				{
					ragdolls.Remove(__instance);
					context.PendingRagdolls = Math.Max(0, context.PendingRagdolls - 1);
					context.Loot.Pending = context.PendingRagdolls > 0;
					if (__exception != null)
					{
						context.Loot.Incomplete = true;
					}
					Publish(context);
					if (!context.Loot.Pending)
					{
						context.EpicItems.Clear();
					}
				}
			}
			catch
			{
				Error();
			}
		}

		internal static void RecordEpic(List<GameObject> objects)
		{
			if (!enabled || current == null || objects == null)
			{
				return;
			}
			foreach (GameObject @object in objects)
			{
				try
				{
					ItemData val2 = (((Object)(object)@object == (Object)null) ? null : @object.GetComponent<ItemDrop>()?.m_itemData);
					if (current.EpicItems.Count >= 256)
					{
						current.Loot.Incomplete = true;
						break;
					}
					if (val2 != null && current.EpicItems.Add(val2))
					{
						current.Loot.AddEpic(EpicLootAdapter.Read(val2, ((Object)@object).name));
						OnObserved?.Invoke(current.Loot.Items.Count);
					}
				}
				catch
				{
					current.Loot.Incomplete = true;
					Error();
				}
			}
		}

		internal static void Install(Harmony harmony)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_006b: Expected O, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Expected O, but got Unknown
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Expected O, but got Unknown
			if (DropCharacter == null)
			{
				throw new MissingFieldException("CharacterDrop.m_character");
			}
			enabled = true;
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Character), "OnDeath", Type.EmptyTypes, (Type[])null), new HarmonyMethod(typeof(BossLootDetector), "BeforeDeath", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(BossLootDetector), "AfterDeath", (Type[])null), (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(CharacterDrop), "GenerateDropList", Type.EmptyTypes, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(BossLootDetector), "AfterRoll", (Type[])null)
			{
				priority = 0
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Game), "RegisterKill", new Type[6]
			{
				typeof(long),
				typeof(string),
				typeof(int),
				typeof(KillModifiers),
				typeof(int),
				typeof(bool)
			}, (Type[])null), new HarmonyMethod(typeof(BossLootDetector), "BeforeCredit", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			foreach (ConstructorInfo declaredConstructor in AccessTools.GetDeclaredConstructors(typeof(ZRoutedRpc), (bool?)null))
			{
				harmony.Patch((MethodBase)declaredConstructor, (HarmonyMethod)null, new HarmonyMethod(typeof(BossLootDetector), "Register", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			Register(ZRoutedRpc.instance);
		}

		private static void Register(ZRoutedRpc __instance)
		{
			try
			{
				if (__instance == null || registered == __instance)
				{
					return;
				}
				__instance.Register<string, string>("ValheimMoments_BossLootCredit_v2", (Action<long, string, string>)delegate(long sender, string enemy, string token)
				{
					if (enabled)
					{
						inbox.Announce(sender, enemy, token, clock.Elapsed.TotalSeconds);
					}
				});
				__instance.Register<string, string>("ValheimMoments_BossLootResult_v2", (Action<long, string, string>)delegate(long sender, string token, string payload)
				{
					if (enabled)
					{
						inbox.Complete(sender, token, payload, clock.Elapsed.TotalSeconds);
					}
				});
				registered = __instance;
				inbox.Clear();
			}
			catch
			{
				Error();
			}
		}

		private static void BeforeDeath(Character __instance, out Context __state)
		{
			__state = current;
			current = null;
			try
			{
				if (!enabled || !__instance.IsOwner())
				{
					return;
				}
				if (!__instance.IsBoss())
				{
					Func<bool> observeOrdinary = BossKillDetector.ObserveOrdinary;
					if (observeOrdinary == null || !observeOrdinary())
					{
						return;
					}
				}
				current = new Context
				{
					Character = __instance
				};
			}
			catch
			{
				Error();
			}
		}

		private static void AfterDeath(Context __state, Exception __exception)
		{
			Context context = current;
			current = __state;
			try
			{
				if (context != null)
				{
					if (__exception != null)
					{
						context.Loot.Incomplete = true;
					}
					Publish(context);
				}
			}
			catch
			{
				Error();
			}
		}

		private static void Publish(Context context)
		{
			context.Loot.Revision++;
			string text = context.Loot.Encode();
			foreach (long recipient in context.Recipients)
			{
				try
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(recipient, "ValheimMoments_BossLootResult_v2", new object[2]
					{
						context.Loot.Id,
						text
					});
				}
				catch
				{
					Error();
				}
			}
		}

		private static void BeforeCredit(long playerPeerID, string enemyName, int bossNumber)
		{
			try
			{
				if (current != null && !(current.Character.m_name != enemyName) && current.Recipients.Add(playerPeerID))
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(playerPeerID, "ValheimMoments_BossLootCredit_v2", new object[2]
					{
						enemyName,
						current.Loot.Id
					});
				}
			}
			catch
			{
				Error();
			}
		}

		internal static BossLoot Take(long sender, string enemy)
		{
			if (sender == 0L && current != null && current.Character.m_name == enemy)
			{
				return current.Loot;
			}
			return inbox.Take(sender, enemy, clock.Elapsed.TotalSeconds);
		}

		private static void AfterRoll(CharacterDrop __instance, List<KeyValuePair<GameObject, int>> __result)
		{
			try
			{
				if (current == null || DropCharacter.GetValue(__instance) != current.Character)
				{
					return;
				}
				if (__result == null)
				{
					current.Loot.Incomplete = true;
					return;
				}
				current.Loot.Observed = true;
				foreach (KeyValuePair<GameObject, int> item in __result)
				{
					if ((Object)(object)item.Key == (Object)null || item.Value <= 0)
					{
						current.Loot.Incomplete = true;
						continue;
					}
					ItemDrop component = item.Key.GetComponent<ItemDrop>();
					if ((Object)(object)component == (Object)null || component.m_itemData?.m_shared == null)
					{
						current.Loot.Incomplete = true;
					}
					else
					{
						current.Loot.Add(((Object)item.Key).name, component.m_itemData.m_shared.m_name, (long)item.Value * (long)component.m_itemData.m_stack);
					}
				}
				OnObserved?.Invoke(current.Loot.Items.Count);
			}
			catch
			{
				if (current != null)
				{
					current.Loot.Incomplete = true;
				}
				Error();
			}
		}

		private static void Error()
		{
			try
			{
				OnError?.Invoke();
			}
			catch
			{
			}
		}

		internal static void Clear()
		{
			enabled = false;
			current = null;
			ragdolls = new ConditionalWeakTable<object, Context>();
			inbox.Clear();
			OnObserved = null;
			OnError = null;
		}
	}
	internal enum LootDecision
	{
		Accept,
		Wait,
		Reject
	}
	internal static class BossLootFilter
	{
		private static readonly Dictionary<string, int> ranks = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

		internal static void SetRarities(Type enumType)
		{
			ranks.Clear();
			foreach (object value in Enum.GetValues(enumType))
			{
				ranks[Enum.GetName(enumType, value)] = Convert.ToInt32(value);
			}
		}

		internal static bool TryRank(string name, out int rank)
		{
			rank = -1;
			if (string.Equals(name?.Trim(), "None", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			return ranks.TryGetValue(name?.Trim() ?? "", out rank);
		}

		internal static LootDecision Decide(BossLoot loot, int minimum, bool deadlineReached)
		{
			if (minimum < 0)
			{
				return LootDecision.Accept;
			}
			if (loot != null)
			{
				foreach (LootItem item in loot.Items)
				{
					if (item.Rank >= minimum)
					{
						return LootDecision.Accept;
					}
				}
			}
			if (!deadlineReached && (loot == null || loot.Pending))
			{
				return LootDecision.Wait;
			}
			return LootDecision.Reject;
		}

		internal static LootDecision Evaluate(BossLoot loot, bool enabled, bool firstKill, bool firstBypasses, string minimumName, bool deadlineReached, out string reason)
		{
			if (!enabled)
			{
				reason = "rarity filter disabled";
				return LootDecision.Accept;
			}
			if (firstKill && firstBypasses)
			{
				reason = "first recorded boss kill bypasses rarity";
				return LootDecision.Accept;
			}
			if (!TryRank(minimumName, out var rank))
			{
				reason = "rarity name unsupported or Epic Loot unavailable";
				return LootDecision.Reject;
			}
			LootDecision lootDecision = Decide(loot, rank, deadlineReached);
			reason = lootDecision switch
			{
				LootDecision.Wait => "awaiting boss loot", 
				LootDecision.Accept => "observed loot meets threshold (or threshold is None)", 
				_ => "no observed item met rarity threshold", 
			};
			return lootDecision;
		}
	}
	internal enum CinematicMovement
	{
		Orbit,
		RiseAndReveal,
		ZoomIn
	}
	internal sealed class CinematicCamera : IDisposable
	{
		private sealed class Shot
		{
			internal string Key;

			internal byte Kind;

			internal double Started;

			internal RaidMedia Media;

			internal Task<string> Worker;

			internal CaptureBuffer.Clip Clip;

			internal byte[] Data;

			internal string Caption;

			internal bool Letterbox;
		}

		internal const string SpawnStamp = "ValheimMoments_CinematicSpawn";

		private readonly List<Shot> shots = new List<Shot>();

		private readonly string root;

		private readonly string helper;

		private readonly Action<string> log;

		private Camera camera;

		private RenderTexture target;

		private CaptureBuffer buffer;

		private Shot active;

		private Transform subject;

		private Vector3 focus;

		private Vector3 direction;

		private float radius;

		private float distanceScale;

		private float panDegrees;

		private float focusHeight;

		private bool rise;

		private bool zoomIn;

		private bool flipImage;

		private double nextFrame;

		private double submitted;

		private AsyncGPUReadbackRequest request;

		private bool reading;

		private bool disposed;

		private int quality;

		internal bool Busy => active != null;

		internal CinematicCamera(string root, string helper, Action<string> log)
		{
			this.root = root;
			this.helper = helper;
			this.log = log;
		}

		internal static string Subject(Character character)
		{
			object obj;
			if (character == null)
			{
				obj = null;
			}
			else
			{
				ZNetView component = ((Component)character).GetComponent<ZNetView>();
				obj = ((component != null) ? component.GetZDO() : null);
			}
			return ((object)Unsafe.As<ZDOID, ZDOID>(ref ((ZDO)(obj?)).m_uid)/*cast due to .constrained prefix*/).ToString();
		}

		private static void BeforeView(out bool __state)
		{
			__state = AccessTools.Field(typeof(ZNetView), "m_initZDO")?.GetValue(null) == null;
		}

		private static void AfterView(ZNetView __instance, bool __state)
		{
			try
			{
				Character component = ((Component)__instance).GetComponent<Character>();
				if (__state && (Object)(object)component != (Object)null && !(component is Player) && __instance.IsOwner() && (Object)(object)ZNet.instance != (Object)null)
				{
					ZDO zDO = __instance.GetZDO();
					if (zDO != null)
					{
						zDO.Set("ValheimMoments_CinematicSpawn", ZNet.instance.GetTime().Ticks);
					}
				}
			}
			catch
			{
			}
		}

		internal static void Install(Harmony harmony)
		{
			//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_0049: Expected O, but got Unknown
			//IL_0049: Expected O, but got Unknown
			harmony.Patch((MethodBase)AccessTools.Method(typeof(ZNetView), "Awake", (Type[])null, (Type[])null), new HarmonyMethod(typeof(CinematicCamera), "BeforeView", (Type[])null), new HarmonyMethod(typeof(CinematicCamera), "AfterView", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		internal bool Start(string key, byte kind, Vector3 center, Transform follow, float size, double now, int q, float distanceMultiplier = 2f, float sweepDegrees = 70f, CinematicMovement movement = CinematicMovement.Orbit, string caption = null, bool letterbox = true, bool flip = true)
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: 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_00d0: Expected O, but got Unknown
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Expected O, but got Unknown
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_030d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Unknown result type (might be due to invalid IL or missing references)
			//IL_0340: Unknown result type (might be due to invalid IL or missing references)
			//IL_0345: Unknown result type (might be due to invalid IL or missing references)
			if (disposed || active != null || shots.Exists((Shot s) => s.Worker != null) || (Object)(object)Camera.main == (Object)null || string.IsNullOrEmpty(key) || shots.Exists((Shot s) => s.Key == key && s.Kind == kind) || Vector3.Distance(((Component)Camera.main).transform.position, center) > 100f)
			{
				return false;
			}
			Tick(now);
			if (shots.Count >= 4)
			{
				return false;
			}
			try
			{
				GameObject val = new GameObject("Valheim Moments cinematic camera")
				{
					hideFlags = (HideFlags)61
				};
				camera = val.AddComponent<Camera>();
				camera.CopyFrom(Camera.main);
				((Behaviour)camera).enabled = false;
				((Component)camera).tag = "Untagged";
				camera.stereoTargetEye = (StereoTargetEyeMask)0;
				camera.fieldOfView = 55f;
				camera.nearClipPlane = 0.15f;
				camera.farClipPlane = Mathf.Min(camera.farClipPlane, 250f);
				Camera obj = camera;
				obj.cullingMask &= -33;
				target = new RenderTexture(640, 360, 24, (RenderTextureFormat)0)
				{
					hideFlags = (HideFlags)61
				};
				target.Create();
				camera.targetTexture = target;
				camera.aspect = 1.7777778f;
				buffer = new CaptureBuffer(640, 360, 10, 0.1, 4.0, 50331648L);
				buffer.TryTriggerSegment(now, 4.0);
				active = new Shot
				{
					Key = key,
					Kind = kind,
					Started = now,
					Media = new RaidMedia(root),
					Caption = (caption ?? CinematicPacket.Label(kind)),
					Letterbox = letterbox
				};
				shots.Add(active);
				subject = follow;
				focus = center;
				quality = q;
				radius = Mathf.Clamp(size * 2.8f, 7f, 24f);
				focusHeight = radius / 5.6f;
				distanceScale = Mathf.Clamp(distanceMultiplier, 1f, 3f);
				panDegrees = Mathf.Clamp(sweepDegrees, 0f, 180f);
				rise = movement == CinematicMovement.RiseAndReveal;
				zoomIn = movement == CinematicMovement.ZoomIn;
				flipImage = flip;
				direction = ((Component)Camera.main).transform.position - center;
				direction.y = 0f;
				if (((Vector3)(ref direction)).sqrMagnitude < 0.1f)
				{
					direction = Vector3.back;
				}
				((Vector3)(ref direction)).Normalize();
				nextFrame = now;
				return true;
			}
			catch (Exception ex)
			{
				log("Camera setup skipped: " + ex.GetType().Name);
				CancelActive();
				return false;
			}
		}

		internal void Render(double now)
		{
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: 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_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: 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_0263: 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_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: 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_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0354: Unknown result type (might be due to invalid IL or missing references)
			//IL_0359: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0381: Unknown result type (might be due to invalid IL or missing references)
			//IL_0387: Unknown result type (might be due to invalid IL or missing references)
			//IL_038e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			//IL_039c: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0303: Unknown result type (might be due to invalid IL or missing references)
			//IL_0308: Unknown result type (might be due to invalid IL or missing references)
			//IL_030a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0314: Unknown result type (might be due to invalid IL or missing references)
			//IL_0319: Unknown result type (might be due to invalid IL or missing references)
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_032d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0330: Unknown result type (might be due to invalid IL or missing references)
			//IL_0335: Unknown result type (might be due to invalid IL or missing references)
			//IL_0408: Unknown result type (might be due to invalid IL or missing references)
			//IL_040d: 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_0344: Unknown result type (might be due to invalid IL or missing references)
			if (active == null || disposed)
			{
				return;
			}
			try
			{
				Drain();
				if (now >= active.Started + 4.0)
				{
					if (reading)
					{
						return;
					}
					CaptureBuffer.Clip clip = buffer.TryComplete(now);
					if (clip == null || clip.Count < 10)
					{
						clip?.Release();
						CancelActive();
						return;
					}
					Shot shot = active;
					shot.Clip = clip;
					int q = quality;
					bool flip = flipImage;
					shot.Worker = shot.Media.Start(delegate(CancellationToken token)
					{
						string result = EncoderClient.Encode(clip, helper, shot.Media.Opening, 640, 360, q, flip, token);
						EncoderClient.Compose(helper, shot.Media.Opening, null, shot.Media.Combined, q, token, shot.Caption, cinematic: true, shot.Letterbox);
						if (new FileInfo(shot.Media.Combined).Length <= 4194304)
						{
							shot.Data = File.ReadAllBytes(shot.Media.Combined);
						}
						return result;
					});
					active = null;
					ReleaseCamera();
					buffer = null;
				}
				else
				{
					if (reading || now < nextFrame)
					{
						return;
					}
					nextFrame = now + 0.1;
					float num = Mathf.SmoothStep(0f, 1f, (float)((now - active.Started) / 4.0));
					if ((Object)(object)subject != (Object)null)
					{
						focus = Vector3.Lerp(focus, subject.position + Vector3.up * focusHeight, 0.15f);
					}
					float num2 = (rise ? Mathf.Lerp(7f, 15f, num) : (radius * Mathf.Lerp(1f, 0.88f, num))) * distanceScale;
					float num3 = (rise ? Mathf.Lerp(3f, 15f, num) : (radius * 0.35f)) * distanceScale;
					if (zoomIn)
					{
						float num4 = Mathf.Lerp(2f, 1f, num);
						num2 = radius * num4;
						num3 = radius * 0.35f * num4;
					}
					Vector3 val = Quaternion.AngleAxis(zoomIn ? 0f : Mathf.Lerp((0f - panDegrees) / 2f, panDegrees / 2f, num), Vector3.up) * direction * num2 + Vector3.up * num3;
					val = Vector3.ClampMagnitude(val, 80f);
					Vector3 val2 = focus + val;
					int mask = LayerMask.GetMask(new string[6] { "Default", "static_solid", "terrain", "piece", "Default_small", "small_solid" });
					if (Blocked(focus, val2, mask, out var hit))
					{
						for (int num5 = 1; num5 <= 3; num5++)
						{
							Vector3 val3 = focus + Vector3.ClampMagnitude(val + Vector3.up * (float)(num5 * 6), 80f);
							if (!Blocked(focus, val3, mask, out var _))
							{
								val2 = val3;
								break;
							}
						}
					}
					if (Blocked(focus, val2, mask, out hit))
					{
						if (((RaycastHit)(ref hit)).distance < 2f)
						{
							CancelActive();
							return;
						}
						Vector3 point = ((RaycastHit)(ref hit)).point;
						Vector3 val4 = focus - ((RaycastHit)(ref hit)).point;
						val2 = point + ((Vector3)(ref val4)).normalized * 0.5f;
					}
					((Component)camera).transform.SetPositionAndRotation(val2, Quaternion.LookRotation(focus - val2, Vector3.up));
					RenderTexture val5 = RenderTexture.active;
					try
					{
						camera.Render();
					}
					finally
					{
						RenderTexture.active = val5;
					}
					submitted = now;
					request = AsyncGPUReadback.Request((Texture)(object)target, 0, (TextureFormat)4, (Action<AsyncGPUReadbackRequest>)null);
					reading = true;
				}
			}
			catch (Exception ex)
			{
				log("Camera shot skipped: " + ex.GetType().Name);
				CancelActive();
			}
		}

		private static bool Blocked(Vector3 center, Vector3 destination, int mask, out RaycastHit hit)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: 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)
			Vector3 val = destination - center;
			return Physics.SphereCast(center, 0.6f, ((Vector3)(ref val)).normalized, ref hit, ((Vector3)(ref val)).magnitude, mask, (QueryTriggerInteraction)1);
		}

		private void Drain()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (reading && ((AsyncGPUReadbackRequest)(ref request)).done)
			{
				reading = false;
				if (((AsyncGPUReadbackRequest)(ref request)).hasError)
				{
					throw new IOException("Camera readback");
				}
				NativeArray<byte> data = ((AsyncGPUReadbackRequest)(ref request)).GetData<byte>(0);
				byte[] array = new byte[data.Length];
				data.CopyTo(array);
				buffer.AddFrame(array, submitted);
			}
		}

		internal void Tick(double now)
		{
			foreach (Shot item in new List<Shot>(shots))
			{
				if (item.Worker != null && item.Worker.IsCompleted)
				{
					if (item.Worker.IsFaulted || item.Worker.IsCanceled)
					{
						item.Data = null;
					}
					_ = item.Worker.Exception;
					item.Clip?.Release();
					item.Clip = null;
					item.Worker = null;
					item.Media.Dispose();
					if (item.Data == null)
					{
						shots.Remove(item);
					}
				}
				if (item != active && item.Worker == null && now - item.Started > 1800.0)
				{
					shots.Remove(item);
				}
			}
		}

		internal List<CinematicPacket.Shot> Take(string key)
		{
			List<CinematicPacket.Shot> list = new List<CinematicPacket.Shot>();
			foreach (Shot item in new List<Shot>(shots))
			{
				if (item.Key == key && item.Worker == null && item.Data != null)
				{
					list.Add(new CinematicPacket.Shot
					{
						Kind = item.Kind,
						Pixels = item.Data
					});
					shots.Remove(item);
				}
			}
			list.Sort((CinematicPacket.Shot a, CinematicPacket.Shot b) => a.Kind.CompareTo(b.Kind));
			return list;
		}

		internal bool Pending(string key)
		{
			return shots.Exists((Shot s) => s.Key == key && (s == active || s.Worker != null));
		}

		private void ReleaseCamera()
		{
			if (reading)
			{
				((AsyncGPUReadbackRequest)(ref request)).WaitForCompletion();
				reading = false;
			}
			if ((Object)(object)camera != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)camera).gameObject);
			}
			camera = null;
			if ((Object)(object)target != (Object)null)
			{
				target.Release();
				Object.Destroy((Object)(object)target);
			}
			target = null;
			subject = null;
		}

		private void CancelActive()
		{
			if (active != null)
			{
				active.Media.Dispose();
				shots.Remove(active);
				active = null;
			}
			ReleaseCamera();
			buffer = null;
		}

		public void Dispose()
		{
			if (disposed)
			{
				return;
			}
			disposed = true;
			CancelActive();
			foreach (Shot shot in shots)
			{
				shot.Media.Dispose();
				if (shot.Worker != null)
				{
					try
					{
						shot.Worker.GetAwaiter().GetResult();
					}
					catch
					{
					}
				}
				shot.Clip?.Release();
			}
			shots.Clear();
		}
	}
	internal static class CinematicPacket
	{
		internal sealed class Shot
		{
			internal byte Kind;

			internal byte[] Pixels;
		}

		internal const uint Chunk = 1229147478u;

		internal const int Limit = 10485760;

		internal static byte[] Strip(byte[] input, out List<Shot> shots)
		{
			shots = new List<Shot>();
			if (input.Length < 20 || input.Length > 10485760)
			{
				throw new InvalidDataException("Camera packet size");
			}
			using MemoryStream memoryStream = new MemoryStream(input, writable: false);
			using BinaryReader binaryReader = new BinaryReader(memoryStream);
			using MemoryStream memoryStream2 = new MemoryStream();
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream2);
			if (binaryReader.ReadUInt32() != 1179011410 || binaryReader.ReadUInt32() != input.Length - 8 || binaryReader.ReadUInt32() != 1346520407)
			{
				throw new InvalidDataException("Camera packet header");
			}
			binaryWriter.Write(1179011410);
			binaryWriter.Write(0);
			binaryWriter.Write(1346520407);
			HashSet<byte> hashSet = new HashSet<byte>();
			while (memoryStream.Position < memoryStream.Length)
			{
				if (memoryStream.Length - memoryStream.Position < 8)
				{
					throw new InvalidDataException("Camera packet chunk");
				}
				uint num = binaryReader.ReadUInt32();
				uint num2 = binaryReader.ReadUInt32();
				if ((long)num2 + (long)(num2 & 1) > memoryStream.Length - memoryStream.Position)
				{
					throw new InvalidDataException("Camera packet length");
				}
				if (num == 1229147478)
				{
					if (num2 < 22 || shots.Count == 2)
					{
						throw new InvalidDataException("Camera packet count");
					}
					byte num3 = binaryReader.ReadByte();
					byte b = binaryReader.ReadByte();
					if (num3 != 1 || b < 1 || b > 4 || !hashSet.Add(b))
					{
						throw new InvalidDataException("Camera packet role");
					}
					byte[] array = binaryReader.ReadBytes((int)(num2 - 2));
					if (ContainsCamera(array))
					{
						throw new InvalidDataException("Nested camera packet");
					}
					Strip(array, out var _);
					shots.Add(new Shot
					{
						Kind = b,
						Pixels = array
					});
				}
				else
				{
					binaryWriter.Write(num);
					binaryWriter.Write(num2);
					binaryWriter.Write(binaryReader.ReadBytes((int)num2));
					if ((num2 & 1) != 0)
					{
						binaryWriter.Write((byte)0);
					}
				}
				if ((num2 & 1) != 0 && binaryReader.ReadByte() != 0)
				{
					throw new InvalidDataException("Camera packet padding");
				}
			}
			binaryWriter.Flush();
			memoryStream2.Position = 4L;
			binaryWriter.Write((int)memoryStream2.Length - 8);
			return memoryStream2.ToArray();
		}

		private static bool ContainsCamera(byte[] data)
		{
			if (data.Length < 12)
			{
				return false;
			}
			using MemoryStream memoryStream = new MemoryStream(data, writable: false);
			using BinaryReader binaryReader = new BinaryReader(memoryStream);
			memoryStream.Position = 12L;
			while (memoryStream.Length - memoryStream.Position >= 8)
			{
				uint num = binaryReader.ReadUInt32();
				uint num2 = binaryReader.ReadUInt32();
				if (num == 1229147478)
				{
					return true;
				}
				long num3 = memoryStream.Position + num2 + (num2 & 1);
				if (num3 > memoryStream.Length)
				{
					return false;
				}
				memoryStream.Position = num3;
			}
			return false;
		}

		internal static void Attach(string file, IList<Shot> shots)
		{
			if (shots.Count == 0)
			{
				return;
			}
			List<Shot> shots2;
			byte[] buffer = Strip(File.ReadAllBytes(file), out shots2);
			using MemoryStream memoryStream = new MemoryStream();
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
			binaryWriter.Write(buffer);
			HashSet<byte> hashSet = new HashSet<byte>();
			foreach (Shot shot in shots)
			{
				if (hashSet.Count != 2 && hashSet.Add(shot.Kind) && shot.Kind >= 1 && shot.Kind <= 4 && shot.Pixels != null && memoryStream.Length + shot.Pixels.Length + 10 + (shot.Pixels.Length & 1) <= 10485760 && !ContainsCamera(shot.Pixels))
				{
					Strip(shot.Pixels, out shots2);
					binaryWriter.Write(1229147478u);
					binaryWriter.Write(shot.Pixels.Length + 2);
					binaryWriter.Write((byte)1);
					binaryWriter.Write(shot.Kind);
					binaryWriter.Write(shot.Pixels);
					if ((shot.Pixels.Length & 1) != 0)
					{
						binaryWriter.Write((byte)0);
					}
				}
			}
			memoryStream.Position = 4L;
			binaryWriter.Write((int)memoryStream.Length - 8);
			string text = file + ".partial";
			try
			{
				File.WriteAllBytes(text, memoryStream.ToArray());
				File.Replace(text, file, null);
			}
			finally
			{
				if (File.Exists(text))
				{
					File.Delete(text);
				}
			}
		}

		internal static string Label(byte kind)
		{
			return kind switch
			{
				3 => "Raid opening", 
				2 => "Enemy aftermath", 
				1 => "Enemy arrival", 
				_ => "Biome discovery", 
			};
		}
	}
	internal sealed class ClipRelay : IDisposable
	{
		private sealed class Waiting
		{
			internal RelayFile File;

			internal ZRpc Peer;

			internal ZNet Origin;

			internal double Deadline;

			internal double Heartbeat;

			internal Action<string> Ready;
		}

		private const string Rpc = "ValheimMoments_ClipRelay_v2";

		private readonly Func<string, bool> allow;

		private readonly Func<int> limit;

		private readonly Action<RelayFile, string, Action<RelayOutcome>> deliver;

		private readonly string directory;

		internal Func<RelayFile, string, long, Func<bool>, Func<Action<string>, bool>, Action<RelayOutcome>, bool> Collect;

		private readonly Dictionary<ZRpc, Waiting> waiting = new Dictionary<ZRpc, Waiting>();

		private readonly Dictionary<ZRpc, long> peerIds = new Dictionary<ZRpc, long>();

		private long nextPeerId;

		private readonly Action<string> log;

		private readonly Func<ZRpc, bool> allowPeer;

		private readonly Func<ZRpc, string, double, bool> acceptEvent;

		private Action<RelayOutcome> outgoingCompletion;

		private readonly HashSet<ZRpc> registered = new HashSet<ZRpc>();

		private readonly Dictionary<ZRpc, double> nextOffer = new Dictionary<ZRpc, double>();

		private ZNet session;

		private ZRpc source;

		private ZRpc target;

		private ZRpc deliveryPeer;

		private RelayFile incoming;

		private RelayFile delivery;

		private int outgoingSize;

		private Task<int> preparation;

		private Task<byte[]> reading;

		private Task<string> cleanup;

		private string outgoingFile;

		private bool keepOutgoing;

		private string offeredKind;

		private string offeredMessage;

		private string offeredEventId;

		private bool offeredFirst;

		private string outgoingId;

		private int sent;

		private int acknowledged;

		private double now;

		private double incomingDeadline;

		private double incomingEnd;

		private double outgoingDeadline;

		private double outgoingEnd;

		private double nextSend;

		private double nextTick;

		private bool delivering;

		private bool awaitingOffer;

		private bool waitingResult;

		private bool disposed;

		internal string LastMessageLink { get; private set; }

		internal bool DeliveryPeerConnected
		{
			get
			{
				if (deliveryPeer != null && registered.Contains(deliveryPeer))
				{
					return deliveryPeer.IsConnected();
				}
				return false;
			}
		}

		internal void PublishReceipt(string id, string link)
		{
			if (!MomentGallery.ValidLink(link) || (Object)(object)session == (Object)null || !session.IsServer())
			{
				return;
			}
			if (delivery != null && delivery.Id == id && deliveryPeer != null)
			{
				Send(deliveryPeer, "L|" + id + "|" + link);
			}
			foreach (Waiting value in waiting.Values)
			{
				if (value.File.Id == id)
				{
					Send(value.Peer, "L|" + id + "|" + link);
				}
			}
		}

		internal void PublishDeliveryReceipt(string link)
		{
			if (delivery != null)
			{
				PublishReceipt(delivery.Id, link);
			}
		}

		internal ClipRelay(Func<string, bool> allow, Func<int> limit, Action<RelayFile, string, Action<RelayOutcome>> deliver, Action<string> log, Func<ZRpc, bool> allowPeer = null, Func<ZRpc, string, double, bool> acceptEvent = null, string directory = null)
		{
			this.allow = allow;
			this.limit = limit;
			this.deliver = deliver;
			this.log = log;
			this.allowPeer = allowPeer ?? ((Func<ZRpc, bool>)((ZRpc rpc) => true));
			this.acceptEvent = acceptEvent ?? ((Func<ZRpc, string, double, bool>)((ZRpc rpc, string kind, double now) => true));
			this.directory = directory ?? Path.Combine(Path.GetTempPath(), "ValheimMoments-Relay");
		}

		internal void Tick(double time)
		{
			if (disposed)
			{
				return;
			}
			now = time;
			if (cleanup != null && cleanup.IsCompleted)
			{
				log(cleanup.GetAwaiter().GetResult());
				cleanup = null;
			}
			if (now < nextTick)
			{
				return;
			}
			nextTick = now + 0.05;
			ZNet instance = ZNet.instance;
			if (session != instance)
			{
				Reset();
				session = instance;
			}
			if ((Object)(object)session == (Object)null)
			{
				return;
			}
			HashSet<ZRpc> live = new HashSet<ZRpc>();
			foreach (ZNetPeer peer in session.GetPeers())
			{
				if (peer.IsReady() && peer.m_rpc.IsConnected())
				{
					live.Add(peer.m_rpc);
					if (!peerIds.ContainsKey(peer.m_rpc))
					{
						peerIds.Add(peer.m_rpc, ++nextPeerId);
					}
					if (registered.Add(peer.m_rpc))
					{
						peer.m_rpc.Register<string>("ValheimMoments_ClipRelay_v2", (Action<ZRpc, string>)Receive);
					}
				}
			}
			registered.RemoveWhere((ZRpc rpc) => !live.Contains(rpc));
			List<ZRpc> list = new List<ZRpc>();
			foreach (KeyValuePair<ZRpc, double> item in nextOffer)
			{
				if (!live.Contains(item.Key))
				{
					list.Add(item.Key);
				}
			}
			foreach (ZRpc item2 in list)
			{
				nextOffer.Remove(item2);
			}
			foreach (ZRpc item3 in new List<ZRpc>(peerIds.Keys))
			{
				if (!live.Contains(item3))
				{
					peerIds.Remove(item3);
				}
			}
			foreach (Waiting item4 in new List<Waiting>(waiting.Values))
			{
				if (!live.Contains(item4.Peer) || now >= item4.Deadline || !allowPeer(item4.Peer) || !allow(item4.File.Kind))
				{
					FinishWaiting(item4, item4.File.Complete ? RelayOutcome.Unknown : RelayOutcome.Failed, abandoned: true);
				}
				else if (now >= item4.Heartbeat)
				{
					Send(item4.Peer, "W|" + item4.File.Id + "|0");
					item4.Heartbeat = now + 5.0;
				}
			}
			if (source != null && (!live.Contains(source) || now > incomingDeadline || now > incomingEnd))
			{
				RejectIncoming();
			}
			PollIncoming();
			if (target != null && (!live.Contains(target) || now > outgoingDeadline || now > outgoingEnd))
			{
				EndInterrupted("Transfer ended or host unavailable; local clip retained.");
			}
			if (preparation != null && preparation.IsCompleted)
			{
				Task<int> task = preparation;
				preparation = null;
				try
				{
					int result = task.GetAwaiter().GetResult();
					if (target != null)
					{
						if (result == 0)
						{
							EndOutgoing("Clip could not be read or exceeds 10 MiB; local copy retained.");
							return;
						}
						outgoingSize = result;
						outgoingDeadline = now + 10.0;
						Send(target, "B|" + outgoingId + "|" + offeredKind + "|" + result + "|" + RelayProtocol.Text(offeredMessage) + "|" + (offeredEventId ?? "-") + "|" + (offeredFirst ? "1" : "0"));
					}
				}
				catch
				{
					EndOutgoing("Clip could not be read or exceeds 10 MiB; local copy retained.");
				}
			}
			if (outgoingSize <= 0 || awaitingOffer || waitingResult || sent != acknowledged || !(now >= nextSend))
			{
				return;
			}
			try
			{
				if (reading == null)
				{
					reading = RelayFile.ReadChunk(outgoingFile, sent, outgoingSize);
				}
				else if (reading.IsCompleted)
				{
					byte[] result2 = reading.GetAwaiter().GetResult();
					reading = null;
					if (result2 == null || result2.Length == 0)
					{
						EndOutgoing("Clip could not be read; local copy retained.");
						return;
					}
					int num = result2.Length;
					int num2 = sent;
					sent += num;
					nextSend = now + 0.05;
					Send(target, "C|" + outgoingId + "|" + num2 + "|" + Convert.ToBase64String(result2));
				}
			}
			catch
			{
				EndInterrupted("Clip transfer failed; local copy retained.");
			}
		}

		internal bool Offer(ZNet capturedSession, string file, string kind, string message, bool saveLocalCopy = true, Action<RelayOutcome> completed = null, string eventId = null, bool personalFirst = false)
		{
			if (disposed || (Object)(object)session == (Object)null || session != capturedSession || session.IsServer() || outgoingSize > 0 || preparation != null || cleanup != null || target != null)
			{
				return false;
			}
			if (!RelayProtocol.ValidKind(kind) || message == null || message.Length > 2000 || (eventId != null && !HighlightDirector.ValidId(eventId)))
			{
				return false;
			}
			ZNetPeer serverPeer = session.GetServerPeer();
			if (serverPeer == null || !registered.Contains(serverPeer.m_rpc))
			{
				return false;
			}
			try
			{
				target = serverPeer.m_rpc;
				outgoingId = Guid.NewGuid().ToString("N");
				LastMessageLink = null;
				outgoingCompletion = completed;
				outgoingFile = file;
				keepOutgoing = saveLocalCopy;
				offeredKind = kind;
				offeredMessage = message;
				offeredEventId = eventId;
				offeredFirst = personalFirst;
				sent = (acknowledged = 0);
				awaitingOffer = true;
				waitingResult = false;
				outgoingDeadline = now + 10.0;
				outgoingEnd = now + 1230.0;
				preparation = RelayFile.Inspect(file);
				log("Offered clip to host; client webhook and bot-name settings are ignored.");
				return true;
			}
			catch
			{
				EndOutgoing("Could not offer clip to host; local copy retained.");
				return false;
			}
		}

		private void Receive(ZRpc rpc, string packet)
		{
			if (disposed || (Object)(object)session == (Object)null || session != ZNet.instance || !registered.Contains(rpc) || !rpc.IsConnected() || packet == null || packet.Length > 24000)
			{
				return;
			}
			try
			{
				string[] array = packet.Split('|');
				if (array.Length >= 2 && RelayProtocol.ValidId(array[1]))
				{
					if (session.IsServer())
					{
						ReceiveHost(rpc, array);
					}
					else if (target == rpc && array[1] == outgoingId)
					{
						ReceiveClient(array);
					}
				}
			}
			catch
			{
				log("Invalid relay data ignored.");
			}
		}

		private void ReceiveHost(ZRpc rpc, string[] p)
		{
			int result;
			if (p[0] == "B" && p.Length == 7)
			{
				if (nextOffer.TryGetValue(rpc, out var value) && now < value)
				{
					Send(rpc, "R|" + p[1] + "|0");
					return;
				}
				nextOffer[rpc] = now + 15.0;
				if ((Collect == null && (source != null || delivering)) || waiting.ContainsKey(rpc) || waiting.Count >= 16 || !allowPeer(rpc) || !RelayProtocol.ValidKind(p[2]) || !allow(p[2]) || !int.TryParse(p[3], out var size))
				{
					Send(rpc, "R|" + p[1] + "|0");
					return;
				}
				string message = RelayProtocol.ReadText(p[4]);
				if (p[6] != "0" && p[6] != "1")
				{
					Send(rpc, "R|" + p[1] + "|0");
					return;
				}
				RelayFile offered;
				try
				{
					offered = new RelayFile(directory, p[1], p[2], message, size, limit(), (p[5] == "-") ? null : p[5], p[6] == "1");
				}
				catch
				{
					Send(rpc, "R|" + p[1] + "|0");
					return;
				}
				if (!acceptEvent(rpc, p[2], now))
				{
					offered.Dispose();
					Send(rpc, "R|" + p[1] + "|0");
				}
				else if (Collect != null)
				{
					Waiting entry = new Waiting
					{
						File = offered,
						Peer = rpc,
						Origin = session,
						Deadline = now + 1200.0
					};
					waiting.Add(rpc, entry);
					string arg = "Connected player";
					foreach (ZNetPeer peer in session.GetPeers())
					{
						if (peer.m_rpc == rpc)
						{
							arg = peer.m_playerName;
							break;
						}
					}
					bool flag = false;
					try
					{
						flag = Collect(offered, arg, peerIds[rpc], () => IsWaiting(entry), delegate(Action<string> ready)
						{
							if (!IsWaiting(entry) || source != null || delivering)
							{
								return false;
							}
							entry.Ready = ready;
							incoming = offered;
							source = rpc;
							incomingDeadline = now + 30.0;
							incomingEnd = now + 30.0 + Math.Ceiling((double)size / 16384.0) * 0.5;
							Send(rpc, "A|" + offered.Id + "|0");
							return true;
						}, delegate(RelayOutcome outcome)
						{
							FinishWaiting(entry, outcome);
						});
					}
					catch
					{
					}
					if (!flag)
					{
						FinishWaiting(entry, RelayOutcome.Omitted);
						return;
					}
					Send(rpc, "W|" + offered.Id + "|0");
					entry.Heartbeat = now + 5.0;
				}
				else
				{
					incoming = offered;
					source = rpc;
					incomingDeadline = now + 30.0;
					incomingEnd = now + 30.0 + Math.Ceiling((double)size / 16384.0) * 0.5;
					Send(rpc, "A|" + p[1] + "|0");
				}
			}
			else if (p[0] == "C" && p.Length == 4 && source == rpc && incoming != null && incoming.Id == p[1] && (!allowPeer(rpc) || !allow(incoming.Kind) || !int.TryParse(p[2], out result) || !incoming.BeginAdd(result, Convert.FromBase64String(p[3]))))
			{
				RejectIncoming();
			}
		}

		private void PollIncoming()
		{
			if (incoming == null || source == null)
			{
				return;
			}
			int num = incoming.Poll();
			if (num == 0)
			{
				return;
			}
			ZRpc rpc = source;
			if (num < 0 || !allowPeer(rpc) || !allow(incoming.Kind))
			{
				RejectIncoming();
				return;
			}
			incomingDeadline = now + 30.0;
			if (num == 1)
			{
				Send(rpc, "A|" + incoming.Id + "|" + incoming.Received);
				return;
			}
			RelayFile complete = incoming;
			incoming = null;
			source = null;
			if (waiting.TryGetValue(rpc, out var value) && value.File == complete)
			{
				Send(rpc, "A|" + complete.Id + "|" + complete.Received);
				Action<string> ready = value.Ready;
				value.Ready = null;
				try
				{
					ready?.Invoke(complete.FilePath);
					return;
				}
				catch
				{
					FinishWaiting(value, RelayOutcome.Failed);
					return;
				}
			}
			delivering = true;
			deliveryPeer = rpc;
			delivery = complete;
			ZNet origin = session;
			string arg = "Connected player";
			foreach (ZNetPeer peer in session.GetPeers())
			{
				if (peer.m_rpc == rpc)
				{
					arg = peer.m_playerName;
					break;
				}
			}
			Send(rpc, "A|" + complete.Id + "|" + complete.Received);
			try
			{
				deliver(complete, arg, delegate(RelayOutcome outcome)
				{
					complete.Dispose();
					if (!disposed && origin == session && delivery == complete)
					{
						delivering = false;
						deliveryPeer = null;
						delivery = null;
						if (registered.Contains(rpc))
						{
							ZRpc rpc2 = rpc;
							string id = complete.Id;
							int num2 = (int)outcome;
							Send(rpc2, "R|" + id + "|" + num2);
						}
					}
				});
			}
			catch
			{
				complete.Dispose();
				delivering = false;
				deliveryPeer = null;
				delivery = null;
				Send(rpc, "R|" + complete.Id + "|0");
			}
		}

		private void ReceiveClient(string[] p)
		{
			if (outgoingSize == 0 || p.Length != 3)
			{
				return;
			}
			int result;
			if (p[0] == "L" && waitingResult && acknowledged == outgoingSize && MomentGallery.ValidLink(p[2]))
			{
				LastMessageLink = p[2];
			}
			else if (p[0] == "W" && (awaitingOffer || waitingResult))
			{
				outgoingDeadline = now + 30.0;
			}
			else if (p[0] == "R")
			{
				if (p[2] == "1")
				{
					if (!waitingResult || acknowledged != outgoingSize)
					{
						return;
					}
					if (!keepOutgoing)
					{
						string file = outgoingFile;
						cleanup = Task.Run(delegate
						{
							try
							{
								File.Delete(file);
								return "Host uploaded clip to Discord; local copy removed.";
							}
							catch
							{
								return "Host uploaded clip to Discord; local copy could not be removed.";
							}
						});
						EndOutgoing("Host uploaded clip to Discord; removing local copy.", RelayOutcome.Uploaded);
					}
					else
					{
						EndOutgoing("Host uploaded clip to Discord; local copy retained.", RelayOutcome.Uploaded);
					}
				}
				else if (p[2] == "0")
				{
					EndOutgoing("Host declined or could not deliver clip; local copy retained.");
				}
				else if (p[2] == "2")
				{
					EndOutgoing("Host omitted this perspective; local copy retained.", RelayOutcome.Omitted);
				}
				else if (p[2] == "3" && waitingResult)
				{
					EndOutgoing("Delivery unknown; check Discord before retrying. Local clip retained.", RelayOutcome.Unknown);
				}
			}
			else if (!(p[0] != "A") && int.TryParse(p[2], out result) && result == sent)
			{
				acknowledged = result;
				awaitingOffer = false;
				if (sent == outgoingSize)
				{
					waitingResult = true;
					outgoingDeadline = now + 90.0;
				}
				else
				{
					outgoingDeadline = now + 30.0;
				}
			}
		}

		private static void Send(ZRpc rpc, string packet)
		{
			rpc.Invoke("ValheimMoments_ClipRelay_v2", new object[1] { packet });
		}

		private void RejectIncoming()
		{
			if (source != null && waiting.TryGetValue(source, out var value) && value.File == incoming)
			{
				FinishWaiting(value, RelayOutcome.Failed);
				return;
			}
			if (source != null && source.IsConnected() && incoming != null)
			{
				Send(source, "R|" + incoming.Id + "|0");
			}
			incoming?.Dispose();
			incoming = null;
			source = null;
		}

		private bool IsWaiting(Waiting entry)
		{
			if (!disposed && session == entry.Origin && waiting.TryGetValue(entry.Peer, out var value) && value == entry && registered.Contains(entry.Peer))
			{
				return entry.Peer.IsConnected();
			}
			return false;
		}

		private void FinishWaiting(Waiting entry, RelayOutcome outcome, bool abandoned = false)
		{
			if (IsWaiting(entry))
			{
				ZRpc peer = entry.Peer;
				string id = entry.File.Id;
				int num = (int)outcome;
				Send(peer, "R|" + id + "|" + num);
			}
			if (waiting.TryGetValue(entry.Peer, out var value) && value == entry)
			{
				waiting.Remove(entry.Peer);
			}
			if (incoming == entry.File)
			{
				incoming = null;
				source = null;
			}
			Action<string> ready = entry.Ready;
			entry.Ready = null;
			ready?.Invoke(null);
			if (!abandoned || !entry.File.Complete)
			{
				entry.File.Dispose();
			}
		}

		internal void StopSending()
		{
			if (target != null)
			{
				EndInterrupted("Client relay disabled; local clip retained.");
			}
		}

		private void EndInterrupted(string reason)
		{
			bool flag = outgoingSize > 0 && sent == outgoingSize;
			EndOutgoing(reason + (flag ? " Delivery unknown; check Discord before retrying." : ""), flag ? RelayOutcome.Unknown : RelayOutcome.Failed);
		}

		private void EndOutgoing(string reason, RelayOutcome outcome = RelayOutcome.Failed)
		{
			Action<RelayOutcome> action = outgoingCompletion;
			outgoingCompletion = null;
			outgoingSize = 0;
			reading = null;
			target = null;
			outgoingId = null;
			outgoingFile = null;
			log(reason);
			try
			{
				action?.Invoke(outcome);
			}
			catch
			{
				log("Clip completion observer unavailable.");
			}
		}

		private void Reset()
		{
			if (target != null)
			{
				EndInterrupted("Session changed; local clip retained.");
			}
			foreach (Waiting item in new List<Waiting>(waiting.Values))
			{
				FinishWaiting(item, RelayOutcome.Failed, abandoned: true);
			}
			waiting.Clear();
			peerIds.Clear();
			nextPeerId = 0L;
			incoming?.Dispose();
			incoming = null;
			source = null;
			delivering = false;
			deliveryPeer = null;
			delivery = null;
			registered.Clear();
			nextOffer.Clear();
		}

		public void Dispose()
		{
			disposed = true;
			Reset();
			session = null;
		}
	}
	internal sealed class ConfigurationMigration
	{
		private readonly Dictionary<string, string> values = new Dictionary<string, string>();

		internal bool SaveLocalCopy => Flag("Discord/SaveLocalCopy", fallback: false);

		internal bool CustomSize
		{
			get
			{
				if (!values.ContainsKey("Capture/Width"))
				{
					return values.ContainsKey("Capture/Height");
				}
				return true;
			}
		}

		internal BossCaptureMode BossMode
		{
			get
			{
				if (!values.ContainsKey("Boss Kill/FirstKillOnly") && !values.ContainsKey("Boss Kill/OnlyCaptureIfLootMeetsRarity"))
				{
					return BossCaptureMode.FirstKillThenRarity;
				}
				bool num = Flag("Boss Kill/FirstKillOnly", fallback: false);
				bool flag = Flag("Boss Kill/OnlyCaptureIfLootMeetsRarity", fallback: false);
				bool flag2 = Flag("Boss Kill/FirstKillBypassesRarity", fallback: true);
				if (num)
				{
					if (!flag || flag2)
					{
						return BossCaptureMode.FirstKillOnly;
					}
					return BossCaptureMode.FirstKillWithRarity;
				}
				if (flag)
				{
					if (!flag2)
					{
						return BossCaptureMode.RarityOnly;
					}
					return BossCaptureMode.FirstKillThenRarity;
				}
				return BossCaptureMode.AllKills;
			}
		}

		internal ConfigurationMigration(string path)
		{
			if (!File.Exists(path))
			{
				return;
			}
			string text = "";
			string[] array = File.ReadAllLines(path);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.StartsWith("#") || text2.Length == 0)
				{
					continue;
				}
				if (text2.StartsWith("[") && text2.EndsWith("]"))
				{
					text = text2.Substring(1, text2.Length - 2);
					continue;
				}
				int num = text2.IndexOf('=');
				if (num >= 1)
				{
					string text3 = text + "/" + text2.Substring(0, num).Trim();
					switch (text3)
					{
					case "Discord/SaveLocalCopy":
					case "Capture/Width":
					case "Capture/Height":
					case "Boss Kill/FirstKillOnly":
					case "Boss Kill/OnlyCaptureIfLootMeetsRarity":
					case "Boss Kill/FirstKillBypassesRarity":
						values[text3] = text2.Substring(num + 1).Trim();
						break;
					}
				}
			}
		}

		private bool Flag(string key, bool fallback)
		{
			if (!values.TryGetValue(key, out var value) || !bool.TryParse(value, out var result))
			{
				return fallback;
			}
			return result;
		}

		internal static void Retire(ConfigFile file)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected O, but got Unknown
			bool saveOnConfigSet = file.SaveOnConfigSet;
			file.SaveOnConfigSet = false;
			try
			{
				string[] array = new string[7] { "Discord/SaveLocalCopy", "Discord/UploadClips", "Discord/EnableClientRelay", "Boss Kill/FirstKillOnly", "Boss Kill/OnlyCaptureIfLootMeetsRarity", "Boss Kill/FirstKillBypassesRarity", "Special Enemies/FirstKillOnly" };
				for (int i = 0; i < array.Length; i++)
				{
					string[] array2 = array[i].Split('/');
					file.Bind<bool>(array2[0], array2[1], false, "Retired setting");
					file.Remove(new ConfigDefinition(array2[0], array2[1]));
				}
				file.Bind<string>("Player Death", "PlayerNameOverride", "", "Retired setting");
				file.Remove(new ConfigDefinition("Player Death", "PlayerNameOverride"));
				file.Save();
			}
			finally
			{
				file.SaveOnConfigSet = saveOnConfigSet;
			}
		}
	}
	internal enum BossCaptureMode
	{
		AllKills,
		FirstKillOnly,
		RarityOnly,
		FirstKillThenRarity,
		FirstKillWithRarity
	}
	internal static class BossCaptureRules
	{
		internal static bool FirstOnly(BossCaptureMode mode)
		{
			if (mode != BossCaptureMode.FirstKillOnly)
			{
				return mode == BossCaptureMode.FirstKillWithRarity;
			}
			return true;
		}

		internal static bool UsesRarity(BossCaptureMode mode)
		{
			if (mode != BossCaptureMode.RarityOnly && mode != BossCaptureMode.FirstKillThenRarity)
			{
				return mode == BossCaptureMode.FirstKillWithRarity;
			}
			return true;
		}

		internal static bool FirstBypasses(BossCaptureMode mode)
		{
			return mode == BossCaptureMode.FirstKillThenRarity;
		}
	}
	internal static class DeathCause
	{
		private static readonly FieldInfo LastHit = typeof(Character).GetField("m_lastHit", BindingFlags.Instance | BindingFlags.NonPublic);

		internal static string Read(Player player)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Invalid comparison between Unknown and I4
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			//IL_0061: 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)
			try
			{
				object? obj = LastHit?.GetValue(player);
				HitData val = (HitData)((obj is HitData) ? obj : null);
				if (val == null)
				{
					return "unknown cause";
				}
				if ((int)val.m_hitType == 1 || (int)val.m_hitType == 2 || (int)val.m_hitType == 0)
				{
					Character attacker = val.GetAttacker();
					if ((Object)(object)attacker != (Object)null)
					{
						string hoverName = attacker.GetHoverName();
						if (!string.IsNullOrWhiteSpace(hoverName))
						{
							return hoverName;
						}
					}
				}
				return Label(val.m_hitType);
			}
			catch
			{
				return "unknown cause";
			}
		}

		internal static string Label(HitType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected I4, but got Unknown
			return (type - 1) switch
			{
				0 => "an enemy (attacker unavailable)", 
				1 => "another player (attacker unavailable)", 
				2 => "fall damage", 
				3 => "drowning", 
				4 => "burning", 
				5 => "freezing", 
				6 => "poison", 
				7 => "water damage", 
				8 => "smoke inhalation", 
				9 => "the edge of the world", 
				10 => "an impact", 
				11 => "a cart", 
				12 => "a falling tree", 
				13 => "self-inflicted damage", 
				14 => "structural damage", 
				15 => "a turret", 
				16 => "a boat", 
				17 => "a falling stalactite", 
				18 => "a catapult", 
				19 => "cinder fire", 
				20 => "the Ashlands ocean", 
				21 => "Ashlands lava", 
				22 => "an incinerator", 
				23 => "a drawbridge", 
				_ => "unknown cause", 
			};
		}
	}
	internal sealed class MomentRateLimit
	{
		private readonly Queue<double> captures = new Queue<double>();

		internal bool TryTake(double now, int maximum, double window)
		{
			if (double.IsNaN(now) || double.IsInfinity(now))
			{
				return false;
			}
			maximum = Math.Max(1, Math.Min(20, maximum));
			window = ((double.IsNaN(window) || double.IsInfinity(window)) ? 60.0 : Math.Max(1.0, Math.Min(3600.0, window)));
			while (captures.Count > 0 && captures.Peek() <= now - window)
			{
				captures.Dequeue();
			}
			if (captures.Count >= maximum)
			{
				return false;
			}
			captures.Enqueue(now);
			return true;
		}

		internal void Clear()
		{
			captures.Clear();
		}
	}
	internal sealed class DeathMoments
	{
		internal sealed class Ticket
		{
			internal long Number;

			internal long Additional;

			internal int Epoch;
		}

		private long observed;

		private long shared;

		private Ticket pending;

		private int epoch;

		private readonly MomentRateLimit quota = new MomentRateLimit();

		internal void Observe()
		{
			if (observed < long.MaxValue)
			{
				observed++;
			}
		}

		internal Ticket Reserve()
		{
			if (pending != null || observed <= shared)
			{
				return null;
			}
			pending = new Ticket
			{
				Number = observed,
				Additional = Math.Max(0L, observed - shared - 1),
				Epoch = epoch
			};
			return pending;
		}

		internal bool TakeSlot(double now, int maximum, double window)
		{
			return quota.TryTake(now, maximum, window);
		}

		internal void Complete(Ticket ticket, bool delivered)
		{
			if (ticket != null && ticket == pending)
			{
				if (delivered)
				{
					shared = Math.Max(shared, ticket.Number);
				}
				pending = null;
			}
		}

		internal bool Reopen(Ticket ticket)
		{
			if (ticket == null || pending != null || ticket.Epoch != epoch || ticket.Number <= shared || ticket.Number > observed)
			{
				return false;
			}
			pending = ticket;
			return true;
		}

		internal void Clear()
		{
			observed = (shared = 0L);
			pending = null;
			quota.Clear();
			epoch++;
		}
	}
	internal sealed class DeathFlavor
	{
		internal static readonly string[] Lines = new string[20]
		{
			"A tactical donation to the local gravestone collection.", "The corpse run has acquired a sequel.", "Confidence: legendary. Survival: common.", "An ambitious approach to becoming a landmark.", "Valhalla put you on hold.", "Your equipment awaits your return. Again.", "The bees are happy. You are not.", "That was almost a plan.", "An unexpected appointment with the respawn screen.", "A bold strategy with a very short conclusion.",
			"Your inventory has become a destination.", "Adventure has requested a brief intermission.", "The saga takes an inconvenient turn.", "Another chapter for the cautionary tale.", "The adventure continues. Slightly less clothed.", "A memorable contribution to local history.", "You found the difficulty setting.", "This shortcut includes a respawn.", "The gravestone business is booming.", "Odin saw that. Unfortunately."
		};

		private int previous = -1;

		internal string Next(Random random)
		{
			int num = random.Next((previous < 0) ? Lines.Length : (Lines.Length - 1));
			if (previous >= 0 && num >= previous)
			{
				num++;
			}
			previous = num;
			return Lines[num];
		}
	}
	internal static class DiscordMentionText
	{
		internal static bool ValidId(string id)
		{
			if (id != null && id.Length >= 17 && id.Length <= 20 && Regex.IsMatch(id, "^[0-9]+$") && ulong.TryParse(id, NumberStyles.None, CultureInfo.InvariantCulture, out var result))
			{
				return result != 0;
			}
			return false;
		}

		internal static string Name(string name)
		{
			string text = (name ?? "").Replace("\r", " ").Replace("\n", " ").Replace("*", "")
				.Replace("`", "")
				.Trim();
			if (text.Length > 80)
			{
				return text.Substring(0, char.IsHighSurrogate(text[79]) ? 79 : 80);
			}
			return text;
		}

		internal static string Format(string message, IDictionary<string, string> identities, out string[] allowed)
		{
			HashSet<string> users = new HashSet<string>(StringComparer.Ordinal);
			string input = Regex.Replace(message ?? "", "<@!?([0-9]+)>", "@$1");
			input = Regex.Replace(input, "(?m)(\\*\\*Recorded by:\\*\\*|\\*\\*Kill credit:\\*\\*|^Kill credit:)[ \\t]*([^\\r\\n]*)", delegate(Match match)
			{
				string[] array = ((match.Groups[1].Value.IndexOf("Kill credit", StringComparison.Ordinal) >= 0) ? match.Groups[2].Value.Split(new string[1] { ", " }, StringSplitOptions.None) : new string[1] { match.Groups[2].Value });
				for (int i = 0; i < array.Length; i++)
				{
					string text = "";
					string text2 = array[i];
					string[] array2 = new string[2] { " (full list unavailable)", " (first kill for this character)" };
					foreach (string text3 in array2)
					{
						if (text2.EndsWith(text3, StringComparison.Ordinal))
						{
							text = text3;
							text2 = text2.Substring(0, text2.Length - text3.Length);
							break;
						}
					}
					if (identities.TryGetValue(text2, out var value) && ValidId(value))
					{
						array[i] = text2 + " (<@" + value + ">)" + text;
						users.Add(value);
					}
				}
				return match.Groups[1].Value + " " + string.Join(", ", array);
			});
			if (input.Length > 2000)
			{
				input = input.Substring(0, char.IsHighSurrogate(input[1999]) ? 1999 : 2000);
			}
			List<string> list = new List<string>();
			foreach (string item in users)
			{
				if (input.Contains("<@" + item + ">"))
				{
					list.Add(item);
				}
			}
			allowed = list.ToArray();
			return input;
		}
	}
	internal sealed class DiscordIdentity
	{
		private const string Rpc = "ValheimMoments_DiscordIdentity_v1";

		private readonly HashSet<ZRpc> registered = new HashSet<ZRpc>();

		private readonly Dictionary<ZRpc, string> identities = new Dictionary<ZRpc, string>();

		private ZNet session;

		private double nextSend;

		private string localId;

		private string localName;

		internal void Tick(double now, string id, string name)
		{
			if (session != ZNet.instance)
			{
				session = ZNet.instance;
				registered.Clear();
				identities.Clear();
				nextSend = 0.0;
			}
			localId = (DiscordMentionText.ValidId(id) ? id : "");
			localName = name;
			if ((Object)(object)session == (Object)null)
			{
				return;
			}
			HashSet<ZRpc> live = new HashSet<ZRpc>();
			foreach (ZNetPeer peer in session.GetPeers())
			{
				if (peer.IsReady() && peer.m_rpc.IsConnected())
				{
					live.Add(peer.m_rpc);
					if (registered.Add(peer.m_rpc))
					{
						peer.m_rpc.Register<string>("ValheimMoments_DiscordIdentity_v1", (Action<ZRpc, string>)Receive);
					}
				}
			}
			registered.RemoveWhere((ZRpc peer) => !live.Contains(peer));
			List<ZRpc> list = new List<ZRpc>();
			foreach (ZRpc key in identities.Keys)
			{
				if (!live.Contains(key))
				{
					list.Add(key);
				}
			}
			foreach (ZRpc item in list)
			{
				identities.Remove(item);
			}
			if (!session.IsServer() && now >= nextSend)
			{
				ZNetPeer serverPeer = session.GetServerPeer();
				if (serverPeer != null && live.Contains(serverPeer.m_rpc))
				{
					serverPeer.m_rpc.Invoke("ValheimMoments_DiscordIdentity_v1", new object[1] { localId });
					nextSend = now + 2.0;
				}
			}
		}

		private void Receive(ZRpc rpc, string id)
		{
			if (!((Object)(object)session == (Object)null) && session == ZNet.instance && session.IsServer() && registered.Contains(rpc) && rpc.IsConnected())
			{
				if (string.IsNullOrEmpty(id))
				{
					identities.Remove(rpc);
				}
				else if (DiscordMentionText.ValidId(id))
				{
					identities[rpc] = id;
				}
			}
		}

		internal void Apply(DiscordOptions options)
		{
			Dictionary<string, string> map = new Dictionary<string, string>(StringComparer.Ordinal);
			HashSet<string> duplicates = new HashSet<string>(StringComparer.Ordinal);
			Action<string, string> action = delegate(string name, string id)
			{
				name = DiscordMentionText.Name(name);
				if (name.Length != 0 && !name.Contains(",") && !name.Contains("<") && !name.Contains(">"))
				{
					if (map.ContainsKey(name))
					{
						duplicates.Add(name);
					}
					else
					{
						map.Add(name, id ?? "");
					}
				}
			};
			action(localName, localId);
			if ((Object)(object)session != (Object)null && session == ZNet.instance && session.IsServer())
			{
				foreach (ZNetPeer peer in session.GetPeers())
				{
					if (peer.IsReady() && peer.m_rpc.IsConnected())
					{
						identities.TryGetValue(peer.m_rpc, out var value);
						action(peer.m_playerName, value);
					}
				}
			}
			foreach (string item in duplicates)
			{
				map.Remove(item);
			}
			options.Message = DiscordMentionText.Format(options.Message, map, out options.MentionUsers);
		}
	}
	internal static class DiscordRouting
	{
		internal static bool CanSubmit(object capturedSession, bool capturedAsHost, object currentSession, bool currentlyHost)
		{
			if (capturedSession != null && capturedAsHost && currentlyHost)
			{
				return capturedSession == currentSession;
			}
			return false;
		}

		internal static string Destination(string kind, string fallback, bool bossOverride, string boss, bool lootOverride, string loot, bool deathOverride, string death, string discovery = null)
		{
			if (kind == "discovery" && !string.IsNullOrWhiteSpace(discovery))
			{
				return discovery;
			}
			if (kind == "boss" && bossOverride)
			{
				return boss;
			}
			if (kind == "loot" && lootOverride)
			{
				return loot;
			}
			if (kind == "death" && deathOverride)
			{
				return death;
			}
			return fallback;
		}
	}
	internal sealed class DiscordClip
	{
		internal readonly string File;

		internal readonly string Recorder;

		internal readonly bool Keep;

		internal DiscordClip(string file, string recorder, bool keep)
		{
			File = file;
			Recorder = recorder;
			Keep = keep;
		}
	}
	internal sealed class DiscordOptions
	{
		internal string WebhookUrl;

		internal string Username;

		internal string[] MentionUsers = new string[0];

		internal string Message = "Valheim moment";

		internal bool SaveLocalCopy;

		internal long MaxUploadBytes = 10485760L;
	}
	internal sealed class UploadResult
	{
		internal bool Success;

		internal string Message;

		internal string MessageId;

		internal string ChannelId;

		internal string GuildId

BepInEx/plugins/ValheimMoments/Encoder/Imazen.WebP.dll

Decompiled 2 months ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using Imazen.WebP.Extern;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("imazen;lilith")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright 2017-2026 Imazen LLC")]
[assembly: AssemblyDescription(".NET bindings for libwebp. Provides WebP encoding and decoding via both System.Drawing (Bitmap) and raw pixel buffer APIs. Requires a platform-specific Imazen.WebP.NativeRuntime package or Imazen.WebP.AllPlatforms.")]
[assembly: AssemblyFileVersion("11.0.0.0")]
[assembly: AssemblyInformationalVersion("11.0.0+a2d53ed552b46e7f3a9a1a1f8ccd23e19f6f1595")]
[assembly: AssemblyProduct("Imazen.WebP")]
[assembly: AssemblyTitle("Imazen.WebP")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/imazen/libwebp-net")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("11.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.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 Imazen.WebP
{
	public static class AbiVersionCheck
	{
		private static volatile bool _validated;

		public static void ValidateOrThrow()
		{
			if (!_validated)
			{
				int num = NativeMethods.WebPGetDecoderVersion();
				int num2 = NativeMethods.WebPGetEncoderVersion();
				int num3 = (num >> 16) & 0xFF;
				int num4 = (num2 >> 16) & 0xFF;
				if (num3 != 1 || num4 != 1)
				{
					throw new NotSupportedException("Incompatible libwebp version. Expected 1.x, got decoder=" + FormatVersion(num) + ", encoder=" + FormatVersion(num2));
				}
				_validated = true;
			}
		}

		public static string GetVersionString()
		{
			int version = NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPGetDecoderVersion());
			int version2 = NativeMethods.WebPGetEncoderVersion();
			return "decoder=" + FormatVersion(version) + ", encoder=" + FormatVersion(version2);
		}

		private static string FormatVersion(int version)
		{
			int num = (version >> 16) & 0xFF;
			int num2 = (version >> 8) & 0xFF;
			int num3 = version & 0xFF;
			return $"{num}.{num2}.{num3}";
		}
	}
	public class AnimInfo
	{
		public int Width { get; }

		public int Height { get; }

		public int FrameCount { get; }

		public int LoopCount { get; }

		public uint BackgroundColor { get; }

		internal AnimInfo(int width, int height, int frameCount, int loopCount, uint bgColor)
		{
			Width = width;
			Height = height;
			FrameCount = frameCount;
			LoopCount = loopCount;
			BackgroundColor = bgColor;
		}
	}
	public class AnimDecoder : IDisposable
	{
		private IntPtr _decoder;

		private GCHandle _dataHandle;

		private bool _disposed;

		private readonly AnimInfo _info;

		private int _prevEndTimestamp;

		public AnimInfo Info => _info;

		public AnimDecoder(byte[] webpData, bool useThreads = false)
		{
			AnimDecoder animDecoder = this;
			if (webpData == null)
			{
				throw new ArgumentNullException("webpData");
			}
			_dataHandle = GCHandle.Alloc(webpData, GCHandleType.Pinned);
			WebPData data = new WebPData
			{
				bytes = _dataHandle.AddrOfPinnedObject(),
				size = (UIntPtr)(ulong)webpData.Length
			};
			WebPAnimDecoderOptions options = default(WebPAnimDecoderOptions);
			NativeLibraryLoader.FixDllNotFoundException("webpdemux", delegate
			{
				if (NativeMethods.WebPAnimDecoderOptionsInit(ref options) == 0)
				{
					throw new Exception("Failed to initialize animation decoder options (version mismatch)");
				}
				return 0;
			});
			options.color_mode = WEBP_CSP_MODE.MODE_BGRA;
			options.use_threads = (useThreads ? 1 : 0);
			_decoder = NativeLibraryLoader.FixDllNotFoundException("webpdemux", () => NativeMethods.WebPAnimDecoderNew(ref data, ref options));
			if (_decoder == IntPtr.Zero)
			{
				throw new Exception("Failed to create animation decoder. Data may not be a valid animated WebP.");
			}
			WebPAnimInfo animInfo = default(WebPAnimInfo);
			if (NativeLibraryLoader.FixDllNotFoundException("webpdemux", () => NativeMethods.WebPAnimDecoderGetInfo(animDecoder._decoder, ref animInfo)) == 0)
			{
				throw new Exception("Failed to get animation info");
			}
			_info = new AnimInfo((int)animInfo.canvas_width, (int)animInfo.canvas_height, (int)animInfo.frame_count, (int)animInfo.loop_count, animInfo.bgcolor);
		}

		public AnimDecoder(Stream stream, bool useThreads = false)
			: this(ReadStreamFully(stream), useThreads)
		{
		}

		public List<AnimFrame> DecodeAllFrames()
		{
			ThrowIfDisposed();
			Reset();
			List<AnimFrame> list = new List<AnimFrame>();
			AnimFrame nextFrame;
			while ((nextFrame = GetNextFrame()) != null)
			{
				list.Add(nextFrame);
			}
			return list;
		}

		public AnimFrame? GetNextFrame()
		{
			ThrowIfDisposed();
			if (!HasMoreFrames())
			{
				return null;
			}
			IntPtr buf = IntPtr.Zero;
			int endTimestamp = 0;
			if (NativeLibraryLoader.FixDllNotFoundException("webpdemux", () => NativeMethods.WebPAnimDecoderGetNext(_decoder, ref buf, ref endTimestamp)) == 0)
			{
				return null;
			}
			int num = _info.Width * _info.Height * 4;
			byte[] array = new byte[num];
			Marshal.Copy(buf, array, 0, num);
			int prevEndTimestamp = _prevEndTimestamp;
			AnimFrame result = new AnimFrame(array, _info.Width, _info.Height, prevEndTimestamp)
			{
				DurationMs = endTimestamp - prevEndTimestamp
			};
			_prevEndTimestamp = endTimestamp;
			return result;
		}

		public bool HasMoreFrames()
		{
			ThrowIfDisposed();
			return NativeLibraryLoader.FixDllNotFoundException("webpdemux", () => NativeMethods.WebPAnimDecoderHasMoreFrames(_decoder)) != 0;
		}

		public void Reset()
		{
			ThrowIfDisposed();
			NativeLibraryLoader.FixDllNotFoundException("webpdemux", delegate
			{
				NativeMethods.WebPAnimDecoderReset(_decoder);
				return 0;
			});
			_prevEndTimestamp = 0;
		}

		private void ThrowIfDisposed()
		{
			if (_disposed)
			{
				throw new ObjectDisposedException("AnimDecoder");
			}
		}

		private static byte[] ReadStreamFully(Stream stream)
		{
			if (stream == null)
			{
				throw new ArgumentNullException("stream");
			}
			if (stream is MemoryStream { Position: 0L } memoryStream)
			{
				return memoryStream.ToArray();
			}
			using MemoryStream memoryStream2 = new MemoryStream();
			byte[] array = new byte[8192];
			int count;
			while ((count = stream.Read(array, 0, array.Length)) > 0)
			{
				memoryStream2.Write(array, 0, count);
			}
			return memoryStream2.ToArray();
		}

		public void Dispose()
		{
			if (_disposed)
			{
				return;
			}
			if (_decoder != IntPtr.Zero)
			{
				NativeLibraryLoader.FixDllNotFoundException("webpdemux", delegate
				{
					NativeMethods.WebPAnimDecoderDelete(_decoder);
					return 0;
				});
				_decoder = IntPtr.Zero;
			}
			if (_dataHandle.IsAllocated)
			{
				_dataHandle.Free();
			}
			_disposed = true;
		}
	}
	public class AnimEncoder : IDisposable
	{
		private IntPtr _encoder;

		private readonly int _width;

		private readonly int _height;

		private bool _disposed;

		private int _lastTimestamp;

		private int _lastDuration = 100;

		private bool _hasFrames;

		public AnimEncoder(int width, int height)
		{
			if (width <= 0)
			{
				throw new ArgumentOutOfRangeException("width");
			}
			if (height <= 0)
			{
				throw new ArgumentOutOfRangeException("height");
			}
			_width = width;
			_height = height;
			WebPAnimEncoderOptions options = default(WebPAnimEncoderOptions);
			NativeLibraryLoader.FixDllNotFoundException("webpmux", delegate
			{
				if (NativeMethods.WebPAnimEncoderOptionsInit(ref options) == 0)
				{
					throw new Exception("Failed to initialize animation encoder options (version mismatch)");
				}
				return 0;
			});
			_encoder = NativeLibraryLoader.FixDllNotFoundException("webpmux", () => NativeMethods.WebPAnimEncoderNew(width, height, ref options));
			if (_encoder == IntPtr.Zero)
			{
				throw new Exception("Failed to create animation encoder");
			}
		}

		public AnimEncoder(int width, int height, int loopCount = 0, uint backgroundColor = 0u, bool allowMixed = false, bool minimizeSize = false)
		{
			if (width <= 0)
			{
				throw new ArgumentOutOfRangeException("width");
			}
			if (height <= 0)
			{
				throw new ArgumentOutOfRangeException("height");
			}
			_width = width;
			_height = height;
			WebPAnimEncoderOptions options = default(WebPAnimEncoderOptions);
			NativeLibraryLoader.FixDllNotFoundException("webpmux", delegate
			{
				if (NativeMethods.WebPAnimEncoderOptionsInit(ref options) == 0)
				{
					throw new Exception("Failed to initialize animation encoder options (version mismatch)");
				}
				return 0;
			});
			options.anim_params.loop_count = loopCount;
			options.anim_params.bgcolor = backgroundColor;
			options.allow_mixed = (allowMixed ? 1 : 0);
			options.minimize_size = (minimizeSize ? 1 : 0);
			_encoder = NativeLibraryLoader.FixDllNotFoundException("webpmux", () => NativeMethods.WebPAnimEncoderNew(width, height, ref options));
			if (_encoder == IntPtr.Zero)
			{
				throw new Exception("Failed to create animation encoder");
			}
		}

		public void AddFrame(byte[] bgraPixels, int timestampMs, float quality = -1f)
		{
			if (bgraPixels == null)
			{
				throw new ArgumentNullException("bgraPixels");
			}
			AddFrameInternal(bgraPixels, _width * 4, WebPPixelFormat.Bgra, timestampMs, quality);
		}

		public void AddFrame(byte[] pixels, int stride, WebPPixelFormat format, int timestampMs, float quality = -1f)
		{
			if (pixels == null)
			{
				throw new ArgumentNullException("pixels");
			}
			AddFrameInternal(pixels, stride, format, timestampMs, quality);
		}

		public void AddFrame(byte[] pixels, int stride, WebPPixelFormat format, int timestampMs, WebPEncoderConfig config)
		{
			if (pixels == null)
			{
				throw new ArgumentNullException("pixels");
			}
			if (config == null)
			{
				throw new ArgumentNullException("config");
			}
			ThrowIfDisposed();
			if (!config.Validate())
			{
				throw new ArgumentException("Invalid encoder configuration", "config");
			}
			WebPConfig config2 = config.GetNativeConfig();
			AddFrameWithConfig(pixels, stride, format, timestampMs, ref config2);
		}

		private void AddFrameInternal(byte[] pixels, int stride, WebPPixelFormat format, int timestampMs, float quality)
		{
			ThrowIfDisposed();
			WebPConfig config = default(WebPConfig);
			NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPConfigInitInternal(ref config, WebPPreset.WEBP_PRESET_DEFAULT, (quality >= 0f) ? quality : 75f, 528));
			if (quality < 0f)
			{
				config.lossless = 1;
				config.quality = 75f;
			}
			else
			{
				config.lossless = 0;
				config.quality = Math.Max(0f, Math.Min(100f, quality));
			}
			AddFrameWithConfig(pixels, stride, format, timestampMs, ref config);
		}

		private void AddFrameWithConfig(byte[] pixels, int stride, WebPPixelFormat format, int timestampMs, ref WebPConfig config)
		{
			WebPConfig localConfig = config;
			WebPPicture picture = default(WebPPicture);
			NativeLibraryLoader.FixDllNotFoundException("webp", delegate
			{
				if (NativeMethods.WebPPictureInitInternal(ref picture, 528) == 0)
				{
					throw new Exception("Failed to initialize WebPPicture (version mismatch)");
				}
				return 0;
			});
			picture.width = _width;
			picture.height = _height;
			picture.use_argb = 1;
			GCHandle gCHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
			try
			{
				IntPtr pixelPtr = gCHandle.AddrOfPinnedObject();
				if (format switch
				{
					WebPPixelFormat.Bgra => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPPictureImportBGRA(ref picture, pixelPtr, stride)), 
					WebPPixelFormat.Rgba => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPPictureImportRGBA(ref picture, pixelPtr, stride)), 
					WebPPixelFormat.Bgr => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPPictureImportBGR(ref picture, pixelPtr, stride)), 
					WebPPixelFormat.Rgb => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPPictureImportRGB(ref picture, pixelPtr, stride)), 
					_ => throw new ArgumentOutOfRangeException("format"), 
				} == 0)
				{
					throw new Exception("Failed to import pixel data into WebPPicture");
				}
				if (NativeLibraryLoader.FixDllNotFoundException("webpmux", () => NativeMethods.WebPAnimEncoderAdd(_encoder, ref picture, timestampMs, ref localConfig)) == 0)
				{
					IntPtr intPtr = NativeMethods.WebPAnimEncoderGetError(_encoder);
					string text = ((intPtr != IntPtr.Zero) ? (Marshal.PtrToStringAnsi(intPtr) ?? "Unknown error") : "Unknown error");
					throw new Exception("Failed to add animation frame: " + text);
				}
				if (_hasFrames)
				{
					_lastDuration = Math.Max(timestampMs - _lastTimestamp, 1);
				}
				_lastTimestamp = timestampMs;
				_hasFrames = true;
			}
			finally
			{
				NativeLibraryLoader.FixDllNotFoundException("webp", delegate
				{
					NativeMethods.WebPPictureFree(ref picture);
					return 0;
				});
				gCHandle.Free();
			}
		}

		public byte[] Assemble()
		{
			ThrowIfDisposed();
			int endTimestamp = _lastTimestamp + Math.Max(_lastDuration, 1);
			NativeLibraryLoader.FixDllNotFoundException("webpmux", () => NativeMethods.WebPAnimEncoderAddNull(_encoder, IntPtr.Zero, endTimestamp, IntPtr.Zero));
			WebPData webpData = default(WebPData);
			if (NativeLibraryLoader.FixDllNotFoundException("webpmux", () => NativeMethods.WebPAnimEncoderAssemble(_encoder, ref webpData)) == 0)
			{
				IntPtr intPtr = NativeMethods.WebPAnimEncoderGetError(_encoder);
				string text = ((intPtr != IntPtr.Zero) ? (Marshal.PtrToStringAnsi(intPtr) ?? "Unknown error") : "Unknown error");
				throw new Exception("Failed to assemble animation: " + text);
			}
			int num = (int)(ulong)webpData.size;
			byte[] array = new byte[num];
			Marshal.Copy(webpData.bytes, array, 0, num);
			return array;
		}

		public void Assemble(Stream outputStream)
		{
			if (outputStream == null)
			{
				throw new ArgumentNullException("outputStream");
			}
			byte[] array = Assemble();
			outputStream.Write(array, 0, array.Length);
		}

		private void ThrowIfDisposed()
		{
			if (_disposed)
			{
				throw new ObjectDisposedException("AnimEncoder");
			}
		}

		public void Dispose()
		{
			if (!_disposed && _encoder != IntPtr.Zero)
			{
				NativeLibraryLoader.FixDllNotFoundException("webpmux", delegate
				{
					NativeMethods.WebPAnimEncoderDelete(_encoder);
					return 0;
				});
				_encoder = IntPtr.Zero;
				_disposed = true;
			}
		}
	}
	public class AnimFrame
	{
		public byte[] Pixels { get; }

		public int TimestampMs { get; }

		public int Width { get; }

		public int Height { get; }

		public int DurationMs { get; internal set; }

		public AnimFrame(byte[] pixels, int width, int height, int timestampMs)
		{
			Pixels = pixels ?? throw new ArgumentNullException("pixels");
			Width = width;
			Height = height;
			TimestampMs = timestampMs;
			DurationMs = -1;
		}
	}
	internal class LoadLogger : ILibraryLoadLogger
	{
		private struct LogEntry
		{
			internal string Basename;

			internal string? FullPath;

			internal bool FileExists;

			internal bool PreviouslyLoaded;

			internal int? LoadErrorCode;
		}

		internal string Verb = "loaded";

		internal string Filename = RuntimeFileLocator.SharedLibraryPrefix.Value + "webp." + RuntimeFileLocator.SharedLibraryExtension.Value;

		internal Exception? FirstException;

		internal Exception? LastException;

		private readonly List<LogEntry> _log = new List<LogEntry>(7);

		public void NotifyAttempt(string basename, string? fullPath, bool fileExists, bool previouslyLoaded, int? loadErrorCode)
		{
			_log.Add(new LogEntry
			{
				Basename = basename,
				FullPath = fullPath,
				FileExists = fileExists,
				PreviouslyLoaded = previouslyLoaded,
				LoadErrorCode = loadErrorCode
			});
		}

		internal void RaiseException()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendFormat(CultureInfo.InvariantCulture, "Looking for \"{0}\" RID=\"{1}-{2}\", IsUnix={3}, IsDotNetCore={4} RelativeSearchPath=\"{5}\"\n", Filename, RuntimeFileLocator.PlatformRuntimePrefix.Value, RuntimeFileLocator.ArchitectureSubdir.Value, RuntimeFileLocator.IsUnix, RuntimeFileLocator.IsDotNetCore.Value, AppDomain.CurrentDomain.RelativeSearchPath);
			if (FirstException != null)
			{
				stringBuilder.AppendFormat(CultureInfo.InvariantCulture, "Before searching: {0}\n", FirstException.Message);
			}
			foreach (LogEntry item in _log)
			{
				if (item.PreviouslyLoaded)
				{
					stringBuilder.AppendFormat(CultureInfo.InvariantCulture, "\"{0}\" is already {1}", item.Basename, Verb);
				}
				else if (!item.FileExists)
				{
					stringBuilder.AppendFormat(CultureInfo.InvariantCulture, "File not found: {0}", item.FullPath);
				}
				else if (item.LoadErrorCode.HasValue)
				{
					string text = ((item.LoadErrorCode.Value < 0) ? string.Format(CultureInfo.InvariantCulture, "0x{0:X8}", item.LoadErrorCode.Value) : item.LoadErrorCode.Value.ToString(CultureInfo.InvariantCulture));
					stringBuilder.AppendFormat(CultureInfo.InvariantCulture, "Error \"{0}\" ({1}) loading {2} from {3}", new Win32Exception(item.LoadErrorCode.Value).Message, text, item.Basename, item.FullPath);
					if (item.LoadErrorCode.Value == 193 && RuntimeFileLocator.PlatformRuntimePrefix.Value == "win")
					{
						string arg = (Environment.Is64BitProcess ? "32-bit (x86)" : "64-bit (x86_64)");
						string arg2 = (Environment.Is64BitProcess ? "64-bit (x86_64)" : "32-bit (x86)");
						stringBuilder.AppendFormat(CultureInfo.InvariantCulture, "\n> You have installed a {0} copy of libwebp but need the {1} version", arg, arg2);
					}
					if (item.LoadErrorCode.Value == 126 && RuntimeFileLocator.PlatformRuntimePrefix.Value == "win")
					{
						string arg3 = "https://aka.ms/vs/17/release/vc_redist." + (Environment.Is64BitProcess ? "x64.exe" : "x86.exe");
						stringBuilder.AppendFormat(CultureInfo.InvariantCulture, "\n> You may need to install the C Runtime from {0}", arg3);
					}
				}
				else
				{
					stringBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0} {1} in {2}", Verb, item.Basename, item.FullPath);
				}
				stringBuilder.Append('\n');
			}
			if (LastException != null)
			{
				stringBuilder.AppendLine(LastException.Message);
			}
			string text2 = (FirstException ?? LastException)?.StackTrace;
			if (text2 != null)
			{
				stringBuilder.AppendLine(text2);
			}
			throw new DllNotFoundException(stringBuilder.ToString());
		}
	}
	internal static class RuntimeFileLocator
	{
		internal static readonly Lazy<string> SharedLibraryPrefix = new Lazy<string>(() => (!IsUnix) ? "" : "lib", LazyThreadSafetyMode.PublicationOnly);

		internal static readonly Lazy<bool> IsDotNetCore = new Lazy<bool>(delegate
		{
			try
			{
				return typeof(GCSettings).GetTypeInfo().Assembly.CodeBase.Contains("Microsoft.NETCore.App");
			}
			catch
			{
				return false;
			}
		}, LazyThreadSafetyMode.PublicationOnly);

		internal static readonly Lazy<string> PlatformRuntimePrefix = new Lazy<string>(delegate
		{
			if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
			{
				return "osx";
			}
			return RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" : "win";
		}, LazyThreadSafetyMode.PublicationOnly);

		internal static readonly Lazy<string> SharedLibraryExtension = new Lazy<string>(delegate
		{
			if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
			{
				return "dylib";
			}
			return RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "so" : "dll";
		}, LazyThreadSafetyMode.PublicationOnly);

		internal static readonly Lazy<string> ArchitectureSubdir = new Lazy<string>(delegate
		{
			switch (RuntimeInformation.ProcessArchitecture)
			{
			case Architecture.X86:
				return "x86";
			case Architecture.X64:
				return "x64";
			case Architecture.Arm:
				return "arm";
			case Architecture.Arm64:
				return "arm64";
			default:
			{
				string environmentVariable = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
				if (environmentVariable != null)
				{
					switch (environmentVariable.ToUpperInvariant())
					{
					case "AMD64":
						return "x64";
					case "IA64":
						return "ia64";
					case "ARM64":
						return "arm64";
					case "EM64T":
						return "x64";
					case "X86":
						return "x86";
					}
				}
				if (!Environment.Is64BitProcess)
				{
					return "x86";
				}
				return "x64";
			}
			}
		}, LazyThreadSafetyMode.PublicationOnly);

		internal static bool IsUnix
		{
			get
			{
				if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
				{
					return RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
				}
				return true;
			}
		}

		private static IEnumerable<Tuple<bool, string>> BaseFolders(IEnumerable<string>? customSearchDirectories = null)
		{
			if (customSearchDirectories != null)
			{
				foreach (string customSearchDirectory in customSearchDirectories)
				{
					yield return Tuple.Create(item1: true, customSearchDirectory);
				}
			}
			if (!string.IsNullOrEmpty(AppDomain.CurrentDomain.RelativeSearchPath) && AppDomain.CurrentDomain.RelativeSearchPath.StartsWith(AppDomain.CurrentDomain.BaseDirectory))
			{
				yield return Tuple.Create(item1: true, AppDomain.CurrentDomain.RelativeSearchPath);
			}
			if (!string.IsNullOrEmpty(AppContext.BaseDirectory))
			{
				yield return Tuple.Create(item1: true, AppContext.BaseDirectory);
			}
			yield return Tuple.Create(item1: true, AppDomain.CurrentDomain.BaseDirectory);
			if (AppDomain.CurrentDomain.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).EndsWith("bin"))
			{
				DirectoryInfo parent = Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory);
				if (parent != null)
				{
					yield return Tuple.Create(item1: false, Path.Combine(parent.FullName, "runtimes", PlatformRuntimePrefix.Value + "-" + ArchitectureSubdir.Value, "native"));
				}
			}
			string text = null;
			try
			{
				text = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			}
			catch (NotImplementedException)
			{
			}
			if (!string.IsNullOrEmpty(text))
			{
				yield return Tuple.Create(item1: true, text);
			}
		}

		internal static IEnumerable<string> SearchPossibilitiesForFile(string filename, IEnumerable<string>? customSearchDirectories = null)
		{
			HashSet<string> attemptedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (Tuple<bool, string> item in BaseFolders(customSearchDirectories))
			{
				if (string.IsNullOrEmpty(item.Item2))
				{
					continue;
				}
				string directory = Path.GetFullPath(item.Item2);
				bool searchSubDirs = item.Item1;
				string text;
				if (searchSubDirs)
				{
					text = Path.Combine(directory, "runtimes", PlatformRuntimePrefix.Value + "-" + ArchitectureSubdir.Value, "native", filename);
					if (attemptedPaths.Add(text))
					{
						yield return text;
					}
				}
				if (searchSubDirs)
				{
					text = Path.Combine(directory, ArchitectureSubdir.Value, filename);
					if (attemptedPaths.Add(text))
					{
						yield return text;
					}
				}
				text = Path.Combine(directory, filename);
				if (attemptedPaths.Add(text))
				{
					yield return text;
				}
			}
		}
	}
	internal interface ILibraryLoadLogger
	{
		void NotifyAttempt(string basename, string? fullPath, bool fileExists, bool previouslyLoaded, int? loadErrorCode);
	}
	internal static class NativeLibraryLoader
	{
		private static readonly Lazy<ConcurrentDictionary<string, IntPtr>> LibraryHandlesByBasename = new Lazy<ConcurrentDictionary<string, IntPtr>>(() => new ConcurrentDictionary<string, IntPtr>(StringComparer.OrdinalIgnoreCase), LazyThreadSafetyMode.PublicationOnly);

		internal static string GetFilenameWithoutDirectory(string basename)
		{
			return RuntimeFileLocator.SharedLibraryPrefix.Value + basename + "." + RuntimeFileLocator.SharedLibraryExtension.Value;
		}

		public static T? FixDllNotFoundException<T>(string basename, Func<T> invokingOperation, IEnumerable<string>? customSearchDirectories = null)
		{
			Exception firstException;
			try
			{
				return invokingOperation();
			}
			catch (BadImageFormatException ex)
			{
				firstException = ex;
			}
			catch (DllNotFoundException ex2)
			{
				firstException = ex2;
			}
			LoadLogger loadLogger = new LoadLogger
			{
				FirstException = firstException,
				Filename = GetFilenameWithoutDirectory(basename)
			};
			if (TryLoadByBasename(basename, loadLogger, out var _, customSearchDirectories))
			{
				try
				{
					return invokingOperation();
				}
				catch (DllNotFoundException lastException)
				{
					loadLogger.LastException = lastException;
				}
			}
			loadLogger.RaiseException();
			return default(T);
		}

		public static bool TryLoadByBasename(string basename, ILibraryLoadLogger log, out IntPtr handle, IEnumerable<string>? customSearchDirectories = null)
		{
			if (string.IsNullOrEmpty(basename))
			{
				throw new ArgumentNullException("basename");
			}
			if (LibraryHandlesByBasename.Value.TryGetValue(basename, out handle))
			{
				log.NotifyAttempt(basename, null, fileExists: true, previouslyLoaded: true, 0);
				return true;
			}
			lock (LibraryHandlesByBasename)
			{
				if (LibraryHandlesByBasename.Value.TryGetValue(basename, out handle))
				{
					log.NotifyAttempt(basename, null, fileExists: true, previouslyLoaded: true, 0);
					return true;
				}
				bool num = TryLoadByBasenameInternal(basename, log, out handle, customSearchDirectories);
				if (num)
				{
					LibraryHandlesByBasename.Value[basename] = handle;
					if (string.Equals(basename, "webp", StringComparison.OrdinalIgnoreCase))
					{
						AbiVersionCheck.ValidateOrThrow();
					}
				}
				return num;
			}
		}

		private static bool TryLoadByBasenameInternal(string basename, ILibraryLoadLogger log, out IntPtr handle, IEnumerable<string>? customSearchDirectories = null)
		{
			string filenameWithoutDirectory = GetFilenameWithoutDirectory(basename);
			List<string> list = new List<string> { filenameWithoutDirectory };
			if (!RuntimeFileLocator.IsUnix)
			{
				string text = "lib" + basename + "." + RuntimeFileLocator.SharedLibraryExtension.Value;
				if (!string.Equals(filenameWithoutDirectory, text, StringComparison.OrdinalIgnoreCase))
				{
					list.Add(text);
				}
			}
			foreach (string item in list)
			{
				foreach (string item2 in RuntimeFileLocator.SearchPossibilitiesForFile(item, customSearchDirectories))
				{
					if (!File.Exists(item2))
					{
						log.NotifyAttempt(basename, item2, fileExists: false, previouslyLoaded: false, 0);
						continue;
					}
					int? errorCode;
					bool num = LoadLibrary(item2, out handle, out errorCode);
					log.NotifyAttempt(basename, item2, fileExists: true, previouslyLoaded: false, errorCode);
					if (!num)
					{
						continue;
					}
					return true;
				}
			}
			handle = IntPtr.Zero;
			return false;
		}

		private static bool LoadLibrary(string fullPath, out IntPtr handle, out int? errorCode)
		{
			handle = (RuntimeFileLocator.IsUnix ? UnixLoadLibrary.Execute(fullPath) : WindowsLoadLibrary.Execute(fullPath));
			if (handle == IntPtr.Zero)
			{
				errorCode = Marshal.GetLastWin32Error();
				return false;
			}
			errorCode = null;
			return true;
		}
	}
	[SuppressUnmanagedCodeSecurity]
	[SecurityCritical]
	internal static class WindowsLoadLibrary
	{
		[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
		private static extern IntPtr LoadLibraryEx(string fileName, IntPtr reservedNull, uint flags);

		public static IntPtr Execute(string fileName)
		{
			return LoadLibraryEx(fileName, IntPtr.Zero, 8u);
		}
	}
	[SuppressUnmanagedCodeSecurity]
	[SecurityCritical]
	internal static class UnixLoadLibrary
	{
		private static volatile bool _preferLibdl2 = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);

		[DllImport("libdl.so.2", CharSet = CharSet.Ansi, EntryPoint = "dlopen", SetLastError = true)]
		private static extern IntPtr dlopen_libdl2(string fileName, int flags);

		[DllImport("libdl", CharSet = CharSet.Ansi, EntryPoint = "dlopen", SetLastError = true)]
		private static extern IntPtr dlopen_libdl(string fileName, int flags);

		public static IntPtr Execute(string fileName)
		{
			if (_preferLibdl2)
			{
				try
				{
					return dlopen_libdl2(fileName, 2);
				}
				catch (DllNotFoundException)
				{
					_preferLibdl2 = false;
				}
			}
			return dlopen_libdl(fileName, 2);
		}
	}
	public class SimpleDecoder
	{
		public static string GetDecoderVersion()
		{
			int num = NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPGetDecoderVersion());
			uint num2 = (uint)num % 256u;
			uint num3 = (uint)(num >>> 8) % 256u;
			uint num4 = (uint)(num >>> 16) % 256u;
			return num4 + "." + num3 + "." + num2;
		}

		public unsafe Bitmap DecodeFromBytes(byte[] data, long length)
		{
			fixed (byte* ptr = data)
			{
				return DecodeFromPointer((IntPtr)ptr, length);
			}
		}

		public Bitmap DecodeFromPointer(IntPtr data, long length)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			int w = 0;
			int h = 0;
			if (NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPGetInfo(data, (UIntPtr)(ulong)length, ref w, ref h)) == 0)
			{
				throw new Exception("Invalid WebP header detected");
			}
			bool flag = false;
			Bitmap val = null;
			BitmapData bd = null;
			try
			{
				val = new Bitmap(w, h, (PixelFormat)2498570);
				bd = val.LockBits(new Rectangle(0, 0, w, h), (ImageLockMode)3, (PixelFormat)2498570);
				IntPtr intPtr = NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPDecodeBGRAInto(data, (UIntPtr)(ulong)length, bd.Scan0, (UIntPtr)(ulong)(bd.Stride * bd.Height), bd.Stride));
				if (bd.Scan0 != intPtr)
				{
					throw new Exception("Failed to decode WebP image with error " + (long)intPtr);
				}
				flag = true;
			}
			finally
			{
				if (bd != null && val != null)
				{
					val.UnlockBits(bd);
				}
				if (!flag && val != null)
				{
					((Image)val).Dispose();
				}
			}
			return val;
		}

		public Bitmap DecodeFromStream(Stream stream)
		{
			if (stream == null)
			{
				throw new ArgumentNullException("stream");
			}
			byte[] array = ReadStreamFully(stream);
			return DecodeFromBytes(array, array.LongLength);
		}

		private static byte[] ReadStreamFully(Stream stream)
		{
			if (stream is MemoryStream { Position: 0L } memoryStream)
			{
				return memoryStream.ToArray();
			}
			using MemoryStream memoryStream2 = new MemoryStream();
			byte[] array = new byte[8192];
			int count;
			while ((count = stream.Read(array, 0, array.Length)) > 0)
			{
				memoryStream2.Write(array, 0, count);
			}
			return memoryStream2.ToArray();
		}
	}
	public class SimpleEncoder
	{
		public static string GetEncoderVersion()
		{
			int num = NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPGetEncoderVersion());
			uint num2 = (uint)num % 256u;
			uint num3 = (uint)(num >>> 8) % 256u;
			uint num4 = (uint)(num >>> 16) % 256u;
			return num4 + "." + num3 + "." + num2;
		}

		[Obsolete("Use Encode(Bitmap, Stream, float) instead")]
		public void Encode(Bitmap from, Stream to, float quality, bool noAlpha)
		{
			Encode(from, to, quality);
		}

		public void Encode(Bitmap from, Stream to, float quality)
		{
			Encode(from, quality, out var result, out var length);
			try
			{
				byte[] array = new byte[4096];
				for (int i = 0; i < length; i += array.Length)
				{
					int num = (int)Math.Min(array.Length, length - i);
					Marshal.Copy((IntPtr)((long)result + i), array, 0, num);
					to.Write(array, 0, num);
				}
			}
			finally
			{
				NativeMethods.WebPSafeFree(result);
			}
		}

		[Obsolete("Use Encode(Bitmap, float, out IntPtr, out long) instead")]
		public void Encode(Bitmap b, float quality, bool noAlpha, out IntPtr result, out long length)
		{
			Encode(b, quality, out result, out length);
		}

		public void Encode(Bitmap b, float quality, out IntPtr result, out long length)
		{
			//IL_006b: 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_00a0: Invalid comparison between Unknown and I4
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Invalid comparison between Unknown and I4
			if (quality < -1f)
			{
				quality = -1f;
			}
			if (quality > 100f)
			{
				quality = 100f;
			}
			int w = ((Image)b).Width;
			int h = ((Image)b).Height;
			BitmapData val = b.LockBits(new Rectangle(0, 0, w, h), (ImageLockMode)1, ((Image)b).PixelFormat);
			try
			{
				result = IntPtr.Zero;
				IntPtr scan0 = val.Scan0;
				int stride = val.Stride;
				if ((int)((Image)b).PixelFormat == 2498570)
				{
					IntPtr res = IntPtr.Zero;
					if (quality == -1f)
					{
						length = (long)(ulong)NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPEncodeLosslessBGRA(scan0, w, h, stride, ref res));
					}
					else
					{
						length = (long)(ulong)NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPEncodeBGRA(scan0, w, h, stride, quality, ref res));
					}
					result = res;
				}
				else if ((int)((Image)b).PixelFormat == 137224)
				{
					IntPtr res2 = IntPtr.Zero;
					if (quality == -1f)
					{
						length = (long)(ulong)NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPEncodeLosslessBGR(scan0, w, h, stride, ref res2));
					}
					else
					{
						length = (long)(ulong)NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPEncodeBGR(scan0, w, h, stride, quality, ref res2));
					}
					result = res2;
				}
				else
				{
					Bitmap val2 = b.Clone(new Rectangle(0, 0, ((Image)b).Width, ((Image)b).Height), (PixelFormat)2498570);
					try
					{
						Encode(val2, quality, out result, out length);
					}
					finally
					{
						((IDisposable)val2)?.Dispose();
					}
				}
				if (length == 0L)
				{
					throw new Exception("WebP encode failed!");
				}
			}
			finally
			{
				b.UnlockBits(val);
			}
		}

		public void Encode(Bitmap b, Stream to, WebPEncoderConfig config)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Invalid comparison between Unknown and I4
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Invalid comparison between Unknown and I4
			//IL_0084: 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_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Invalid comparison between Unknown and I4
			if (b == null)
			{
				throw new ArgumentNullException("b");
			}
			if (to == null)
			{
				throw new ArgumentNullException("to");
			}
			if (config == null)
			{
				throw new ArgumentNullException("config");
			}
			int width = ((Image)b).Width;
			int height = ((Image)b).Height;
			Bitmap val = b;
			bool flag = false;
			if ((int)((Image)b).PixelFormat != 2498570 && (int)((Image)b).PixelFormat != 137224)
			{
				val = b.Clone(new Rectangle(0, 0, ((Image)b).Width, ((Image)b).Height), (PixelFormat)2498570);
				flag = true;
			}
			try
			{
				BitmapData val2 = val.LockBits(new Rectangle(0, 0, width, height), (ImageLockMode)1, ((Image)val).PixelFormat);
				try
				{
					_ = ((Image)val).PixelFormat;
					_ = 137224;
					int stride = val2.Stride;
					byte[] array = new byte[Math.Abs(stride) * height];
					Marshal.Copy(val2.Scan0, array, 0, array.Length);
					WebPPixelFormat format = (((int)((Image)val).PixelFormat == 137224) ? WebPPixelFormat.Bgr : WebPPixelFormat.Bgra);
					WebPEncoder.Encode(array, width, height, Math.Abs(stride), format, config, to);
				}
				finally
				{
					val.UnlockBits(val2);
				}
			}
			finally
			{
				if (flag)
				{
					((Image)val).Dispose();
				}
			}
		}
	}
	public enum WebPPixelFormat
	{
		Bgra,
		Rgba,
		Bgr,
		Rgb
	}
	public static class WebPDecoder
	{
		public static byte[] Decode(byte[] data, out int width, out int height)
		{
			return Decode(data, out width, out height, WebPPixelFormat.Bgra);
		}

		public static byte[] Decode(byte[] data, out int width, out int height, WebPPixelFormat format)
		{
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			GCHandle gCHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
			try
			{
				IntPtr dataPtr = gCHandle.AddrOfPinnedObject();
				UIntPtr dataSize = (UIntPtr)(ulong)data.Length;
				int w = 0;
				int h = 0;
				if (NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPGetInfo(dataPtr, dataSize, ref w, ref h)) == 0)
				{
					throw new Exception("Invalid WebP header detected");
				}
				width = w;
				height = h;
				int num = ((format == WebPPixelFormat.Bgr || format == WebPPixelFormat.Rgb) ? 3 : 4);
				int num2 = w * num;
				byte[] array = new byte[num2 * h];
				GCHandle gCHandle2 = GCHandle.Alloc(array, GCHandleType.Pinned);
				try
				{
					IntPtr intPtr = gCHandle2.AddrOfPinnedObject();
					UIntPtr outSize = (UIntPtr)(ulong)array.Length;
					IntPtr intPtr2 = DecodeInto(dataPtr, dataSize, intPtr, outSize, num2, format);
					if (intPtr != intPtr2)
					{
						throw new Exception("Failed to decode WebP image");
					}
				}
				finally
				{
					gCHandle2.Free();
				}
				return array;
			}
			finally
			{
				gCHandle.Free();
			}
		}

		public static void Decode(byte[] data, byte[] output, int stride, WebPPixelFormat format)
		{
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			if (output == null)
			{
				throw new ArgumentNullException("output");
			}
			GCHandle gCHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
			GCHandle gCHandle2 = GCHandle.Alloc(output, GCHandleType.Pinned);
			try
			{
				IntPtr dataPtr = gCHandle.AddrOfPinnedObject();
				IntPtr intPtr = gCHandle2.AddrOfPinnedObject();
				UIntPtr dataSize = (UIntPtr)(ulong)data.Length;
				UIntPtr outSize = (UIntPtr)(ulong)output.Length;
				IntPtr intPtr2 = DecodeInto(dataPtr, dataSize, intPtr, outSize, stride, format);
				if (intPtr != intPtr2)
				{
					throw new Exception("Failed to decode WebP image");
				}
			}
			finally
			{
				gCHandle2.Free();
				gCHandle.Free();
			}
		}

		public static byte[] DecodeFromStream(Stream stream, out int width, out int height)
		{
			return DecodeFromStream(stream, out width, out height, WebPPixelFormat.Bgra);
		}

		public static byte[] DecodeFromStream(Stream stream, out int width, out int height, WebPPixelFormat format)
		{
			if (stream == null)
			{
				throw new ArgumentNullException("stream");
			}
			return Decode(ReadStreamFully(stream), out width, out height, format);
		}

		public static bool IsWebP(byte[] data)
		{
			if (data == null || data.Length < 12)
			{
				return false;
			}
			if (data[0] == 82 && data[1] == 73 && data[2] == 70 && data[3] == 70 && data[8] == 87 && data[9] == 69 && data[10] == 66)
			{
				return data[11] == 80;
			}
			return false;
		}

		public static bool IsWebP(Stream stream)
		{
			if (stream == null)
			{
				throw new ArgumentNullException("stream");
			}
			if (!stream.CanSeek)
			{
				throw new ArgumentException("Stream must be seekable", "stream");
			}
			long position = stream.Position;
			try
			{
				byte[] array = new byte[12];
				int num;
				for (int i = 0; i < 12; i += num)
				{
					num = stream.Read(array, i, 12 - i);
					if (num == 0)
					{
						return false;
					}
				}
				return IsWebP(array);
			}
			finally
			{
				stream.Position = position;
			}
		}

		private static byte[] ReadStreamFully(Stream stream)
		{
			if (stream is MemoryStream { Position: 0L } memoryStream)
			{
				return memoryStream.ToArray();
			}
			using MemoryStream memoryStream2 = new MemoryStream();
			byte[] array = new byte[8192];
			int count;
			while ((count = stream.Read(array, 0, array.Length)) > 0)
			{
				memoryStream2.Write(array, 0, count);
			}
			return memoryStream2.ToArray();
		}

		private static IntPtr DecodeInto(IntPtr dataPtr, UIntPtr dataSize, IntPtr outPtr, UIntPtr outSize, int stride, WebPPixelFormat format)
		{
			return format switch
			{
				WebPPixelFormat.Bgra => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPDecodeBGRAInto(dataPtr, dataSize, outPtr, outSize, stride)), 
				WebPPixelFormat.Rgba => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPDecodeRGBAInto(dataPtr, dataSize, outPtr, outSize, stride)), 
				WebPPixelFormat.Bgr => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPDecodeBGRInto(dataPtr, dataSize, outPtr, outSize, stride)), 
				WebPPixelFormat.Rgb => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPDecodeRGBInto(dataPtr, dataSize, outPtr, outSize, stride)), 
				_ => throw new ArgumentOutOfRangeException("format"), 
			};
		}
	}
	public static class WebPEncoder
	{
		private class EncodeOutput
		{
			public MemoryStream Stream = new MemoryStream();
		}

		[ThreadStatic]
		private static WebPWriterFunction? _writerDelegate;

		private static int ManagedWriter(IntPtr data, UIntPtr dataSize, ref WebPPicture picture)
		{
			int num = (int)(uint)dataSize;
			if (num <= 0)
			{
				return 1;
			}
			byte[] array = new byte[num];
			Marshal.Copy(data, array, 0, num);
			((EncodeOutput)GCHandle.FromIntPtr(picture.custom_ptr).Target).Stream.Write(array, 0, num);
			return 1;
		}

		public static byte[] Encode(byte[] pixels, int width, int height, int stride, WebPPixelFormat format, float quality)
		{
			if (pixels == null)
			{
				throw new ArgumentNullException("pixels");
			}
			if (width <= 0)
			{
				throw new ArgumentOutOfRangeException("width");
			}
			if (height <= 0)
			{
				throw new ArgumentOutOfRangeException("height");
			}
			GCHandle gCHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
			try
			{
				IntPtr data = gCHandle.AddrOfPinnedObject();
				IntPtr result = IntPtr.Zero;
				UIntPtr uIntPtr;
				if (quality < 0f)
				{
					uIntPtr = EncodeLossless(data, width, height, stride, format, ref result);
				}
				else
				{
					if (quality > 100f)
					{
						quality = 100f;
					}
					uIntPtr = EncodeLossy(data, width, height, stride, format, quality, ref result);
				}
				if ((ulong)uIntPtr == 0L || result == IntPtr.Zero)
				{
					throw new Exception("WebP encode failed!");
				}
				try
				{
					byte[] array = new byte[(uint)(ulong)uIntPtr];
					Marshal.Copy(result, array, 0, array.Length);
					return array;
				}
				finally
				{
					NativeMethods.WebPSafeFree(result);
				}
			}
			finally
			{
				gCHandle.Free();
			}
		}

		public static void Encode(byte[] pixels, int width, int height, int stride, WebPPixelFormat format, float quality, Stream output)
		{
			if (output == null)
			{
				throw new ArgumentNullException("output");
			}
			byte[] array = Encode(pixels, width, height, stride, format, quality);
			output.Write(array, 0, array.Length);
		}

		private static UIntPtr EncodeLossy(IntPtr data, int width, int height, int stride, WebPPixelFormat format, float quality, ref IntPtr result)
		{
			IntPtr res = result;
			UIntPtr result2 = format switch
			{
				WebPPixelFormat.Bgra => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPEncodeBGRA(data, width, height, stride, quality, ref res)), 
				WebPPixelFormat.Rgba => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPEncodeRGBA(data, width, height, stride, quality, ref res)), 
				WebPPixelFormat.Bgr => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPEncodeBGR(data, width, height, stride, quality, ref res)), 
				WebPPixelFormat.Rgb => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPEncodeRGB(data, width, height, stride, quality, ref res)), 
				_ => throw new ArgumentOutOfRangeException("format"), 
			};
			result = res;
			return result2;
		}

		private static UIntPtr EncodeLossless(IntPtr data, int width, int height, int stride, WebPPixelFormat format, ref IntPtr result)
		{
			IntPtr res = result;
			UIntPtr result2 = format switch
			{
				WebPPixelFormat.Bgra => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPEncodeLosslessBGRA(data, width, height, stride, ref res)), 
				WebPPixelFormat.Rgba => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPEncodeLosslessRGBA(data, width, height, stride, ref res)), 
				WebPPixelFormat.Bgr => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPEncodeLosslessBGR(data, width, height, stride, ref res)), 
				WebPPixelFormat.Rgb => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPEncodeLosslessRGB(data, width, height, stride, ref res)), 
				_ => throw new ArgumentOutOfRangeException("format"), 
			};
			result = res;
			return result2;
		}

		public static byte[] Encode(byte[] pixels, int width, int height, int stride, WebPPixelFormat format, WebPEncoderConfig config)
		{
			if (pixels == null)
			{
				throw new ArgumentNullException("pixels");
			}
			if (config == null)
			{
				throw new ArgumentNullException("config");
			}
			if (width <= 0)
			{
				throw new ArgumentOutOfRangeException("width");
			}
			if (height <= 0)
			{
				throw new ArgumentOutOfRangeException("height");
			}
			if (!config.Validate())
			{
				throw new ArgumentException("Invalid WebP encoder configuration", "config");
			}
			WebPConfig nativeConfig = config.GetNativeConfig();
			WebPPicture picture = default(WebPPicture);
			NativeLibraryLoader.FixDllNotFoundException("webp", delegate
			{
				if (NativeMethods.WebPPictureInitInternal(ref picture, 528) == 0)
				{
					throw new Exception("WebP version mismatch: failed to initialize picture");
				}
				return 0;
			});
			picture.width = width;
			picture.height = height;
			picture.use_argb = 1;
			GCHandle gCHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
			EncodeOutput encodeOutput = new EncodeOutput();
			GCHandle value = GCHandle.Alloc(encodeOutput);
			_writerDelegate = ManagedWriter;
			try
			{
				IntPtr pixelPtr = gCHandle.AddrOfPinnedObject();
				if (format switch
				{
					WebPPixelFormat.Bgra => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPPictureImportBGRA(ref picture, pixelPtr, stride)), 
					WebPPixelFormat.Rgba => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPPictureImportRGBA(ref picture, pixelPtr, stride)), 
					WebPPixelFormat.Bgr => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPPictureImportBGR(ref picture, pixelPtr, stride)), 
					WebPPixelFormat.Rgb => NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPPictureImportRGB(ref picture, pixelPtr, stride)), 
					_ => throw new ArgumentOutOfRangeException("format"), 
				} == 0)
				{
					throw new Exception("Failed to import pixel data into WebPPicture");
				}
				picture.writer = _writerDelegate;
				picture.custom_ptr = GCHandle.ToIntPtr(value);
				if (NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPEncode(ref nativeConfig, ref picture)) == 0)
				{
					throw new Exception($"WebP encode failed with error: {picture.error_code}");
				}
				return encodeOutput.Stream.ToArray();
			}
			finally
			{
				NativeLibraryLoader.FixDllNotFoundException("webp", delegate
				{
					NativeMethods.WebPPictureFree(ref picture);
					return 0;
				});
				gCHandle.Free();
				value.Free();
				_writerDelegate = null;
			}
		}

		public static void Encode(byte[] pixels, int width, int height, int stride, WebPPixelFormat format, WebPEncoderConfig config, Stream outputStream)
		{
			if (outputStream == null)
			{
				throw new ArgumentNullException("outputStream");
			}
			byte[] array = Encode(pixels, width, height, stride, format, config);
			outputStream.Write(array, 0, array.Length);
		}
	}
	public class WebPEncoderConfig
	{
		private WebPConfig _config;

		private bool _initialized;

		public WebPEncoderConfig()
		{
			_config = default(WebPConfig);
			if (NativeMethods.WebPConfigInit(ref _config) == 0)
			{
				throw new Exception("WebP version mismatch: failed to initialize config");
			}
			_initialized = true;
		}

		public WebPEncoderConfig(WebPPreset preset, float quality)
		{
			_config = default(WebPConfig);
			if (NativeMethods.WebPConfigPreset(ref _config, preset, quality) == 0)
			{
				throw new Exception("WebP version mismatch: failed to initialize config with preset");
			}
			_initialized = true;
		}

		public WebPEncoderConfig SetQuality(float quality)
		{
			EnsureInitialized();
			_config.quality = Math.Max(0f, Math.Min(100f, quality));
			_config.lossless = 0;
			return this;
		}

		public WebPEncoderConfig SetLossless(bool lossless = true)
		{
			EnsureInitialized();
			_config.lossless = (lossless ? 1 : 0);
			return this;
		}

		public WebPEncoderConfig SetLosslessPreset(int level)
		{
			EnsureInitialized();
			_config.lossless = 1;
			NativeMethods.WebPConfigLosslessPreset(ref _config, level);
			return this;
		}

		public WebPEncoderConfig SetMethod(int method)
		{
			EnsureInitialized();
			_config.method = Math.Max(0, Math.Min(6, method));
			return this;
		}

		public WebPEncoderConfig SetNearLossless(int level)
		{
			EnsureInitialized();
			_config.near_lossless = Math.Max(0, Math.Min(100, level));
			return this;
		}

		public WebPEncoderConfig SetTargetSize(int bytes)
		{
			EnsureInitialized();
			_config.target_size = bytes;
			return this;
		}

		public WebPEncoderConfig SetTargetPSNR(float psnr)
		{
			EnsureInitialized();
			_config.target_PSNR = psnr;
			return this;
		}

		public WebPEncoderConfig SetMultiThreaded(bool enabled = true)
		{
			EnsureInitialized();
			_config.thread_level = (enabled ? 1 : 0);
			return this;
		}

		public WebPEncoderConfig SetSnsStrength(int strength)
		{
			EnsureInitialized();
			_config.sns_strength = Math.Max(0, Math.Min(100, strength));
			return this;
		}

		public WebPEncoderConfig SetFilterStrength(int strength)
		{
			EnsureInitialized();
			_config.filter_strength = Math.Max(0, Math.Min(100, strength));
			return this;
		}

		public WebPEncoderConfig SetAlphaQuality(int quality)
		{
			EnsureInitialized();
			_config.alpha_quality = Math.Max(0, Math.Min(100, quality));
			return this;
		}

		public WebPEncoderConfig SetImageHint(WebPImageHint hint)
		{
			EnsureInitialized();
			_config.image_hint = hint;
			return this;
		}

		public WebPEncoderConfig SetExact(bool exact = true)
		{
			EnsureInitialized();
			_config.exact = (exact ? 1 : 0);
			return this;
		}

		public WebPEncoderConfig SetSharpYuv(bool enabled = true)
		{
			EnsureInitialized();
			_config.use_sharp_yuv = (enabled ? 1 : 0);
			return this;
		}

		public bool Validate()
		{
			EnsureInitialized();
			return NativeMethods.WebPValidateConfig(ref _config) != 0;
		}

		public WebPConfig GetNativeConfig()
		{
			EnsureInitialized();
			return _config;
		}

		private void EnsureInitialized()
		{
			if (!_initialized)
			{
				throw new InvalidOperationException("Config not initialized");
			}
		}
	}
	public class WebPImageInfo
	{
		public int Width { get; }

		public int Height { get; }

		public bool HasAlpha { get; }

		public bool HasAnimation { get; }

		public int Format { get; }

		internal WebPImageInfo(int width, int height, bool hasAlpha, bool hasAnimation, int format)
		{
			Width = width;
			Height = height;
			HasAlpha = hasAlpha;
			HasAnimation = hasAnimation;
			Format = format;
		}
	}
	public static class WebPInfo
	{
		public static bool TryGetSize(byte[] data, out int width, out int height)
		{
			width = 0;
			height = 0;
			if (data == null || data.Length < 12)
			{
				return false;
			}
			GCHandle gCHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
			try
			{
				IntPtr ptr = gCHandle.AddrOfPinnedObject();
				UIntPtr size = (UIntPtr)(ulong)data.Length;
				int w = 0;
				int h = 0;
				int num = NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPGetInfo(ptr, size, ref w, ref h));
				width = w;
				height = h;
				return num != 0;
			}
			finally
			{
				gCHandle.Free();
			}
		}

		public static WebPImageInfo GetImageInfo(byte[] data)
		{
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			if (data.Length < 12)
			{
				throw new ArgumentException("Data too short to be a valid WebP file", "data");
			}
			GCHandle gCHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
			try
			{
				return GetImageInfo(gCHandle.AddrOfPinnedObject(), data.Length);
			}
			finally
			{
				gCHandle.Free();
			}
		}

		public static WebPImageInfo GetImageInfo(IntPtr data, long length)
		{
			if (data == IntPtr.Zero)
			{
				throw new ArgumentNullException("data");
			}
			if (length < 12)
			{
				throw new ArgumentException("Data too short to be a valid WebP file", "length");
			}
			WebPBitstreamFeatures features = default(WebPBitstreamFeatures);
			VP8StatusCode vP8StatusCode = NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPGetFeatures(data, (UIntPtr)(ulong)length, ref features));
			if (vP8StatusCode != VP8StatusCode.VP8_STATUS_OK)
			{
				throw new Exception($"Failed to get WebP features: {vP8StatusCode}");
			}
			return new WebPImageInfo(features.width, features.height, features.has_alpha != 0, features.has_animation != 0, features.format);
		}
	}
}
namespace Imazen.WebP.Extern
{
	public class NativeMethods
	{
		public const int WEBP_DECODER_ABI_VERSION = 528;

		public const int WEBP_ENCODER_ABI_VERSION = 528;

		public const int WEBP_MAX_DIMENSION = 16383;

		public const int WEBP_DEMUX_ABI_VERSION = 263;

		public const int WEBP_MUX_ABI_VERSION = 265;

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPGetDecoderVersion();

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPGetInfo([In] IntPtr data, UIntPtr data_size, ref int width, ref int height);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPDecodeRGBA([In] IntPtr data, UIntPtr data_size, ref int width, ref int height);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPDecodeARGB([In] IntPtr data, UIntPtr data_size, ref int width, ref int height);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPDecodeBGRA([In] IntPtr data, UIntPtr data_size, ref int width, ref int height);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPDecodeRGB([In] IntPtr data, UIntPtr data_size, ref int width, ref int height);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPDecodeBGR([In] IntPtr data, UIntPtr data_size, ref int width, ref int height);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPDecodeYUV([In] IntPtr data, UIntPtr data_size, ref int width, ref int height, ref IntPtr u, ref IntPtr v, ref int stride, ref int uv_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPDecodeRGBAInto([In] IntPtr data, UIntPtr data_size, IntPtr output_buffer, UIntPtr output_buffer_size, int output_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPDecodeARGBInto([In] IntPtr data, UIntPtr data_size, IntPtr output_buffer, UIntPtr output_buffer_size, int output_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPDecodeBGRAInto([In] IntPtr data, UIntPtr data_size, IntPtr output_buffer, UIntPtr output_buffer_size, int output_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPDecodeRGBInto([In] IntPtr data, UIntPtr data_size, IntPtr output_buffer, UIntPtr output_buffer_size, int output_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPDecodeBGRInto([In] IntPtr data, UIntPtr data_size, IntPtr output_buffer, UIntPtr output_buffer_size, int output_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPDecodeYUVInto([In] IntPtr data, UIntPtr data_size, IntPtr luma, UIntPtr luma_size, int luma_stride, IntPtr u, UIntPtr u_size, int u_stride, IntPtr v, UIntPtr v_size, int v_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPInitDecBufferInternal(ref WebPDecBuffer param0, int param1);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern void WebPFreeDecBuffer(ref WebPDecBuffer buffer);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPINewDecoder(ref WebPDecBuffer output_buffer);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPINewRGB(WEBP_CSP_MODE csp, IntPtr output_buffer, UIntPtr output_buffer_size, int output_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPINewYUVA(IntPtr luma, UIntPtr luma_size, int luma_stride, IntPtr u, UIntPtr u_size, int u_stride, IntPtr v, UIntPtr v_size, int v_stride, IntPtr a, UIntPtr a_size, int a_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPINewYUV(IntPtr luma, UIntPtr luma_size, int luma_stride, IntPtr u, UIntPtr u_size, int u_stride, IntPtr v, UIntPtr v_size, int v_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern void WebPIDelete(ref WebPIDecoder idec);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern VP8StatusCode WebPIAppend(ref WebPIDecoder idec, [In] IntPtr data, UIntPtr data_size);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern VP8StatusCode WebPIUpdate(ref WebPIDecoder idec, [In] IntPtr data, UIntPtr data_size);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPIDecGetRGB(ref WebPIDecoder idec, ref int last_y, ref int width, ref int height, ref int stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPIDecGetYUVA(ref WebPIDecoder idec, ref int last_y, ref IntPtr u, ref IntPtr v, ref IntPtr a, ref int width, ref int height, ref int stride, ref int uv_stride, ref int a_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPIDecodedArea(ref WebPIDecoder idec, ref int left, ref int top, ref int width, ref int height);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern VP8StatusCode WebPGetFeaturesInternal([In] IntPtr param0, UIntPtr param1, ref WebPBitstreamFeatures param2, int param3);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPInitDecoderConfigInternal(ref WebPDecoderConfig param0, int param1);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPIDecode([In] IntPtr data, UIntPtr data_size, ref WebPDecoderConfig config);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern VP8StatusCode WebPDecode([In] IntPtr data, UIntPtr data_size, ref WebPDecoderConfig config);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPValidateDecoderConfig(ref WebPDecoderConfig config);

		public static bool WebPIsPremultipliedMode(WEBP_CSP_MODE mode)
		{
			if (mode != WEBP_CSP_MODE.MODE_rgbA && mode != WEBP_CSP_MODE.MODE_bgrA && mode != WEBP_CSP_MODE.MODE_Argb)
			{
				return mode == WEBP_CSP_MODE.MODE_rgbA_4444;
			}
			return true;
		}

		public static bool WebPIsRGBMode(WEBP_CSP_MODE mode)
		{
			return mode < WEBP_CSP_MODE.MODE_YUV;
		}

		public static bool WebPIsAlphaMode(WEBP_CSP_MODE mode)
		{
			if (mode != WEBP_CSP_MODE.MODE_RGBA && mode != WEBP_CSP_MODE.MODE_BGRA && mode != WEBP_CSP_MODE.MODE_ARGB && mode != WEBP_CSP_MODE.MODE_RGBA_4444 && mode != WEBP_CSP_MODE.MODE_YUVA)
			{
				return WebPIsPremultipliedMode(mode);
			}
			return true;
		}

		public static VP8StatusCode WebPGetFeatures(IntPtr data, UIntPtr data_size, ref WebPBitstreamFeatures features)
		{
			return WebPGetFeaturesInternal(data, data_size, ref features, 528);
		}

		public static int WebPInitDecoderConfig(ref WebPDecoderConfig config)
		{
			return WebPInitDecoderConfigInternal(ref config, 528);
		}

		public static int WebPInitDecBuffer(ref WebPDecBuffer buffer)
		{
			return WebPInitDecBufferInternal(ref buffer, 528);
		}

		[DllImport("libwebpdemux", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPAnimDecoderOptionsInitInternal(ref WebPAnimDecoderOptions dec_options, int abi_version);

		[DllImport("libwebpdemux", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPAnimDecoderNewInternal(ref WebPData webp_data, ref WebPAnimDecoderOptions dec_options, int abi_version);

		[DllImport("libwebpdemux", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPAnimDecoderNewInternal")]
		public static extern IntPtr WebPAnimDecoderNewInternalDefault(ref WebPData webp_data, IntPtr dec_options, int abi_version);

		[DllImport("libwebpdemux", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPAnimDecoderGetInfo(IntPtr dec, ref WebPAnimInfo info);

		[DllImport("libwebpdemux", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPAnimDecoderGetNext(IntPtr dec, ref IntPtr buf, ref int timestamp);

		[DllImport("libwebpdemux", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPAnimDecoderHasMoreFrames(IntPtr dec);

		[DllImport("libwebpdemux", CallingConvention = CallingConvention.Cdecl)]
		public static extern void WebPAnimDecoderReset(IntPtr dec);

		[DllImport("libwebpdemux", CallingConvention = CallingConvention.Cdecl)]
		public static extern void WebPAnimDecoderDelete(IntPtr dec);

		public static int WebPAnimDecoderOptionsInit(ref WebPAnimDecoderOptions dec_options)
		{
			return WebPAnimDecoderOptionsInitInternal(ref dec_options, 263);
		}

		public static IntPtr WebPAnimDecoderNew(ref WebPData webp_data, ref WebPAnimDecoderOptions dec_options)
		{
			return WebPAnimDecoderNewInternal(ref webp_data, ref dec_options, 263);
		}

		public static IntPtr WebPAnimDecoderNewDefault(ref WebPData webp_data)
		{
			return WebPAnimDecoderNewInternalDefault(ref webp_data, IntPtr.Zero, 263);
		}

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPGetEncoderVersion();

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern UIntPtr WebPEncodeRGB([In] IntPtr rgb, int width, int height, int stride, float quality_factor, ref IntPtr output);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern UIntPtr WebPEncodeBGR([In] IntPtr bgr, int width, int height, int stride, float quality_factor, ref IntPtr output);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern UIntPtr WebPEncodeRGBA([In] IntPtr rgba, int width, int height, int stride, float quality_factor, ref IntPtr output);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern UIntPtr WebPEncodeBGRA([In] IntPtr bgra, int width, int height, int stride, float quality_factor, ref IntPtr output);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern UIntPtr WebPEncodeLosslessRGB([In] IntPtr rgb, int width, int height, int stride, ref IntPtr output);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern UIntPtr WebPEncodeLosslessBGR([In] IntPtr bgr, int width, int height, int stride, ref IntPtr output);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern UIntPtr WebPEncodeLosslessRGBA([In] IntPtr rgba, int width, int height, int stride, ref IntPtr output);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern UIntPtr WebPEncodeLosslessBGRA([In] IntPtr bgra, int width, int height, int stride, ref IntPtr output);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPConfigInitInternal(ref WebPConfig param0, WebPPreset param1, float param2, int param3);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPConfigLosslessPreset(ref WebPConfig config, int level);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPValidateConfig(ref WebPConfig config);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern void WebPMemoryWriterInit(ref WebPMemoryWriter writer);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern void WebPMemoryWriterClear(ref WebPMemoryWriter writer);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPMemoryWrite([In] IntPtr data, UIntPtr data_size, ref WebPPicture picture);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureInitInternal(ref WebPPicture param0, int param1);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureAlloc(ref WebPPicture picture);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern void WebPPictureFree(ref WebPPicture picture);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureCopy(ref WebPPicture src, ref WebPPicture dst);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureDistortion(ref WebPPicture src, ref WebPPicture reference, int metric_type, ref float result);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureCrop(ref WebPPicture picture, int left, int top, int width, int height);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureView(ref WebPPicture src, int left, int top, int width, int height, ref WebPPicture dst);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureIsView(ref WebPPicture picture);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureRescale(ref WebPPicture pic, int width, int height);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureImportRGB(ref WebPPicture picture, [In] IntPtr rgb, int rgb_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureImportRGBA(ref WebPPicture picture, [In] IntPtr rgba, int rgba_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureSmartARGBToYUVA(ref WebPPicture picture);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureImportRGBX(ref WebPPicture picture, [In] IntPtr rgbx, int rgbx_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureImportBGR(ref WebPPicture picture, [In] IntPtr bgr, int bgr_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureImportBGRA(ref WebPPicture picture, [In] IntPtr bgra, int bgra_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureImportBGRX(ref WebPPicture picture, [In] IntPtr bgrx, int bgrx_stride);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureARGBToYUVA(ref WebPPicture picture, WebPEncCSP colorspace);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureYUVAToARGB(ref WebPPicture picture);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern void WebPCleanupTransparentArea(ref WebPPicture picture);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureHasTransparency(ref WebPPicture picture);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureARGBToYUVADithered(ref WebPPicture picture, WebPEncCSP colorspace, float dithering);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPictureSharpARGBToYUVA(ref WebPPicture picture);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPPlaneDistortion([In] IntPtr src, UIntPtr src_stride, [In] IntPtr reference, UIntPtr ref_stride, int width, int height, UIntPtr x_step, int type, ref float distortion, ref float result);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern void WebPBlendAlpha(ref WebPPicture picture, uint background_rgb);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPEncode(ref WebPConfig config, ref WebPPicture picture);

		public static int WebPConfigInit(ref WebPConfig config)
		{
			return WebPConfigInitInternal(ref config, WebPPreset.WEBP_PRESET_DEFAULT, 75f, 528);
		}

		public static int WebPConfigPreset(ref WebPConfig config, WebPPreset preset, float quality)
		{
			return WebPConfigInitInternal(ref config, preset, quality, 528);
		}

		public static int WebPPictureInit(ref WebPPicture picture)
		{
			return WebPPictureInitInternal(ref picture, 528);
		}

		public static void WebPSafeFree(IntPtr toDeallocate)
		{
			NativeLibraryLoader.FixDllNotFoundException("webp", delegate
			{
				WebPFree(toDeallocate);
				return 0;
			});
		}

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern void WebPFree(IntPtr toDeallocate);

		[DllImport("libwebp", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPMalloc(UIntPtr size);

		[DllImport("libwebpmux", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPAnimEncoderOptionsInitInternal(ref WebPAnimEncoderOptions enc_options, int abi_version);

		[DllImport("libwebpmux", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPAnimEncoderNewInternal(int width, int height, ref WebPAnimEncoderOptions enc_options, int abi_version);

		[DllImport("libwebpmux", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPAnimEncoderNewInternal")]
		public static extern IntPtr WebPAnimEncoderNewInternalDefault(int width, int height, IntPtr enc_options, int abi_version);

		[DllImport("libwebpmux", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPAnimEncoderAdd(IntPtr enc, ref WebPPicture frame, int timestamp_ms, ref WebPConfig config);

		[DllImport("libwebpmux", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPAnimEncoderAdd")]
		public static extern int WebPAnimEncoderAddDefaultConfig(IntPtr enc, ref WebPPicture frame, int timestamp_ms, IntPtr config);

		[DllImport("libwebpmux", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPAnimEncoderAdd")]
		public static extern int WebPAnimEncoderAddNull(IntPtr enc, IntPtr frame, int timestamp_ms, IntPtr config);

		[DllImport("libwebpmux", CallingConvention = CallingConvention.Cdecl)]
		public static extern int WebPAnimEncoderAssemble(IntPtr enc, ref WebPData webp_data);

		[DllImport("libwebpmux", CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr WebPAnimEncoderGetError(IntPtr enc);

		[DllImport("libwebpmux", CallingConvention = CallingConvention.Cdecl)]
		public static extern void WebPAnimEncoderDelete(IntPtr enc);

		public static int WebPAnimEncoderOptionsInit(ref WebPAnimEncoderOptions enc_options)
		{
			return WebPAnimEncoderOptionsInitInternal(ref enc_options, 265);
		}

		public static IntPtr WebPAnimEncoderNew(int width, int height, ref WebPAnimEncoderOptions enc_options)
		{
			return WebPAnimEncoderNewInternal(width, height, ref enc_options, 265);
		}

		public static IntPtr WebPAnimEncoderNewDefault(int width, int height)
		{
			return WebPAnimEncoderNewInternalDefault(width, height, IntPtr.Zero, 265);
		}
	}
	public struct WebPIDecoder
	{
	}
	public enum WEBP_CSP_MODE
	{
		MODE_RGB,
		MODE_RGBA,
		MODE_BGR,
		MODE_BGRA,
		MODE_ARGB,
		MODE_RGBA_4444,
		MODE_RGB_565,
		MODE_rgbA,
		MODE_bgrA,
		MODE_Argb,
		MODE_rgbA_4444,
		MODE_YUV,
		MODE_YUVA,
		MODE_LAST
	}
	public struct WebPRGBABuffer
	{
		public IntPtr rgba;

		public int stride;

		public UIntPtr size;
	}
	public struct WebPYUVABuffer
	{
		public IntPtr y;

		public IntPtr u;

		public IntPtr v;

		public IntPtr a;

		public int y_stride;

		public int u_stride;

		public int v_stride;

		public int a_stride;

		public UIntPtr y_size;

		public UIntPtr u_size;

		public UIntPtr v_size;

		public UIntPtr a_size;
	}
	[StructLayout(LayoutKind.Explicit)]
	public struct Anonymous_690ed5ec_4c3d_40c6_9bd0_0747b5a28b54
	{
		[FieldOffset(0)]
		public WebPRGBABuffer RGBA;

		[FieldOffset(0)]
		public WebPYUVABuffer YUVA;
	}
	public struct WebPDecBuffer
	{
		public WEBP_CSP_MODE colorspace;

		public int width;

		public int height;

		public int is_external_memory;

		public Anonymous_690ed5ec_4c3d_40c6_9bd0_0747b5a28b54 u;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U4)]
		public uint[] pad;

		public IntPtr private_memory;
	}
	public enum VP8StatusCode
	{
		VP8_STATUS_OK,
		VP8_STATUS_OUT_OF_MEMORY,
		VP8_STATUS_INVALID_PARAM,
		VP8_STATUS_BITSTREAM_ERROR,
		VP8_STATUS_UNSUPPORTED_FEATURE,
		VP8_STATUS_SUSPENDED,
		VP8_STATUS_USER_ABORT,
		VP8_STATUS_NOT_ENOUGH_DATA
	}
	public struct WebPBitstreamFeatures
	{
		public int width;

		public int height;

		public int has_alpha;

		public int has_animation;

		public int format;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5, ArraySubType = UnmanagedType.U4)]
		public uint[] pad;
	}
	public struct WebPDecoderOptions
	{
		public int bypass_filtering;

		public int no_fancy_upsampling;

		public int use_cropping;

		public int crop_left;

		public int crop_top;

		public int crop_width;

		public int crop_height;

		public int use_scaling;

		public int scaled_width;

		public int scaled_height;

		public int use_threads;

		public int dithering_strength;

		public int flip;

		public int alpha_dithering_strength;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5, ArraySubType = UnmanagedType.U4)]
		public uint[] pad;
	}
	public struct WebPDecoderConfig
	{
		public WebPBitstreamFeatures input;

		public WebPDecBuffer output;

		public WebPDecoderOptions options;
	}
	public struct WebPAnimDecoderOptions
	{
		public WEBP_CSP_MODE color_mode;

		public int use_threads;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 7, ArraySubType = UnmanagedType.U4)]
		public uint[] padding;
	}
	public struct WebPAnimInfo
	{
		public uint canvas_width;

		public uint canvas_height;

		public uint loop_count;

		public uint bgcolor;

		public uint frame_count;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U4)]
		public uint[] pad;
	}
	public enum WebPImageHint
	{
		WEBP_HINT_DEFAULT,
		WEBP_HINT_PICTURE,
		WEBP_HINT_PHOTO,
		WEBP_HINT_GRAPH,
		WEBP_HINT_LAST
	}
	public struct WebPConfig
	{
		public int lossless;

		public float quality;

		public int method;

		public WebPImageHint image_hint;

		public int target_size;

		public float target_PSNR;

		public int segments;

		public int sns_strength;

		public int filter_strength;

		public int filter_sharpness;

		public int filter_type;

		public int autofilter;

		public int alpha_compression;

		public int alpha_filtering;

		public int alpha_quality;

		public int pass;

		public int show_compressed;

		public int preprocessing;

		public int partitions;

		public int partition_limit;

		public int emulate_jpeg_size;

		public int thread_level;

		public int low_memory;

		public int near_lossless;

		public int exact;

		public int use_delta_palette;

		public int use_sharp_yuv;

		public int qmin;

		public int qmax;
	}
	public enum WebPPreset
	{
		WEBP_PRESET_DEFAULT,
		WEBP_PRESET_PICTURE,
		WEBP_PRESET_PHOTO,
		WEBP_PRESET_DRAWING,
		WEBP_PRESET_ICON,
		WEBP_PRESET_TEXT
	}
	public struct WebPAuxStats
	{
		public int coded_size;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5, ArraySubType = UnmanagedType.R4)]
		public float[] PSNR;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.I4)]
		public int[] block_count;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.I4)]
		public int[] header_bytes;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12, ArraySubType = UnmanagedType.I4)]
		public int[] residual_bytes;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.I4)]
		public int[] segment_size;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.I4)]
		public int[] segment_quant;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.I4)]
		public int[] segment_level;

		public int alpha_data_size;

		public int layer_data_size;

		public uint lossless_features;

		public int histogram_bits;

		public int transform_bits;

		public int cache_bits;

		public int palette_size;

		public int lossless_size;

		public int lossless_hdr_size;

		public int lossless_data_size;

		public int cross_color_transform_bits;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1, ArraySubType = UnmanagedType.U4)]
		public uint[] pad;
	}
	[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
	public delegate int WebPWriterFunction([In] IntPtr data, UIntPtr data_size, ref WebPPicture picture);
	public struct WebPMemoryWriter
	{
		public IntPtr mem;

		public UIntPtr size;

		public UIntPtr max_size;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1, ArraySubType = UnmanagedType.U4)]
		public uint[] pad;
	}
	[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
	public delegate int WebPProgressHook(int percent, ref WebPPicture picture);
	public enum WebPEncCSP
	{
		WEBP_YUV420 = 0,
		WEBP_CSP_UV_MASK = 3,
		WEBP_YUV420A = 4,
		WEBP_CSP_ALPHA_BIT = 4
	}
	public enum WebPEncodingError
	{
		VP8_ENC_OK,
		VP8_ENC_ERROR_OUT_OF_MEMORY,
		VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY,
		VP8_ENC_ERROR_NULL_PARAMETER,
		VP8_ENC_ERROR_INVALID_CONFIGURATION,
		VP8_ENC_ERROR_BAD_DIMENSION,
		VP8_ENC_ERROR_PARTITION0_OVERFLOW,
		VP8_ENC_ERROR_PARTITION_OVERFLOW,
		VP8_ENC_ERROR_BAD_WRITE,
		VP8_ENC_ERROR_FILE_TOO_BIG,
		VP8_ENC_ERROR_USER_ABORT,
		VP8_ENC_ERROR_LAST
	}
	public struct WebPPicture
	{
		public int use_argb;

		public WebPEncCSP colorspace;

		public int width;

		public int height;

		public IntPtr y;

		public IntPtr u;

		public IntPtr v;

		public int y_stride;

		public int uv_stride;

		public IntPtr a;

		public int a_stride;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.U4)]
		public uint[] pad1;

		public IntPtr argb;

		public int argb_stride;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.U4)]
		public uint[] pad2;

		public WebPWriterFunction writer;

		public IntPtr custom_ptr;

		public int extra_info_type;

		public IntPtr extra_info;

		public IntPtr stats;

		public WebPEncodingError error_code;

		public WebPProgressHook progress_hook;

		public IntPtr user_data;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.U4)]
		public uint[] pad3;

		public IntPtr pad4;

		public IntPtr pad5;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8, ArraySubType = UnmanagedType.U4)]
		public uint[] pad6;

		public IntPtr memory_;

		public IntPtr memory_argb_;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2, ArraySubType = UnmanagedType.SysUInt)]
		public IntPtr[] pad7;
	}
	[Obsolete("Use NativeLibraryLoader.FixDllNotFoundException instead. Library loading is now automatic.")]
	public static class LoadLibrary
	{
		public static void LoadWebPOrFail()
		{
			NativeLibraryLoader.FixDllNotFoundException("webp", () => NativeMethods.WebPGetDecoderVersion());
		}

		[Obsolete]
		public static bool AutoLoadNearby(string name, bool throwFailure)
		{
			LoadWebPOrFail();
			return true;
		}
	}
	public struct WebPMuxAnimParams
	{
		public uint bgcolor;

		public int loop_count;
	}
	public struct WebPAnimEncoderOptions
	{
		public WebPMuxAnimParams anim_params;

		public int minimize_size;

		public int kmin;

		public int kmax;

		public int allow_mixed;

		public int verbose;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U4)]
		public uint[] padding;
	}
	public struct WebPData
	{
		public IntPtr bytes;

		public UIntPtr size;
	}
	public enum WebPMuxAnimDispose
	{
		WEBP_MUX_DISPOSE_NONE,
		WEBP_MUX_DISPOSE_BACKGROUND
	}
	public enum WebPMuxAnimBlend
	{
		WEBP_MUX_BLEND,
		WEBP_MUX_NO_BLEND
	}
	[Flags]
	public enum WebPFeatureFlags : uint
	{
		ANIMATION_FLAG = 2u,
		XMP_FLAG = 4u,
		EXIF_FLAG = 8u,
		ALPHA_FLAG = 0x10u,
		ICCP_FLAG = 0x20u,
		ALL_VALID_FLAGS = 0x3Eu
	}
}

BepInEx/plugins/ValheimMoments/Encoder/ValheimMoments.Encoder.exe

Decompiled 2 months ago
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using Imazen.WebP;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("ValheimMoments.Encoder")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+843a04a0cdca497472f3afcc175d172032bdee69")]
[assembly: AssemblyProduct("ValheimMoments.Encoder")]
[assembly: AssemblyTitle("ValheimMoments.Encoder")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
internal static class CinematicTitle
{
	internal static byte[] Create(int width, int height, string caption)
	{
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Expected O, but got Unknown
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Expected O, but got Unknown
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Expected O, but got Unknown
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Expected O, but got Unknown
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00de: Expected O, but got Unknown
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f2: Expected O, but got Unknown
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Expected O, but got Unknown
		if (caption == null || caption.Length > 512 || caption.Contains("\r"))
		{
			throw new ArgumentException("Invalid cinematic caption");
		}
		string[] array = caption.Split(new char[1] { '\n' });
		if (array.Length > 3)
		{
			throw new ArgumentException("Too many cinematic caption lines");
		}
		Bitmap val = new Bitmap(width, height, (PixelFormat)2498570);
		try
		{
			Graphics val2 = Graphics.FromImage((Image)(object)val);
			try
			{
				Font val3 = new Font("Georgia", Math.Max(8f, (float)height / 17f), (FontStyle)1, (GraphicsUnit)2);
				try
				{
					Font val4 = new Font("Georgia", Math.Max(7f, (float)height / 28f), (FontStyle)0, (GraphicsUnit)2);
					try
					{
						SolidBrush val5 = new SolidBrush(Color.FromArgb(255, 241, 211, 151));
						try
						{
							SolidBrush val6 = new SolidBrush(Color.FromArgb(255, 237, 236, 226));
							try
							{
								SolidBrush val7 = new SolidBrush(Color.FromArgb(230, 0, 0, 0));
								try
								{
									StringFormat val8 = new StringFormat
									{
										Alignment = (StringAlignment)1,
										LineAlignment = (StringAlignment)1,
										Trimming = (StringTrimming)3,
										FormatFlags = (StringFormatFlags)4096
									};
									try
									{
										val2.Clear(Color.Transparent);
										val2.TextRenderingHint = (TextRenderingHint)3;
										float num = (float)height * 0.63f;
										for (int i = 0; i < array.Length; i++)
										{
											Font val9 = ((i == 0) ? val3 : val4);
											float num2 = val9.Size * 1.45f;
											RectangleF rectangleF = new RectangleF((float)width * 0.08f, num, (float)width * 0.84f, num2);
											RectangleF rectangleF2 = rectangleF;
											rectangleF2.Offset(1f, 2f);
											val2.DrawString(array[i], val9, (Brush)(object)val7, rectangleF2, val8);
											val2.DrawString(array[i], val9, (Brush)(object)((i == 0) ? val5 : val6), rectangleF, val8);
											num += num2;
										}
										BitmapData val10 = val.LockBits(new Rectangle(0, 0, width, height), (ImageLockMode)1, (PixelFormat)2498570);
										try
										{
											byte[] array2 = new byte[width * height * 4];
											Marshal.Copy(val10.Scan0, array2, 0, array2.Length);
											return array2;
										}
										finally
										{
											val.UnlockBits(val10);
										}
									}
									finally
									{
										((IDisposable)val8)?.Dispose();
									}
								}
								finally
								{
									((IDisposable)val7)?.Dispose();
								}
							}
							finally
							{
								((IDisposable)val6)?.Dispose();
							}
						}
						finally
						{
							((IDisposable)val5)?.Dispose();
						}
					}
					finally
					{
						((IDisposable)val4)?.Dispose();
					}
				}
				finally
				{
					((IDisposable)val3)?.Dispose();
				}
			}
			finally
			{
				((IDisposable)val2)?.Dispose();
			}
		}
		finally
		{
			((IDisposable)val)?.Dispose();
		}
	}

	internal static void Apply(byte[] frame, int width, int height, byte[] title, int milliseconds, bool letterbox)
	{
		float num = Math.Max(0f, Math.Min(1f, (float)milliseconds / 850f));
		num = num * num * (3f - 2f * num);
		int num2 = (letterbox ? ((int)Math.Round(Math.Max(0.0, ((double)height - (double)width / 2.39) / 2.0) * (double)num)) : 0);
		float num3 = Math.Max(0f, Math.Min(1f, (float)(milliseconds - 500) / 600f));
		for (int i = 0; i < height; i++)
		{
			for (int j = 0; j < width; j++)
			{
				int num4 = (i * width + j) * 4;
				if (i < num2 || i >= height - num2)
				{
					frame[num4] = (frame[num4 + 1] = (frame[num4 + 2] = 0));
					frame[num4 + 3] = byte.MaxValue;
				}
				else
				{
					if (title == null)
					{
						continue;
					}
					int num5 = (int)((float)(int)title[num4 + 3] * num3);
					if (num5 != 0)
					{
						for (int k = 0; k < 3; k++)
						{
							frame[num4 + k] = (byte)((title[num4 + k] * num5 + frame[num4 + k] * (255 - num5) + 127) / 255);
						}
					}
				}
			}
		}
	}
}
internal static class DirectorNameplate
{
	internal static byte[] Create(int width, int height, string name)
	{
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Expected O, but got Unknown
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Expected O, but got Unknown
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Expected O, but got Unknown
		//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_00bf: 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_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Expected O, but got Unknown
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Expected O, but got Unknown
		if (string.IsNullOrWhiteSpace(name) || name.Length > 128 || name.IndexOfAny(new char[2] { '\r', '\n' }) >= 0)
		{
			throw new ArgumentException("Invalid recorder label");
		}
		int num = Math.Max(1, width * 3 / 5);
		int num2 = Math.Max(4, height / 9);
		Bitmap val = new Bitmap(num, num2, (PixelFormat)2498570);
		try
		{
			Graphics val2 = Graphics.FromImage((Image)(object)val);
			try
			{
				SolidBrush val3 = new SolidBrush(Color.FromArgb(255, 241, 211, 151));
				try
				{
					Pen val4 = new Pen(Color.FromArgb(235, 183, 141, 64), Math.Max(1f, (float)height / 540f));
					try
					{
						StringFormat val5 = new StringFormat
						{
							Alignment = (StringAlignment)1,
							LineAlignment = (StringAlignment)1,
							Trimming = (StringTrimming)3,
							FormatFlags = (StringFormatFlags)4096
						};
						try
						{
							val2.Clear(Color.FromArgb(180, 8, 12, 13));
							val2.TextRenderingHint = (TextRenderingHint)3;
							val2.DrawLine(val4, (float)num * 0.12f, 1f, (float)num * 0.88f, 1f);
							Font val6 = new Font("Georgia", Math.Max(8f, (float)height / 27f), (FontStyle)1, (GraphicsUnit)2);
							try
							{
								val2.DrawString(name, val6, (Brush)(object)val3, new RectangleF(8f, 2f, Math.Max(1, num - 16), num2 - 2), val5);
							}
							finally
							{
								((IDisposable)val6)?.Dispose();
							}
							BitmapData val7 = val.LockBits(new Rectangle(0, 0, num, num2), (ImageLockMode)1, (PixelFormat)2498570);
							try
							{
								byte[] array = new byte[num * num2 * 4];
								Marshal.Copy(val7.Scan0, array, 0, array.Length);
								return array;
							}
							finally
							{
								val.UnlockBits(val7);
							}
						}
						finally
						{
							((IDisposable)val5)?.Dispose();
						}
					}
					finally
					{
						((IDisposable)val4)?.Dispose();
					}
				}
				finally
				{
					((IDisposable)val3)?.Dispose();
				}
			}
			finally
			{
				((IDisposable)val2)?.Dispose();
			}
		}
		finally
		{
			((IDisposable)val)?.Dispose();
		}
	}

	internal static void Apply(byte[] frame, int width, int height, byte[] plate)
	{
		int num = Math.Max(1, width * 3 / 5);
		int num2 = Math.Max(4, height / 9);
		int num3 = (width - num) / 2;
		int num4 = height - num2 - height / 12;
		for (int i = 0; i < num2; i++)
		{
			for (int j = 0; j < num; j++)
			{
				int num5 = (i * num + j) * 4;
				int num6 = ((num4 + i) * width + num3 + j) * 4;
				int num7 = plate[num5 + 3];
				for (int k = 0; k < 3; k++)
				{
					frame[num6 + k] = (byte)((plate[num5 + k] * num7 + frame[num6 + k] * (255 - num7) + 127) / 255);
				}
			}
		}
	}
}
internal static class Program
{
	public static int Main(string[] args)
	{
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Expected O, but got Unknown
		//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fa: Expected O, but got Unknown
		string text = null;
		try
		{
			if (args.Length == 6 && args[0] == "--cinematic")
			{
				if (args[5] != "0" && args[5] != "1")
				{
					throw new ArgumentException("Letterbox flag");
				}
				string recorder = Encoding.UTF8.GetString(Convert.FromBase64String(args[4]));
				SegmentComposer.Compose(args[1], null, args[2], int.Parse(args[3], CultureInfo.InvariantCulture), recorder, cinematic: true, args[5] == "1");
				return 0;
			}
			if (args.Length == 4 && args[0] == "--normalize")
			{
				SegmentComposer.Compose(args[1], null, args[2], int.Parse(args[3], CultureInfo.InvariantCulture));
				return 0;
			}
			if (args.Length == 5 && args[0] == "--label")
			{
				string recorder2 = Encoding.UTF8.GetString(Convert.FromBase64String(args[4]));
				SegmentComposer.Compose(args[1], null, args[2], int.Parse(args[3], CultureInfo.InvariantCulture), recorder2);
				return 0;
			}
			if (args.Length == 5 && args[0] == "--compose")
			{
				SegmentComposer.Compose(args[1], args[2], args[3], int.Parse(args[4], CultureInfo.InvariantCulture));
				return 0;
			}
			if (args.Length == 2 && args[0] == "--verify")
			{
				AnimDecoder val = new AnimDecoder(File.ReadAllBytes(args[1]), false);
				try
				{
					int num = 0;
					int num2 = 0;
					while (val.HasMoreFrames())
					{
						AnimFrame nextFrame = val.GetNextFrame();
						num++;
						num2 += nextFrame.DurationMs;
					}
					Console.WriteLine("frames={0} width={1} height={2} decoder_duration_ms={3:F0}", num, val.Info.Width, val.Info.Height, num2);
				}
				finally
				{
					((IDisposable)val)?.Dispose();
				}
				return 0;
			}
			if (args.Length != 1)
			{
				throw new ArgumentException("Expected output WebP path");
			}
			Stopwatch stopwatch = Stopwatch.StartNew();
			using (BinaryReader binaryReader = new BinaryReader(Console.OpenStandardInput()))
			{
				if (binaryReader.ReadInt32() != 826492246)
				{
					throw new InvalidDataException("Bad VEC1 header");
				}
				int num3 = binaryReader.ReadInt32();
				int num4 = binaryReader.ReadInt32();
				int num5 = binaryReader.ReadInt32();
				int num6 = binaryReader.ReadInt32();
				bool flag = binaryReader.ReadBoolean();
				if (num3 < 16 || num3 > 1920 || num4 < 16 || num4 > 1080 || num5 < 1 || num5 > 1800 || num6 < 1 || num6 > 100)
				{
					throw new InvalidDataException("Invalid capture dimensions/count/quality");
				}
				int num7 = checked(num3 * num4 * 4);
				if ((long)num7 * (long)num5 > 268435456)
				{
					throw new InvalidDataException("Clip exceeds encoder's 256 MiB raw-frame limit");
				}
				byte[] array = new byte[num7];
				WebPEncoderConfig val2 = new WebPEncoderConfig().SetQuality((float)num6).SetMethod(3).SetMultiThreaded(false);
				AnimEncoder val3 = new AnimEncoder(num3, num4, 0, 0u, false, false);
				try
				{
					int num8 = 0;
					int num9 = 0;
					Stopwatch stopwatch2 = Stopwatch.StartNew();
					for (int i = 0; i < num5; i++)
					{
						int num10 = binaryReader.ReadInt32();
						if (num10 < 1 || num10 > 60000 || (num8 += num10) > 60000)
						{
							throw new InvalidDataException("Invalid frame timing");
						}
						int num11;
						for (int j = 0; j < num7; j += num11)
						{
							num11 = binaryReader.Read(array, j, num7 - j);
							if (num11 == 0)
							{
								throw new EndOfStreamException("Incomplete frame");
							}
						}
						if (flag)
						{
							int num12 = num3 * 4;
							for (int k = 0; k < num4 / 2; k++)
							{
								for (int l = 0; l < num12; l++)
								{
									int num13 = k * num12 + l;
									int num14 = (num4 - k - 1) * num12 + l;
									byte b = array[num13];
									array[num13] = array[num14];
									array[num14] = b;
								}
							}
						}
						val3.AddFrame(array, num3 * 4, (WebPPixelFormat)1, num8 - num10, val2);
						num9 = num10;
					}
					string fullPath = Path.GetFullPath(args[0]);
					Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
					text = fullPath + ".partial";
					FieldInfo? field = typeof(AnimEncoder).GetField("_lastDuration", BindingFlags.Instance | BindingFlags.NonPublic);
					if (field == null)
					{
						throw new MissingFieldException("Pinned animation duration contract changed");
					}
					field.SetValue(val3, num9);
					File.WriteAllBytes(text, val3.Assemble());
					stopwatch2.Stop();
					File.Move(text, fullPath);
					text = null;
					Console.WriteLine("frames={0} duration_ms={1} elapsed_ms={2} encode_ms={3} bytes={4} helper_peak_mib={5:F1}", num5, num8, stopwatch.ElapsedMilliseconds, stopwatch2.ElapsedMilliseconds, new FileInfo(fullPath).Length, (double)Process.GetCurrentProcess().PeakWorkingSet64 / 1048576.0);
				}
				finally
				{
					((IDisposable)val3)?.Dispose();
				}
			}
			return 0;
		}
		catch (Exception ex)
		{
			Console.Error.WriteLine(ex.GetType().Name + ": " + ex.Message);
			return 1;
		}
		finally
		{
			if (text != null)
			{
				try
				{
					File.Delete(text);
				}
				catch
				{
				}
			}
		}
	}
}
internal static class SegmentComposer
{
	private sealed class Segment
	{
		internal byte[] Bytes;

		internal int Width;

		internal int Height;

		internal int Frames;

		internal int Duration;
	}

	internal static void Compose(string opening, string ending, string output, int quality, string recorder = null, bool cinematic = false, bool letterbox = true)
	{
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Expected O, but got Unknown
		//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b5: Expected O, but got Unknown
		if (quality < 1 || quality > 100)
		{
			throw new ArgumentOutOfRangeException("quality");
		}
		string fullPath = Path.GetFullPath(output);
		if (string.Equals(fullPath, Path.GetFullPath(opening), StringComparison.OrdinalIgnoreCase) || (ending != null && string.Equals(fullPath, Path.GetFullPath(ending), StringComparison.OrdinalIgnoreCase)))
		{
			throw new InvalidDataException("Output must differ from inputs");
		}
		Segment segment = Read(opening);
		Segment segment2 = ((ending == null) ? null : Read(ending));
		int num = segment.Frames + (segment2?.Frames ?? 0);
		if ((segment2 != null && (segment.Width != segment2.Width || segment.Height != segment2.Height)) || num > 1800 || segment.Duration + (segment2?.Duration ?? 0) > 60000 || (long)segment.Width * (long)segment.Height * 4 * num > 268435456)
		{
			throw new InvalidDataException("Incompatible or oversized segments");
		}
		byte[] array = ((recorder == null) ? null : (cinematic ? CinematicTitle.Create(segment.Width, segment.Height, recorder) : DirectorNameplate.Create(segment.Width, segment.Height, recorder)));
		WebPEncoderConfig val = new WebPEncoderConfig().SetQuality((float)quality).SetMethod(3).SetMultiThreaded(false);
		Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
		string text = fullPath + "." + Guid.NewGuid().ToString("N") + ".partial";
		bool flag = false;
		try
		{
			AnimEncoder val2 = new AnimEncoder(segment.Width, segment.Height, 0, 0u, false, false);
			try
			{
				int num2 = 0;
				int num3 = 0;
				Segment[] array2 = ((segment2 != null) ? new Segment[2] { segment, segment2 } : new Segment[1] { segment });
				foreach (Segment segment3 in array2)
				{
					AnimDecoder val3 = new AnimDecoder(segment3.Bytes, false);
					try
					{
						if (val3.Info.Width != segment3.Width || val3.Info.Height != segment3.Height)
						{
							throw new InvalidDataException("Decoded canvas differs from header");
						}
						int num4 = 0;
						int num5 = 0;
						while (val3.HasMoreFrames())
						{
							AnimFrame nextFrame = val3.GetNextFrame();
							if (++num4 > segment3.Frames || nextFrame.DurationMs < 1 || nextFrame.DurationMs > 60000 || nextFrame.Pixels.Length != segment3.Width * segment3.Height * 4)
							{
								throw new InvalidDataException("Decoded segment exceeds declared budget");
							}
							if (cinematic)
							{
								CinematicTitle.Apply(nextFrame.Pixels, segment.Width, segment.Height, array, num2, letterbox);
							}
							else if (array != null)
							{
								DirectorNameplate.Apply(nextFrame.Pixels, segment.Width, segment.Height, array);
							}
							val2.AddFrame(nextFrame.Pixels, segment.Width * 4, (WebPPixelFormat)0, num2, val);
							num3 = nextFrame.DurationMs;
							num2 += num3;
							num5 += num3;
							if (num5 > segment3.Duration)
							{
								throw new InvalidDataException("Decoded timing exceeds segment");
							}
						}
						if (num4 != segment3.Frames || num5 != segment3.Duration)
						{
							throw new InvalidDataException("Decoded timing/count differs from segment");
						}
					}
					finally
					{
						((IDisposable)val3)?.Dispose();
					}
				}
				FieldInfo? field = typeof(AnimEncoder).GetField("_lastDuration", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field == null)
				{
					throw new MissingFieldException("Pinned animation duration contract changed");
				}
				field.SetValue(val2, num3);
				using (FileStream fileStream = new FileStream(text, FileMode.CreateNew, FileAccess.Write, FileShare.None))
				{
					flag = true;
					byte[] array3 = val2.Assemble();
					fileStream.Write(array3, 0, array3.Length);
					fileStream.Flush(flushToDisk: true);
				}
				File.Move(text, fullPath);
				flag = false;
				Console.WriteLine("segments=2 frames={0} duration_ms={1} bytes={2}", num, num2, new FileInfo(fullPath).Length);
			}
			finally
			{
				((IDisposable)val2)?.Dispose();
			}
		}
		finally
		{
			if (flag)
			{
				try
				{
					File.Delete(text);
				}
				catch
				{
				}
			}
		}
	}

	private static Segment Read(string path)
	{
		Segment segment = new Segment();
		using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
		{
			if (fileStream.Length < 30 || fileStream.Length > 10485760)
			{
				throw new InvalidDataException("Segment file size out of range");
			}
			using BinaryReader binaryReader = new BinaryReader(fileStream);
			segment.Bytes = binaryReader.ReadBytes((int)fileStream.Length);
			if (segment.Bytes.Length != fileStream.Length)
			{
				throw new EndOfStreamException();
			}
		}
		using BinaryReader binaryReader2 = new BinaryReader(new MemoryStream(segment.Bytes));
		if (Text(binaryReader2, 4) != "RIFF" || binaryReader2.ReadUInt32() != segment.Bytes.Length - 8 || Text(binaryReader2, 4) != "WEBP")
		{
			throw new InvalidDataException("Invalid segment RIFF");
		}
		bool flag = false;
		while (binaryReader2.BaseStream.Position < binaryReader2.BaseStream.Length)
		{
			if (binaryReader2.BaseStream.Length - binaryReader2.BaseStream.Position < 8)
			{
				throw new InvalidDataException("Truncated chunk");
			}
			string text = Text(binaryReader2, 4);
			uint num = binaryReader2.ReadUInt32();
			long num2 = binaryReader2.BaseStream.Position + num + (num & 1);
			if (num2 > binaryReader2.BaseStream.Length)
			{
				throw new InvalidDataException("Truncated chunk body");
			}
			if (text == "VP8X")
			{
				if (flag || num != 10)
				{
					throw new InvalidDataException("Invalid canvas");
				}
				byte[] bytes = binaryReader2.ReadBytes(10);
				segment.Width = 1 + Number(bytes, 4);
				segment.Height = 1 + Number(bytes, 7);
				flag = true;
				if (segment.Width < 16 || segment.Width > 1920 || segment.Height < 16 || segment.Height > 1080)
				{
					throw new InvalidDataException("Canvas exceeds capture bounds");
				}
			}
			if (text == "ANMF")
			{
				if (!flag || num < 16)
				{
					throw new InvalidDataException("Invalid frame header");
				}
				byte[] bytes2 = binaryReader2.ReadBytes(16);
				int num3 = Number(bytes2, 12);
				if ((long)Number(bytes2, 0) * 2L + Number(bytes2, 6) + 1 > segment.Width || (long)Number(bytes2, 3) * 2L + Number(bytes2, 9) + 1 > segment.Height)
				{
					throw new InvalidDataException("Frame rectangle exceeds canvas");
				}
				if (num3 < 1 || num3 > 60000 || ++segment.Frames > 1800 || (segment.Duration += num3) > 60000 || (long)segment.Width * (long)segment.Height * 4 * segment.Frames > 268435456)
				{
					throw new InvalidDataException("Segment exceeds frame/timing budget");
				}
			}
			binaryReader2.BaseStream.Position = num2;
		}
		if (!flag || segment.Frames == 0)
		{
			throw new InvalidDataException("Expected animated segment");
		}
		return segment;
	}

	private static string Text(BinaryReader reader, int count)
	{
		return Encoding.ASCII.GetString(reader.ReadBytes(count));
	}

	private static int Number(byte[] bytes, int offset)
	{
		return bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16);
	}
}