Decompiled source of ShopGuardian v1.0.2

ShopGuardian.dll

Decompiled 3 days ago
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[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("ShopGuardian")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ShopGuardian")]
[assembly: AssemblyTitle("ShopGuardian")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 ShopGuardian
{
	[BepInPlugin("pl.kuba.shopguardian", "Shop Guardian", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class ShopGuardianPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "pl.kuba.shopguardian";

		public const string PluginName = "Shop Guardian";

		public const string PluginVersion = "1.0.1";

		private Harmony _harmony;

		internal static ManualLogSource LogS;

		private ConfigEntry<bool> _blockArcadia;

		private ConfigEntry<bool> _blockAllThirdParty;

		private ConfigEntry<bool> _forceDisableRandomSelection;

		private ConfigEntry<bool> _writeArcadiaCfg;

		private static ShopGuardianPlugin _instance;

		internal static ShopGuardianPlugin Instance => _instance;

		private void Awake()
		{
			_instance = this;
			LogS = ((BaseUnityPlugin)this).Logger;
			_blockArcadia = ((BaseUnityPlugin)this).Config.Bind<bool>("Blockers", "BlockArcadiaBetterShop", true, "Block shop registrations coming specifically from ArcadiaBetterShop/KagomesMod.");
			_blockAllThirdParty = ((BaseUnityPlugin)this).Config.Bind<bool>("Blockers", "BlockAllThirdPartyShopOverrides", false, "Block ANY third-party shop registrations (allows only vanilla/REPOShopLib internal).");
			_forceDisableRandomSelection = ((BaseUnityPlugin)this).Config.Bind<bool>("Toggles", "DisableRandomShopSelection", true, "Force-disable random/alt shop selection in REPOShopLib, when possible.");
			_writeArcadiaCfg = ((BaseUnityPlugin)this).Config.Bind<bool>("Toggles", "WriteArcadiaBetterShopConfigOverride", true, "On startup, write/update ArcadiaBetterShop config to disable its shop features.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[Shop Guardian] Booting…");
			TryWriteArcadiaConfig();
			TryForceREPOShopLibFlags();
			TryPatchREPOShopLib();
		}

		private void TryWriteArcadiaConfig()
		{
			if (!_writeArcadiaCfg.Value)
			{
				return;
			}
			try
			{
				string text = Path.Combine(Paths.ConfigPath, "ArcadiaBetterShop.arcadiabettershop.cfg");
				string[] array = new string[6] { "# Auto-generated by Shop Guardian to keep the vanilla shop intact.", "# You can set WriteArcadiaBetterShopConfigOverride=false in pl.kuba.shopguardian.cfg to stop this.", "", "[Shop]", "UseRandomShop = false", "EnableRandomShopSelection = false" };
				if (!File.Exists(text))
				{
					File.WriteAllLines(text, array);
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Wrote " + text));
					return;
				}
				string text2 = File.ReadAllText(text);
				if (!text2.Contains("UseRandomShop") || !text2.Contains("EnableRandomShopSelection"))
				{
					File.AppendAllText(text, Environment.NewLine + string.Join(Environment.NewLine, array.Skip(3)));
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Updated " + text + " with missing keys."));
					return;
				}
				text2 = Regex.Replace(text2, "UseRandomShop\\s*=\\s*\\w+", "UseRandomShop = false");
				text2 = Regex.Replace(text2, "EnableRandomShopSelection\\s*=\\s*\\w+", "EnableRandomShopSelection = false");
				File.WriteAllText(text, text2);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Ensured disabling flags in " + text + "."));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Arcadia config override failed: " + ex.Message));
			}
		}

		private void TryForceREPOShopLibFlags()
		{
			if (!_forceDisableRandomSelection.Value)
			{
				return;
			}
			try
			{
				Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name.Contains("REPOShopLib"));
				if (assembly == null)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"REPOShopLib not loaded yet; skipping flag force.");
					return;
				}
				Type type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == "ShopUtils");
				if (type == null)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"REPOShopLib.ShopUtils not found; skipping flag force.");
					return;
				}
				string[] array = new string[2] { "EnableRandomShopSelection", "UseRandomShop" };
				foreach (string text in array)
				{
					PropertyInfo property = type.GetProperty(text, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null && property.CanWrite)
					{
						try
						{
							property.SetValue(null, false);
							((BaseUnityPlugin)this).Logger.LogInfo((object)("Set REPOShopLib.ShopUtils." + text + " = false"));
						}
						catch (Exception ex)
						{
							((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to set ShopUtils." + text + ": " + ex.Message));
						}
					}
				}
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Error while forcing REPOShopLib flags: " + ex2.Message));
			}
		}

		private void TryPatchREPOShopLib()
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Expected O, but got Unknown
			try
			{
				Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name.Contains("REPOShopLib"));
				if (assembly == null)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"REPOShopLib not loaded; will rely on config override.");
					return;
				}
				Type type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == "ShopUtils");
				if (type == null)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"REPOShopLib.ShopUtils not found; cannot patch.");
					return;
				}
				_harmony = new Harmony("pl.kuba.shopguardian");
				string[] array = new string[2] { "RegisterNamedShop", "RegisterShopRoom" };
				foreach (string text in array)
				{
					MethodInfo methodInfo = AccessTools.Method(type, text, (Type[])null, (Type[])null);
					if (methodInfo != null)
					{
						HarmonyMethod val = new HarmonyMethod(typeof(ShopGuardianPlugin).GetMethod("GenericShopGatePrefix", BindingFlags.Static | BindingFlags.NonPublic));
						_harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						((BaseUnityPlugin)this).Logger.LogInfo((object)("Patched ShopUtils." + text));
					}
				}
				MethodInfo methodInfo2 = AccessTools.Method(type, "EnableRandomShopSelection", (Type[])null, (Type[])null);
				if (methodInfo2 != null)
				{
					HarmonyMethod val2 = new HarmonyMethod(typeof(ShopGuardianPlugin).GetMethod("ForceDisableRandomSelectionPrefix", BindingFlags.Static | BindingFlags.NonPublic));
					_harmony.Patch((MethodBase)methodInfo2, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Patched ShopUtils.EnableRandomShopSelection");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Harmony patching failed: " + ex.Message));
			}
		}

		private static bool GenericShopGatePrefix(MethodBase __originalMethod)
		{
			try
			{
				if ((Object)(object)Instance != (Object)null && Instance._blockAllThirdParty.Value)
				{
					LogS.LogDebug((object)("Blocked " + __originalMethod?.Name + " (all third-party)."));
					return false;
				}
				if ((Object)(object)Instance != (Object)null && Instance._blockArcadia.Value && IsCallFromAssemblyMatching("ArcadiaBetterShop", "KagomesMod"))
				{
					LogS.LogInfo((object)("Blocked " + __originalMethod?.Name + " from ArcadiaBetterShop/KagomesMod."));
					return false;
				}
			}
			catch (Exception ex)
			{
				LogS.LogWarning((object)("GenericShopGatePrefix error: " + ex.Message));
			}
			return true;
		}

		private static bool ForceDisableRandomSelectionPrefix()
		{
			try
			{
				if ((Object)(object)Instance != (Object)null && Instance._forceDisableRandomSelection.Value)
				{
					LogS.LogInfo((object)"Prevented enabling random shop selection.");
					return false;
				}
			}
			catch (Exception ex)
			{
				LogS.LogWarning((object)("ForceDisableRandomSelectionPrefix error: " + ex.Message));
			}
			return true;
		}

		private static bool IsCallFromAssemblyMatching(params string[] substrings)
		{
			StackFrame[] frames = new StackTrace(2, fNeedFileInfo: false).GetFrames();
			foreach (StackFrame obj in frames)
			{
				Type type = obj.GetMethod()?.DeclaringType;
				if (!(type == null))
				{
					string asmName = type.Assembly.GetName().Name;
					if (!asmName.Contains("ShopGuardian") && !asmName.Contains("REPOShopLib") && substrings.Any((string s) => asmName.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0))
					{
						return true;
					}
				}
			}
			return false;
		}
	}
}