#pragma once #include "stdafx.h" #include //Globals //Stored team data u32 teamFlags; //Number of flags team has u32 playersInTeam; //For drawing the amount of players in the team const char *lastTeam; //For comparing so you know whether to draw a new team header u32 playerlist[512]; //The playerlist, for temporary storage so sorting is easy char playerBuffer[1024]; //For properly parsed name u32 playerFlags; //Number of flags that the player has u32 curStruct; //Current list entry u32 ListCapacity; //Number of entries in the list u32 NumPlayers; //Number of players in the arena u32 ListWidth; //The current parse constant for the entry u32 vertAlign; //The vertical alignment for the next entry u32 fontclass; //The pointer to the class used for drawing stuff u32 alignconst1; //Another alignment thing u32 entitiesdrawn; //Number of entities actually drawn to the screen (for use with ListCapacity) u32 index; //Index for the list (in player terms) u32 ofs; //Offset from the top of the list to start (due to scrollbar) u32 PixelWidth; //For the getpixelwidth function u32 TeamNameAlign; //Alignment offset for the team name //These vars are to be able to select players and for the scrollbar u32 *numteams; //To calculate offsets u32 *teamofs; //The number of teams up to the point where the teamlist is drawn u32 *numtotalteams; //The total number of teams present in the game bool offsetlist[1024]; //List of where the teams are in the table u32 oldindex; //For the declspec hckSelect u32 newindex; //For the declspec hckSelect u16 lasthighlight; //The playerid of the player last highlighted RECT selectRect; //For selected players ////////////////////////////////////////////////////////////////////// // Helper asm functions u32 GetPixelLength(u32 me, const char *thestring) { _asm { mov ecx, [me] mov [PixelWidth], 0 mov edi, [thestring] mov dl, byte ptr [edi] xor eax, eax xor esi, esi test dl, dl je endofloop mov ebx, dword ptr [ecx+8] startloop: movsx edx, dl sub edx, ebx js invalidchar mov ebp, dword ptr [ecx+4] movsx ebp, word ptr [ebp+0xA4] cmp edx, ebp jge invalidchar movsx edx, word ptr [ecx+edx*2+0x210] jmp skipinvalidchar invalidchar: mov edx, 128 sub edx, ebx movsx edx, word ptr [ecx+edx*2] skipinvalidchar: add [PixelWidth], edx inc edi mov dl, byte ptr [edi] inc eax test dl, dl jnz startloop endofloop: } return PixelWidth; } ////////////////////////////////////////////////////////////////////// // Helper c functions u32 AlignString(const char *thestring, u32 lWidth, u32 me) { u32 Width = GetPixelLength(me, thestring); u32 Result = lWidth / 2; Result -= Width / 2; return Result; } ////////////////////////////////////////////////////////////////////// // DrawPlayer - Draws a player entry void DrawPlayer(u32 me, char *buf, s32 colourbase) { _asm { mov esi, [me] } //Make string suitable for display (so it doesn't overlap stuff) _asm { push [ListWidth] mov ecx, dword ptr [esi+0x58] mov eax, [curStruct] add eax, 0x32 push eax mov eax, 0x4047E0 call eax inc eax push eax mov eax, [curStruct] add eax, 0x32 push eax mov eax, [buf] push eax mov eax, 0x476970 call eax add esp, 0xC } //Setup colour _asm { mov edi, [colourbase] neg edi sbb edi, edi and edi, 4 } //Check to see if player is selected _asm { mov eax, [index] mov ecx, 0x4BF838 mov ecx, [ecx] mov ecx, [ecx+0x586] mov eax, [ecx+eax*4] test eax, eax ;safety check jz skipselected movzx eax, word ptr [eax] movzx ecx, word ptr [lasthighlight] cmp eax, ecx ;dword ptr [esi+0x40] mov ebx, 0x13 jnz skipselected mov ecx, [ListWidth] lea eax, dword ptr [ebx-2] lea edx, dword ptr [eax+ecx+4] lea ebx, [selectRect] mov [ebx], eax mov eax, dword ptr [esi+0x4C] mov [ebx+8], edx push dword ptr [vertAlign] pop dword ptr [ebx+4] mov ecx, dword ptr [eax+4] movsx edx, word ptr [ecx+0xAA] mov eax, dword ptr [esi+0x38] add edx, [vertAlign] mov [ebx+12], edx mov eax, dword ptr [eax] push ebx mov ecx, 0x4BF838 mov ecx, [ecx] xor edx, edx mov dx, word ptr [ecx+edi*2+0x42] mov ecx, dword ptr [esi+0x38] push edx call dword ptr [eax+0x28] mov edi, 0xE skipselected: } //Draw it! _asm { mov edx, dword ptr [esi+0x38] mov ecx, dword ptr [esi+0x4C] push 3 push [vertAlign] push 0x13 push edx push edi mov eax, [buf] push eax mov eax, 0x404610 call eax } //NOTE: 0x13 is an alignment constant //See if player is carrying flags _asm { lea ecx, [playerlist] mov eax, [index] mov ecx, [ecx+eax*4] ;ecx <- Player structs mov eax, [ecx+0xDE] ;ecx <- # flags test eax, eax jz skipflagdraw push 10 push [buf] push eax call itoa add esp, 0xC mov edx, dword ptr [esi+0x38] mov ecx, dword ptr [esi+0x4C] push 3 mov eax, [vertAlign] add eax, [alignconst1] push eax push 10 push edx push 6 mov eax, [buf] push eax mov eax, 0x404610 call eax skipflagdraw: } //See if the player has a crown _asm { lea ecx, [playerlist] mov eax, [index] mov ecx, [ecx+eax*4] ;ecx <- Player structs mov eax, [ecx+0xE6] ;ecx <- bool crown test eax, eax jz skipdrawcrown push 'k' ;Push character buffer onto stack mov edx, esp mov ecx, dword ptr [esi+0x4C] push 3 mov eax, [vertAlign] push eax push 9 push dword ptr [esi+0x38] push 2 push edx mov eax, 0x404610 call eax add esp, 4 skipdrawcrown: } _asm { mov edi, [curStruct] mov ecx, edi mov eax, 0x44F510 call eax test eax, eax je skipspec mov ecx, [alignconst1] mov eax, dword ptr [esi+0x38] push 3 mov ebx, [vertAlign] lea edi, dword ptr [ecx+ebx] mov ecx, [fontclass] push edi ;lea edx, dword ptr [ebx-0x2] mov edx, 0x11 ;0x13(constant) - 0x2 push edx push eax push 5 push 0x48B37C mov eax, 0x404590 call eax mov ecx, [curStruct] mov eax, dword ptr [ecx+0xE6] test eax, eax je skipmedal mov edx, dword ptr [esi+0x38] push 3 push edi add ebx, -7 push ebx push edx jmp halfcall skipspec: mov eax, dword ptr [edi+0xE6] test eax, eax je skipmedal mov eax, [alignconst1] mov ecx, dword ptr [esi+0x38] push 3 add eax, ebp push eax add ebx, -2 push ebx push ecx halfcall: mov ecx, [fontclass] push 2 push 0x48B39C mov eax, 0x404590 call eax skipmedal: } } ////////////////////////////////////////////////////////////////////// // DrawTeam - Draws a team entry void DrawTeam(u32 me, char* buf) { _asm { mov esi, [me] } //Convert to ASCII _asm { push 10 push [buf] push [playersInTeam] call itoa add esp, 0xC } //Draw the amount of players in the team _asm { ;Calculate position push [buf] push [esi+0x4C] call GetPixelLength add esp, 0x8 push eax mov ecx, [ListWidth] add ecx, 0x13 sub ecx, eax mov eax, ecx mov edx, dword ptr [esi+0x38] mov ecx, dword ptr [esi+0x4C] push 3 push [vertAlign] push eax push edx push 0x5 push [buf] mov eax, 0x404610 call eax } //Align the string _asm { push [esi+0x4C] push [ListWidth] mov eax, [curStruct] add eax, 0x6 push eax call AlignString add esp, 0xC add eax, 0x13 mov [TeamNameAlign], eax } //Make string suitable for display (so it doesn't overlap stuff) _asm { mov ecx, [ListWidth] sub ecx, eax pop eax push ecx mov ecx, dword ptr [esi+0x58] mov eax, [curStruct] add eax, 0x6 push eax mov eax, 0x4047E0 call eax inc eax push eax mov eax, [curStruct] add eax, 0x6 push eax mov eax, [buf] push eax mov eax, 0x476970 call eax add esp, 0xC } //Draw the teamname _asm { mov edx, dword ptr [esi+0x38] mov ecx, dword ptr [esi+0x4C] push 3 push [vertAlign] push [TeamNameAlign] push edx push 0x5 push [buf] mov eax, 0x404610 call eax } _asm { push 10 push [buf] push [teamFlags] call itoa add esp, 0xC } //Draw the number of flags team has _asm { mov eax, [teamFlags] test eax, eax jz skipdrawflags ;Calculate position push [buf] push [esi+0x4C] call GetPixelLength add esp, 0x8 mov ecx, [TeamNameAlign] sub ecx, eax sub ecx, 7 mov eax, ecx mov edx, dword ptr [esi+0x38] mov ecx, dword ptr [esi+0x4C] push 3 push [vertAlign] push eax push edx push 6 push [buf] mov eax, 0x404610 call eax skipdrawflags: } } //////////////////////////////////////////////////////////////////////////////////////////// /// This file handles the drawing of the playerlist (now teamlist :)) void _declspec(naked) hckTeamList() { //Initialize variables _asm { mov ecx, 0x4BF838 mov edi, [ecx] mov ecx, dword ptr [edi+0x586] mov eax, dword ptr [ecx+0x3E8] mov edx, dword ptr [esi+0x48] mov [ListCapacity], edx mov [NumPlayers], eax mov [ListWidth], 0x83 mov [vertAlign], 0x20 mov [alignconst1], 2 mov [lastTeam], 0 mov eax, [numteams] mov [eax], 0 mov eax, [teamofs] mov [eax], 0 ;Start according to the scrollbar mov eax, [edi+0x56E] test eax, eax jz noclass mov eax, [eax+0x44] ;eax <- amount scrolled by noclass: mov [ofs], eax mov [index], 0 mov [entitiesdrawn], 0 ;Clear offsetlist lea edi, [offsetlist] xor eax, eax mov ecx, 1024 rep stosb ;Clear playerlist lea edi, [playerlist] xor eax, eax mov ecx, 512 rep stosd ;Clear rest of stored playerlist mov edi, 0x4bf838 mov edi, [edi] mov edi, dword ptr [edi+0x586] xor eax, eax mov ecx, 0xFA mov edx, dword ptr [edi+0x3E8] sub ecx, edx shl edx, 2 add edi, edx rep stosd ;Sort the stored playerlist push 0x444D30 push [NumPlayers] mov eax, 0x4bf838 mov eax, [eax] mov eax, dword ptr [eax+0x586] push eax mov eax, 0x407800 call eax add esp, 12 ;Initialize playerlist push esi mov esi, 0x4bf838 mov esi, [esi] mov esi, dword ptr [esi+0x586] lea edi, [playerlist] mov ecx, [NumPlayers] rep movsd pop esi } //Start of loop _asm { startloop: mov eax, 0x4bf838 mov edx, [eax] lea edi, dword ptr [playerlist] mov eax, [index] mov eax, dword ptr [edi+eax*4] mov ebx, dword ptr [edx+0xD9F0] mov [curStruct], eax test ebx, ebx je skipcolourbase xor edi, edi mov di, word ptr [eax+4] mov edx, dword ptr [edx+0xD9EC] sub di, word ptr [edx+4] neg di sbb edi, edi inc edi skipcolourbase: } //Update globals _asm { push [esp+0x18] pop [fontclass] } //Check to see if a new team is being drawn _asm { mov ecx, [curStruct] add ecx, 6 mov eax, [lastTeam] mov [lastTeam], ecx test eax, eax jz newteam push ecx push ecx push eax call dword ptr ds:[lstrcmpi] pop ecx test eax, eax jz drawThePlayer newteam: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Count teams, even if we skip them mov eax, [numteams] inc dword ptr [eax] ;Add team into offset list (even if it's skipped) push edi ;save colour flags lea edi, [offsetlist] mov eax, [numteams] mov eax, [eax] mov edx, [index] add eax, edx add edi, eax mov byte ptr [edi], 1 pop edi ;If we're scrolled up, don't need to draw this cmp [ofs], 0 jnz skippedateam ;New team, find the amount of players in the team mov [playersInTeam], 1 pushad lea edi, dword ptr [playerlist] mov esi, [index] inc esi mov ebx, ecx mov edx, dword ptr [edi+esi*4] ;edx <- curStruct, ebx <- lastTeam, esi <- index, onstack = numPlayers startloop2: ;Check if we've any players left mov eax, [NumPlayers] cmp esi, eax jge endloop ;Compare add edx, 6 ; Get teamname push edx push ebx call dword ptr ds:[lstrcmpi] test eax, eax jnz endloop inc esi inc [playersInTeam] mov edx, dword ptr [edi+esi*4] jmp startloop2 endloop: popad } //Get number of flags team has _asm { mov ecx, 0x4BF838 mov ecx, [ecx] mov ecx, dword ptr [ecx+0x5A2] mov eax, [curStruct] movsx eax, word ptr [eax+4] push eax mov eax, 0x43A290 call eax mov [teamFlags], eax } //Otherwise we draw the team first _asm { lea eax, [playerBuffer] push eax push esi call DrawTeam add esp, 0x8 inc [entitiesdrawn] ;Update the vertical alignment mov edx, dword ptr [esi+0x4C] mov eax, dword ptr [edx+0x4] movsx ecx, word ptr [eax+0xAA] add [vertAlign], ecx } //If going from Team -> Player, need to check if there's space _asm { mov eax, [ListCapacity] cmp eax, [entitiesdrawn] jle endloop2 } //Then draw the player _asm { jmp drawThePlayer skippedateam: ;We've skipped drawing a team, and want to draw a player now. ;Decrement the offset, and get on with it. dec [ofs] ;We also have to increase the team offset mov eax, [teamofs] inc dword ptr [eax] drawThePlayer: ;If we're scrolled up, don't need to draw this cmp [ofs], 0 jnz scrollbarskip push edi lea eax, [playerBuffer] push eax push esi call DrawPlayer add esp, 0xC inc [entitiesdrawn] ;Update the vertical alignment mov edx, dword ptr [esi+0x4C] mov eax, dword ptr [edx+0x4] movsx ecx, word ptr [eax+0xAA] add [vertAlign], ecx } _asm { jmp endscrollbarskip scrollbarskip: ;We've skipped an entry due to the offset, ;so decrement the offset and continue. dec [ofs] endscrollbarskip: ;Check to see if we've run out of players mov eax, [index] mov ecx, [NumPlayers] inc eax mov [index], eax cmp eax, ecx jge endloop2andindex ;Check to see if we've run out of space mov eax, [entitiesdrawn] cmp eax, [ListCapacity] jl startloop endloop2: ;Increase index since it wasn't done in the loop inc dword ptr [index] endloop2andindex: } //Now we need to find the total amount of teams, for statistical reference _asm { ;Initialize variables mov [lastTeam], 0 mov esi, [index] mov eax, [numteams] mov eax, [eax] mov edi, [numtotalteams] mov dword ptr [edi], eax startendloop: cmp esi, [NumPlayers] jge realendloop ;No more players -> no more teams ;Get the player's struct lea ecx, [playerlist] mov ecx, [ecx+esi*4] ;Is this player in a new team? add ecx, 6 mov eax, [lastTeam] mov [lastTeam], ecx test eax, eax jz anothernewteam push ecx push eax call dword ptr ds:[lstrcmpi] test eax, eax jz continueendloop anothernewteam: ;Add team into offset list lea ecx, [offsetlist] mov eax, [edi] ;num teams add eax, esi ;esi <- index add ecx, eax mov byte ptr [ecx], 1 ;Increase the amount of teams spotted inc dword ptr [edi] continueendloop: ;Increase the index inc esi ;Continue the loop jmp startendloop realendloop: ;Return execution, after having drawn the list :) mov eax, 0x44D91F jmp eax } } ///////////////////////////////////////////////////////////////////////////// // Left mouse button selection hook void __declspec(naked) hckSelect() { _asm { ;eax <- player index mov [newindex], eax mov [oldindex], eax pushad } for (index=0 ; index <= oldindex ; index++) { if (offsetlist[index]) newindex--; } _asm { ;Obtain the new player's id mov eax, [newindex] mov edx, 0x4BF838 mov edx, [edx] mov ecx, [edx+0x586] mov eax, [ecx+eax*4] test eax, eax jz invalidguy movzx eax, word ptr [eax] mov [lasthighlight], ax invalidguy: ;Continue popad mov eax, [newindex] mov dword ptr [esi+0x3C], eax mov ecx, 0x4BF838 mov ecx, [ecx] push 0x44C870 retn } } ///////////////////////////////////////////////////////////////////////////// // Hook to correct the credentials given for scrolling the scrollbar // (implemented due to 0 space) const u32 scrolljmp = 0x44BF0E; const u32 scrolljmp2 = 0x44D6AF; void __declspec(naked) hckScrollDown() { _asm { ;This hook performs two functions, so check the return address add esp, 4 cmp dword ptr [esp-4], 0x44D6AA je checkhook cmp dword ptr [esp-4], 0x44C1D3 je pagescrollhook scrolldownhook: ;Perform the overwritten code mov eax, dword ptr [eax+0x3E8] ;eax <- number of players in list ;Add the total number of teams in the list mov edx, [numtotalteams] add eax, [edx] ;Use this to set the correct flags sub eax, dword ptr [ecx+0x40] ;Jump back to where we should be jmp [scrolljmp] pagescrollhook: sub esp, 4 ;Perform original instructions mov eax, 0x4BF838 mov eax, [eax] mov edx, [eax+0x586] ;Update the player id pushad rehandle: cmp ebp, 0xFA jge overflowedplayer test ebp, ebp ;The first highlight is always a team js incrementplayer ;We're below 0 mov eax, [edx+ebp*4] test eax, eax jz invalidplayer mov ax, word ptr [eax] mov [lasthighlight], ax jmp endhandle invalidplayer: ;If the tick isn't above the top, we need to back track test ebp, ebp jns backtrackplayer ;Otherwise, we need to increase it incrementplayer: inc ebp jmp rehandle backtrackplayer: dec ebp jmp rehandle endhandle: ;Keep the new ebp mov [esp+8], ebp popad ;Perform the overwritten code mov dword ptr [esi+0x3C], ebp mov eax, dword ptr [edx+0x3E8] ;eax <- number of players in list ;Add the total number of teams in the list mov ecx, [numtotalteams] add eax, [ecx] ;Jump back to where we should be retn overflowedplayer: ;Set player to last player mov ebp, [edx+0x3E8] jmp rehandle checkhook: ;Perform the overwritten code mov eax, dword ptr [edx+0x3E8] ;eax <- number of players in list ;Add the total number of teams in the list mov edx, [numtotalteams] add eax, [edx] ;Use this to set the correct flags mov edi, dword ptr [esi+0x48] ;Subtract to set the flags sub eax, edi ;Jump back to where we should be jmp [scrolljmp2] } } ///////////////////////////////////////////////////////////////////////////// // Hook to find the currently selected player // (implemented due to 0 space) void __declspec(naked) hckGetSel() { _asm { ;Setup stack push ebx push esi ;Prepare the loop mov esi, 0x4BF838 mov esi, [esi] mov esi, [esi+0x586] xor edx, edx mov bx, word ptr [lasthighlight] ;Begin searching for the associated player nextguy: ;Get the current player mov eax, [esi+edx*4] test eax, eax jz notfound ;Is this the guy? inc edx cmp word ptr [eax], bx jne nextguy ;Otherwise, we've got him jmp found notfound: xor eax, eax found: pop esi pop ebx retn } }