Decompiled source of MemeBanner v1.0.1

MemeBanner.dll

Decompiled 8 minutes ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using RiskOfOptions;
using RiskOfOptions.Options;
using RoR2;
using UnityEngine;

[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("MemeBanner")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+e978bd25aa951e3bbc9ee26b71fc6d8a1a3ec81e")]
[assembly: AssemblyProduct("MemeBanner")]
[assembly: AssemblyTitle("MemeBanner")]
[assembly: AssemblyVersion("1.0.1.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 ApngDecoder
{
	private sealed class FrameInfo
	{
		internal int Width;

		internal int Height;

		internal int X;

		internal int Y;

		internal float Delay;

		internal byte Dispose;

		internal byte Blend;

		internal readonly List<byte[]> Parts = new List<byte[]>();
	}

	private const byte DISPOSE_BACKGROUND = 1;

	private const byte DISPOSE_PREVIOUS = 2;

	private const byte BLEND_OVER = 1;

	private const int FCTL_LENGTH = 26;

	private const int CHUNK_OVERHEAD = 12;

	private static readonly uint[] CrcTable = BuildCrcTable();

	internal static bool TryDecode(byte[] data, out Color32[][] frames, out float[] delays, out int width, out int height)
	{
		//IL_03c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d0: Expected O, but got Unknown
		//IL_04ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_04bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_04dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_04e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_04e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_04d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_04ed: Unknown result type (might be due to invalid IL or missing references)
		frames = null;
		delays = null;
		width = 0;
		height = 0;
		if (data.Length < 8 || data[0] != 137 || data[1] != 80 || data[2] != 78 || data[3] != 71)
		{
			return false;
		}
		byte[] array = null;
		List<byte[]> list = new List<byte[]>();
		List<FrameInfo> list2 = new List<FrameInfo>();
		bool flag = false;
		int num = 8;
		while (num + 8 <= data.Length)
		{
			int num2 = ReadInt(data, num);
			if (num2 < 0 || num + 12 + num2 > data.Length)
			{
				break;
			}
			string text = Encoding.ASCII.GetString(data, num + 4, 4);
			int num3 = num + 8;
			switch (text)
			{
			case "IHDR":
				array = Slice(data, num3, num2);
				width = ReadInt(data, num3);
				height = ReadInt(data, num3 + 4);
				break;
			case "acTL":
				flag = true;
				break;
			case "fcTL":
				if (num2 >= 26)
				{
					int num4 = (data[num3 + 22] << 8) | data[num3 + 23];
					list2.Add(new FrameInfo
					{
						Width = ReadInt(data, num3 + 4),
						Height = ReadInt(data, num3 + 8),
						X = ReadInt(data, num3 + 12),
						Y = ReadInt(data, num3 + 16),
						Delay = (float)((data[num3 + 20] << 8) | data[num3 + 21]) / ((num4 == 0) ? 100f : ((float)num4)),
						Dispose = data[num3 + 24],
						Blend = data[num3 + 25]
					});
				}
				break;
			case "IDAT":
				if (list2.Count == 1)
				{
					list2[0].Parts.Add(Slice(data, num3, num2));
				}
				break;
			case "fdAT":
				if (list2.Count > 0 && num2 > 4)
				{
					list2[list2.Count - 1].Parts.Add(Slice(data, num3 + 4, num2 - 4));
				}
				break;
			case "PLTE":
			case "tRNS":
			case "gAMA":
			case "cHRM":
			case "sRGB":
			case "iCCP":
			case "sBIT":
			case "pHYs":
				list.Add(Slice(data, num, num2 + 12));
				break;
			}
			num += 12 + num2;
			if (text == "IEND")
			{
				break;
			}
		}
		if (!flag || array == null || width <= 0 || height <= 0 || list2.Count == 0)
		{
			return false;
		}
		List<Color32[]> list3 = new List<Color32[]>();
		List<float> list4 = new List<float>();
		Color32[] array2 = (Color32[])(object)new Color32[width * height];
		Color32[] array3 = null;
		Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
		foreach (FrameInfo item in list2)
		{
			if (item.Parts.Count == 0 || item.Width <= 0 || item.Height <= 0 || !ImageConversion.LoadImage(val, BuildPng(array, list, item)))
			{
				continue;
			}
			Color32[] pixels = val.GetPixels32();
			if (pixels.Length < item.Width * item.Height)
			{
				continue;
			}
			if (item.Dispose == 2)
			{
				array3 = (Color32[])array2.Clone();
			}
			int num5 = height - item.Y - item.Height;
			for (int i = 0; i < item.Height; i++)
			{
				int num6 = num5 + i;
				if (num6 < 0 || num6 >= height)
				{
					continue;
				}
				for (int j = 0; j < item.Width; j++)
				{
					int num7 = item.X + j;
					if (num7 >= 0 && num7 < width)
					{
						Color32 val2 = pixels[i * item.Width + j];
						int num8 = num6 * width + num7;
						array2[num8] = ((item.Blend == 1) ? Over(val2, array2[num8]) : val2);
					}
				}
			}
			list3.Add((Color32[])array2.Clone());
			list4.Add(Mathf.Max(item.Delay, 0.01f));
			if (item.Dispose == 1)
			{
				PixelCanvas.ClearRect(array2, width, height, item.X, num5, item.Width, item.Height);
			}
			else if (item.Dispose == 2 && array3 != null)
			{
				array2 = array3;
			}
		}
		Object.Destroy((Object)(object)val);
		if (list3.Count == 0)
		{
			return false;
		}
		frames = list3.ToArray();
		delays = list4.ToArray();
		return true;
	}

	private static Color32 Over(Color32 source, Color32 under)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		if (source.a == byte.MaxValue || under.a == 0)
		{
			return source;
		}
		if (source.a == 0)
		{
			return under;
		}
		float num = (float)(int)source.a / 255f;
		float num2 = (float)(int)under.a / 255f;
		float num3 = num + num2 * (1f - num);
		if (num3 <= 0f)
		{
			return new Color32((byte)0, (byte)0, (byte)0, (byte)0);
		}
		return new Color32(Mix(source.r, under.r, num, num2, num3), Mix(source.g, under.g, num, num2, num3), Mix(source.b, under.b, num, num2, num3), (byte)Mathf.RoundToInt(num3 * 255f));
	}

	private static byte Mix(byte source, byte under, float sa, float ua, float outA)
	{
		return (byte)Mathf.Clamp(Mathf.RoundToInt(((float)(int)source / 255f * sa + (float)(int)under / 255f * ua * (1f - sa)) / outA * 255f), 0, 255);
	}

	private static byte[] BuildPng(byte[] header, List<byte[]> carried, FrameInfo info)
	{
		byte[] array = (byte[])header.Clone();
		WriteInt(array, 0, info.Width);
		WriteInt(array, 4, info.Height);
		int num = 0;
		foreach (byte[] part in info.Parts)
		{
			num += part.Length;
		}
		byte[] array2 = new byte[num];
		int num2 = 0;
		foreach (byte[] part2 in info.Parts)
		{
			Buffer.BlockCopy(part2, 0, array2, num2, part2.Length);
			num2 += part2.Length;
		}
		List<byte> list = new List<byte>(num + 256);
		list.AddRange(new byte[8] { 137, 80, 78, 71, 13, 10, 26, 10 });
		AppendChunk(list, "IHDR", array);
		foreach (byte[] item in carried)
		{
			list.AddRange(item);
		}
		AppendChunk(list, "IDAT", array2);
		AppendChunk(list, "IEND", new byte[0]);
		return list.ToArray();
	}

	private static void AppendChunk(List<byte> png, string type, byte[] body)
	{
		byte[] array = new byte[4];
		WriteInt(array, 0, body.Length);
		png.AddRange(array);
		byte[] array2 = new byte[4 + body.Length];
		for (int i = 0; i < 4; i++)
		{
			array2[i] = (byte)type[i];
		}
		Buffer.BlockCopy(body, 0, array2, 4, body.Length);
		png.AddRange(array2);
		byte[] array3 = new byte[4];
		WriteInt(array3, 0, (int)Crc32(array2));
		png.AddRange(array3);
	}

	private static uint[] BuildCrcTable()
	{
		uint[] array = new uint[256];
		for (uint num = 0u; num < 256; num++)
		{
			uint num2 = num;
			for (int i = 0; i < 8; i++)
			{
				num2 = (((num2 & 1) != 0) ? (0xEDB88320u ^ (num2 >> 1)) : (num2 >> 1));
			}
			array[num] = num2;
		}
		return array;
	}

	private static uint Crc32(byte[] bytes)
	{
		uint num = uint.MaxValue;
		foreach (byte b in bytes)
		{
			num = CrcTable[(num ^ b) & 0xFF] ^ (num >> 8);
		}
		return num ^ 0xFFFFFFFFu;
	}

	private static byte[] Slice(byte[] data, int offset, int length)
	{
		byte[] array = new byte[length];
		Buffer.BlockCopy(data, offset, array, 0, length);
		return array;
	}

	private static int ReadInt(byte[] data, int offset)
	{
		return (data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3];
	}

	private static void WriteInt(byte[] data, int offset, int value)
	{
		data[offset] = (byte)(value >> 24);
		data[offset + 1] = (byte)(value >> 16);
		data[offset + 2] = (byte)(value >> 8);
		data[offset + 3] = (byte)value;
	}
}
internal static class GifDecoder
{
	private enum BlockResult
	{
		Ok,
		Skip,
		Fatal
	}

	private struct GraphicControl
	{
		internal int DelayHundredths;

		internal int Disposal;

		internal int TransparentIndex;

		internal static GraphicControl Default()
		{
			return new GraphicControl
			{
				DelayHundredths = 10,
				Disposal = 0,
				TransparentIndex = -1
			};
		}

		internal float Delay()
		{
			if (DelayHundredths > 1)
			{
				return (float)DelayHundredths / 100f;
			}
			return 0.1f;
		}
	}

	private struct ImageBlock
	{
		internal int Left;

		internal int Top;

		internal int Width;

		internal int Height;

		internal bool Interlaced;

		internal Color32[] Table;

		internal byte[] Indices;
	}

	private const byte BLOCK_EXTENSION = 33;

	private const byte BLOCK_IMAGE = 44;

	private const byte BLOCK_TRAILER = 59;

	private const byte EXTENSION_GRAPHIC_CONTROL = 249;

	private const int COLOR_TABLE_FLAG = 128;

	private const int INTERLACE_FLAG = 64;

	private const int DISPOSE_BACKGROUND = 2;

	private const int DISPOSE_PREVIOUS = 3;

	private const int MAX_CODES = 4096;

	internal static bool TryDecode(byte[] data, out Color32[][] frames, out float[] delays, out int width, out int height)
	{
		frames = null;
		delays = null;
		width = 0;
		height = 0;
		if (data.Length < 14 || data[0] != 71 || data[1] != 73 || data[2] != 70)
		{
			return false;
		}
		int num = 6;
		width = data[num] | (data[num + 1] << 8);
		height = data[num + 2] | (data[num + 3] << 8);
		int num2 = data[num + 4];
		num += 7;
		if (width <= 0 || height <= 0)
		{
			return false;
		}
		Color32[] table = null;
		if ((num2 & 0x80) != 0 && !TryReadTable(data, ref num, 2 << (num2 & 7), out table))
		{
			return false;
		}
		List<Color32[]> list = new List<Color32[]>();
		List<float> list2 = new List<float>();
		Color32[] array = (Color32[])(object)new Color32[width * height];
		Color32[] array2 = null;
		GraphicControl control = GraphicControl.Default();
		while (num < data.Length)
		{
			switch (data[num++])
			{
			case 33:
				if (ReadExtension(data, ref num, ref control))
				{
					continue;
				}
				break;
			case 44:
			{
				ImageBlock image;
				switch (ReadImageBlock(data, ref num, table, out image))
				{
				default:
					if (control.Disposal == 3)
					{
						array2 = (Color32[])array.Clone();
					}
					Composite(array, width, height, image, control.TransparentIndex);
					list.Add((Color32[])array.Clone());
					list2.Add(control.Delay());
					if (control.Disposal == 2)
					{
						PixelCanvas.ClearRect(array, width, height, image.Left, height - image.Top - image.Height, image.Width, image.Height);
					}
					else if (control.Disposal == 3 && array2 != null)
					{
						array = array2;
					}
					continue;
				case BlockResult.Skip:
					continue;
				case BlockResult.Fatal:
					break;
				}
				break;
			}
			}
			break;
		}
		if (list.Count == 0)
		{
			return false;
		}
		frames = list.ToArray();
		delays = list2.ToArray();
		return true;
	}

	private static bool ReadExtension(byte[] data, ref int pos, ref GraphicControl control)
	{
		if (pos >= data.Length)
		{
			return false;
		}
		if (data[pos++] == 249 && pos + 5 <= data.Length)
		{
			int num = data[pos++];
			int num2 = data[pos];
			control.DelayHundredths = data[pos + 1] | (data[pos + 2] << 8);
			control.TransparentIndex = (((num2 & 1) != 0) ? data[pos + 3] : (-1));
			control.Disposal = (num2 >> 2) & 7;
			pos += num;
		}
		SkipBlocks(data, ref pos);
		return true;
	}

	private static BlockResult ReadImageBlock(byte[] data, ref int pos, Color32[] globalTable, out ImageBlock image)
	{
		image = default(ImageBlock);
		if (pos + 9 > data.Length)
		{
			return BlockResult.Fatal;
		}
		image.Left = data[pos] | (data[pos + 1] << 8);
		image.Top = data[pos + 2] | (data[pos + 3] << 8);
		image.Width = data[pos + 4] | (data[pos + 5] << 8);
		image.Height = data[pos + 6] | (data[pos + 7] << 8);
		int num = data[pos + 8];
		pos += 9;
		Color32[] table = null;
		if ((num & 0x80) != 0 && !TryReadTable(data, ref pos, 2 << (num & 7), out table))
		{
			return BlockResult.Fatal;
		}
		image.Table = table ?? globalTable;
		image.Interlaced = (num & 0x40) != 0;
		if (pos >= data.Length)
		{
			return BlockResult.Fatal;
		}
		int num2 = data[pos++];
		byte[] input = ReadBlocks(data, ref pos);
		if (image.Table == null || image.Width <= 0 || image.Height <= 0 || num2 < 1 || num2 > 11)
		{
			return BlockResult.Skip;
		}
		image.Indices = Decompress(input, num2, image.Width * image.Height);
		return BlockResult.Ok;
	}

	private static void Composite(Color32[] canvas, int width, int height, ImageBlock image, int transparentIndex)
	{
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < image.Height; i++)
		{
			int num = (image.Interlaced ? InterlacedRow(i, image.Height) : i);
			int num2 = height - 1 - (image.Top + num);
			if (num2 < 0 || num2 >= height)
			{
				continue;
			}
			for (int j = 0; j < image.Width; j++)
			{
				int num3 = image.Left + j;
				if (num3 >= 0 && num3 < width)
				{
					int num4 = image.Indices[i * image.Width + j];
					if (num4 != transparentIndex && num4 < image.Table.Length)
					{
						canvas[num2 * width + num3] = image.Table[num4];
					}
				}
			}
		}
	}

	private static int InterlacedRow(int decodedRow, int height)
	{
		int num = (height + 7) / 8;
		int num2 = (height + 3) / 8;
		int num3 = (height + 1) / 4;
		if (decodedRow < num)
		{
			return decodedRow * 8;
		}
		decodedRow -= num;
		if (decodedRow < num2)
		{
			return decodedRow * 8 + 4;
		}
		decodedRow -= num2;
		if (decodedRow < num3)
		{
			return decodedRow * 4 + 2;
		}
		return (decodedRow - num3) * 2 + 1;
	}

	private static bool TryReadTable(byte[] data, ref int pos, int count, out Color32[] table)
	{
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		table = null;
		if (pos + count * 3 > data.Length)
		{
			return false;
		}
		table = (Color32[])(object)new Color32[count];
		for (int i = 0; i < count; i++)
		{
			table[i] = new Color32(data[pos], data[pos + 1], data[pos + 2], byte.MaxValue);
			pos += 3;
		}
		return true;
	}

	private static void SkipBlocks(byte[] data, ref int pos)
	{
		while (pos < data.Length)
		{
			int num = data[pos++];
			if (num == 0)
			{
				break;
			}
			pos += num;
		}
	}

	private static byte[] ReadBlocks(byte[] data, ref int pos)
	{
		List<byte> list = new List<byte>();
		while (pos < data.Length)
		{
			int num = data[pos++];
			if (num == 0)
			{
				break;
			}
			for (int i = 0; i < num; i++)
			{
				if (pos >= data.Length)
				{
					break;
				}
				list.Add(data[pos++]);
			}
		}
		return list.ToArray();
	}

	private static byte[] Decompress(byte[] input, int minCodeSize, int pixelCount)
	{
		short[] array = new short[4096];
		byte[] array2 = new byte[4096];
		byte[] array3 = new byte[4097];
		byte[] array4 = new byte[pixelCount];
		int num = 1 << minCodeSize;
		int num2 = num + 1;
		int num3 = minCodeSize + 1;
		int num4 = num2 + 1;
		int num5 = -1;
		int num6 = 0;
		int num7 = 0;
		int num8 = 0;
		int num9 = input.Length * 8;
		for (int i = 0; i < num; i++)
		{
			array2[i] = (byte)i;
		}
		while (num8 < pixelCount && num7 + num3 <= num9)
		{
			int num10 = ReadBits(input, num7, num3);
			num7 += num3;
			if (num10 == num)
			{
				num3 = minCodeSize + 1;
				num4 = num2 + 1;
				num5 = -1;
				continue;
			}
			if (num10 == num2)
			{
				break;
			}
			int num11 = num10;
			int num12 = 0;
			if (num10 >= num4)
			{
				if (num5 < 0)
				{
					break;
				}
				array3[num12++] = (byte)num6;
				num10 = num5;
			}
			while (num10 >= num && num12 < 4096)
			{
				array3[num12++] = array2[num10];
				num10 = array[num10];
			}
			num6 = array2[num10];
			array3[num12++] = (byte)num6;
			while (num12 > 0 && num8 < pixelCount)
			{
				array4[num8++] = array3[--num12];
			}
			if (num5 >= 0 && num4 < 4096)
			{
				array[num4] = (short)num5;
				array2[num4] = (byte)num6;
				num4++;
				if (num4 == 1 << num3 && num3 < 12)
				{
					num3++;
				}
			}
			num5 = num11;
		}
		return array4;
	}

	private static int ReadBits(byte[] data, int bitPos, int count)
	{
		int num = 0;
		for (int i = 0; i < count; i++)
		{
			int num2 = bitPos + i;
			num |= ((data[num2 >> 3] >> (num2 & 7)) & 1) << i;
		}
		return num;
	}
}
internal static class PixelCanvas
{
	internal static void ClearRect(Color32[] canvas, int width, int height, int left, int bottom, int w, int h)
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < h; i++)
		{
			int num = bottom + i;
			if (num < 0 || num >= height)
			{
				continue;
			}
			for (int j = 0; j < w; j++)
			{
				int num2 = left + j;
				if (num2 >= 0 && num2 < width)
				{
					canvas[num * width + num2] = new Color32((byte)0, (byte)0, (byte)0, (byte)0);
				}
			}
		}
	}
}
internal sealed class BannerImage
{
	internal readonly Texture2D[] Frames;

	internal readonly float[] Delays;

	internal BannerImage(Texture2D[] frames, float[] delays)
	{
		Frames = frames;
		Delays = delays;
	}
}
[BepInPlugin("com.hibiscus.memebanner", "MemeBanner", "1.0.1")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class MemeBannerPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "com.hibiscus.memebanner";

	public const string PluginName = "MemeBanner";

	public const string PluginVersion = "1.0.1";

	internal const string FLAG_MATERIAL = "matWarbannerFlag";

	private const string RISK_OF_OPTIONS_GUID = "com.rune580.riskofoptions";

	private const int ANIMATED_SIZE = 256;

	private const int MAX_FRAMES = 200;

	private static readonly string[] _extensions = new string[4] { ".png", ".jpg", ".jpeg", ".gif" };

	private static readonly Dictionary<string, BannerImage> _cache = new Dictionary<string, BannerImage>();

	private static Rect _clothWindow;

	private static Color32[] _clothPixels;

	private static int _clothWidth;

	private static int _clothHeight;

	private static string[] _images = Array.Empty<string>();

	private static int _next;

	private static string _lastShown;

	private static ConfigEntry<bool> _useStockAlpha;

	private static ConfigEntry<bool> _randomBanner;

	internal static ManualLogSource Log;

	private static string _pluginDir;

	public void Awake()
	{
		Log = ((BaseUnityPlugin)this).Logger;
		_useStockAlpha = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UseStockAlphaMask", false, "Off: your image covers the whole cloth as a rectangle.\nOn: an image that has no transparency of its own borrows the stock banner's alpha, so it keeps the torn-cloth silhouette. An image that already has transparency always keeps its own, either way.");
		_randomBanner = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RandomBanner", false, "Off: a queue of all your images is generated, so nothing repeats until every image has been shown once.\nOn: every banner picks an image at random, which can repeat.");
		_pluginDir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
		string text = Path.Combine(_pluginDir, "Banners");
		Directory.CreateDirectory(text);
		_images = (from f in Directory.GetFiles(text, "*", SearchOption.AllDirectories)
			where _extensions.Contains(Path.GetExtension(f).ToLowerInvariant())
			select f).ToArray();
		_next = _images.Length;
		((BaseUnityPlugin)this).Logger.LogInfo((object)$"{_images.Length} banner image(s) found in {text}");
		if (Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions"))
		{
			SetupRiskOfOptions();
		}
		RoR2Application.onLoadFinished = (Action)Delegate.Combine(RoR2Application.onLoadFinished, new Action(PatchWardPrefab));
	}

	private void SetupRiskOfOptions()
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Expected O, but got Unknown
		ModSettingsManager.SetModDescription("Every Warbanner you drop gets a random image from the mod's Banners folder.");
		ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(_useStockAlpha, true));
		ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(_randomBanner));
		((BaseUnityPlugin)this).Logger.LogInfo((object)"registered in-game settings via Risk of Options");
	}

	private static void PatchWardPrefab()
	{
		GameObject val = LegacyResourcesAPI.Load<GameObject>("Prefabs/NetworkedObjects/WarbannerWard");
		if (!Object.op_Implicit((Object)(object)val))
		{
			Log.LogError((object)"WarbannerWard prefab not found - banners will look normal.");
			return;
		}
		MeasureCloth(val);
		if (!Object.op_Implicit((Object)(object)val.GetComponent<RandomBannerTexture>()))
		{
			val.AddComponent<RandomBannerTexture>();
		}
	}

	private static void MeasureCloth(GameObject prefab)
	{
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val in componentsInChildren)
		{
			Material sharedMaterial = val.sharedMaterial;
			if (!Object.op_Implicit((Object)(object)sharedMaterial) || !((Object)sharedMaterial).name.StartsWith("matWarbannerFlag", StringComparison.Ordinal))
			{
				continue;
			}
			MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
			Mesh val2 = (Object.op_Implicit((Object)(object)component) ? component.sharedMesh : null);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				SkinnedMeshRenderer val3 = (SkinnedMeshRenderer)(object)((val is SkinnedMeshRenderer) ? val : null);
				val2 = (Object.op_Implicit((Object)(object)val3) ? val3.sharedMesh : null);
			}
			Texture texture = sharedMaterial.GetTexture("_MainTex");
			if (!Object.op_Implicit((Object)(object)val2) || !Object.op_Implicit((Object)(object)texture))
			{
				Log.LogWarning((object)"banner cloth has no mesh or texture - images will be used unscaled");
				break;
			}
			float num = float.MaxValue;
			float num2 = float.MinValue;
			float num3 = float.MaxValue;
			float num4 = float.MinValue;
			Vector2[] uv = val2.uv;
			foreach (Vector2 val4 in uv)
			{
				num = Mathf.Min(num, val4.x);
				num2 = Mathf.Max(num2, val4.x);
				num3 = Mathf.Min(num3, val4.y);
				num4 = Mathf.Max(num4, val4.y);
			}
			if (num2 <= num || num4 <= num3)
			{
				Log.LogWarning((object)"banner cloth has a degenerate UV window - images will be used unscaled");
				break;
			}
			_clothWindow = Rect.MinMaxRect(num, num3, num2, num4);
			_clothWidth = texture.width;
			_clothHeight = texture.height;
			_clothPixels = ReadBack(texture);
			Log.LogInfo((object)$"banner cloth: {_clothWidth}x{_clothHeight}, uv window {_clothWindow}");
			break;
		}
	}

	private static Color32[] ReadBack(Texture source)
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: 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_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Expected O, but got Unknown
		RenderTexture temporary = RenderTexture.GetTemporary(source.width, source.height, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2);
		Graphics.Blit(source, temporary);
		RenderTexture active = RenderTexture.active;
		RenderTexture.active = temporary;
		Texture2D val = new Texture2D(source.width, source.height, (TextureFormat)4, false);
		val.ReadPixels(new Rect(0f, 0f, (float)source.width, (float)source.height), 0, 0);
		val.Apply();
		RenderTexture.active = active;
		RenderTexture.ReleaseTemporary(temporary);
		Color32[] pixels = val.GetPixels32();
		Object.Destroy((Object)val);
		return pixels;
	}

	internal static BannerImage RandomImage()
	{
		if (_images.Length == 0)
		{
			return null;
		}
		string text = NextPath();
		if (_cache.TryGetValue(text, out var value))
		{
			return value;
		}
		BannerImage bannerImage = null;
		try
		{
			bannerImage = Load(text);
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("could not read " + Path.GetFileName(text) + ": " + ex.Message));
		}
		_cache[text] = bannerImage;
		return bannerImage;
	}

	private static string NextPath()
	{
		if (_randomBanner.Value)
		{
			return _images[Random.Range(0, _images.Length)];
		}
		if (_next >= _images.Length)
		{
			for (int num = _images.Length - 1; num > 0; num--)
			{
				int num2 = Random.Range(0, num + 1);
				string text = _images[num];
				_images[num] = _images[num2];
				_images[num2] = text;
			}
			if (_images.Length > 1 && _images[0] == _lastShown)
			{
				int num3 = Random.Range(1, _images.Length);
				string text2 = _images[0];
				_images[0] = _images[num3];
				_images[num3] = text2;
			}
			_next = 0;
		}
		_lastShown = _images[_next++];
		return _lastShown;
	}

	private static BannerImage Load(string path)
	{
		//IL_006f: 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_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Expected O, but got Unknown
		byte[] array = File.ReadAllBytes(path);
		string text = Path.GetExtension(path).ToLowerInvariant();
		Color32[][] frames = null;
		float[] delays = null;
		int width = 0;
		int height = 0;
		if (text == ".gif")
		{
			GifDecoder.TryDecode(array, out frames, out delays, out width, out height);
		}
		else if (text == ".png")
		{
			ApngDecoder.TryDecode(array, out frames, out delays, out width, out height);
		}
		if (frames != null && frames.Length != 0)
		{
			return BuildAnimation(path, frames, delays, width, height);
		}
		Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
		{
			name = Path.GetFileName(path),
			wrapMode = (TextureWrapMode)1
		};
		if (!ImageConversion.LoadImage(val, array))
		{
			Log.LogWarning((object)("could not decode " + Path.GetFileName(path)));
			Object.Destroy((Object)(object)val);
			return null;
		}
		Color32[] pixels = val.GetPixels32();
		int width2 = ((Texture)val).width;
		int height2 = ((Texture)val).height;
		string name = ((Object)val).name;
		Object.Destroy((Object)(object)val);
		bool useMask = _useStockAlpha.Value && IsFullyOpaque(pixels);
		Texture2D val2 = FitToCloth(pixels, width2, height2, name, useMask, _clothWidth);
		return new BannerImage((Texture2D[])(object)new Texture2D[1] { val2 }, new float[1]);
	}

	private static BannerImage BuildAnimation(string path, Color32[][] rawFrames, float[] rawDelays, int w, int h)
	{
		int num = Mathf.Min(rawFrames.Length, 200);
		if (num < rawFrames.Length)
		{
			Log.LogWarning((object)$"{Path.GetFileName(path)} has {rawFrames.Length} frames, using the first {num}");
		}
		bool flag = _useStockAlpha.Value;
		int num2 = 0;
		while (flag && num2 < num)
		{
			flag = IsFullyOpaque(rawFrames[num2]);
			num2++;
		}
		int size = ((num > 1) ? Mathf.Min(256, _clothWidth) : _clothWidth);
		string fileName = Path.GetFileName(path);
		Texture2D[] array = (Texture2D[])(object)new Texture2D[num];
		float[] array2 = new float[num];
		for (int i = 0; i < num; i++)
		{
			array[i] = FitToCloth(rawFrames[i], w, h, fileName, flag, size);
			array2[i] = Mathf.Max(rawDelays[i], 0.01f);
		}
		Log.LogInfo((object)$"{Path.GetFileName(path)}: {num} frame(s), {w}x{h}");
		return new BannerImage(array, array2);
	}

	private static bool IsFullyOpaque(Color32[] pixels)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < pixels.Length; i++)
		{
			if (pixels[i].a < byte.MaxValue)
			{
				return false;
			}
		}
		return true;
	}

	private static Texture2D FitToCloth(Color32[] source, int sourceWidth, int sourceHeight, string name, bool useMask, int size)
	{
		//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_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		if (_clothPixels == null)
		{
			return MakeTexture(source, sourceWidth, sourceHeight, name, mipChain: false);
		}
		float num = ((Rect)(ref _clothWindow)).xMin * (float)size;
		float num2 = ((Rect)(ref _clothWindow)).xMax * (float)size;
		float num3 = ((Rect)(ref _clothWindow)).yMin * (float)size;
		float num4 = ((Rect)(ref _clothWindow)).yMax * (float)size;
		int num5 = Mathf.Max(0, Mathf.FloorToInt(num));
		int num6 = Mathf.Min(size, Mathf.CeilToInt(num2));
		int num7 = Mathf.Max(0, Mathf.FloorToInt(num3));
		int num8 = Mathf.Min(size, Mathf.CeilToInt(num4));
		Color32[] array = (Color32[])(object)new Color32[size * size];
		for (int i = num7; i < num8; i++)
		{
			float num9 = ((float)i + 0.5f - num3) / (num4 - num3);
			int num10 = i * size;
			int num11 = i * _clothHeight / size * _clothWidth;
			for (int j = num5; j < num6; j++)
			{
				float num12 = 1f - ((float)j + 0.5f - num) / (num2 - num);
				Color32 val = Sample(source, sourceWidth, sourceHeight, Mathf.Clamp01(num12), Mathf.Clamp01(num9));
				if (useMask)
				{
					val.a = (byte)(val.a * _clothPixels[num11 + j * _clothWidth / size].a / 255);
				}
				array[num10 + j] = val;
			}
		}
		return MakeTexture(array, size, size, name, mipChain: true);
	}

	private static Texture2D MakeTexture(Color32[] pixels, int width, int height, string name, bool mipChain)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Expected O, but got Unknown
		Texture2D val = new Texture2D(width, height, (TextureFormat)4, mipChain)
		{
			name = name,
			wrapMode = (TextureWrapMode)1
		};
		val.SetPixels32(pixels);
		val.Apply();
		return val;
	}

	private static Color32 Sample(Color32[] pixels, int width, int height, float u, float v)
	{
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		float num = u * (float)width;
		float num2 = v * (float)height;
		int num3 = Mathf.FloorToInt(num);
		int num4 = Mathf.FloorToInt(num2);
		float tx = num - (float)num3;
		float ty = num2 - (float)num4;
		int num5 = Mathf.Clamp(num3, 0, width - 1);
		int num6 = Mathf.Clamp(num3 + 1, 0, width - 1);
		int num7 = Mathf.Clamp(num4, 0, height - 1) * width;
		int num8 = Mathf.Clamp(num4 + 1, 0, height - 1) * width;
		Color32 val = pixels[num7 + num5];
		Color32 val2 = pixels[num7 + num6];
		Color32 val3 = pixels[num8 + num5];
		Color32 val4 = pixels[num8 + num6];
		return new Color32(Blend(val.r, val2.r, val3.r, val4.r, tx, ty), Blend(val.g, val2.g, val3.g, val4.g, tx, ty), Blend(val.b, val2.b, val3.b, val4.b, tx, ty), Blend(val.a, val2.a, val3.a, val4.a, tx, ty));
	}

	private static byte Blend(byte c00, byte c10, byte c01, byte c11, float tx, float ty)
	{
		float num = (float)(int)c00 + (float)(c10 - c00) * tx;
		float num2 = (float)(int)c01 + (float)(c11 - c01) * tx;
		return (byte)(num + (num2 - num) * ty + 0.5f);
	}
}
public class RandomBannerTexture : MonoBehaviour
{
	private static bool _warned;

	private readonly List<Material> _cloth = new List<Material>();

	private BannerImage _image;

	private int _frame;

	private float _timer;

	private void OnEnable()
	{
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		_cloth.Clear();
		_frame = 0;
		_timer = 0f;
		_image = MemeBannerPlugin.RandomImage();
		if (_image == null || _image.Frames.Length == 0)
		{
			return;
		}
		Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			Material[] materials = componentsInChildren[i].materials;
			foreach (Material val in materials)
			{
				if (((Object)val).name.StartsWith("matWarbannerFlag", StringComparison.Ordinal))
				{
					val.SetColor("_Color", Color.white);
					_cloth.Add(val);
				}
			}
		}
		if (_cloth.Count == 0)
		{
			if (!_warned)
			{
				_warned = true;
				MemeBannerPlugin.Log.LogWarning((object)"no 'matWarbannerFlag' material on the banner - the game model changed, banners stay vanilla");
			}
		}
		else
		{
			Apply();
		}
	}

	private void Update()
	{
		if (_image == null || _image.Frames.Length < 2 || _cloth.Count == 0)
		{
			return;
		}
		_timer += Time.deltaTime;
		bool flag = false;
		for (int i = 0; i < _image.Frames.Length; i++)
		{
			if (!(_timer >= _image.Delays[_frame]))
			{
				break;
			}
			_timer -= _image.Delays[_frame];
			_frame = (_frame + 1) % _image.Frames.Length;
			flag = true;
		}
		if (flag)
		{
			Apply();
		}
	}

	private void Apply()
	{
		Texture2D val = _image.Frames[_frame];
		foreach (Material item in _cloth)
		{
			item.SetTexture("_MainTex", (Texture)(object)val);
		}
	}
}