Decompiled source of BonekiniBottom v1.0.1

CustomizerT2D.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("CustomizerT2DOnly")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("CustomizerT2DOnly")]
[assembly: AssemblyTitle("CustomizerT2DOnly")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace CustomizerT2DOnly;

[BepInPlugin("com.yourname.customizer.t2donly", "CustomizerT2DOnly", "2.0.0")]
public class CustomizerT2DOnly : BaseUnityPlugin
{
	[HarmonyPatch(typeof(Material), "set_mainTexture")]
	internal static class H_Material_Setters
	{
		private static void Postfix(Material __instance, Texture value)
		{
			Texture2D val = (Texture2D)(object)((value is Texture2D) ? value : null);
			if (val != null)
			{
				Instance?.TrySwap(val);
			}
		}

		[HarmonyPatch(typeof(Material), "SetTexture", new Type[]
		{
			typeof(string),
			typeof(Texture)
		})]
		[HarmonyPostfix]
		private static void SetTexture_Postfix(Material __instance, string name, Texture value)
		{
			Texture2D val = (Texture2D)(object)((value is Texture2D) ? value : null);
			if (val != null)
			{
				Instance?.TrySwap(val);
			}
		}
	}

	[HarmonyPatch(typeof(SpriteRenderer), "set_sprite")]
	internal static class H_SpriteRenderer_SetSprite
	{
		private static void Postfix(Sprite value)
		{
			Texture2D val = (Object.op_Implicit((Object)(object)value) ? value.texture : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				Instance?.TrySwap(val);
			}
		}
	}

	internal static class H_UI_Image_SetSprite
	{
		public static void Postfix(object __instance, Sprite value)
		{
			if (Object.op_Implicit((Object)(object)value))
			{
				Texture2D texture = value.texture;
				if (Object.op_Implicit((Object)(object)texture))
				{
					Instance?.TrySwap(texture);
				}
			}
		}
	}

	[CompilerGenerated]
	private sealed class <AdaptiveWatcher>d__41 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public CustomizerT2DOnly <>4__this;

		private Stopwatch <sw>5__2;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <AdaptiveWatcher>d__41(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<sw>5__2 = null;
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Expected O, but got Unknown
			int num = <>1__state;
			CustomizerT2DOnly customizerT2DOnly = <>4__this;
			if (num != 0)
			{
				if (num != 1)
				{
					return false;
				}
				<>1__state = -1;
			}
			else
			{
				<>1__state = -1;
				<sw>5__2 = new Stopwatch();
			}
			if (customizerT2DOnly._keys != null && customizerT2DOnly._satisfiedKeys.Count < customizerT2DOnly._keys.Count)
			{
				if (customizerT2DOnly._scanSnapshot == null || customizerT2DOnly._scanIndex >= customizerT2DOnly._scanSnapshot.Length)
				{
					customizerT2DOnly._scanSnapshot = Resources.FindObjectsOfTypeAll<Texture2D>();
					customizerT2DOnly._scanIndex = 0;
				}
				<sw>5__2.Restart();
				while (customizerT2DOnly._scanIndex < customizerT2DOnly._scanSnapshot.Length && <sw>5__2.Elapsed.TotalMilliseconds < 3.0)
				{
					Texture2D val = customizerT2DOnly._scanSnapshot[customizerT2DOnly._scanIndex++];
					if (Object.op_Implicit((Object)(object)val))
					{
						customizerT2DOnly.TrySwap(val);
					}
				}
				<sw>5__2.Stop();
			}
			else
			{
				customizerT2DOnly._scanSnapshot = null;
				customizerT2DOnly._scanIndex = 0;
			}
			<>2__current = (object)new WaitForSeconds(0.1f);
			<>1__state = 1;
			return true;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	[CompilerGenerated]
	private sealed class <WatchRoutine>d__34 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public CustomizerT2DOnly <>4__this;

		private float <elapsed>5__2;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <WatchRoutine>d__34(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			int num = <>1__state;
			CustomizerT2DOnly customizerT2DOnly = <>4__this;
			switch (num)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<elapsed>5__2 = 0f;
				break;
			case 1:
				<>1__state = -1;
				<elapsed>5__2 += 0.5f;
				break;
			}
			if (<elapsed>5__2 < 4f)
			{
				customizerT2DOnly.ApplyOverridesPass();
				if (customizerT2DOnly._satisfiedKeys.Count < customizerT2DOnly._keys.Count)
				{
					<>2__current = (object)new WaitForSeconds(0.5f);
					<>1__state = 1;
					return true;
				}
			}
			customizerT2DOnly._watchRoutine = null;
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	private const float MENU_DELAY = 0.6f;

	private const float GAMEPLAY_DELAY = 0.5f;

	private const float WATCH_WINDOW_SECONDS = 4f;

	private const float WATCH_TICK_SECONDS = 0.5f;

	private const float ADAPTIVE_TICK = 0.1f;

	private const double ADAPTIVE_BUDGET_MS = 3.0;

	private static readonly string[] MENU_SCENE_PATTERNS = new string[3] { "menu", "mainmenu", "title" };

	private static MethodInfo _miLoadImageBool;

	private static MethodInfo _miLoadImage;

	private static MethodInfo _miTexLoadImage;

	private Dictionary<string, (byte[] bytes, int w, int h)> _map;

	private HashSet<string> _keys;

	private readonly HashSet<int> _replacedIds = new HashSet<int>();

	private readonly HashSet<int> _notOurTex = new HashSet<int>();

	private readonly HashSet<string> _satisfiedKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	private bool _armedForGameplayOnce;

	private Coroutine _watchRoutine;

	private Coroutine _adaptiveRoutine;

	private Type _typeSpriteAtlas;

	private Type _typeSpriteAtlasManager;

	private EventInfo _evAtlasRegistered;

	private MethodInfo _miGetSprites_Array;

	private Delegate _atlasCallbackDelegate;

	private Texture2D[] _scanSnapshot;

	private int _scanIndex;

	private Harmony _harmony;

	private static CustomizerT2DOnly Instance
	{
		get
		{
			GameObject managerObject = Chainloader.ManagerObject;
			if (managerObject == null)
			{
				return null;
			}
			return managerObject.GetComponent<CustomizerT2DOnly>();
		}
	}

	private void OnEnable()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Expected O, but got Unknown
		_harmony = new Harmony("com.yourname.customizer.t2donly.setters");
		try
		{
			_harmony.PatchAll(typeof(H_Material_Setters));
			_harmony.PatchAll(typeof(H_SpriteRenderer_SetSprite));
			TryPatchUIImageSetter(_harmony);
		}
		catch (Exception arg)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)$"[T2D] Harmony setters failed: {arg}");
		}
		TryHookSpriteAtlasRegistered();
		SceneManager.sceneLoaded += OnSceneLoaded;
	}

	private void OnDisable()
	{
		SceneManager.sceneLoaded -= OnSceneLoaded;
		if (_watchRoutine != null)
		{
			((MonoBehaviour)this).StopCoroutine(_watchRoutine);
		}
		if (_adaptiveRoutine != null)
		{
			((MonoBehaviour)this).StopCoroutine(_adaptiveRoutine);
		}
		TryUnhookSpriteAtlasRegistered();
		try
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
		catch
		{
		}
	}

	private void Awake()
	{
		CacheLoadImageMethods();
	}

	private void Start()
	{
		//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_003c: Unknown result type (might be due to invalid IL or missing references)
		BuildOverrideMap();
		if (_keys != null && _keys.Count > 0 && _adaptiveRoutine == null)
		{
			_adaptiveRoutine = ((MonoBehaviour)this).StartCoroutine(AdaptiveWatcher());
		}
		Scene activeScene = SceneManager.GetActiveScene();
		if (IsMenu(activeScene))
		{
			_armedForGameplayOnce = true;
			((MonoBehaviour)this).Invoke("ApplyOnMenu", 0.6f);
		}
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		if (IsMenu(scene))
		{
			_armedForGameplayOnce = true;
			((MonoBehaviour)this).Invoke("ApplyOnMenu", 0.6f);
		}
		else if (_armedForGameplayOnce)
		{
			_armedForGameplayOnce = false;
			((MonoBehaviour)this).Invoke("StartGameplayWatch", 0.5f);
		}
	}

	private static bool IsMenu(Scene scene)
	{
		string text = ((Scene)(ref scene)).name ?? string.Empty;
		string[] mENU_SCENE_PATTERNS = MENU_SCENE_PATTERNS;
		foreach (string value in mENU_SCENE_PATTERNS)
		{
			if (text.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		return false;
	}

	private void ApplyOnMenu()
	{
		_satisfiedKeys.Clear();
		ApplyOverridesPass();
	}

	private void StartGameplayWatch()
	{
		if (_watchRoutine != null)
		{
			((MonoBehaviour)this).StopCoroutine(_watchRoutine);
		}
		_watchRoutine = ((MonoBehaviour)this).StartCoroutine(WatchRoutine());
	}

	[IteratorStateMachine(typeof(<WatchRoutine>d__34))]
	private IEnumerator WatchRoutine()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <WatchRoutine>d__34(0)
		{
			<>4__this = this
		};
	}

	private void CacheLoadImageMethods()
	{
		try
		{
			Type type = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule", throwOnError: false);
			if (type != null)
			{
				_miLoadImageBool = type.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3]
				{
					typeof(Texture2D),
					typeof(byte[]),
					typeof(bool)
				}, null);
				if (_miLoadImageBool == null)
				{
					_miLoadImage = type.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
					{
						typeof(Texture2D),
						typeof(byte[])
					}, null);
				}
			}
			_miTexLoadImage = typeof(Texture2D).GetMethod("LoadImage", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(byte[]) }, null);
		}
		catch
		{
		}
	}

	private bool LoadImageCompat(Texture2D tex, byte[] data, bool makeNoLongerReadable)
	{
		try
		{
			if ((Object)(object)tex == (Object)null || data == null)
			{
				return false;
			}
			if (_miLoadImageBool != null)
			{
				return (bool)_miLoadImageBool.Invoke(null, new object[3] { tex, data, makeNoLongerReadable });
			}
			if (_miLoadImage != null)
			{
				return (bool)_miLoadImage.Invoke(null, new object[2] { tex, data });
			}
			if (_miTexLoadImage != null)
			{
				return (bool)_miTexLoadImage.Invoke(tex, new object[1] { data });
			}
			return false;
		}
		catch
		{
			return false;
		}
	}

	private static string NormalizeName(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return name;
		}
		if (name.StartsWith("sactx-", StringComparison.OrdinalIgnoreCase))
		{
			int num = name.LastIndexOf('-');
			if (num > 0 && num < name.Length - 1)
			{
				int num2 = name.Length - (num + 1);
				if (num2 >= 6 && num2 <= 10)
				{
					bool flag = true;
					for (int i = num + 1; i < name.Length; i++)
					{
						char c = name[i];
						if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F'))
						{
							flag = false;
							break;
						}
					}
					if (flag)
					{
						return name.Substring(0, num);
					}
				}
			}
		}
		return name;
	}

	private void BuildOverrideMap()
	{
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Expected O, but got Unknown
		_map = new Dictionary<string, (byte[], int, int)>(StringComparer.OrdinalIgnoreCase);
		try
		{
			string path = Path.Combine(Application.dataPath, "Mods", "Customizer");
			if (!Directory.Exists(path))
			{
				return;
			}
			string[] directories = Directory.GetDirectories(path);
			foreach (string path2 in directories)
			{
				string path3 = Path.Combine(path2, "Texture2D");
				if (!Directory.Exists(path3))
				{
					continue;
				}
				foreach (string item in Directory.EnumerateFiles(path3, "*.png", SearchOption.TopDirectoryOnly))
				{
					string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item);
					string key = NormalizeName(fileNameWithoutExtension);
					try
					{
						byte[] array = File.ReadAllBytes(item);
						Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
						if (LoadImageCompat(val, array, makeNoLongerReadable: false))
						{
							_map[key] = (array, ((Texture)val).width, ((Texture)val).height);
						}
						Object.Destroy((Object)(object)val);
					}
					catch
					{
					}
				}
			}
			_keys = new HashSet<string>(_map.Keys, StringComparer.OrdinalIgnoreCase);
		}
		catch
		{
		}
	}

	private void ApplyOverridesPass()
	{
		if (_map != null && _map.Count != 0)
		{
			Texture2D[] array = Resources.FindObjectsOfTypeAll<Texture2D>();
			for (int i = 0; i < array.Length; i++)
			{
				TrySwap(array[i]);
			}
		}
	}

	private bool TrySwap(Texture2D tex)
	{
		if (!Object.op_Implicit((Object)(object)tex))
		{
			return false;
		}
		int instanceID = ((Object)tex).GetInstanceID();
		if (_replacedIds.Contains(instanceID) || _notOurTex.Contains(instanceID))
		{
			return false;
		}
		string text = NormalizeName(((Object)tex).name);
		if (!_keys.Contains(text))
		{
			_notOurTex.Add(instanceID);
			return false;
		}
		(byte[], int, int) tuple = _map[text];
		if (((Texture)tex).width != tuple.Item2 || ((Texture)tex).height != tuple.Item3)
		{
			_notOurTex.Add(instanceID);
			return false;
		}
		if (LoadImageCompat(tex, tuple.Item1, makeNoLongerReadable: false))
		{
			_replacedIds.Add(instanceID);
			_satisfiedKeys.Add(text);
			if (_replacedIds.Count > 16384 || _notOurTex.Count > 16384)
			{
				_replacedIds.Clear();
				_notOurTex.Clear();
			}
			return true;
		}
		_notOurTex.Add(instanceID);
		return false;
	}

	[IteratorStateMachine(typeof(<AdaptiveWatcher>d__41))]
	private IEnumerator AdaptiveWatcher()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <AdaptiveWatcher>d__41(0)
		{
			<>4__this = this
		};
	}

	private void TryHookSpriteAtlasRegistered()
	{
		try
		{
			_typeSpriteAtlas = Type.GetType("UnityEngine.U2D.SpriteAtlas, UnityEngine.U2DModule", throwOnError: false);
			_typeSpriteAtlasManager = Type.GetType("UnityEngine.U2D.SpriteAtlasManager, UnityEngine.U2DModule", throwOnError: false);
			if (!(_typeSpriteAtlas == null) && !(_typeSpriteAtlasManager == null))
			{
				_evAtlasRegistered = _typeSpriteAtlasManager.GetEvent("atlasRegistered", BindingFlags.Static | BindingFlags.Public);
				_miGetSprites_Array = _typeSpriteAtlas.GetMethod("GetSprites", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(Sprite[]) }, null);
				if (!(_evAtlasRegistered == null) && !(_miGetSprites_Array == null))
				{
					MethodInfo method = typeof(CustomizerT2DOnly).GetMethod("OnAtlasRegisteredHandler", BindingFlags.Instance | BindingFlags.NonPublic);
					Type type = typeof(Action<>).MakeGenericType(_typeSpriteAtlas);
					_atlasCallbackDelegate = Delegate.CreateDelegate(type, this, method);
					_evAtlasRegistered.GetAddMethod().Invoke(null, new object[1] { _atlasCallbackDelegate });
				}
			}
		}
		catch
		{
		}
	}

	private void TryUnhookSpriteAtlasRegistered()
	{
		try
		{
			if (_evAtlasRegistered != null && (object)_atlasCallbackDelegate != null)
			{
				_evAtlasRegistered.GetRemoveMethod().Invoke(null, new object[1] { _atlasCallbackDelegate });
			}
		}
		catch
		{
		}
		finally
		{
			_atlasCallbackDelegate = null;
			_evAtlasRegistered = null;
			_miGetSprites_Array = null;
			_typeSpriteAtlas = null;
			_typeSpriteAtlasManager = null;
		}
	}

	private void OnAtlasRegisteredHandler(object atlasObj)
	{
		try
		{
			if (atlasObj == null || _miGetSprites_Array == null || _map == null || _map.Count == 0)
			{
				return;
			}
			Sprite[] array = (Sprite[])(object)new Sprite[1024];
			int num = (int)_miGetSprites_Array.Invoke(atlasObj, new object[1] { array });
			if (num <= 0)
			{
				return;
			}
			for (int i = 0; i < num && i < array.Length; i++)
			{
				Sprite val = array[i];
				if (Object.op_Implicit((Object)(object)val))
				{
					Texture2D texture = val.texture;
					if (Object.op_Implicit((Object)(object)texture))
					{
						TrySwap(texture);
					}
				}
			}
		}
		catch
		{
		}
	}

	private void TryPatchUIImageSetter(Harmony h)
	{
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Expected O, but got Unknown
		try
		{
			Type type = Type.GetType("UnityEngine.UI.Image, UnityEngine.UI", throwOnError: false) ?? Type.GetType("UnityEngine.UI.Image, UnityEngine.UIModule", throwOnError: false);
			if (!(type == null))
			{
				MethodInfo methodInfo = AccessTools.Property(type, "sprite")?.GetSetMethod();
				if (!(methodInfo == null))
				{
					HarmonyMethod val = new HarmonyMethod(typeof(H_UI_Image_SetSprite).GetMethod("Postfix", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
					h.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					((BaseUnityPlugin)this).Logger.LogInfo((object)"[T2D] Patched UI.Image.set_sprite");
				}
			}
		}
		catch
		{
		}
	}
}