📄 clientcore.cpp
字号:
//////////////////////////////////////////////////////////////////////
// ClientCore.cpp
// -------------------------------------------------------------------
// Default Client Dll entrypoint.
//
// <thohell@home.se>
// <Dan_Goon@diabloworld.com>
//
// 0.7 : Updated by Dan_Goon
//////////////////////////////////////////////////////////////////////
#include "D2Client.h"
//////////////////////////////////////////////////////////////////////
// Global server struct holding pointers to exported functions
//////////////////////////////////////////////////////////////////////
FUNCTIONENTRYPOINTS g_SF = { 0 };
FUNCTIONENTRYPOINTS *server = &g_SF;
HMODULE g_hModule = NULL; // New Global
PSERVERINFO g_pSI = NULL; // 0.7
BOOL PRIVATE SetupClient(HMODULE hModule, LPCSTR szModule);
LPCSTR szLodErr = "D2Client Load Error";
LPSTR m_szLargeBuffer = NULL;
DWORD m_nLargeBufferSize = 0;
DWORD m_nSectNameIndex = 0;
#ifdef D2HACKIT_DEBUG
char g_szDebug[512];
#endif
#ifdef _AFXDLL
static AFX_EXTENSION_MODULE D2H_CLIENT = { NULL, NULL };
#endif
//********************************************************************************************
// Important Notice on UNLOADING PROCESS!!
//
// Client Module's DllMain(DLL_PROCESS_DETACH) can be called in two different sequences as follows!
//
// 1. When FreeLibray() is called by D2HackIt during normal unloading process
// (by unload command or during ServerStop() process).
// 2. Diablo II is exiting by user op (this is called prior to D2HackIt's ServerStop()!).
// In this case, all client's thread memory is detached before OnClientStop() call!!!!
// So, if client references any memory allocated in client's thread while OnClientStop() process,
// it may casue memory exception, crash, hanging or remained-in-memory.
// To prevent this situation, the unloading process is changed as follows:
//
// New Unloading Process
//
// 1. When client's DllMain(DLL_PROCESS_DETACH) is called, calls server->ClenaMeUp()
// 2. server->CleanMeUp() handles actuall unloading process and calls client's OnGameLeave() and OnClientStop().
// 3. While unloading client by normal command or in ServerStop() process, just do as before.
//
// So, the actual unload sequence for two different cases will be :
//
// 1. Normal Sequence (Unloading D2HackIt by second "D2Loader.exe" execution or by ".unload" command)
//
// 1) D2HackIt's DllMain(DLL_PROCESS_DETACH) : ServerStop() - this is D2HackIt's unloading process.
// 2) calls optional OnGameLeave() and OnClientStop() of client.
// 3) frees up client info structure and delete linked list.
// 4) calls FreeLibrary(hClient).
// 5) After D2HackIt is destructed, client's DllMain(DLL_PROCESS_DETACH) will be called.
// 6) server->CleanMeUp() is called : BUT THIS IS TREATED AS DUMMY CALL - DO NOTHING.
//
// 2. D2-fisrt Exiting Sequence (Exit from D2 by user op or accidental exiting)
//
// 1) Client's DllMain(DLL_PROCESS_DETACH) is called first.
// 2) server->CleanMeUp() is called.
// 3) then calls optional OnGameLeave() and OnClientStop() of client.
// 4) frees up client info structure and delete linked list.
// 5) then D2HackIt's DllMain(DLL_PROCESS_DETACH) : ServerStop() is called.
// 6) clients are already unloaded - DO NOTHING.
//
//********************************************************************************************
//////////////////////////////////////////////////////////////////////
// Dll entry/exit
//////////////////////////////////////////////////////////////////////
extern "C" BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
char szTemp[MAX_PATH], szFile[MAX_PATH];
BOOL bResult = TRUE;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
#ifdef _AFXDLL
if (!AfxInitExtensionModule(D2H_CLIENT, (HINSTANCE)hModule)) return FALSE;
#endif
DGTRACE(( CHDR "DLL_PROCESS_ATTACH\n" ));
g_hModule = (HMODULE)hModule;
GetModuleFileName( g_hModule, szTemp, sizeof(szTemp) );
_splitpath(szTemp, NULL, NULL, szFile, NULL); // Extract File Title
bResult = FALSE;
// Bind exported functions from server
HMODULE hD2Hackit;
hD2Hackit = (HMODULE)GetModuleHandle("D2HackIt");
if(!hD2Hackit)
{
sprintf(szTemp, "[%s] : Failed to get D2HackIt!", szFile);
MessageBox(NULL, szTemp, szLodErr, MB_OK);
break;
}
// Fill in the server & g_pSI (server info)
return SetupClient(hD2Hackit, szFile);
case DLL_THREAD_DETACH:
// Fall here first in case of accidental exit of Diablo II
DGTRACE(( CHDR "DLL_THREAD_DETACH\n" ));
break;
case DLL_PROCESS_DETACH:
// This is normal call from FreeLibrary
DGTRACE(( CHDR "DLL_PROCESS_DETACH\n" ));
// This call is available from V0.8
if(!IsBadCodePtr((FARPROC)server->CleanMeUp)) server->CleanMeUp(g_hModule);
if(m_szLargeBuffer) delete [] m_szLargeBuffer;
m_szLargeBuffer = NULL;
#ifdef _AFXDLL
AfxTermExtensionModule(D2H_CLIENT);
#endif
break;
}
return bResult;
}
// From V0.7 by Dan_Goon
BOOL PRIVATE SetupClient(HMODULE hModule, LPCSTR szModule)
{
char tbf[256];
BIND_TO_SERVER(GetServerInfo); // This is available from V0.59
// We need at least 0.59
if(!server->GetServerInfo)
{
sprintf(tbf, "[%s] : Failed to bind server functions!\n\nPlease use D2HackIt! V0.59 or later.", szModule);
MessageBox(NULL, tbf, szLodErr, MB_OK);
return FALSE;
}
g_pSI = server->GetServerInfo(__SERVERVERSION__, szModule);
BIND_TO_SERVER(GamePrintString);
BIND_AND_CHECK(GamePrintInfo);
BIND_AND_CHECK(GamePrintError);
BIND_AND_CHECK(GamePrintVerbose);
BIND_AND_CHECK(GamePrintString);
BIND_AND_CHECK(GameCommandLine);
BIND_AND_CHECK(GameSendPacketToServer);
BIND_AND_CHECK(GameSendPacketToGame);
BIND_AND_CHECK(GetFingerprint);
BIND_AND_CHECK(Intercept);
BIND_AND_CHECK(GetThisgameStruct);
// BIND_AND_CHECK(GetHackProfileString); // This will be obsoleted. Don't use this after 0.7!
// BIND_AND_CHECK(GetHackProfileStringEx); // This will be obsoleted. Don't use this after 0.7!
// BIND_AND_CHECK(GetHackProfileSectionNames); // This will be obsoleted. Don't use this after 0.7!
BIND_AND_CHECK(SetHackProfileString);
BIND_AND_CHECK(GameSendPacketToGame);
BIND_AND_CHECK(GameInsertPacketToGame); // 0.59
BIND_AND_CHECK(GameSendMessageToChat); // 0.59
BIND_AND_CHECK(D2FindFile);
// BIND_AND_CHECK(FreeHackProfileString); // 0.62 This will be obsoleted. Don't use this after 0.7!
BIND_AND_CHECK(GameSaveAndExit); // 0.62
BIND_AND_CHECK(GetIniFileName); // 0.71
BIND_AND_CHECK(GetIniString); // 0.71
BIND_AND_CHECK(GetIniSectionNames); // 0.71
BIND_AND_CHECK(FindMyD2WndHandle); // 0.73
BIND_AND_CHECK(HookD2WndProc); // 0.80
BIND_AND_CHECK(CleanMeUp); // 0.80
if(!g_pSI)
{
server->GamePrintError("D2HackIt Server Info not available!");
}
else
{
_snprintf(tbf, sizeof(tbf), "D2HackIt directory = %s", g_pSI->PluginDirectory);
server->GamePrintVerbose(tbf);
}
return TRUE;
}
//======================================================================
// EnumIniSectionNames (Helper Funtion)
//======================================================================
//
// This helper function illustrates how to utilize GetIniSectionNames call.
// This maybe useful for modules which handles long lines of ini file text.
// You may remove this function from your code, if you don't need this feature.
//
// Usage:
//
// LPSTR pString = EnumIniSectionNames( "YourD2hName", ENUM_START );
// while( pString && *pString )
// {
// // <<<== Do your job here with returned pString
//
// pString = EnumIniSectionNames( "YourD2hName", ENUM_NEXT );
// }
// EnumIniSectionNames( "YourD2hName", ENUM_FINISH );
//
//======================================================================
LPSTR PRIVATE EnumIniSectionNames( LPCSTR lpHackName, int nEnumAction )
{
if(!server->GetIniSectionNames) return NULL;
switch(nEnumAction)
{
case ENUM_START: // = Start Enum
if(m_szLargeBuffer) delete [] m_szLargeBuffer;
m_szLargeBuffer = NULL;
// Get required buffer size
m_nLargeBufferSize = server->GetIniSectionNames(lpHackName, NULL, 0);
if(!m_nLargeBufferSize) break;
m_szLargeBuffer = new char[m_nLargeBufferSize];
if(!m_szLargeBuffer)
{
server->GamePrintError("Failed to allocate buffer");
break;
}
server->GetIniSectionNames(lpHackName, m_szLargeBuffer, m_nLargeBufferSize);
m_nSectNameIndex = 0;
return m_szLargeBuffer;
case ENUM_NEXT: // = Enum Next
if(!m_szLargeBuffer) break;
m_nSectNameIndex += strlen(&m_szLargeBuffer[m_nSectNameIndex]) + 1; // point to next z-string start
if(m_nSectNameIndex >= m_nLargeBufferSize ||
!m_szLargeBuffer[m_nSectNameIndex]) break; // end of buffer reached
return &m_szLargeBuffer[m_nSectNameIndex];
case ENUM_FINISH: // = Finish Enum
if(m_szLargeBuffer) delete [] m_szLargeBuffer;
m_szLargeBuffer = NULL;
m_nSectNameIndex = 0;
break;
}
return NULL;
}
//======================================================================
// GetHackProfileString
//======================================================================
//
// This private function is for previous modules which used to call
// server->GetHackProfileString, to keep its function-level consistency.
// Just remove your current 'server->' part.
//
// Notice : Never delete returned string pointer and do not save this pointer
// for future use. The string context pointed by this pointer will be
// changed to new value whenever this function is called again.
//
//======================================================================
LPSTR PRIVATE GetHackProfileString( LPCSTR lpHackName, LPCSTR lpSectionName, LPCSTR lpKeyName)
{
static char szLineBuffer[1024]; // this maybe enough for most of ini file lines
if(!server->GetIniString ||
!server->GetIniString(lpHackName, lpSectionName, lpKeyName, szLineBuffer, 1024))
szLineBuffer[0] = 0;
return szLineBuffer;
}
//======================================================================
// D2WndProcHooker
//======================================================================
// Code below is for illustration of Diablo II Window Procedure Hooker.
//
//======================================================================
/*
BOOL CALLBACK MyHooker(LRESULT* pResult, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if(uMsg >= WM_USER)
{
if(uMsg == WM_MY_SPECIFIC_COMMAND)
{
.
.
.
*pResult = 0x1234; // This is return value for message-sender
return TRUE; // "I processed this message"
}
}
return FALSE; // "I did not process this message"
}
BOOL EXPORT OnClientStart()
{
server->HookD2WndProc(g_hModule, MyHooker); // Setup Hook Link of 'MyHooker'
.
.
.
return TRUE;
}
BOOL EXPORT OnClientStop()
{
.
.
.
server->HookD2WndProc(g_hModule, NULL); // Unhook me
return TRUE;
}
*/
//////////////////////////////////////////////////////////////////////
// Stubfunctions for 'property get' functions.
//////////////////////////////////////////////////////////////////////
LPCSTR EXPORT GetModuleAuthor() {return ModuleAuthor;}
LPCSTR EXPORT GetModuleWebsite() {return ModuleWebsite;}
DWORD EXPORT GetModuleVersion() {return ModuleVersion;}
LPCSTR EXPORT GetModuleEmail() {return ModuleEmail;}
LPCSTR EXPORT GetModuleDescription() {return ModuleDescription;}
//////////////////////////////////////////////////////////////////////
// OnClientCommandLine
// -------------------------------------------------------------------
// The modules own extension of the command line interface. Any custom
// commands you add are parsed here.
//
// Return value should be TRUE, but it is not used at this
// time.
//
// Arguments when we get here:
// argv[0] Name of module
// argv[1] Name of command (If supplied)
// argv[2 ... n] The rest
//
// Syntax in the game: .<module> <arguments>
//////////////////////////////////////////////////////////////////////
BOOL EXPORT OnGameCommandLine(char* argv[], int argc)
{
DGTRACE((DBGHDR "OnGameCommandLine : (%d) [%s]..\n", argc, argc > 0 ? argv[0] : "Mola"));
// Check if user supplied anything at all, if not assume help...
if (argc==1) argv[argc++] = "help";
MODULECOMMANDSTRUCT* mcs = ModuleCommands;
while (mcs->szName)
{
if (!stricmp(mcs->szName, argv[1])) break;
mcs++;
}
char *p, fMsg[256];
//
// Is this a built-in function ?
if (mcs->szName)
{
// If functions returns false, show usage help
if (!mcs->pFunc(argv, argc))
{
server->GamePrintInfo("Usage:");
p = strtok(mcs->szUsage, "\n");
while(p)
{
sprintf(fMsg, "
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -