Decompiled source of FeelingBetterAlready v1.0.0

BepInEx/plugins/FeelingBetterAlready/FeelingBetterAlready.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text.Json;
using BepInEx;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("FeelingBetterAlready")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("FeelingBetterAlready")]
[assembly: AssemblyTitle("FeelingBetterAlready")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace FeelingBetterAlready;

[HarmonyPatch(typeof(ANBGameLogic), "Update")]
internal static class KeepRegenerating
{
	private static bool failed;

	private static void Postfix(ANBGameLogic __instance)
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Expected O, but got Unknown
		if (failed)
		{
			return;
		}
		try
		{
			if (!(__instance.regainHealthTimeCurrent > 0f))
			{
				float playerHitPoints = __instance.playerHitPoints;
				if (!(playerHitPoints <= 0f) && !(playerHitPoints >= __instance.playerHitPointsMaxRecharge))
				{
					__instance.StartRegainHealth();
				}
			}
		}
		catch (Exception ex)
		{
			failed = true;
			ManualLogSource logger = Plugin.Logger;
			if (logger != null)
			{
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(49, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("FeelingBetterAlready regeneration timer stopped: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				logger.LogWarning(val);
			}
		}
	}
}
internal static class PatchEngine
{
	internal sealed class Manifest
	{
		public string OriginalSha256 { get; set; } = "";

		public string PatchedSha256 { get; set; } = "";

		public Patch[] Patches { get; set; } = Array.Empty<Patch>();
	}

	internal sealed class Patch
	{
		public string Name { get; set; } = "";

		public string Rva { get; set; } = "";

		public string Before { get; set; } = "";

		public string After { get; set; } = "";
	}

	internal static bool Apply(IntPtr module, int moduleSize, string dllPath)
	{
		if (!OperatingSystem.IsWindows() || IntPtr.Size != 8)
		{
			throw new PlatformNotSupportedException("FeelingBetterAlready requires Windows x64.");
		}
		using Stream utf8Json = Assembly.GetExecutingAssembly().GetManifestResourceStream("FeelingBetterAlready.patches.json");
		Manifest manifest = JsonSerializer.Deserialize<Manifest>(utf8Json, new JsonSerializerOptions
		{
			PropertyNameCaseInsensitive = true
		});
		using FileStream inputStream = File.OpenRead(dllPath);
		using SHA256 sHA = SHA256.Create();
		string text = Convert.ToHexString(sHA.ComputeHash(inputStream)).ToLowerInvariant();
		if (text != manifest.OriginalSha256 && text != manifest.PatchedSha256)
		{
			throw new InvalidOperationException("Unsupported GameAssembly.dll build (" + text + "). No memory changed.");
		}
		(IntPtr, byte[], byte[], string)[] array = manifest.Patches.Select(delegate(Patch p)
		{
			int num5 = int.Parse(p.Rva.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
			byte[] array4 = Convert.FromHexString(p.Before);
			byte[] array5 = Convert.FromHexString(p.After);
			if (num5 < 0 || array4.Length != array5.Length || array4.Length == 0 || num5 > moduleSize - array4.Length)
			{
				throw new InvalidOperationException("Invalid patch bounds: " + p.Name);
			}
			return (Address: IntPtr.Add(module, num5), Before: array4, After: array5, Name: p.Name);
		}).ToArray();
		bool flag = true;
		bool flag2 = true;
		(IntPtr, byte[], byte[], string)[] array2 = array;
		for (int num = 0; num < array2.Length; num++)
		{
			(IntPtr, byte[], byte[], string) tuple = array2[num];
			byte[] array3 = new byte[tuple.Item2.Length];
			Marshal.Copy(tuple.Item1, array3, 0, array3.Length);
			flag &= array3.SequenceEqual(tuple.Item2);
			flag2 &= array3.SequenceEqual(tuple.Item3);
		}
		if (flag2)
		{
			return false;
		}
		if (!flag)
		{
			throw new InvalidOperationException("Conflicting native patches detected. No memory changed; check other scoring mods.");
		}
		int num2 = -1;
		try
		{
			for (int num3 = 0; num3 < array.Length; num3++)
			{
				num2 = num3;
				Write(array[num3].Item1, array[num3].Item3);
			}
		}
		catch (Exception item)
		{
			List<Exception> list = new List<Exception> { item };
			for (int num4 = num2; num4 >= 0; num4--)
			{
				try
				{
					Write(array[num4].Item1, array[num4].Item2);
				}
				catch (Exception item2)
				{
					list.Add(item2);
				}
			}
			throw new AggregateException("Patch failed; original memory restoration was attempted for every changed site. Restart the game before playing.", list);
		}
		return true;
	}

	private static void Write(IntPtr address, byte[] bytes)
	{
		if (!VirtualProtect(address, (UIntPtr)(ulong)bytes.Length, 64u, out var oldProtection))
		{
			throw new Win32Exception(Marshal.GetLastWin32Error());
		}
		bool flag;
		try
		{
			Marshal.Copy(bytes, 0, address, bytes.Length);
			if (!FlushInstructionCache(GetCurrentProcess(), address, (UIntPtr)(ulong)bytes.Length))
			{
				throw new Win32Exception(Marshal.GetLastWin32Error());
			}
			byte[] array = new byte[bytes.Length];
			Marshal.Copy(address, array, 0, array.Length);
			if (!array.SequenceEqual(bytes))
			{
				throw new IOException("Memory patch verification failed.");
			}
		}
		finally
		{
			flag = VirtualProtect(address, (UIntPtr)(ulong)bytes.Length, oldProtection, out var _);
		}
		if (!flag)
		{
			throw new Win32Exception(Marshal.GetLastWin32Error());
		}
	}

	[DllImport("kernel32", SetLastError = true)]
	[return: MarshalAs(UnmanagedType.Bool)]
	private static extern bool VirtualProtect(IntPtr address, UIntPtr size, uint protection, out uint oldProtection);

	[DllImport("kernel32", SetLastError = true)]
	[return: MarshalAs(UnmanagedType.Bool)]
	private static extern bool FlushInstructionCache(IntPtr process, IntPtr address, UIntPtr size);

	[DllImport("kernel32")]
	private static extern IntPtr GetCurrentProcess();
}
[BepInPlugin("feelingbetteralready.gunmancontracts", "FeelingBetterAlready", "1.0.0")]
[BepInProcess("GunmanContracts.exe")]
public sealed class Plugin : BasePlugin
{
	internal static ManualLogSource Logger;

	public override void Load()
	{
		Logger = ((BasePlugin)this).Log;
		Start();
	}

	public override bool Unload()
	{
		return false;
	}

	private static void Start()
	{
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Expected O, but got Unknown
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Expected O, but got Unknown
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		bool flag2 = default(bool);
		try
		{
			using Process process = Process.GetCurrentProcess();
			ProcessModule processModule = ((IEnumerable)process.Modules).Cast<ProcessModule>().Single((ProcessModule m) => string.Equals(m.ModuleName, "GameAssembly.dll", StringComparison.OrdinalIgnoreCase));
			bool flag = PatchEngine.Apply(processModule.BaseAddress, processModule.ModuleMemorySize, processModule.FileName ?? throw new IOException("Cannot resolve the loaded game DLL path."));
			Logger.LogInfo((object)(flag ? "FeelingBetterAlready enabled: health regenerates up to the difficulty's own maximum instead of stopping at 2 HP." : "FeelingBetterAlready patches already present. No duplicate changes applied."));
		}
		catch (Exception ex)
		{
			ManualLogSource logger = Logger;
			BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(41, 1, ref flag2);
			if (flag2)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("FeelingBetterAlready could not activate: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
			}
			logger.LogError(val);
		}
		try
		{
			new Harmony("feelingbetteralready.gunmancontracts").PatchAll(typeof(KeepRegenerating));
		}
		catch (Exception ex2)
		{
			ManualLogSource logger2 = Logger;
			BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(63, 1, ref flag2);
			if (flag2)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("FeelingBetterAlready could not restart the regeneration timer: ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex2);
			}
			logger2.LogWarning(val2);
		}
	}
}