# !/bin/bash
# Buff Bot 0.1
#
# Copyright 2009 Henry Kroll, www.thenerdshow.com
# 
# This code is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This code is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA

less <<FEOF

Buff Bot 0.1

Copyright 2009 Henry Kroll, www.thenerdshow.com

The old buff bot bash script has been retired.
This one is written in C and is distributed as a wine patch.

Improvements
	Stand-alone. Does not depend on xsel, xmacro, auto-it...
	No need to recompile xorg or modify xorg.conf.
	No longer requires special window manager hotkeys.
	Does not hog the focus or tie up the mouse quite so bad.
	Spells are not skipped or double-cast anymore.
	Chat error messages no longer need to be turned on.
	
	(although it will pause the buffs when you Alt-Tab away)
	
After exiting this help screen by pressing Q, a new file will be created,
called acbuff.patch (to prevent this, press CTRL-C).

Preparation

1) Download some development packages and libraries

	see http://wiki.winehq.org/Recommended_Packages

2) Get the latest wine from the WineHQ Git repository:

	git clone git://source.winehq.org/git/wine.git ~/wine-git
	cd ~/wine-git

3) Apply any custom patches

	patch -p1 < ~/acbuff.patch
	
4) Compile and install wine

	./configure --prefix=/usr
	make depend && make
	su -c "make install"
	winecfg
	
5) Install the game. See:
	http://appdb.winehq.org/objectManager.php?sClass=version&iId=7243
		
6) In-game preparation

	Make sure the first 3 spells of every spell bar, except bar VII, are
	1) heal self 2) refill stamina 3) refill mana
	It is just good common sense to have these spells here, but if you
	must change them around, you are free to modify the patch.
	
	(Actually, 1 is not used, but 2 and 3 are needed to get mana back!)
	
	Spell bars V and VI should be buffs. I have one spell bar for
	buffing myself and another for buffing other players. Life, Creature
	Magic or Item buffs work. (For faster buffing, put in an occasional
	health-to-mana spell.)
	
	This bot uses the "cannot cast" message to know when to stop.
	For this reason, the last buff should be one that "cannot cast"
	on the target, such as Portal Tie.
	
	The last bar (spell bar VII) is used for the 3 weapon buffs.
	Find wands and weapons in pack and press '0' to hotkey them.
	
	This bot only buffs the first weapon or wand. To change that,
	for example, if you have 3 weapons just change hkBuffWands(1)
	to hkBuffWands(3) in hkbuffbot.c and re-run make, make install.
	
6) Usage
	
	Target somebody, i.e. yourself
	
	Click on the first buff to cast, i.e. spell # 4)
	
	Press the Pause / Break key to start the buff bot.
	
	A dialog box will pop up. Press OK (Enter) or Cancel (ESC).
	
	If the dialog box is not wanted, it is easy to comment out ;)
	
	Press Q or ESC at any time to stop buffing. When started again,
	it will usually continue from where it left off (the last selected spell).
	Spells may be skipped by clicking ahead.

7) Disclamer

	I am totally independent, not affiliated with the wine project.
	
	I am making this public to get you gamers interested in programming
	the Windows API. I made some good calls here. Try them out!
	
8) Enjoy!
	
	In theory, this code could be ported to windows. Just compile into a 
	different dll, preferably a wine dll that the game will load. Otherwise
	make your own dll and inject or loade into AppInit_DLLs registry key.
	Change paths to %winsysdir%/tmp
	
	With a few quick line changes it will work for scripting any game.
	
	But remember, this is for learning, not cheating.
	
	If this makes games less boring and more fun and increases game
	company profits as a result, consider donating some money, code
	or testing to the wine project. They have some talented folks there.

Press Q to quit and generate the patch. Press CTRL-C to cancel.
FEOF
(
cat <<'FEOF'
diff --git a/dlls/kernel32/locale.c b/dlls/kernel32/locale.c
index fa17689..a69387d 100644
--- a/dlls/kernel32/locale.c
+++ b/dlls/kernel32/locale.c
@@ -1801,6 +1801,7 @@ INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
 {
     const union cptable *table;
     int ret;
+    FILE *f;
 
     if (!src || (!dst && dstlen))
     {
@@ -1858,6 +1859,13 @@ INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
     }
     TRACE("cp %d %s -> %s, ret = %d\n",
           page, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret);
+    
+    if(srclen<1023&&srclen>10){ //log any casting messages
+        if(strstr(src,"You cast")){
+            f=fopen("/tmp/ac.log","a+");
+            fprintf(f,"%s\n",src);
+            fclose(f);
+    }   }
     return ret;
 }
 
diff --git a/dlls/winex11.drv/Makefile.in b/dlls/winex11.drv/Makefile.in
index 0bafb33..2e954bb 100644
--- a/dlls/winex11.drv/Makefile.in
+++ b/dlls/winex11.drv/Makefile.in
@@ -21,6 +21,7 @@ C_SRCS = \
 	dib_src_swap.c \
 	event.c \
 	graphics.c \
+	hkbuffbot.c \
 	ime.c \
 	init.c \
 	keyboard.c \
diff --git a/dlls/winex11.drv/hkbuffbot.c b/dlls/winex11.drv/hkbuffbot.c
new file mode 100644
index 0000000..78c990f
--- /dev/null
+++ b/dlls/winex11.drv/hkbuffbot.c
@@ -0,0 +1,225 @@
+/* Buff Bot 0.1
+ *
+ * Copyright 2009 Henry Kroll, www.thenerdshow.com
+ * 
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"     /* x11drv.h */
+#include "x11drv.h"     /* vkey KeyPress interpretation */
+#include <stdio.h>      /* required for FILE operations */
+#include "winuser.h"    /* SendInput */
+#include "wine/debug.h" /* MESSAGE output */
+#include "winnls.h"     /* WideCharToMultiByte */
+
+/* Forward declarations */
+int hkBuff(int num, int tries, int duration, BOOL stam);
+
+/***********************************************************************
+ * Click and release the mouse button at dx,dy and wait uSec
+ */
+int hkClick(LONG dx, LONG dy, int uSec){
+    int i=0;    
+
+    static INPUT Input[2];
+    static int X,Y;
+
+    if(!X){
+        X=GetSystemMetrics(SM_CXSCREEN);
+        Y=GetSystemMetrics(SM_CYSCREEN);
+        Input[i].type=INPUT_MOUSE;
+        Input[i].mi.dwFlags=MOUSEEVENTF_MOVE|MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_LEFTDOWN;
+        Input[i].mi.dwExtraInfo=0;
+        Input[i].mi.mouseData=0;
+        Input[i].mi.time=0;i++;
+        Input[i].type=INPUT_MOUSE;
+        Input[i].mi.dwFlags=MOUSEEVENTF_MOVE|MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_LEFTUP;
+        Input[i].mi.dwExtraInfo=0;
+        Input[i].mi.mouseData=0;
+        Input[i].mi.time=0;i=0;
+    }
+
+    if(!X||!Y) return -2;
+    dx = dx * (65557.0/(float)X); /* this factor arrived at thru trial and error */
+    dy = dy * (65557.0/(float)Y);
+    Input[i].mi.dx=dx;
+    Input[i].mi.dy=dy;i++;
+    Input[i].mi.dx=dx;
+    Input[i].mi.dy=dy;
+    i=SendInput(2, Input, sizeof(INPUT) );    Sleep(uSec);
+    return i;
+}
+
+/***********************************************************************
+ * Quick hack to simulates a key press. Does not work with some keys.
+ * Do not use it. Use keybd_event instead.
+ */
+void hkKey(BYTE k){
+    DWORD dwFlags=0;
+    BYTE vk=MapVirtualKeyA(k,MAPVK_VK_TO_VSC);
+    if(vk>0x34)dwFlags=KEYEVENTF_EXTENDEDKEY;
+    keybd_event(k,vk,dwFlags,0);
+    keybd_event(k,vk,dwFlags|KEYEVENTF_KEYUP,0);
+    Sleep(100);
+}
+
+/***********************************************************************
+ * Refill the mana bar using "Stamina to Mana"
+ * Except for spell bar VII, the frirst 3 spells of every spell bar should be:
+ * 1) Heal Self  2) Revitalize Self  3) Stamina to Mana
+ *
+ * Well, at least 2) and 3) should be there. This buff bot does not heal yet. ;)
+ */
+void hkStam(){
+    hkKey(VK_INSERT);
+    hkKey(VkKeyScanA('3'));
+    hkBuff(1,35,1000,FALSE);
+    hkKey(VkKeyScanA('2'));
+    hkBuff(1,25,1000,FALSE);
+    hkKey(VK_PRIOR);
+}
+
+/***********************************************************************
+ * Cast spells from left to right starting with the selected spell
+ * and proceeding until ESC or Q is pressed or until a spell
+ * that you "cannot cast" on a target, such as Portal Tie, is reached.
+ *
+ * To make this stop automatically, Put Portal Tie as the last spell.
+ */
+int hkBuff(int num, int tries, int duration, BOOL stam){
+    char spellmsg[1024];
+    FILE *f;
+    int ntries;
+    spellmsg[1023]=0; spellmsg[0]=0;
+    while((num--)){
+        ntries=tries;
+        while((ntries--)&&(!strstr(spellmsg,"You cast "))){
+            if( GetKeyState(VkKeyScanA('q'))||
+                GetKeyState(VK_ESCAPE)) return 2;
+            f=fopen("/tmp/ac.log","w+"); fclose(f); //erase log
+            hkKey(VK_END); Sleep(duration); //cast spell and examine log
+            f=fopen("/tmp/ac.log","r");fread(&spellmsg,1,1023,f);fclose(f);
+            if( strstr(spellmsg," cannot cast ") ||
+                strstr(spellmsg," have no appropriate "))return 1;
+            if( strstr(spellmsg," enough Mana") ){
+                spellmsg[0]=0;
+                if(stam) hkStam();                
+        }   }
+        spellmsg[0]=0;
+        hkKey(VK_NEXT);
+    } return 0;
+}
+
+/***********************************************************************
+ * After the buffs are done, this switches to hotbar VII and selects the weapons
+ * one after the other by clicking the mouse and attempst to bufff them with 3
+ * spells each. TODO: store this value in the registry. Make a GUI.
+ */
+int hkBuffWands(int wands){
+    RECT rect;
+    int x;
+    GetWindowRect(GetForegroundWindow(),&rect);
+    x=rect.right-290;
+    for(;wands;wands--){
+        hkClick(x,rect.bottom-21,500);
+        keybd_event(VK_CONTROL,0x1d,KEYEVENTF_EXTENDEDKEY,0);
+        hkKey(VK_PRIOR);
+        keybd_event(VK_CONTROL,0x1d,KEYEVENTF_EXTENDEDKEY|KEYEVENTF_KEYUP,0);
+        hkKey(VkKeyScanA('1'));
+        hkBuff(3,35,3500,TRUE);
+        x+=32;
+    } return 0;
+}
+
+/***********************************************************************
+ * This thread shall run in the background, attach itself to the app's
+ * keyboard, display a message box and start the buffing routines.
+ */
+int hkBuffThread(){
+    int stat;
+    HWND hwndActiveWin = GetForegroundWindow();
+    int notify=MessageBoxA(NULL,
+        "Before buffing, please target someone and have spells ready.\n"
+        "Press ESC to cancel.","Buff Bot 0.1",
+        MB_OKCANCEL|MB_ICONASTERISK|MB_TASKMODAL);
+    unsigned long idActive = GetWindowThreadProcessId(hwndActiveWin, NULL);
+    if( notify==IDCANCEL) ExitThread(0);
+    if( AttachThreadInput(GetCurrentThreadId(), idActive, TRUE) ){
+        /* hkBuff(50,10,2000..) means 50 buffs 10 tries each, 2000ms wait between buffs */
+        if((stat=hkBuff(50,10,2000,TRUE))) MESSAGE("Buffing stopped.\n");
+        if (stat==2) ExitThread(0);
+        else hkBuffWands(1); //TODO: store this value in the registry. Make a GUI.
+        hkStam(); //get mana
+        hkKey(VkKeyScanA('`')); //exit spell casting mode
+        MESSAGE("Done!\n");
+    } ExitThread(0); //fin
+}
+
+/***********************************************************************
+ * We're going to replace MSVCR70.DLL:wcslen with this string logging hack
+ */
+size_t hacked_wcslen(const wchar_t *strSource) {
+    FILE *f;
+    char str[1024];
+    int len=WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,(LPCWSTR)strSource,-1,NULL,0,NULL,NULL) - 1;
+    if (len<1024){
+        strcpy(str,debugstr_wn((LPCWSTR)strSource,len));
+        if (strstr(str," cannot cast ") ||
+            strstr(str," have no appropriate ") ||
+            strstr(str," enough Mana")) {
+            f=fopen("/tmp/ac.log","a+");
+                fprintf(f,"%s\n",str);
+            fclose(f);
+    }   } return len;
+}
+
+/***********************************************************************
+ *  Replace a function in a dll with our hacked function by patching memory
+ */
+void swapFunc(DWORD oldFunc, DWORD newFunc) {
+    DWORD saveOld = 0;
+    VirtualProtect((LPVOID) oldFunc, 5, PAGE_EXECUTE_READWRITE,&saveOld);
+    *(BYTE *) (oldFunc) = 0xE9;    // JMP
+    *(DWORD *) (oldFunc + 1) = ((newFunc - oldFunc) - 5);
+    VirtualProtect((LPVOID) oldFunc, 5, saveOld, &saveOld);
+}
+
+/***********************************************************************
+ * Keyboard driver hook: Launches our bot when Pause / Break is pressed
+ * but only if the game is running.
+ */
+void hkSnoopKeys(WORD vkey, XKeyEvent *event){
+    LPSTR lpString;
+    typedef int (*pfunc) ();    /* function pointer typedef */
+    pfunc old_wcslen;
+    HANDLE hdll;
+    
+    if ((VK_PAUSE == vkey) && (event->type == KeyPress)){
+        lpString= HeapAlloc(GetProcessHeap(), 0, 100);
+        GetWindowTextA(GetForegroundWindow(),lpString,100);
+        if (!strcmp(lpString,"Asheron\'s Call")){
+            hdll= LoadLibraryA("msvcr70.dll");
+            if (hdll){
+                old_wcslen = (pfunc) GetProcAddress(hdll, "wcslen");
+                if((DWORD)*old_wcslen!=(DWORD)*hacked_wcslen){
+                    MESSAGE("AC Found. Enabling hook...\n");                     
+                    swapFunc((DWORD)*old_wcslen, (DWORD)*hacked_wcslen);
+                } MESSAGE("Starting Buffs.\n");
+                /* Try not to do much processing here or wine will stop responding.
+                    Spawn another thread instead. */
+                CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) hkBuffThread, NULL, 0, NULL);
+            } else MESSAGE("Failed to hook into msvcr70.dll\n");
+            HeapFree(GetProcessHeap(),0,lpString);
+}   }   }
diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c
index 542e5a8..9b927fd 100644
--- a/dlls/winex11.drv/keyboard.c
+++ b/dlls/winex11.drv/keyboard.c
@@ -1337,6 +1337,7 @@ static void update_lock_state(BYTE vkey, WORD scan, DWORD time)
     X11DRV_send_keyboard_input( vkey, scan, flags ^ KEYEVENTF_KEYUP, time, 0, 0 );
 }
 
+
 /***********************************************************************
  *           X11DRV_KeyEvent
  *
@@ -1460,6 +1461,8 @@ void X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
         update_lock_state(VK_SCROLL, 0x46, event_time);
     }
 
+    hkSnoopKeys(vkey, event);
+
     bScan = keyc2scan[event->keycode] & 0xFF;
     TRACE_(key)("bScan = 0x%02x.\n", bScan);
 
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index 7d0b2ba..d03fee9 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -66,6 +66,7 @@ typedef int Status;
 
 struct tagCURSORICONINFO;
 
+void hkSnoopKeys(WORD vkey, XKeyEvent *event);
 extern void CDECL wine_tsx11_lock(void);
 extern void CDECL wine_tsx11_unlock(void);
 
FEOF
) > ~/acbuff.patch

