Decompiled source of IntroReplacer v1.0.2

IntroReplacer.dll

Decompiled a day ago
using System;
using System.Collections;
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 HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne.UI.MainMenu;
using Il2CppTMPro;
using IntroReplacer;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("IntroReplacer")]
[assembly: AssemblyDescription("by Virtunerd")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: MelonInfo(typeof(IntroReplacerMod), "IntroReplacer", "1.0.2", "Virtunerd", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: AssemblyFileVersion("1.0.2")]
[assembly: AssemblyInformationalVersion("1.0.2")]
[assembly: AssemblyVersion("1.0.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 IntroReplacer
{
	[HarmonyPatch(typeof(Disclaimer))]
	public static class DisclaimerPatch
	{
		private static CanvasGroup group;

		private static CanvasGroup textGroup;

		private static bool introStarted;

		[HarmonyPostfix]
		[HarmonyPatch("Awake")]
		public static void AwakePostfix(Disclaimer __instance)
		{
			try
			{
				if (introStarted)
				{
					return;
				}
				introStarted = true;
				group = __instance.Group;
				textGroup = __instance.TextGroup;
				if ((Object)(object)group == (Object)null || (Object)(object)textGroup == (Object)null)
				{
					MelonLogger.Warning("[PATCH] Disclaimer.Awake: Group or TextGroup is null");
					return;
				}
				string text = IntroReplacerMod.Mode.Value.ToLowerInvariant();
				if (!HasContent(text))
				{
					MelonLogger.Warning("[PATCH] Mode '" + text + "' has no content - leaving the game's own disclaimer untouched.");
					return;
				}
				((MonoBehaviour)__instance).StopAllCoroutines();
				IntroReplacerMod.ArmOverlayWatchdog(group);
				for (int i = 0; i < ((Component)textGroup).transform.childCount; i++)
				{
					((Component)((Component)textGroup).transform.GetChild(i)).gameObject.SetActive(false);
				}
				GameObject gameObject = ((Component)FadeController.CreateContentGroup(((Component)group).gameObject)).gameObject;
				string text2 = IntroReplacerMod.Mode.Value.ToLowerInvariant();
				switch (text2)
				{
				case "images":
				case "graffiti":
					GraffitiHandler.Setup(gameObject);
					break;
				case "text":
					TextHandler.Setup(gameObject);
					break;
				case "video":
					VideoHandler.Setup(gameObject);
					break;
				default:
					MelonLogger.Warning("[PATCH] Unknown mode: " + text2 + ". Using images.");
					GraffitiHandler.Setup(gameObject);
					break;
				}
				float duration = __instance.Duration;
				float bonusTime = ((duration > 1f) ? (duration - 1f) : 0f);
				MelonCoroutines.Start(DeferredStart(group, textGroup, text2, bonusTime));
				group = null;
				textGroup = null;
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[PATCH] Disclaimer.Awake failed: " + ex.ToString());
			}
		}

		private static bool HasContent(string mode)
		{
			try
			{
				switch (mode)
				{
				case "images":
				case "graffiti":
					return GraffitiHandler.HasPreloadedContent;
				case "video":
					return Directory.Exists(IntroReplacerMod.VideoPath) && Directory.GetFiles(IntroReplacerMod.VideoPath).Length != 0;
				case "text":
					return !string.IsNullOrWhiteSpace(IntroReplacerMod.CustomText.Value);
				default:
					return GraffitiHandler.HasPreloadedContent;
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[PATCH] HasContent check failed, not taking over: " + ex.Message);
				return false;
			}
		}

		private static IEnumerator DeferredStart(CanvasGroup group, CanvasGroup textGroup, string mode, float bonusTime)
		{
			yield return null;
			MelonCoroutines.Start(FadeController.RunCustomIntro(group, textGroup, mode, bonusTime));
		}
	}
	public static class FadeController
	{
		internal static CanvasGroup contentGroup;

		private static bool IsAlive(CanvasGroup cg)
		{
			try
			{
				return (Object)(object)cg != (Object)null && (Object)(object)((Component)cg).gameObject != (Object)null;
			}
			catch
			{
				return false;
			}
		}

		public static CanvasGroup CreateContentGroup(GameObject groupGO)
		{
			//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_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("IntroReplacerContent");
			val.transform.SetParent(groupGO.transform, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.offsetMin = Vector2.zero;
			obj.offsetMax = Vector2.zero;
			contentGroup = val.AddComponent<CanvasGroup>();
			contentGroup.alpha = 0f;
			return contentGroup;
		}

		public static IEnumerator RunCustomIntro(CanvasGroup group, CanvasGroup textGroup, string mode, float bonusTime = 0f)
		{
			float fadeIn = 1f;
			float display = 3f + bonusTime;
			float fadeOut = 1f;
			if (!IsAlive(group) || !IsAlive(textGroup))
			{
				MelonLogger.Warning("[FADE] group or textGroup destroyed. Skipping.");
				yield break;
			}
			group.alpha = 1f;
			textGroup.alpha = 0f;
			if (!IsAlive(contentGroup))
			{
				MelonLogger.Warning("[FADE] contentGroup is null. Skipping.");
				yield break;
			}
			switch (mode)
			{
			case "images":
			case "graffiti":
				yield return GraffitiHandler.PlaySequence(contentGroup, fadeIn, display, fadeOut);
				break;
			case "text":
				yield return RunSingleFade(contentGroup, fadeIn, display, fadeOut);
				break;
			case "video":
				yield return VideoHandler.PlayVideo(contentGroup, fadeIn, display, fadeOut);
				break;
			default:
				yield return RunSingleFade(contentGroup, fadeIn, display, fadeOut);
				break;
			}
			yield return FadeAlpha(group, 1f, 0f, 1f);
			try
			{
				group.blocksRaycasts = false;
				group.interactable = false;
			}
			catch
			{
			}
			finally
			{
				IntroReplacerMod.DisarmOverlayWatchdog();
			}
			if (IsAlive(contentGroup))
			{
				Object.Destroy((Object)(object)((Component)contentGroup).gameObject);
				contentGroup = null;
			}
			else
			{
				contentGroup = null;
			}
			switch (mode)
			{
			case "images":
			case "graffiti":
				GraffitiHandler.Cleanup();
				break;
			case "video":
				VideoHandler.Cleanup();
				break;
			}
			try
			{
				if (IsAlive(group))
				{
					((Component)group).gameObject.SetActive(false);
				}
			}
			catch
			{
			}
			Disclaimer.Shown = true;
		}

		public static IEnumerator RunSingleFade(CanvasGroup cg, float fadeIn, float display, float fadeOut)
		{
			yield return FadeAlpha(cg, 0f, 1f, fadeIn);
			yield return (object)new WaitForSeconds(display);
			yield return FadeAlpha(cg, 1f, 0f, fadeOut);
		}

		public static IEnumerator FadeAlpha(CanvasGroup cg, float from, float to, float duration)
		{
			if (!IsAlive(cg))
			{
				yield break;
			}
			float elapsed = 0f;
			cg.alpha = from;
			while (elapsed < duration)
			{
				if (!IsAlive(cg))
				{
					yield break;
				}
				elapsed += Time.deltaTime;
				float num = Mathf.Clamp01(elapsed / duration);
				cg.alpha = Mathf.Lerp(from, to, num);
				yield return null;
			}
			if (IsAlive(cg))
			{
				cg.alpha = to;
			}
		}
	}
	internal static class GifDecoder
	{
		internal struct GifFrame
		{
			public byte[] Rgba;

			public int Width;

			public int Height;

			public float Delay;
		}

		internal static List<GifFrame> Decode(string filePath)
		{
			return Decode(File.ReadAllBytes(filePath));
		}

		internal static List<GifFrame> Decode(byte[] data)
		{
			List<GifFrame> list = new List<GifFrame>();
			int num = 0;
			if (data.Length < 13)
			{
				throw new InvalidDataException("Not a valid GIF file (too short)");
			}
			string text = Encoding.ASCII.GetString(data, 0, 6);
			if (text != "GIF87a" && text != "GIF89a")
			{
				throw new InvalidDataException("Not a valid GIF file: " + text);
			}
			num = 6;
			int num2 = BitConverter.ToUInt16(data, num);
			num += 2;
			int num3 = BitConverter.ToUInt16(data, num);
			num += 2;
			byte b = data[num++];
			num++;
			num++;
			bool num4 = (b & 0x80) != 0;
			int num5 = 1 << (b & 7) + 1;
			byte[] array = null;
			if (num4)
			{
				array = new byte[num5 * 3];
				Buffer.BlockCopy(data, num, array, 0, num5 * 3);
				num += num5 * 3;
			}
			byte[] array2 = new byte[num2 * num3 * 4];
			float delay = 0.1f;
			int num6 = -1;
			int num7 = 0;
			while (num < data.Length)
			{
				switch (data[num++])
				{
				case 33:
					if (num >= data.Length)
					{
						break;
					}
					if (data[num++] == 249)
					{
						num++;
						byte num22 = data[num++];
						num7 = (num22 >> 2) & 7;
						bool num23 = (num22 & 1) != 0;
						int num24 = BitConverter.ToUInt16(data, num);
						num += 2;
						delay = ((num24 <= 0) ? 0.1f : ((float)num24 / 100f));
						num6 = (num23 ? data[num++] : (-1));
						if (!num23)
						{
							num++;
						}
						num++;
					}
					else
					{
						SkipSubBlocks(data, ref num);
					}
					continue;
				case 44:
				{
					if (num + 9 > data.Length)
					{
						break;
					}
					int num8 = BitConverter.ToUInt16(data, num);
					num += 2;
					int num9 = BitConverter.ToUInt16(data, num);
					num += 2;
					int num10 = BitConverter.ToUInt16(data, num);
					num += 2;
					int num11 = BitConverter.ToUInt16(data, num);
					num += 2;
					byte b2 = data[num++];
					bool num12 = (b2 & 0x80) != 0;
					bool flag = (b2 & 0x40) != 0;
					int num13 = (num12 ? (1 << (b2 & 7) + 1) : 0);
					byte[] array3 = array;
					if (num12)
					{
						array3 = new byte[num13 * 3];
						Buffer.BlockCopy(data, num, array3, 0, num13 * 3);
						num += num13 * 3;
					}
					byte[] array4 = null;
					if (num7 == 3)
					{
						array4 = new byte[array2.Length];
						Buffer.BlockCopy(array2, 0, array4, 0, array2.Length);
					}
					int minCodeSize = data[num++];
					byte[] array5 = DecodeLzw(data, ref num, num10 * num11, minCodeSize);
					if (flag)
					{
						array5 = Deinterlace(array5, num10, num11);
					}
					for (int i = 0; i < num11; i++)
					{
						for (int j = 0; j < num10; j++)
						{
							int num14 = i * num10 + j;
							if (num14 >= array5.Length)
							{
								continue;
							}
							int num15 = array5[num14] & 0xFF;
							if (num15 == num6)
							{
								continue;
							}
							int num16 = num8 + j;
							int num17 = num9 + i;
							if (num16 < num2 && num17 < num3)
							{
								int num18 = ((num3 - 1 - num17) * num2 + num16) * 4;
								if (array3 != null && num15 * 3 + 2 < array3.Length)
								{
									array2[num18] = array3[num15 * 3];
									array2[num18 + 1] = array3[num15 * 3 + 1];
									array2[num18 + 2] = array3[num15 * 3 + 2];
									array2[num18 + 3] = byte.MaxValue;
								}
							}
						}
					}
					byte[] array6 = new byte[array2.Length];
					Buffer.BlockCopy(array2, 0, array6, 0, array2.Length);
					list.Add(new GifFrame
					{
						Rgba = array6,
						Width = num2,
						Height = num3,
						Delay = delay
					});
					switch (num7)
					{
					case 2:
					{
						for (int k = 0; k < num11; k++)
						{
							for (int l = 0; l < num10; l++)
							{
								int num19 = num8 + l;
								int num20 = num9 + k;
								if (num19 < num2 && num20 < num3)
								{
									int num21 = ((num3 - 1 - num20) * num2 + num19) * 4;
									array2[num21] = 0;
									array2[num21 + 1] = 0;
									array2[num21 + 2] = 0;
									array2[num21 + 3] = 0;
								}
							}
						}
						break;
					}
					case 3:
						if (array4 != null)
						{
							Buffer.BlockCopy(array4, 0, array2, 0, array2.Length);
						}
						break;
					}
					delay = 0.1f;
					num6 = -1;
					num7 = 0;
					continue;
				}
				default:
					SkipSubBlocks(data, ref num);
					continue;
				case 59:
					break;
				}
				break;
			}
			return list;
		}

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

		private static byte[] Deinterlace(byte[] src, int width, int height)
		{
			byte[] array = new byte[src.Length];
			int num = 0;
			for (int i = 0; i < height; i += 8)
			{
				CopyRow(src, array, width, num++, i);
			}
			for (int j = 4; j < height; j += 8)
			{
				CopyRow(src, array, width, num++, j);
			}
			for (int k = 2; k < height; k += 4)
			{
				CopyRow(src, array, width, num++, k);
			}
			for (int l = 1; l < height; l += 2)
			{
				CopyRow(src, array, width, num++, l);
			}
			return array;
		}

		private static void CopyRow(byte[] src, byte[] dst, int width, int srcRow, int dstRow)
		{
			int num = srcRow * width;
			int num2 = dstRow * width;
			if (num + width <= src.Length && num2 + width <= dst.Length)
			{
				Buffer.BlockCopy(src, num, dst, num2, width);
			}
		}

		private static byte[] DecodeLzw(byte[] data, ref int pos, int pixelCount, int minCodeSize)
		{
			List<byte> list = new List<byte>(pixelCount);
			while (pos < data.Length)
			{
				int num = data[pos++];
				if (num == 0 || pos + num > data.Length)
				{
					break;
				}
				for (int i = 0; i < num; i++)
				{
					list.Add(data[pos++]);
				}
			}
			int num2 = 1 << minCodeSize;
			int num3 = num2 + 1;
			int num4 = minCodeSize + 1;
			int num5 = num3 + 1;
			int num6 = (1 << num4) - 1;
			int[] array = new int[4096];
			byte[] array2 = new byte[4096];
			int[] array3 = new int[4096];
			for (int j = 0; j < num2; j++)
			{
				array[j] = -1;
				array2[j] = (byte)j;
				array3[j] = 1;
			}
			byte[] array4 = new byte[pixelCount];
			int num7 = 0;
			int num8 = 0;
			int k = 0;
			int num9 = 0;
			byte[] array5 = list.ToArray();
			int num10 = -1;
			byte[] array6 = new byte[4096];
			while (num7 < pixelCount)
			{
				for (; k < num4; k += 8)
				{
					if (num9 >= array5.Length)
					{
						goto end_IL_0234;
					}
					num8 |= array5[num9++] << k;
				}
				int num11 = num8 & num6;
				num8 >>= num4;
				k -= num4;
				if (num11 == num3)
				{
					break;
				}
				if (num11 == num2)
				{
					num4 = minCodeSize + 1;
					num6 = (1 << num4) - 1;
					num5 = num3 + 1;
					num10 = -1;
					continue;
				}
				int num12 = num11;
				if (num11 >= num5)
				{
					if (num10 < 0)
					{
						continue;
					}
					num12 = num10;
				}
				int num13 = 0;
				int num14 = num12;
				while (num14 >= 0 && num13 < 4096)
				{
					array6[num13++] = array2[num14];
					num14 = array[num14];
				}
				int num15 = num13 - 1;
				while (num15 >= 0 && num7 < pixelCount)
				{
					array4[num7++] = array6[num15];
					num15--;
				}
				if (num11 >= num5 && num7 < pixelCount)
				{
					array4[num7++] = array6[num13 - 1];
				}
				if (num10 >= 0 && num5 < 4096)
				{
					array[num5] = num10;
					array2[num5] = array6[num13 - 1];
					array3[num5] = ((num10 < 4096) ? array3[num10] : 0) + 1;
					num5++;
					if (num5 > num6 && num4 < 12)
					{
						num4++;
						num6 = (1 << num4) - 1;
					}
				}
				num10 = num11;
				continue;
				end_IL_0234:
				break;
			}
			return array4;
		}
	}
	public static class GraffitiHandler
	{
		private static RawImage rawImage;

		private static Texture2D[] frames;

		private static float[] frameDelays;

		private static bool isAnimated;

		private static byte[] cachedStaticBytes;

		private static string cachedStaticName;

		private static List<GifDecoder.GifFrame> cachedGifFrames;

		private static string cachedGifName;

		private static bool preloaded;

		internal static bool HasPreloadedContent => preloaded;

		public static void Preload()
		{
			try
			{
				string imagesPath = IntroReplacerMod.ImagesPath;
				if (!Directory.Exists(imagesPath))
				{
					MelonLogger.Warning("[IMAGES] Preload: Folder not found: " + imagesPath);
					return;
				}
				string[] array = (from f in Directory.GetFiles(imagesPath)
					where f.EndsWith(".png", StringComparison.OrdinalIgnoreCase) || f.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) || f.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) || f.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)
					orderby f
					select f).ToArray();
				if (array.Length == 0)
				{
					MelonLogger.Warning("[IMAGES] Preload: No images found in: " + imagesPath);
					return;
				}
				int num = IntroReplacerMod.LastSlideIndex % array.Length;
				string text = array[num];
				IntroReplacerMod.LastSlideIndex = (num + 1) % array.Length;
				IntroReplacerMod.SaveState();
				if (text.EndsWith(".gif", StringComparison.OrdinalIgnoreCase))
				{
					cachedGifFrames = GifDecoder.Decode(text);
					cachedGifName = Path.GetFileNameWithoutExtension(text);
					if (cachedGifFrames.Count == 0)
					{
						MelonLogger.Warning("[IMAGES] Preload: GIF has no frames: " + text);
						return;
					}
				}
				else
				{
					cachedStaticBytes = File.ReadAllBytes(text);
					cachedStaticName = Path.GetFileNameWithoutExtension(text);
				}
				preloaded = true;
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[IMAGES] Preload failed: " + ex.ToString());
			}
		}

		public static void Setup(GameObject parent)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: 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_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: 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_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!preloaded)
				{
					MelonLogger.Warning("[IMAGES] No preloaded data available. Skipping.");
					return;
				}
				if (cachedGifFrames != null && cachedGifFrames.Count > 0)
				{
					CreateGifTextures();
				}
				else if (cachedStaticBytes != null)
				{
					CreateStaticTexture();
				}
				cachedStaticBytes = null;
				cachedGifFrames = null;
				if (frames != null && frames.Length != 0)
				{
					GameObject val = new GameObject("IntroImage");
					val.transform.SetParent(parent.transform, false);
					rawImage = val.AddComponent<RawImage>();
					rawImage.texture = (Texture)(object)frames[0];
					((Graphic)rawImage).color = new Color(1f, 1f, 1f, 1f);
					RectTransform component = val.GetComponent<RectTransform>();
					component.anchorMin = new Vector2(0.5f, 0.5f);
					component.anchorMax = new Vector2(0.5f, 0.5f);
					component.pivot = new Vector2(0.5f, 0.5f);
					RectTransform component2 = parent.GetComponent<RectTransform>();
					Rect rect = component2.rect;
					float width = ((Rect)(ref rect)).width;
					rect = component2.rect;
					FitToParent(parentH: ((Rect)(ref rect)).height, rt: component, imgW: ((Texture)frames[0]).width, imgH: ((Texture)frames[0]).height, parentW: width);
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[IMAGES] Setup failed: " + ex.ToString());
			}
		}

		private static void CreateStaticTexture()
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Expected O, but got Unknown
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			if (!ImageConversion.LoadImage(val, Il2CppStructArray<byte>.op_Implicit(cachedStaticBytes), true))
			{
				Object.Destroy((Object)(object)val);
				MelonLogger.Warning("[IMAGES] Failed to decode static image");
				return;
			}
			((Object)val).name = cachedStaticName ?? "intro_static";
			frames = (Texture2D[])(object)new Texture2D[1] { val };
			frameDelays = new float[1];
			isAnimated = false;
		}

		private static void CreateGifTextures()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			string text = cachedGifName ?? "intro_gif";
			Texture2D[] array = (Texture2D[])(object)new Texture2D[cachedGifFrames.Count];
			float[] array2 = new float[cachedGifFrames.Count];
			for (int i = 0; i < cachedGifFrames.Count; i++)
			{
				GifDecoder.GifFrame gifFrame = cachedGifFrames[i];
				Texture2D val = new Texture2D(gifFrame.Width, gifFrame.Height, (TextureFormat)4, false);
				val.LoadRawTextureData(Il2CppStructArray<byte>.op_Implicit(gifFrame.Rgba));
				val.Apply(false, true);
				((Object)val).name = text + "_f" + i;
				array[i] = val;
				array2[i] = gifFrame.Delay;
			}
			frames = array;
			frameDelays = array2;
			isAnimated = cachedGifFrames.Count > 1;
		}

		public static IEnumerator PlaySequence(CanvasGroup contentGroup, float fadeIn, float display, float fadeOut)
		{
			if (frames == null || frames.Length == 0 || (Object)(object)rawImage == (Object)null)
			{
				MelonLogger.Warning("[IMAGES] Nothing to play. Skipping.");
				yield break;
			}
			yield return FadeController.FadeAlpha(contentGroup, 0f, 1f, fadeIn);
			if (isAnimated)
			{
				float elapsed = 0f;
				int frameIndex = 0;
				while (elapsed < display)
				{
					rawImage.texture = (Texture)(object)frames[frameIndex];
					float frameDelay = frameDelays[frameIndex];
					float waited = 0f;
					while (waited < frameDelay && elapsed < display)
					{
						yield return null;
						float deltaTime = Time.deltaTime;
						waited += deltaTime;
						elapsed += deltaTime;
					}
					frameIndex = (frameIndex + 1) % frames.Length;
				}
			}
			else
			{
				yield return (object)new WaitForSeconds(display);
			}
			yield return FadeController.FadeAlpha(contentGroup, 1f, 0f, fadeOut);
		}

		private static void FitToParent(RectTransform rt, float imgW, float imgH, float parentW, float parentH)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if (imgW <= 0f || imgH <= 0f || parentW <= 0f || parentH <= 0f)
			{
				return;
			}
			float num = imgW;
			float num2 = imgH;
			if (num > parentW || num2 > parentH)
			{
				float num3 = imgW / imgH;
				float num4 = parentW / parentH;
				if (num3 > num4)
				{
					num = parentW;
					num2 = parentW / num3;
				}
				else
				{
					num2 = parentH;
					num = parentH * num3;
				}
			}
			rt.sizeDelta = new Vector2(num, num2);
		}

		public static void Cleanup()
		{
			if (frames != null)
			{
				Texture2D[] array = frames;
				foreach (Texture2D val in array)
				{
					if ((Object)(object)val != (Object)null)
					{
						Object.Destroy((Object)(object)val);
					}
				}
				frames = null;
			}
			frameDelays = null;
			isAnimated = false;
			rawImage = null;
			cachedStaticBytes = null;
			cachedStaticName = null;
			cachedGifFrames = null;
			cachedGifName = null;
			preloaded = false;
		}
	}
	public class IntroReplacerMod : MelonMod
	{
		internal static MelonPreferences_Category Category;

		internal static MelonPreferences_Entry<string> Mode;

		internal static MelonPreferences_Entry<string> CustomText;

		internal static MelonPreferences_Entry<int> VideoVolume;

		internal const float FadeInDuration = 1f;

		internal const float DisplayDuration = 3f;

		internal const float FadeOutDuration = 1f;

		internal static int LastSlideIndex;

		internal static int LastTextIndex;

		internal static int LastVideoIndex;

		internal static string BasePath;

		internal static string ImagesPath;

		internal static string VideoPath;

		internal static string TextPath;

		private static string statePath;

		private static CanvasGroup watchedOverlay;

		private static float watchdogDeadline;

		private static bool watchdogFired;

		private const float WatchdogGraceSeconds = 10f;

		public override void OnInitializeMelon()
		{
			BasePath = Path.Combine(MelonEnvironment.UserDataDirectory, "IntroReplacer");
			ImagesPath = Path.Combine(BasePath, "images");
			VideoPath = Path.Combine(BasePath, "video");
			TextPath = Path.Combine(BasePath, "text");
			statePath = Path.Combine(BasePath, ".state");
			Category = MelonPreferences.CreateCategory("IntroReplacer", "Intro Replacer");
			Mode = Category.CreateEntry<string>("Mode", "images", "Mode", "Active mode: images, text, video", false, false, (ValueValidator)null, (string)null);
			CustomText = Category.CreateEntry<string>("CustomText", "Welcome to Schedule I", "Custom Text", "Text displayed in text mode", false, false, (ValueValidator)null, (string)null);
			VideoVolume = Category.CreateEntry<int>("VideoVolume", 90, "Video Volume", "Volume for video playback (0-100)", false, false, (ValueValidator)null, (string)null);
			LoadState();
			EnsureDirectories();
			string text = Mode.Value.ToLowerInvariant();
			if (text == "images" || text == "graffiti")
			{
				GraffitiHandler.Preload();
			}
		}

		internal static void ArmOverlayWatchdog(CanvasGroup overlay)
		{
			watchedOverlay = overlay;
			watchdogDeadline = Time.realtimeSinceStartup + 1f + 3f + 1f + 10f;
			watchdogFired = false;
		}

		internal static void DisarmOverlayWatchdog()
		{
			watchedOverlay = null;
		}

		public override void OnUpdate()
		{
			if ((Object)(object)watchedOverlay == (Object)null || watchdogFired || Time.realtimeSinceStartup < watchdogDeadline)
			{
				return;
			}
			watchdogFired = true;
			try
			{
				watchedOverlay.blocksRaycasts = false;
				watchedOverlay.interactable = false;
				watchedOverlay.alpha = 0f;
				MelonLogger.Warning("[WATCHDOG] Intro overlay was still blocking input after the intro should have ended - released it. The intro may have been cut short; the main menu is usable.");
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[WATCHDOG] Could not release the intro overlay: " + ex.Message);
			}
			finally
			{
				watchedOverlay = null;
			}
		}

		internal static void SaveState()
		{
			try
			{
				File.WriteAllText(statePath, LastSlideIndex + "\n" + LastTextIndex + "\n" + LastVideoIndex);
			}
			catch
			{
			}
		}

		private static void LoadState()
		{
			try
			{
				if (File.Exists(statePath))
				{
					string[] array = File.ReadAllLines(statePath);
					if (array.Length >= 1)
					{
						int.TryParse(array[0], out LastSlideIndex);
					}
					if (array.Length >= 2)
					{
						int.TryParse(array[1], out LastTextIndex);
					}
					if (array.Length >= 3)
					{
						int.TryParse(array[2], out LastVideoIndex);
					}
				}
			}
			catch
			{
			}
		}

		private void EnsureDirectories()
		{
			try
			{
				Directory.CreateDirectory(ImagesPath);
				Directory.CreateDirectory(VideoPath);
				Directory.CreateDirectory(TextPath);
				string path = Path.Combine(TextPath, "custom_text.txt");
				if (!File.Exists(path))
				{
					File.WriteAllText(path, "Welcome to Schedule I\n\nThis file is displayed when Mode = text.\nYou can create multiple .txt files in this folder.\nEach game launch shows the next file in rotation.\n\nThis template is only created once and never overwritten.");
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[INIT] Could not create directories: " + ex.Message);
			}
		}
	}
	public static class TextHandler
	{
		public static void Setup(GameObject parent)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				string text = SelectText();
				if (string.IsNullOrWhiteSpace(text))
				{
					MelonLogger.Warning("[TEXT] Text is empty. Skipping.");
					return;
				}
				GameObject val = new GameObject("CustomText");
				val.transform.SetParent(parent.transform, false);
				TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
				((TMP_Text)obj).text = text;
				((TMP_Text)obj).fontSize = 36f;
				((TMP_Text)obj).alignment = (TextAlignmentOptions)514;
				((Graphic)obj).color = new Color(1f, 1f, 1f, 1f);
				((TMP_Text)obj).enableWordWrapping = true;
				RectTransform component = val.GetComponent<RectTransform>();
				component.anchorMin = new Vector2(0.1f, 0.2f);
				component.anchorMax = new Vector2(0.9f, 0.8f);
				component.offsetMin = Vector2.zero;
				component.offsetMax = Vector2.zero;
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[TEXT] Setup failed: " + ex.ToString());
			}
		}

		private static string SelectText()
		{
			string textPath = IntroReplacerMod.TextPath;
			if (Directory.Exists(textPath))
			{
				string[] array = (from f in Directory.GetFiles(textPath, "*.txt")
					orderby f
					select f).ToArray();
				if (array.Length != 0)
				{
					int num = IntroReplacerMod.LastTextIndex % array.Length;
					string path = array[num];
					IntroReplacerMod.LastTextIndex = (num + 1) % array.Length;
					IntroReplacerMod.SaveState();
					return File.ReadAllText(path).Trim();
				}
			}
			return IntroReplacerMod.CustomText.Value;
		}
	}
	public static class VideoHandler
	{
		private static VideoPlayer videoPlayer;

		private static RawImage rawImage;

		private static RenderTexture renderTexture;

		private static RectTransform imageRT;

		public static void Setup(GameObject parent)
		{
			//IL_0013: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: 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)
			try
			{
				string text = SelectVideo();
				if (text != null)
				{
					GameObject val = new GameObject("VideoImage");
					val.transform.SetParent(parent.transform, false);
					rawImage = val.AddComponent<RawImage>();
					((Graphic)rawImage).color = new Color(1f, 1f, 1f, 1f);
					imageRT = val.GetComponent<RectTransform>();
					imageRT.anchorMin = new Vector2(0.5f, 0.5f);
					imageRT.anchorMax = new Vector2(0.5f, 0.5f);
					imageRT.pivot = new Vector2(0.5f, 0.5f);
					videoPlayer = parent.AddComponent<VideoPlayer>();
					videoPlayer.playOnAwake = false;
					videoPlayer.renderMode = (VideoRenderMode)2;
					videoPlayer.source = (VideoSource)1;
					videoPlayer.url = "file:///" + text.Replace("\\", "/");
					videoPlayer.isLooping = false;
					videoPlayer.audioOutputMode = (VideoAudioOutputMode)2;
					videoPlayer.SetDirectAudioMute((ushort)0, true);
					videoPlayer.SetDirectAudioVolume((ushort)0, Mathf.Clamp01((float)IntroReplacerMod.VideoVolume.Value / 100f));
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[VIDEO] Setup failed: " + ex.ToString());
			}
		}

		public static IEnumerator PlayVideo(CanvasGroup contentGroup, float fadeIn, float display, float fadeOut)
		{
			if ((Object)(object)videoPlayer == (Object)null || (Object)(object)rawImage == (Object)null)
			{
				MelonLogger.Warning("[VIDEO] VideoPlayer or RawImage is null. Skipping.");
				yield break;
			}
			videoPlayer.Prepare();
			float timeout = 10f;
			float waited = 0f;
			while (!videoPlayer.isPrepared && waited < timeout)
			{
				waited += Time.deltaTime;
				yield return null;
			}
			if (!videoPlayer.isPrepared)
			{
				MelonLogger.Warning("[VIDEO] Prepare timed out after " + timeout + "s");
				yield break;
			}
			RenderTexture placeholderRT = new RenderTexture(64, 64, 0);
			placeholderRT.Create();
			videoPlayer.targetTexture = placeholderRT;
			videoPlayer.Play();
			float dimTimeout = 5f;
			float dimWaited = 0f;
			int vidW = 0;
			int vidH = 0;
			while (dimWaited < dimTimeout)
			{
				vidW = (int)videoPlayer.width;
				vidH = (int)videoPlayer.height;
				if (vidW > 0 && vidH > 0)
				{
					break;
				}
				dimWaited += Time.deltaTime;
				yield return null;
			}
			if (vidW <= 0 || vidH <= 0)
			{
				MelonLogger.Warning("[VIDEO] Could not determine dimensions after " + dimTimeout + "s");
				vidW = 1920;
				vidH = 1080;
			}
			renderTexture = new RenderTexture(vidW, vidH, 0);
			renderTexture.Create();
			videoPlayer.targetTexture = renderTexture;
			rawImage.texture = (Texture)(object)renderTexture;
			placeholderRT.Release();
			Object.Destroy((Object)(object)placeholderRT);
			Canvas componentInParent = ((Component)rawImage).GetComponentInParent<Canvas>();
			float num = (((Object)(object)componentInParent != (Object)null && componentInParent.scaleFactor > 0f) ? componentInParent.scaleFactor : 1f);
			RectTransform component = ((Component)((Component)rawImage).transform.parent).GetComponent<RectTransform>();
			Rect rect = component.rect;
			float width = ((Rect)(ref rect)).width;
			rect = component.rect;
			float height = ((Rect)(ref rect)).height;
			FitToParent(imageRT, (float)vidW / num, (float)vidH / num, width, height);
			videoPlayer.SetDirectAudioMute((ushort)0, false);
			yield return FadeController.FadeAlpha(contentGroup, 0f, 1f, fadeIn);
			float elapsed = 0f;
			while ((Object)(object)videoPlayer != (Object)null && videoPlayer.isPlaying && elapsed < display)
			{
				elapsed += Time.deltaTime;
				yield return null;
			}
			if ((Object)(object)videoPlayer != (Object)null && videoPlayer.isPlaying)
			{
				videoPlayer.Stop();
			}
			yield return FadeController.FadeAlpha(contentGroup, 1f, 0f, fadeOut);
		}

		private static void FitToParent(RectTransform rt, float imgW, float imgH, float parentW, float parentH)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			if (!(imgW <= 0f) && !(imgH <= 0f) && !(parentW <= 0f) && !(parentH <= 0f))
			{
				float num = imgW;
				float num2 = imgH;
				if (num > parentW || num2 > parentH)
				{
					float num3 = Mathf.Min(parentW / num, parentH / num2);
					num *= num3;
					num2 *= num3;
				}
				rt.sizeDelta = new Vector2(num, num2);
			}
		}

		private static string SelectVideo()
		{
			string videoPath = IntroReplacerMod.VideoPath;
			if (!Directory.Exists(videoPath))
			{
				MelonLogger.Warning("[VIDEO] Folder not found: " + videoPath);
				return null;
			}
			string[] array = (from f in Directory.GetFiles(videoPath, "*.mp4")
				orderby f
				select f).ToArray();
			if (array.Length == 0)
			{
				MelonLogger.Warning("[VIDEO] No .mp4 files found in: " + videoPath);
				return null;
			}
			int num = IntroReplacerMod.LastVideoIndex % array.Length;
			string result = array[num];
			IntroReplacerMod.LastVideoIndex = (num + 1) % array.Length;
			IntroReplacerMod.SaveState();
			return result;
		}

		public static void Cleanup()
		{
			if ((Object)(object)videoPlayer != (Object)null)
			{
				videoPlayer.Stop();
				Object.Destroy((Object)(object)videoPlayer);
				videoPlayer = null;
			}
			if ((Object)(object)renderTexture != (Object)null)
			{
				renderTexture.Release();
				Object.Destroy((Object)(object)renderTexture);
				renderTexture = null;
			}
			rawImage = null;
			imageRT = null;
		}
	}
}