using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
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.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("SilkSongDeathSwap")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.2.1.0")]
[assembly: AssemblyInformationalVersion("1.2.1")]
[assembly: AssemblyProduct("Silk Song Death Swap")]
[assembly: AssemblyTitle("SilkSongDeathSwap")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.1.0")]
[module: UnverifiableCode]
[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.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace SilkSongSwitcher
{
internal static class WinAPI
{
private const int SW_MINIMIZE = 6;
private const int SW_RESTORE = 9;
[DllImport("user32.dll")]
private static extern int ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
public static void MinimizeWindow()
{
IntPtr mainWindowHandleFallback = GetMainWindowHandleFallback();
ShowWindow(mainWindowHandleFallback, 6);
}
public static void RestoreWindow()
{
IntPtr mainWindowHandleFallback = GetMainWindowHandleFallback();
ShowWindow(mainWindowHandleFallback, 9);
}
private static IntPtr GetMainWindowHandleFallback()
{
try
{
Process currentProcess = Process.GetCurrentProcess();
if (currentProcess != null && currentProcess.MainWindowHandle != IntPtr.Zero)
{
return currentProcess.MainWindowHandle;
}
}
catch
{
}
return GetForegroundWindow();
}
}
[BepInPlugin("SilkSongDeathSwap", "Silk Song Death Swap", "1.2.1")]
public class Plugin : BaseUnityPlugin
{
internal static ManualLogSource Logger;
private Harmony _harmony;
public static bool gameStarted;
public static bool gamePaused;
public static bool switchOnDamage;
public static bool damageTaken;
private static Thread listenerThread;
private static volatile bool keepListening;
private static InputHandler inputHandler;
private ConfigEntry<bool> cfgSwitchOnDamage;
private ConfigEntry<KeyboardShortcut> cfgPauseToggleKey;
private ConfigEntry<KeyboardShortcut> cfgSwitchOnDamageToggleKey;
private void Awake()
{
//IL_0058: 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_00c9: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Expected O, but got Unknown
Logger = ((BaseUnityPlugin)this).Logger;
Logger.LogInfo((object)"Plugin SilkSongDeathSwap is loaded!");
cfgSwitchOnDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SwitchOnDamage", false, "If true, switching occurs when player takes damage.");
cfgPauseToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "TogglePauseResumeKey", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Key used to toggle pause/resume.");
cfgSwitchOnDamageToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "ToggleSwitchOnDamageKey", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Key used to toggle switch on damage.");
switchOnDamage = cfgSwitchOnDamage.Value;
cfgSwitchOnDamage.SettingChanged += delegate
{
switchOnDamage = cfgSwitchOnDamage.Value;
};
_harmony = new Harmony("SilkSongDeathSwap");
_harmony.PatchAll();
}
private void Update()
{
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: 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_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_011b: Unknown result type (might be due to invalid IL or missing references)
//IL_0120: Unknown result type (might be due to invalid IL or missing references)
//IL_0123: 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_00e9: Unknown result type (might be due to invalid IL or missing references)
//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
KeyboardShortcut value;
if (gameStarted)
{
value = cfgPauseToggleKey.Value;
if (Input.GetKeyDown(((KeyboardShortcut)(ref value)).MainKey))
{
if (gamePaused)
{
ManualLogSource logger = Logger;
value = cfgPauseToggleKey.Value;
logger.LogInfo((object)$"{((KeyboardShortcut)(ref value)).MainKey} pressed: Resuming game.");
ResumeGame();
}
else
{
ManualLogSource logger2 = Logger;
value = cfgPauseToggleKey.Value;
logger2.LogInfo((object)$"{((KeyboardShortcut)(ref value)).MainKey} pressed: Pausing game.");
PauseGame();
}
}
}
if (gameStarted)
{
value = cfgSwitchOnDamageToggleKey.Value;
if (Input.GetKeyDown(((KeyboardShortcut)(ref value)).MainKey))
{
if (!switchOnDamage)
{
ManualLogSource logger3 = Logger;
value = cfgSwitchOnDamageToggleKey.Value;
logger3.LogInfo((object)$"{((KeyboardShortcut)(ref value)).MainKey} pressed: Enabling switch on damage.");
switchOnDamage = true;
}
else
{
ManualLogSource logger4 = Logger;
value = cfgSwitchOnDamageToggleKey.Value;
logger4.LogInfo((object)$"{((KeyboardShortcut)(ref value)).MainKey} pressed: Disabling switch on damage.");
switchOnDamage = false;
}
}
}
if (gameStarted && gamePaused)
{
inputHandler = ManagerSingleton<InputHandler>.Instance;
if ((Object)(object)inputHandler != (Object)null)
{
inputHandler.StopAcceptingInput();
inputHandler.PreventPause();
}
Time.timeScale = 0f;
}
}
public static void PauseGame()
{
gamePaused = true;
AudioListener.pause = true;
Logger.LogInfo((object)"Game paused.");
}
public static void ResumeGame()
{
gamePaused = false;
Time.timeScale = 1f;
AudioListener.pause = false;
WinAPI.RestoreWindow();
if (damageTaken)
{
damageTaken = false;
}
inputHandler = ManagerSingleton<InputHandler>.Instance;
if ((Object)(object)inputHandler != (Object)null)
{
Logger.LogInfo((object)"Resuming input handling.");
inputHandler.StartAcceptingInput();
inputHandler.AllowPause();
}
}
public static void SendMessageToHollowKnight(string message)
{
try
{
using TcpClient tcpClient = new TcpClient("127.0.0.1", 5001);
using NetworkStream networkStream = tcpClient.GetStream();
byte[] bytes = Encoding.UTF8.GetBytes(message);
networkStream.Write(bytes, 0, bytes.Length);
byte[] array = new byte[1024];
int count = networkStream.Read(array, 0, array.Length);
string @string = Encoding.UTF8.GetString(array, 0, count);
Logger.LogInfo((object)("Received from Hollow Knight: " + @string));
}
catch (Exception ex)
{
Logger.LogInfo((object)("TCP Error: " + ex.Message));
}
}
public static void StartListeningToHollowKnight()
{
keepListening = true;
listenerThread = new Thread(ListenForHollowKnightMessages);
listenerThread.IsBackground = true;
listenerThread.Start();
}
public static void StopListeningToHollowKnight()
{
keepListening = false;
listenerThread?.Join();
}
private static void ListenForHollowKnightMessages()
{
TcpListener tcpListener = null;
try
{
tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 5002);
tcpListener.Start();
Logger.LogInfo((object)"Listening for Hollow Knight messages on port 5002...");
while (keepListening)
{
if (tcpListener.Pending())
{
using TcpClient tcpClient = tcpListener.AcceptTcpClient();
using NetworkStream networkStream = tcpClient.GetStream();
byte[] array = new byte[1024];
int num = networkStream.Read(array, 0, array.Length);
if (num > 0)
{
string @string = Encoding.UTF8.GetString(array, 0, num);
Logger.LogInfo((object)("Received from Hollow Knight: " + @string));
if (@string == "resume_game")
{
ResumeGame();
}
if (@string == "pause_game")
{
PauseGame();
}
if (@string == "enable_switch_on_damage")
{
switchOnDamage = true;
Logger.LogInfo((object)"Switch on damage enabled.");
}
byte[] bytes = Encoding.UTF8.GetBytes("ACK");
networkStream.Write(bytes, 0, bytes.Length);
}
}
Thread.Sleep(100);
}
}
catch (Exception ex)
{
Logger.LogInfo((object)("TCP Listen Error: " + ex.Message));
}
finally
{
tcpListener?.Stop();
}
}
}
[HarmonyPatch(typeof(GameManager), "RespawningHero")]
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
public static class GameManager_RespawningHero_Set_Patch
{
public static void Prefix(bool value)
{
if (value && Plugin.gameStarted)
{
Plugin.Logger.LogInfo((object)"Player death detected: RespawningHero set to true.");
Plugin.SendMessageToHollowKnight("resume_game");
Plugin.PauseGame();
}
}
}
[HarmonyPatch(typeof(HeroController), "Invulnerable")]
public static class HeroController_Invulnerable_Patch
{
[CompilerGenerated]
private sealed class <WrapCoroutine>d__1 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public IEnumerator original;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <WrapCoroutine>d__1(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
break;
case 1:
<>1__state = -1;
break;
}
if (original.MoveNext())
{
<>2__current = original.Current;
<>1__state = 1;
return true;
}
if (Plugin.switchOnDamage && Plugin.gameStarted)
{
Plugin.Logger.LogInfo((object)"Player damage detected: Invulnerable coroutine completed.");
Plugin.PauseGame();
Plugin.SendMessageToHollowKnight("resume_game");
}
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();
}
}
public static void Postfix(ref IEnumerator __result)
{
__result = WrapCoroutine(__result);
}
[IteratorStateMachine(typeof(<WrapCoroutine>d__1))]
private static IEnumerator WrapCoroutine(IEnumerator original)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <WrapCoroutine>d__1(0)
{
original = original
};
}
}
[HarmonyPatch(typeof(GameManager), "RunContinueGame", new Type[] { typeof(bool) })]
public static class GameManager_RunContinueGame_Get_Patch
{
[CompilerGenerated]
private sealed class <WrapCoroutine>d__1 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public IEnumerator original;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <WrapCoroutine>d__1(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
break;
case 1:
<>1__state = -1;
break;
}
if (original.MoveNext())
{
<>2__current = original.Current;
<>1__state = 1;
return true;
}
Plugin.Logger.LogInfo((object)"Game start detected: RunContinueGame completed.");
Plugin.gameStarted = true;
Plugin.StartListeningToHollowKnight();
if (Plugin.switchOnDamage)
{
Plugin.SendMessageToHollowKnight("enable_switch_on_damage");
}
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();
}
}
public static void Postfix(ref IEnumerator __result)
{
__result = WrapCoroutine(__result);
}
[IteratorStateMachine(typeof(<WrapCoroutine>d__1))]
private static IEnumerator WrapCoroutine(IEnumerator original)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <WrapCoroutine>d__1(0)
{
original = original
};
}
}
[HarmonyPatch(typeof(GameManager), "ReturnToMainMenu", new Type[]
{
typeof(bool),
typeof(Action<bool>),
typeof(bool),
typeof(bool)
})]
public static class GameManager_ReturnToMainMenu_Patch
{
public static void Postfix()
{
Plugin.Logger.LogInfo((object)"Return to Main Menu detected.");
Plugin.gameStarted = false;
Plugin.StopListeningToHollowKnight();
}
}
[HarmonyPatch(typeof(HeroController), "CanOpenInventory")]
public static class HeroController_CanOpenInventory_Patch
{
public static bool Prefix(ref bool __result)
{
if (Plugin.gamePaused)
{
__result = false;
return false;
}
return true;
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "SilkSongDeathSwap";
public const string PLUGIN_NAME = "Silk Song Death Swap";
public const string PLUGIN_VERSION = "1.2.1";
}
}