New MQ in-game text display code

A forum for feature requests/discussions and user submitted patches that improve MQ2

Moderator: MacroQuest Developers

Lax
We're not worthy!
We're not worthy!
Posts: 3524
Joined: Thu Oct 17, 2002 1:01 pm
Location: ISBoxer
Contact:

New MQ in-game text display code

Post by Lax » Sun Oct 26, 2003 8:26 pm

I got us some code to prevent GM's from discovering MQ usage based on /report or chat window snooping. Along with this, the code lets us embed STML sequences directly (color, wndnotification tags, etc). This means no relying on user defined colors for things like ... consider, which should use real colors :)

Code to insert will follow, just excited and thought I'd share.
Last edited by Lax on Sun Oct 26, 2003 11:26 pm, edited 1 time in total.
Lax Lacks
Master of MQ2 Disaster
Purveyor of premium, EULA-safe MMORPG Multiboxing Software
* Multiboxing with ISBoxer: Quick Start Video
* EQPlayNice, WinEQ 2.0

fwiggles
a hill giant
a hill giant
Posts: 161
Joined: Mon Jun 17, 2002 8:29 pm

Post by fwiggles » Sun Oct 26, 2003 9:21 pm

sweeeeeet
[color=red]Latest survey shows that 3 out of 4 people make up 75% of the world's population.[/color]

Lax
We're not worthy!
We're not worthy!
Posts: 3524
Joined: Thu Oct 17, 2002 1:01 pm
Location: ISBoxer
Contact:

Post by Lax » Sun Oct 26, 2003 11:17 pm

Ok. Here we go... still work to do but here's the code:

MQ.h (make sure these parts are up to date):

Code: Select all

#define CWS_VSCROLL 0x1
#define CWS_HSCROLL 0x2
#define CWS_TITLE   0x4
#define CWS_CLOSE   0x8
#define CWS_MINIMIZE  0x20
#define CWS_BORDER      0x40
#define CWS_RESIZEALL   0x200
#define CWS_TRANSPARENT  0x400
#define CWS_NOMOVE     0x8000

typedef struct _EQCHATMGR
{
/*0x000*/ struct _EQCHATWINDOW* ChatWnd[0x2a];
/*0x080*/ DWORD NumWindows; 
/*0x084*/ DWORD ActiveWindow; // CChatManager::GetActiveChatWindow
/*0x088*/ DWORD LockedWindow; // CChatManager::GetActiveChatWindow
/*0x08c*/ DWORD Unknown0x08c;
/*0x090*/ DWORD ChannelMap[0x2a];   // channel map
/*0x138*/ DWORD Unknown0x138;
/*0x13c*/ DWORD Unknown0x13c;
/*0x140*/ DWORD Unknown0x140;
/*0x144*/ LPVOID ChatMenu;
/*0x148*/ DWORD Unknown0x148; 
/*0x14c*/ DWORD Unknown0x14c;
/*0x150*/ DWORD Unknown0x150;
/*0x154*/ DWORD Unknown0x154;
/*0x158*/ LPVOID MeleeMenu; 
/*0x15c*/ DWORD Unknown0x15c;
/*0x160*/ LPVOID SpellMenu;
/*0x164*/ DWORD Unknown0x164;
/*0x168*/ DWORD Unknown0x168; 
/*0x16c*/ LPVOID ChannelMenu;
/*0x170*/ LPVOID ChatMenu2;
/*0x174*/ LPVOID FilterMenu;
/*0x178*/ DWORD Unknown0x178; 
/*0x17c*/ DWORD Unknown0x17c;
/*0x180*/ DWORD Unknown0x180;
/*0x184*/ LPVOID HitsModeMenu;
/*0x188*/ LPVOID MeleeMenu2; 
/*0x18c*/ LPVOID HitsModeMenu2;
/*0x190*/ DWORD Unknown0x190;
/*0x194*/ LPVOID HitsModeMenu3;
/*0x198*/ DWORD Unknown0x198; 
/*0x19c*/ LPVOID HitsModeMenu4;
/*0x1a0*/ DWORD Unknown0x1a0;
/*0x1a4*/ LPVOID HitsModeMenu5;
/*0x1a8*/ DWORD Unknown0x1a8; 
/*0x1ac*/ LPVOID HitsModeMenu6;
/*0x1b0*/ DWORD Unknown0x1b0;
/*0x1b4*/ DWORD Unknown0x1b4; // CChatManager__UpdateTellMenus
/*0x1b8*/ DWORD Unknown0x1b8; 
/*0x1bc*/ DWORD Unknown0x1bc; // CChatManager__UpdateTellMenus
/*0x1c0*/ DWORD Unknown0x1c0; 
/*0x1c4*/ DWORD Unknown0x1c4; // CChatManager__UpdateTellMenus // raid
/*0x1c8*/ DWORD Unknown0x1c8; 
/*0x1cc*/ DWORD Unknown0x1cc; // CChatManager__UpdateTellMenus
/*0x1d0*/ DWORD Unknown0x1d0; // CChatManager__UpdateTellMenus
/*0x1d4*/ DWORD Unknown0x1d4; // CChatManager__UpdateTellMenus
/*0x1d8*/ DWORD Unknown0x1d8; 
/*0x1dc*/ DWORD Unknown0x1dc;
/*0x1e0*/ DWORD Unknown0x1e0;
/*0x1e4*/ BYTE  Unknown0x1e4[0x1c];
/*0x200*/ DWORD Unknown0x200;
/*0x204*/ DWORD Unknown0x204;
/*0x208*/
} EQCHATMGR, *PEQCHATMGR;

// Actual size 0x248 10-9-2003
typedef struct _EQCHATWINDOW
{
/*0x000*/ struct _CSIDLWND Wnd;
/*0x138*/ DWORD  Unknown0x138;
/*0x13c*/ LPVOID ChatManager; // --> 0x13c
/*0x140*/ struct _CSIDLWND* InputWnd;
/*0x144*/ struct _CSIDLWND* OutputWnd;
/*0x148*/ DWORD Unknown0x140;
/*0x14c*/ DWORD Unknown0x144;
/*0x150*/ BYTE Unknown0x148;
/*0x151*/ BYTE Unknown0x149[0x3f];
/*0x18c*/ DWORD Unknown0x188;
/*0x190*/ DWORD Unknown0x18c;
/*0x194*/ struct _CXSTR *CommandHistory[0x28]; // ->0x198
/*0x238*/ DWORD Unknown0x230; // CChatWindow::HistoryBack/forward .. maybe total or current history lines
/*0x23c*/ DWORD Unknown0x234; // CChatWindow::HistoryBack/forward .. maybe total or current history lines
/*0x240*/ DWORD FontSize; // 
/*0x244*/ DWORD Unknown0x23C; 
/*0x248*/
} EQCHATWINDOW, *PEQCHATWINDOW;
EQLib.h:

Code: Select all

extern VOID SetCXSTRText(PCXSTR pCXStr, PCHAR Text);
extern VOID Chat(PCHAR Text, DWORD Color);

extern DWORD *EQADDR_CHATMANAGER;
extern DWORD EQADDR_CCHATMANAGERGETRGBAFROMINDEX;
extern DWORD EQADDR_CXSTRCONSTRUCTOR;
extern DWORD EQADDR_CXSTRFREE;
extern DWORD EQADDR_APPENDSTML;
extern DWORD EQADDR_CCHATMANAGERCREATECHATWINDOW;
EQLib_Main.cpp

Code: Select all

	GetPrivateProfileString("Class Locations","ClassChatManager","0",szBuffer,MAX_STRING,ClientINI);			EQADDR_CHATMANAGER = (PDWORD)strtoul(szBuffer,NULL,16); 

	GetPrivateProfileString("Function Locations","CXStrConstructor","0",szBuffer,MAX_STRING,ClientINI);   EQADDR_CXSTRCONSTRUCTOR = (DWORD)strtoul(szBuffer,NULL,16);
	GetPrivateProfileString("Function Locations","CXStrFree","0",szBuffer,MAX_STRING,ClientINI);   EQADDR_CXSTRFREE = (DWORD)strtoul(szBuffer,NULL,16);
	GetPrivateProfileString("Function Locations","CStmlWndAppend","0",szBuffer,MAX_STRING,ClientINI);   EQADDR_APPENDSTML = (DWORD)strtoul(szBuffer,NULL,16);
	GetPrivateProfileString("Function Locations","CChatManagerCreateChatWindow","0",szBuffer,MAX_STRING,ClientINI);   EQADDR_CCHATMANAGERCREATECHATWINDOW = (DWORD)strtoul(szBuffer,NULL,16);
	GetPrivateProfileString("Function Locations","CChatManagerGetRGBAFromIndex","0",szBuffer,MAX_STRING,ClientINI);   EQADDR_CCHATMANAGERGETRGBAFROMINDEX = (DWORD)strtoul(szBuffer,NULL,16);
NEW FILE EQLib_UI.cpp (maybe we can get to putting all the stuff directly related to user interface here, and/or break it up more later)

Code: Select all

#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x510
#define DIRECTINPUT_VERSION 0x800

#if !defined(CINTERFACE)
#error /DCINTERFACE
#endif

#define DBG_SPEW

#include <stdio.h>
#include <stdlib.h>
#include "dinput.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <winsock.h>
#include "MQ.h"
#include "EQLib.h"


DWORD EQADDR_CCHATMANAGERGETRGBAFROMINDEX=0;
DWORD EQADDR_CXSTRCONSTRUCTOR=0;
DWORD EQADDR_CXSTRFREE=0;
DWORD EQADDR_APPENDSTML=0;
DWORD EQADDR_CCHATMANAGERCREATECHATWINDOW=0;

DWORD *EQADDR_CHATMANAGER=(DWORD*)0;

PEQCHATWINDOW MQChatWnd=0;

void CChatWindow__AddOutputText(PEQCHATWINDOW pWnd, const char *Text, int RGBA=0)
{
	if (!pWnd || !Text)
		return;

	char out[MAX_STRING];
	DWORD saddr	=(DWORD)&out[0];
	if (RGBA)
		sprintf(&out[0],"<c \"#%08X\">%s</c><br>",RGBA,Text);
	else
		sprintf(&out[0],"%s<br>",Text);

	PCXSTR stri=0;
	DWORD outwnd=(DWORD)pWnd->OutputWnd;
	__asm{
		push ecx;
		push eax;
		push saddr;
		lea ecx, [stri];
		call [EQADDR_CXSTRCONSTRUCTOR];
		pop eax;
		pop ecx;
	};

	__asm{
		push eax;
		push ecx;
		push esi;
		push edx;
		push [stri];
		push [stri];
		mov ecx, outwnd;
		call [EQADDR_APPENDSTML];
		pop edx;
		pop esi;	
		pop ecx;
		pop eax;
	};
	__asm{
		push eax;
		push ecx;
		push [stri];
		mov ecx, [stri];
		call [EQADDR_CXSTRFREE];
		pop ecx;
		pop eax;
	};


	// set vertical scroll
	DWORD VSMax=pWnd->OutputWnd->VScrollMax;
	DWORD SetVScrollPos=(DWORD)pWnd->OutputWnd->pvfTable->SetVScrollPos;
	__asm
	{
		push eax;
		push ecx;
		push VSMax;
		mov ecx, outwnd;
		call [SetVScrollPos];
		pop ecx;
		pop eax;
	};


}



void MakeChatWindow()
{
	// find where this window would go
	PEQCHATMGR pChatManager= *(PEQCHATMGR*)EQADDR_CHATMANAGER;
	for (int i = 0 ; i < 0x20 ; i++)
	{
		if (!pChatManager->ChatWnd[i])
			goto chatwndfound;
	}
	// cant!
	return;
chatwndfound:
	__asm{
		push eax;
		push ecx;
		mov ecx, pChatManager;
		call [EQADDR_CCHATMANAGERCREATECHATWINDOW];
		pop ecx;
		pop eax;
	};
	if (!pChatManager->ChatWnd[i])
	{
		DebugSpewAlways("New chat window not where we think it should be...");
		return;
	}
	MQChatWnd=pChatManager->ChatWnd[i];
	// now remove it from the chat manager! =)
	pChatManager->ChatWnd[i]=0;
	pChatManager->NumWindows--;
	pChatManager->ActiveWindow=0;
	// should be ok now, set style etc
	MQChatWnd->Wnd.WindowStyle=CWS_TITLE|CWS_MINIMIZE|CWS_BORDER|CWS_RESIZEALL;
	SetCXSTRText(MQChatWnd->Wnd.WindowText,"MQ");
}



void Chat(const char *Text, int Color)
{
	PEQCHATMGR pChatManager= *(PEQCHATMGR*)EQADDR_CHATMANAGER;
	if (!MQChatWnd)
		MakeChatWindow();
	
	if (!MQChatWnd)
		return;

	CChatWindow__AddOutputText(MQChatWnd,Text,Color);
}

EQLib_Utilities.cpp

Code: Select all

VOID WriteChatColor(PCHAR Line, DWORD Color) {
	if (!gbInGame) return;
	if (gFilterMacro != FILTERMACRO_NONE) {
	PEQCHATMGR pChatManager=*(PEQCHATMGR*)EQADDR_CHATMANAGER;
	__asm
	{
		push eax;
		push ecx;
		mov ecx, pChatManager;
		push Color;
		call [EQADDR_CCHATMANAGERGETRGBAFROMINDEX];
		mov [Color], eax;
		pop ecx;
		pop eax;
	};
	char out[MAX_STRING];
	int i = 0;
	int o = 0;
	while(Line[i]!=0)
	{
		switch(Line[i])
		{
		case '&':
			out[o++]='A';
			out[o++]='M';
			out[o++]='P';
			out[o++]=';';
			break;
		case '%':
			out[o++]='P';
			out[o++]='C';
			out[o++]='T';
			out[o++]=';';
			break;
		case '<':
			out[o++]='L';
			out[o++]='T';
			out[o++]=';';
			break;
		case '>':
			out[o++]='G';
			out[o++]='T';
			out[o++]=';';
			break;
		case '"':
			out[o++]='Q';
			out[o++]='U';
			out[o++]='O';
			out[o++]='T';
			out[o++]=';';
			break;
		default:
			out[o++]=Line[i];
			break;
		}
		i++;
	}

	Chat(Line,Color);
        }
        else if (gTelnetServer && gTelnetConnection) {
		TelnetServer_Write(Line);
	}
};
EQGAME.INI

Code: Select all

[Function Locations] 
CXStrConstructor=005372F0
CXStrFree=5372F0
CStmlWndAppend=55BEA0
CChatManagerCreateChatWindow=4D2024
CChatManagerGetRGBAFromIndex=4D008A

[Class Locations] 
ClassChatManager=00777418
Lax Lacks
Master of MQ2 Disaster
Purveyor of premium, EULA-safe MMORPG Multiboxing Software
* Multiboxing with ISBoxer: Quick Start Video
* EQPlayNice, WinEQ 2.0

Lax
We're not worthy!
We're not worthy!
Posts: 3524
Joined: Thu Oct 17, 2002 1:01 pm
Location: ISBoxer
Contact:

Post by Lax » Sun Oct 26, 2003 11:25 pm

Pretty sure that's everything. The changes to WriteChatColor force the new window to be used with the old function calls, and still use the same old colors.

Convert to using VOID Chat(PCHAR Text, DWORD Color) if desired. Limitations.. the Text must be "STML safe" which means extraneous &<>"% must be converted to the standard HTML safe text (see WriteChatColor changes). Color is RGBA as EQ calls it, ARGB as MQ calls it, don't care myself. 0xff0000 is red, 0x00ff00 is green, 0x0000ff is blue, easy enough. To put colors wherever you want.. <c "#ff0000">text</c> for red, and so on. If you want to colorize it yourself, send "0" as the color (yes that disallows the blackest color. sue me).. otherwise, the given color will be applied around the entire text. Should make a function to go along with this and WriteChatColor that does the STML safe conversion and uses RGBA color parameter instead of user-defined... that's TODO.

Also TODO, if you unload MQ and reload it, another window is created each time. Closing the existing window is not allowed because it causes a crash, so the close button is disabled. To make MQ reuse the existing window we'll have to search the window tree for our window.

Let me know if I forgot something required to make it compile
Lax Lacks
Master of MQ2 Disaster
Purveyor of premium, EULA-safe MMORPG Multiboxing Software
* Multiboxing with ISBoxer: Quick Start Video
* EQPlayNice, WinEQ 2.0

User avatar
Clone39
a ghoul
a ghoul
Posts: 91
Joined: Mon Jul 07, 2003 7:26 pm
Location: Montreal, Canada

Post by Clone39 » Mon Oct 27, 2003 12:10 am

Nice work on that.. can't wait for it to be CVSed.. only one question... is the new window screenshot aware? will it show up if i take a screen shot?

Clone39

Lax
We're not worthy!
We're not worthy!
Posts: 3524
Joined: Thu Oct 17, 2002 1:01 pm
Location: ISBoxer
Contact:

Post by Lax » Mon Oct 27, 2003 6:57 am

Yes it will currently show up in screenshots. We could hide it for screenshots though.
Lax Lacks
Master of MQ2 Disaster
Purveyor of premium, EULA-safe MMORPG Multiboxing Software
* Multiboxing with ISBoxer: Quick Start Video
* EQPlayNice, WinEQ 2.0

User avatar
ap50
a snow griffon
a snow griffon
Posts: 425
Joined: Sun Aug 18, 2002 2:29 pm

Post by ap50 » Mon Oct 27, 2003 8:45 am

Nice work Lax, thanks for your continued work.
[color=yellow][size=92][b]Just because you're paranoid, it doesn't mean everyone isn't out to get you![/b][/size][/color]

Mckorr
Developer
Developer
Posts: 2326
Joined: Fri Oct 18, 2002 1:16 pm
Location: Texas

Post by Mckorr » Mon Oct 27, 2003 9:49 am

Okay, will go to work getting this in the CVS. Gonna take a bit, will post when it's in there. Also won't be able to compile it, so will need another dev to download and check for compiler errors when it's done.
MQ2: Think of it as Evolution in action.

wassup
Official Guardian and Writer of TFM
Official Guardian and Writer of TFM
Posts: 1487
Joined: Sat Oct 26, 2002 5:15 pm

Post by wassup » Mon Oct 27, 2003 10:17 am

So this opens a new window when you start EQ?

Does this window only display text generated from MQ commands?

Lax
We're not worthy!
We're not worthy!
Posts: 3524
Joined: Thu Oct 17, 2002 1:01 pm
Location: ISBoxer
Contact:

Post by Lax » Mon Oct 27, 2003 10:31 am

Yes. All MQ generated chat text currently goes through "WriteChatColor" which I've modified to use the new code. What this code does is create a new window the first time it is used per session, and all MQ text will be displayed in the new window rather than existing chat windows, regardless of colors, etc. The window can be minimized and resized but not closed. You also can't type in the input box, clicking on it will turn on typing mode in the active chat window's box (active meaning, the last place you typed in a chat window or whichever one is locked to "always chat here").

I'm thinking about writing a function tonight to make it easier to colorize text -- this can already be used to make certain words stick out by using different colors per line, but it's a pain to make sure everything is "stml safe".. so I'll work on that.
Lax Lacks
Master of MQ2 Disaster
Purveyor of premium, EULA-safe MMORPG Multiboxing Software
* Multiboxing with ISBoxer: Quick Start Video
* EQPlayNice, WinEQ 2.0

Mckorr
Developer
Developer
Posts: 2326
Joined: Fri Oct 18, 2002 1:16 pm
Location: Texas

Post by Mckorr » Mon Oct 27, 2003 10:34 am

Okay, it's in... now, please provide a layman's explanation of what this does so I can add it to the changes.txt file and the readme file.

Edit: NM, hadn't refreshed while cutting and pasting the code. Fixing changes.txt now.
MQ2: Think of it as Evolution in action.

Lax
We're not worthy!
We're not worthy!
Posts: 3524
Joined: Thu Oct 17, 2002 1:01 pm
Location: ISBoxer
Contact:

Post by Lax » Mon Oct 27, 2003 10:53 am

TODO:
Directly related
-- Add coloring system that can be used to implement STML color tags and make it STML-safe (this would use a separate chat function and to be used for multiple-colored lines. existing functions will handle single-colored lines)
-- Add code to detect and use existing MQ window (I have a plan for this too)

Indirectly related
-- Convert "con color" chat to use the real colors instead of user-defined.. no purple cons!
Lax Lacks
Master of MQ2 Disaster
Purveyor of premium, EULA-safe MMORPG Multiboxing Software
* Multiboxing with ISBoxer: Quick Start Video
* EQPlayNice, WinEQ 2.0

tehHax0r
a lesser mummy
a lesser mummy
Posts: 40
Joined: Sat Oct 11, 2003 12:13 am

Post by tehHax0r » Mon Oct 27, 2003 11:17 am

someone make Lax a dev, that is first rate work, thanks :)

wassup
Official Guardian and Writer of TFM
Official Guardian and Writer of TFM
Posts: 1487
Joined: Sat Oct 26, 2002 5:15 pm

Post by wassup » Mon Oct 27, 2003 11:28 am

Hmmm... tried to add it manually and am getting quite a few errors. I'll wait for the CVS hehe.

Lax
We're not worthy!
We're not worthy!
Posts: 3524
Joined: Thu Oct 17, 2002 1:01 pm
Location: ISBoxer
Contact:

Post by Lax » Mon Oct 27, 2003 11:39 am

I may have forgot something, there's lots of changes and I haven't tried to make the changes myself on a clean MQ source :)
Lax Lacks
Master of MQ2 Disaster
Purveyor of premium, EULA-safe MMORPG Multiboxing Software
* Multiboxing with ISBoxer: Quick Start Video
* EQPlayNice, WinEQ 2.0