Page 1 of 6
New MQ in-game text display code
Posted: Sun Oct 26, 2003 8:26 pm
by Lax
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.
Posted: Sun Oct 26, 2003 9:21 pm
by fwiggles
sweeeeeet
Posted: Sun Oct 26, 2003 11:17 pm
by Lax
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
Posted: Sun Oct 26, 2003 11:25 pm
by Lax
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
Posted: Mon Oct 27, 2003 12:10 am
by Clone39
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
Posted: Mon Oct 27, 2003 6:57 am
by Lax
Yes it will currently show up in screenshots. We could hide it for screenshots though.
Posted: Mon Oct 27, 2003 8:45 am
by ap50
Nice work Lax, thanks for your continued work.
Posted: Mon Oct 27, 2003 9:49 am
by Mckorr
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.
Posted: Mon Oct 27, 2003 10:17 am
by wassup
So this opens a new window when you start EQ?
Does this window only display text generated from MQ commands?
Posted: Mon Oct 27, 2003 10:31 am
by Lax
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.
Posted: Mon Oct 27, 2003 10:34 am
by Mckorr
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.
Posted: Mon Oct 27, 2003 10:53 am
by Lax
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!
Posted: Mon Oct 27, 2003 11:17 am
by tehHax0r
someone make Lax a dev, that is first rate work, thanks :)
Posted: Mon Oct 27, 2003 11:28 am
by wassup
Hmmm... tried to add it manually and am getting quite a few errors. I'll wait for the CVS hehe.
Posted: Mon Oct 27, 2003 11:39 am
by Lax
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 :)