Trickjump bot for Wolf:ET

Wolf:ET inspired me to want to learn how to code especially in C++. Seeing all the cool things people were creating made me want to join in. And I eventually did. Out of all the bots released, a trickjump bot never was... and I really wanted one.

The overall design is inspired by pretty much all the code I've seen. Inject .dll and hook to gain access to the juicy stuff. I will be combining two projects in this write up, the later project lacked key a lot of features but was pretty tiny ~500 lines of code.

Possibly the most annoying thing to do was every update I had to hop back into IDA and track down a couple of offsets so my bot would continue to work. And so the first thing in simplifying my code was to dynamically locate the offset for the last remaining function I needed hooked. The functions bDataCompare and dwFindPattern solved this for me, I also didn't write them. Luckily most of the offsets I needed were for the 2.6b executable, and so they would never change.

Since I played on a server accross the world, my screen would always pop up with disconnected status even though there was no lag, a simple return statement on that pesky DrawDisconnect(void) function saved me from going insane.

There were a few other things like autojump, no hitback when attacted, run while scoped, but it also logged a bunch of stuff on how far off perfect you were jumping, speeds etc.

Eventually I also created a little chat program so I could automatically translate the ingame chat, and see list of players whilst having the game minimised. Heres the code. This was also the first time I had C++ talking to a C# application.


	//processing chat everytime something is sent
	case CG_GETSERVERCOMMAND:		//processing chat and then sends to logger
	{
					int ret = orig_syscall(command, arg[0]);
					if (!arg[0])
					return ret;
					if (p.connected){
					if (!strcmp(trap_CG_Argv(0), "chat") || !strcmp(trap_CG_Argv(0), "tchat") || !strcmp(trap_CG_Argv(0), "enc_chat")){
						char s[MAX_SAY_TEXT] = { 0 };
						strcpy_s(s, sizeof(s), trap_CG_Argv(1));

						for (int i = 0; i < MAX_SAY_TEXT; i++){
						if (s[i] == '\n'){
							s[i] = '\0';
							break;
						}
						}
						//if (!strcmp(trap_CG_Argv(0), "enc_chat"))					//damn zero's new cross server chat system!
						ReplaceSpecialChar(s);

						Network.Send(s, sizeof(s));
					}
					}
	}


  //network.cpp
  #define PORT	8888
  #define DESTIP	"127.0.0.1"

  pNetwork Network;

  void pNetwork::Stop()
  {
  	if (p.connected){
  		p.connected = false;
  		shutdown(s, 2);
  		closesocket(s);
  		WSACleanup();
  	}
  }

  bool pNetwork::Connect()
  {
  	if (p.connected)
  		this->Stop();
  	if (!p.wantConnection)
  		return false;

  	WSADATA wsadata;
  	int error = WSAStartup(0x0202, &wsadata);

  	if (error){
  		return false;
  	}
  	if (wsadata.wVersion != 0x0202)
  	{
  		WSACleanup();
  		return false;
  	}
  	s= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  	if (s == INVALID_SOCKET){
  		WSACleanup();
  		return false;
  	}
  	SOCKADDR_IN		target;
  	target.sin_family = AF_INET;
  	target.sin_addr.S_un.S_addr = inet_addr(DESTIP);
  	target.sin_port = htons(PORT);

  	if (connect(s, (SOCKADDR*)&target, sizeof(target)) == SOCKET_ERROR)
  	{
  		WSACleanup();
  		return false;
  	}
  	return true;
  }

  int pNetwork::Send(char* msg, int len)
  {
  	int ret = send(s, msg, len, 0);
  	if (ret <= 0){
  		p.connected = this->Connect();
  		if (p.connected)
  			ret = send(s, msg, len, 0);
  	}
  	return ret;
  }

  int pNetwork::Recv(char* msg, int len)
  {
  	int i = shutdown(s, 1);
  	if (i < 0)
  		this->Stop();

  	return recv(s, msg, len, 0);
  }
	

Here is the C# application code.



  using System;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Data;
  using System.Drawing;
  using System.IO;
  using System.Linq;
  using System.Security.AccessControl;
  using System.Text;
  using System.Text.RegularExpressions;
  using System.Threading.Tasks;
  using System.Web.Script.Serialization;
  using System.Windows.Forms;
  using System.Net.Sockets;
  using System.Threading;
  using System.Net;
  using System.Web;


  namespace Server_test
  {

      public partial class Form1 : Form
      {
          private List PlayerList = new List();

          public System.Windows.Forms.TextBox textBox2;
          private Server _server;

          private bool _colourCodes = false;
          private bool _translate = false;
          private bool _saveOnExit = false;


          public Form1()
          {
              InitializeComponent();
              this.Text = "Logme";
          }

          private void Form1_Load(object sender, EventArgs e)
          {
              _server = new Server(this);
              GetPlayerData();
          }

          public void AppendText(String text)
          {
              if (this.InvokeRequired)
              {
                  this.Invoke(new Action(AppendText), new object[] { text });
                  return;
              }
              this.textBox1.AppendText(RemoveColour(text));
              this.textBox1.AppendText(Environment.NewLine);


              this.textBox4.AppendText(text);
              this.textBox4.AppendText(Environment.NewLine);

              if (_translate)
              {
                  string trans = (TranslateET(text));
                  if (trans != null)
                  {
                      this.textBox5.AppendText(trans);
                      this.textBox5.AppendText(Environment.NewLine);
                  }
              }

          }

          public static string RemoveColour(string s)
          {
              while (s.Contains("^"))
              {
                  var toRemove = s.Substring(s.IndexOf("^"), 2);
                  s = s.Replace(toRemove, "");
              }
              return s;
          }

          private void GetPlayerData()
          {
              UdpClient udpClient = new UdpClient();
              udpClient.Connect("future.etjump.com", 27960);

              Byte[] prefix = { 0xff, 0xff, 0xff, 0xff };
              Byte[] command = Encoding.ASCII.GetBytes("getstatus");
              Byte[] sendBytes = prefix.Concat(command).ToArray();
              udpClient.Send(sendBytes, sendBytes.Length);

              IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 0);
              Byte[] recvBytes = udpClient.Receive(ref ipEndPoint);

              udpClient.Close();
              PlayerList.Clear();

              string[] raw = Encoding.ASCII.GetString(recvBytes).Split('"', '"');

              for (int i = 1, j = 0; i < raw.Length; i += 2, j++)
              {
                  var p = raw[i];
                  //p.name = raw[i];
                  PlayerList.Add(p);
              }
              PrintPlayerList();
          }

          private void PrintPlayerList()
          {
              textBox3.Clear();
              foreach (var p in PlayerList)
              {
                  textBox3.AppendText(RemoveColour(p) + Environment.NewLine);
              }
          }

          private void button1_Click(object sender, EventArgs e)
          {
              GetPlayerData();
          }
          private void button2_Click(object sender, EventArgs e)
          {
  //            MessageBox.Show("Sending data");
              if (textBox6.Text != null)
              {
  //                MessageBox.Show("Sending: " + textBox6.Text);
                  _server.HandleSend(textBox6.Text);
                  textBox6.Clear();
              }
          }

          private void Form1_FormClosing(object sender, FormClosingEventArgs e)
          {
              _server.Shutdown();
              if (_saveOnExit)
              {
                  var cwd = Directory.GetCurrentDirectory() + "\\";
                  int i = 0;
                  while (File.Exists(cwd + "log" + i + ".log"))
                  {
                      i++;
                  }
                  File.WriteAllText(cwd+"log"+i+".log", textBox1.Text);
                  File.WriteAllText(cwd + "log_colour" + i + ".log", textBox4.Text);

              }
          }

          private void checkBox1_CheckedChanged(object sender, EventArgs e)
          {
              _colourCodes = !_colourCodes;
              if (_colourCodes)
              {
                  textBox1.Hide();
                  textBox4.Show();
              }
              else
              {
                  textBox1.Show();
                  textBox4.Hide();
              }

          }
          private void checkBox2_CheckedChanged(object sender, EventArgs e)
          {
              _translate = !_translate;
          }

          private void checkBox3_CheckedChanged(object sender, EventArgs e)
          {
              _saveOnExit = !_saveOnExit;
          }

          private string TranslateET(string text)
          {
              string pname;
              string toTranslate;
              try
              {
                  pname = text.Substring(0, text.IndexOf("^7:"));
              }
              catch
              {
                  return null;
              }
              pname = RemoveColour(pname);

              toTranslate = text.Substring(text.IndexOf("^7:")+3);
              toTranslate = RemoveColour(toTranslate);


              var translated = TranslateGoogle(toTranslate);

              return pname + ": " + translated;
          }

          //from http://weblog.west-wind.com/posts/2011/Aug/06/Translating-with-Google-Translate-without-API-and-C-Code
          //changed a bit
          public string TranslateGoogle(string text)
          {
              string url = string.Format(@"http://translate.google.com/translate_a/t?client=j&text={0}&hl=en&sl=auto&tl=en",
                                        HttpUtility.UrlEncode(text));

              // Retrieve Translation with HTTP GET call
              string html = null;
              try
              {
                  WebClient web = new WebClient();

                  // MUST add a known browser user agent or else response encoding doen't return UTF-8 (WTF Google?)
                  web.Headers.Add(HttpRequestHeader.UserAgent, "Mozilla/5.0");
                  web.Headers.Add(HttpRequestHeader.AcceptCharset, "UTF-8");

                  // Make sure we have response encoding to UTF-8
                  web.Encoding = Encoding.UTF8;
                  html = web.DownloadString(url);
              }
              catch
              {
                  return null;
              }

              // Extract out trans":"...[Extracted]...","from the JSON string
              string result = Regex.Match(html, "trans\":(\".*?\"),\"", RegexOptions.IgnoreCase).Groups[1].Value;

              if (string.IsNullOrEmpty(result))
              {
              }


              // Result is a JavaScript string so we need to deserialize it properly
              JavaScriptSerializer ser = new JavaScriptSerializer();
              return ser.Deserialize(result, typeof(string)) as string;
          }

          private void KeyPress(object sender, KeyPressEventArgs e)
          {
             // if(e.KeyChar == Key.)
          }

          private void textBox6_Enter(object sender, EventArgs e)
          {
              ActiveForm.AcceptButton = button2;
          }

          private void textBox6_Leave(object sender, EventArgs e)
          {
              ActiveForm.AcceptButton =null;
          }


      }

  }
	

  #pragma once

  #define WIN32_LEAN_AND_MEAN
  #define VC_EXTRALEAN
  #include 
  #include 
  #include 
  #include 
  #include 

  //et
  #include ETSDK\\src\\cgame\\cg_local.h
  #include ETSDK\\src\\ui\\ui_public.h
  #include ETSDK\\src\\qcommon\\vm_local.h

  //detours
  #include Detours\\detours.h

  //2.6 offsets
  #define OFF_SYSCALL		0x004488A0
  #define OFF_GLCONFIG	0x15D0924
  #define OFF_FRAMETIME	0x13C08E0
  #define OFF_ADDCOMMAND	0x41FD20
  #define OFF_VIEWX		0x013EEA8C
  #define OFF_VIEWY		0x013EEA88
  #define OFF_INBUTTONS	0x835AF8

  typedef	HINSTANCE(__stdcall *LoadLibraryA_t) (LPCSTR lpLibName);
  extern	LoadLibraryA_t	orig_LoadLibraryA;
  HINSTANCE __stdcall	p_LoadLibraryA(LPCSTR lpLibName);

  typedef int(__cdecl *Syscall_t)(int cmd, ...);
  extern Syscall_t orig_syscall;

  typedef int(__cdecl *vmMain_t) (int command, ...);
  extern vmMain_t orig_vmMain;
  int __cdecl vmMain(int command, ...);

  typedef void(__cdecl *Cmd_AddCommand_t)(const char *cmd_name, xcommand_t function);
  extern Cmd_AddCommand_t orig_Cmd_AddCommand;

  typedef void(__cdecl *CG_DrawDisconnect_t)(void);
  void __cdecl pCG_DrawDisconnect(void);
  extern CG_DrawDisconnect_t orig_CG_DrawDisconnect;

  struct kbutton{
  	int				down[2];
  	unsigned		downtime;
  	unsigned		msec;
  	int				active;
  	int				wasPressed;
  };

  struct game{
  	glconfig_t		*glconfig;
  	float			*vax;
  	float			*vay;
  	int				*com_frametime;
  	kbutton		*jumpbtn;
  };

  struct cgame{
  	float			ourvax;

  	float			speed;
  	float			optAng;
  	float			velAng;
  	float			accAng;
  	float			optStart;
  	float			angleDiff;
  	bool			touchingGround;
  	bool			dobot;

  	//downkeys
  	bool			w;
  	bool			s;
  	bool			a;
  	bool			d;
  };

  struct settings{
  	bool _bot;
  	bool _jump;

  	float _offset = 0;
  	float _startoffset = 0.3;
  	int _startspeed = 450;
  };

  class AutoBot {
  public:
  	void intention(HINSTANCE);

  	void syshook(bool);
  	void ethook(bool);
  	void cghook(bool);

  	int CG_Shutdown();
  //private:
  	char	_path[MAX_PATH + 1];

  	HMODULE _cghandle;
  	HMODULE _etprocess;
  	HMODULE _dllprocess;

  	game			_game;
  	cgame			_cg;
  	playerState_t	*_ps;

  	settings _settings;


  	bool _ethooked;
  	bool _syshooked;
  	bool _cghooked;

  	bool _youtube;
  	char _currentURL[512];
  };

  extern AutoBot bot;

	

Sadly even with all the formulae on calculating the perfect angles, I never quite got there myself. And so the actual angle calculation comes from Setups trickjump bot. Even now I don't understand the magic numbers that come from ET SDK, but it works.


  #include Autobot.h
  #include commands.h

  AutoBot bot;

  LoadLibraryA_t			orig_LoadLibraryA = NULL;
  Syscall_t				orig_syscall = NULL;
  vmMain_t				orig_vmMain = NULL;
  Cmd_AddCommand_t		orig_Cmd_AddCommand = NULL;
  CG_DrawDisconnect_t		orig_CG_DrawDisconnect = NULL;

  //forward decs
  int __cdecl vmMain(int command, ...);

  bool bDataCompare(const BYTE *pData, const BYTE *bMask, const char *szMask);
  DWORD dwFindPattern(DWORD dwAddress, DWORD dwLen, BYTE *bMask, char *szMask);
  void startbot();
  void autojump();
  void anglebot();

  //etsdk
  float AngleNormalize180(float angle);
  float AngleNormalize360(float angle);
  float AngleDelta(float angle1, float angle2);


  BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
  {
  	switch (fdwReason)
  	{
  	case DLL_PROCESS_ATTACH:
  	{
  		bot.intention(hinstDLL);
  	}
  	}
  	return true;
  }

  HINSTANCE __stdcall p_LoadLibraryA(LPCSTR lpLibName)
  {
  	HMODULE hm = orig_LoadLibraryA(lpLibName);
  	if (strstr(lpLibName, "cgame_mp_x86.dll")){
  		bot._cghandle = hm;
  		bot.cghook(true);
  		bot._dllprocess = GetModuleHandleA(lpLibName);
  	}
  	return hm;
  }

  void AutoBot::intention(HINSTANCE hinst)
  {
  	int len = GetModuleFileNameA(hinst, _path, sizeof(_path));
  	bool ret = len;
  	while (len && _path[len] != '\\') --len;
  	_path[len + 1] = '\0';

  	syshook(ret);
  	ethook(ret);
  }

  void AutoBot::syshook(bool state)
  {
  	static bool hooked;

  	if (state && !hooked) {
  		orig_LoadLibraryA = (LoadLibraryA_t)DetourFunction((BYTE*)LoadLibraryA, (BYTE*)p_LoadLibraryA);
  		_game.glconfig = (glconfig_t*)OFF_GLCONFIG;
  		_game.com_frametime = (int*)OFF_FRAMETIME;
  		_game.vax = (float*)OFF_VIEWX;
  		_game.vay = (float*)OFF_VIEWY;
  		_game.jumpbtn = (kbutton*)((DWORD)OFF_INBUTTONS - 2 * sizeof(kbutton));
  	}
  	else if (hooked)
  		DetourRemove((BYTE*)orig_LoadLibraryA, (BYTE*)p_LoadLibraryA);
  	hooked = state;
  }

  void AutoBot::ethook(bool state)
  {
  	static bool hooked = false;

  	if (state && !hooked){
  		orig_syscall = (Syscall_t)OFF_SYSCALL;
  		orig_Cmd_AddCommand = (Cmd_AddCommand_t)OFF_ADDCOMMAND;
  		Commands::RegisterCommands();
  		hooked = true;
  	}
  }

  void AutoBot::cghook(bool state)
  {
  	if (state && !_cghooked){
  		orig_vmMain = (vmMain_t)DetourFunction((BYTE*)GetProcAddress(_cghandle, "vmMain"), (BYTE*)vmMain);

  		//2.0.5RC1
  		_ps = (playerState_t*)((*(DWORD*)(dwFindPattern((DWORD)_cghandle, (DWORD)(_cghandle + 0x02000000),
  			(BYTE*)"\x68\x00\x00\x00\x00\x0F\x44\xD9", "x????xxx") + 1)));

  		//orig_CG_DrawDisconnect = (CG_DrawDisconnect_t)DetourFunction((BYTE*)(dwFindPattern((DWORD)_cghandle, (DWORD)(_cghandle + 0x02000000),
  		//	(BYTE*)"\x55\x8B\xEC\x83\xEC\x1C\x83\x3D\x00\x00\x00\x00\x00\x74\x19", "xxxxxxxx?????xx")), (BYTE*)pCG_DrawDisconnect);

  	}
  	else if (_cghooked) {
  		DetourRemove((BYTE*)orig_vmMain, (BYTE*)vmMain);
  		//DetourRemove((BYTE*)orig_CG_DrawDisconnect, (BYTE*)pCG_DrawDisconnect);
  	}

  	_cghooked = true;
  }

  int AutoBot::CG_Shutdown(){
  	cghook(false);
  	_cghooked = false;
  	int r = orig_vmMain(CG_SHUTDOWN);
  	ethook(true);
  	return r;
  }

  void __cdecl pCG_DrawDisconnect(void)
  {
  	return;
  }

  int __cdecl vmMain(int command, ...)
  {
  	int arg[12];
  	va_list arglist;
  	va_start(arglist, command);
  	int count = 0;
  	for (; count < 12; count++)
  		arg[count] = va_arg(arglist, int);
  	va_end(arglist);

  	switch (command)
  	{
  	case CG_INIT: {
  		orig_vmMain(command, arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7], arg[8], arg[9], arg[10], arg[11]);

  		bot._game.glconfig->vidWidth / 640.f;
  		bot._game.glconfig->vidHeight / 480.0f;

  		return 0;
  	}
  	case CG_SHUTDOWN:{
  		return bot.CG_Shutdown();
  		break;

  	}
  	case CG_DRAW_ACTIVE_FRAME:{

  		usercmd_t cmd;
  		int cmdnum = 0;
  		cmdnum = orig_syscall(CG_GETCURRENTCMDNUMBER);
  		orig_syscall(CG_GETUSERCMD, cmdnum, &cmd);

  		bot._cg.ourvax = *bot._game.vax + SHORT2ANGLE(bot._ps->delta_angles[1]);
  		if (bot._cg.ourvax > 180.f)		bot._cg.ourvax -= 360.f;
  		if (bot._cg.ourvax < -180.f)	bot._cg.ourvax += 360.f;
  		float velx = bot._ps->velocity[0];
  		float vely = bot._ps->velocity[1];
  		bot._cg.speed = sqrt(velx*velx + vely*vely);
  		bot._cg.optAng = bot._cg.optStart =  AngleNormalize180(RAD2DEG(acos(317.44f / bot._cg.speed *1.1)));
  		bot._cg.velAng = RAD2DEG(atan2(vely, velx));
  		bot._cg.accAng = RAD2DEG(atan2((float)-cmd.rightmove, (float)cmd.forwardmove));

  		bot._cg.d = bot._cg.a = bot._cg.s = bot._cg.w = false;
  		if (cmd.rightmove > 0) bot._cg.d = true;
  		if (cmd.forwardmove > 0) bot._cg.w = true;
  		if (cmd.rightmove < 0) bot._cg.a = true;
  		if (cmd.forwardmove < 0) bot._cg.s = true;

  		if (bot._cg.a && !bot._cg.d){
  			bot._cg.angleDiff = (AngleDelta(bot._cg.ourvax + bot._cg.accAng, bot._cg.velAng + bot._cg.optAng)) - 0.3;
  			bot._cg.dobot = true;
  		}
  		else if
  			(bot._cg.d && !bot._cg.a){
  			bot._cg.angleDiff = (AngleDelta(bot._cg.ourvax + bot._cg.accAng, bot._cg.velAng - bot._cg.optAng)) + 0.3;
  			bot._cg.dobot = true;
  		}
  		else
  			bot._cg.dobot = false;
  		break;
  	}
  	}

  	int ret = orig_vmMain(command, arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7], arg[8], arg[9], arg[10], arg[11]);

  	switch (command)
  	{
  	case CG_DRAW_ACTIVE_FRAME:{
  		if (bot._settings._jump) autojump();
  		if (bot._settings._bot) anglebot();
  		break;
  	}
  	}

  	return ret;
  }

  //botstuff
  void autojump()
  {
  	if (bot._ps->groundEntityNum != ENTITYNUM_NONE){
  		bot._game.jumpbtn->active = bot._game.jumpbtn->wasPressed = 1;
  		bot._game.jumpbtn->down[0] = -1;
  		bot._game.jumpbtn->downtime = *bot._game.com_frametime;
  	}
  	else
  		bot._game.jumpbtn->active = bot._game.jumpbtn->down[0] = bot._game.jumpbtn->down[1] = 0;
  }

  void startbot()
  {
  	bool justWS = ((bot._cg.w || bot._cg.s) && (!bot._cg.a && !bot._cg.d));
  	if (bot._cg.speed < 321 || justWS) return;

  	if (bot._cg.speed < bot._settings._startspeed){
  		if (bot._cg.a)
  			*(float *)bot._game.vax -= bot._cg.angleDiff + bot._settings._startoffset;
  		else
  			*(float *)bot._game.vax -= bot._cg.angleDiff - bot._settings._startoffset;
  	}
  }

  void anglebot()
  {
  	//if (bot._cg.speed > bot._settings._startspeed && bot._cg.dobot)
  	//	*(float*)bot._game.vax -= bot._cg.angleDiff;

  	float offset = bot._settings._offset;

  	if (bot._cg.speed > bot._settings._startspeed){
  			if ((bot._cg.a && !bot._cg.d) || (bot._cg.w && bot._cg.a && bot._cg.d) || (bot._cg.w && !bot._cg.a && !bot._cg.d))
  				*(float *)bot._game.vax -= AngleDelta(bot._cg.ourvax + bot._cg.accAng, bot._cg.velAng + bot._cg.optAng) - 0.3 - offset;
  			else if ((bot._cg.d && !bot._cg.a) || (bot._cg.s && bot._cg.a && bot._cg.d) || (bot._cg.s && !bot._cg.a && !bot._cg.d))
  				*(float *)bot._game.vax -= AngleDelta(bot._cg.ourvax + bot._cg.accAng, bot._cg.velAng - bot._cg.optAng) + 0.3 + offset;
  	}
  }

  //etsdk
  float AngleNormalize180(float angle) {
  	angle = AngleNormalize360(angle);
  	if (angle > 180.0)
  		angle -= 360.0;
  	return angle;
  }

  float AngleNormalize360(float angle) {
  	return (360.0 / 65536) * ((int)(angle * (65536 / 360.0)) & 65535);
  }

  float AngleDelta(float angle1, float angle2) {
  	return AngleNormalize180(angle1 - angle2);
  }

  //fromweb -- worst findpattern there is :D
  bool bDataCompare(const BYTE *pData, const BYTE *bMask, const char *szMask)
  {
  	for (; *szMask; ++szMask, ++pData, ++bMask)
  		if (*szMask == 'x' && *pData != *bMask)
  			return false;
  	return (*szMask) == NULL;
  }

  DWORD dwFindPattern(DWORD dwAddress, DWORD dwLen, BYTE *bMask, char *szMask)
  {
  	for (DWORD i = 0; i < dwLen; i++)
  		if (bDataCompare((BYTE*)(dwAddress + i), bMask, szMask))
  			return (DWORD)(dwAddress + i);
  	return 0;
  }

	

This is just a little spammer function that worked for either youtube in browser or winamp, had to let the server know what I was listening to....


  BOOL CALLBACK callback_createspam(HWND hwnd, LPARAM lparam)
  {
  	bot._youtube = false;
  	char buf[512];
  	GetWindowTextA(hwnd, buf, sizeof(buf));
  	char* strend = strstr(buf, " &foobar");
  	if (strend){
  		*strend = '\0';
  		sprintf_s(wintitle, sizeof(wintitle), "say \"^7NP: ^o%s\"\n", buf);
  		return false;
  	}
  	strend = strstr(buf, " - Winamp");
  	if (strend){
  		*strend = '\0';
  		int i = 0, j = -1;
  		for (; buf[i] != ' '; ++i);
  		while (buf[i] != '\0') buf[++j] = buf[++i]; buf[j] = '\0';
  		sprintf_s(wintitle, sizeof(wintitle), "say \"^7NP: ^o%s\"\n", buf);
  		return false;

  	}
  	strend = strstr(buf, " - YouTube");
  	if (strend){
  		bot._youtube = true;
  		*strend = '\0';
  		sprintf_s(wintitle, sizeof(wintitle), "say \"^7NP: ^o%s\"\n", buf);
  		return false;
  	}
  	return true;
  }