Owner-authorized source migration: 31 helper-only translation units were merged into their callers or existing inline headers; both build manifests updated. Includes the preserved source payload, Lua configuration matching wowdecomp, and exact malloca expansion without diagnostic pragmas. 449 native units compiled locally and 449 staged units compiled by the pinned server toolchain. No previously byte-matched function regressed. Compiler mapping diagnostics remain recorded as needs_cleanup, not accepted or peer-confirmed. Existing independent review records are preserved. README.md is intentionally unchanged.
389 lines
12 KiB
C++
389 lines
12 KiB
C++
#include "Client/Mode/ActiveGameMode.h"
|
|
#include "Client/Application/UnhandledExceptionHandler.h"
|
|
|
|
#include <windows.h>
|
|
#include <dbghelp.h>
|
|
#include <psapi.h>
|
|
#include <ctime>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
// STATIC: 0x01389c78
|
|
// GLOBAL: gl_24469
|
|
CUnhandledExceptionHandler* g_unhandledExceptionHandler = nullptr;
|
|
|
|
// STATIC: 0x01389c7c
|
|
// GLOBAL: gl_24470
|
|
int (*g_unhandledExceptionCallback)() = nullptr;
|
|
|
|
// STATIC: 0x01389c80
|
|
// GLOBAL: gl_24359
|
|
char g_filename[264] = {};
|
|
|
|
// STATIC: 0x01389d88
|
|
// GLOBAL: gl_24471
|
|
char g_moduleBaseName[260] = {};
|
|
|
|
// STATIC: 0x01389e90
|
|
// GLOBAL: gl_24360
|
|
char g_crashString[8096] = {};
|
|
|
|
// STATIC: 0x0173ad64
|
|
// GLOBAL: gl_24472
|
|
extern uint32_t g_clientVersion;
|
|
|
|
// STATIC: 0x0171dd30
|
|
// GLOBAL: gl_24473
|
|
extern uint32_t g_launchInfo[32];
|
|
|
|
// STATIC: 0x01733550
|
|
// GLOBAL: gl_24476
|
|
extern int g_playerX;
|
|
|
|
// STATIC: 0x01733554
|
|
// GLOBAL: gl_24477
|
|
extern int g_playerY;
|
|
|
|
// STATIC: 0x0173a770
|
|
// GLOBAL: gl_24478
|
|
extern int64_t g_startTimeTicks;
|
|
|
|
// STATIC: 0x0173a778
|
|
// GLOBAL: gl_24479
|
|
extern int64_t g_loginTimeTicks;
|
|
|
|
// STATIC: 0x01732638
|
|
// GLOBAL: gl_24474
|
|
extern const uint8_t g_session[];
|
|
|
|
namespace {
|
|
int ReadPlayerJob() {
|
|
int job;
|
|
std::memcpy(&job, g_session + 0x1628, sizeof(job));
|
|
return job;
|
|
}
|
|
|
|
const char* ReadPlayerJobName(int jobId) {
|
|
const char* const* namesBegin;
|
|
const char* const* namesEnd;
|
|
std::memcpy(&namesBegin, g_session + 0xFA8, sizeof(namesBegin));
|
|
std::memcpy(&namesEnd, g_session + 0xFAC, sizeof(namesEnd));
|
|
|
|
if (!namesBegin || !namesEnd || namesBegin >= namesEnd) {
|
|
return "";
|
|
}
|
|
|
|
size_t count = namesEnd - namesBegin;
|
|
if (static_cast<size_t>(jobId) >= count || !namesBegin[jobId]) {
|
|
return "";
|
|
}
|
|
|
|
return namesBegin[jobId];
|
|
}
|
|
|
|
// INLINED
|
|
const char* ReadCurrentMapName() {
|
|
if (g_world.state == 1 && g_world.gameMode != nullptr) {
|
|
return reinterpret_cast<const char*>(g_world.gameMode) + 0x68;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
}
|
|
extern "C" long long __cdecl _Xtime_get_ticks();
|
|
|
|
// OFFSET: 0x00635A70
|
|
// FUNCTION: fn_04701
|
|
CUnhandledExceptionHandler::CUnhandledExceptionHandler() {
|
|
}
|
|
|
|
// OFFSET: 0x00635A80
|
|
CUnhandledExceptionHandler::~CUnhandledExceptionHandler() = default;
|
|
|
|
// OFFSET: 0x00635AB0
|
|
// FUNCTION: fn_04703
|
|
bool InitUnhandledExceptionHandler(int (*callback)()) {
|
|
if (g_unhandledExceptionHandler != nullptr) {
|
|
return false;
|
|
}
|
|
|
|
auto* handler = new CUnhandledExceptionHandler();
|
|
if (!handler) {
|
|
g_unhandledExceptionHandler = nullptr;
|
|
return false;
|
|
}
|
|
|
|
g_unhandledExceptionCallback = callback;
|
|
GetModuleFileNameA(nullptr, g_filename, sizeof(g_filename));
|
|
|
|
const char* baseName = strrchr(g_filename, '\\');
|
|
if (baseName) {
|
|
baseName++;
|
|
} else {
|
|
baseName = g_filename;
|
|
}
|
|
|
|
_snprintf_s(g_moduleBaseName, sizeof(g_moduleBaseName), static_cast<size_t>(-1), "%s", baseName);
|
|
|
|
char* sep = strrchr(g_filename, '\\');
|
|
if (sep) {
|
|
*sep = '\0';
|
|
}
|
|
|
|
char* dot = strrchr(g_moduleBaseName, '.');
|
|
if (dot) {
|
|
*dot = '\0';
|
|
}
|
|
|
|
SetUnhandledExceptionFilter(TopLevelExceptionFilter);
|
|
_set_invalid_parameter_handler(InvalidParameterHandler);
|
|
_set_purecall_handler(PurecallHandler);
|
|
|
|
g_unhandledExceptionHandler = handler;
|
|
return true;
|
|
}
|
|
|
|
// OFFSET: 0x00635C10
|
|
// FUNCTION: fn_04704
|
|
int CleanupUnhandledExceptionHandler() {
|
|
if (g_unhandledExceptionHandler != nullptr) {
|
|
delete g_unhandledExceptionHandler;
|
|
g_unhandledExceptionHandler = nullptr;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// OFFSET: 0x00635C30
|
|
// FUNCTION: fn_04705
|
|
INT_PTR CALLBACK DialogFunc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
if (uMsg == WM_INITDIALOG) {
|
|
HWND item = GetDlgItem(hDlg, 1006);
|
|
RECT rect = {};
|
|
SetWindowTextA(item, g_crashString);
|
|
GetWindowRect(hDlg, &rect);
|
|
|
|
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
|
|
int dlgHeight = rect.bottom - rect.top;
|
|
int y = (screenHeight / 2) - (dlgHeight / 2);
|
|
|
|
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
|
|
int dlgWidth = rect.right - rect.left;
|
|
int x = (screenWidth / 2) - (dlgWidth / 2);
|
|
|
|
MoveWindow(hDlg, x, y, dlgWidth, dlgHeight, TRUE);
|
|
} else if (uMsg == WM_COMMAND) {
|
|
if (LOWORD(wParam) == 2) {
|
|
size_t len = strlen(g_crashString);
|
|
HGLOBAL memory = GlobalAlloc(GMEM_MOVEABLE, len + 1);
|
|
if (memory != nullptr) {
|
|
char* locked = static_cast<char*>(GlobalLock(memory));
|
|
if (locked != nullptr) {
|
|
memcpy(locked, g_crashString, len + 1);
|
|
GlobalUnWire(memory);
|
|
|
|
if (OpenClipboard(hDlg)) {
|
|
EmptyClipboard();
|
|
SetClipboardData(CF_TEXT, memory);
|
|
CloseClipboard();
|
|
}
|
|
|
|
HWND button = GetDlgItem(hDlg, 7);
|
|
SetFocus(button);
|
|
} else {
|
|
GlobalFree(memory);
|
|
}
|
|
}
|
|
} else if (LOWORD(wParam) == 7) {
|
|
EndDialog(hDlg, 7);
|
|
}
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// OFFSET: 0x00635DB0
|
|
// FUNCTION: fn_04706
|
|
BOOL CALLBACK EnumerateLoadedModulesCallback(PCSTR moduleName, ULONG moduleBase, ULONG moduleSize, PVOID userContext) {
|
|
auto* address = static_cast<const DWORD*>(userContext);
|
|
if (*address < moduleBase || *address > moduleBase + moduleSize) {
|
|
return TRUE;
|
|
}
|
|
|
|
sprintf(&g_crashString[strlen(g_crashString)], "%s", moduleName);
|
|
return FALSE;
|
|
}
|
|
|
|
// OFFSET: 0x00635E00
|
|
// FUNCTION: fn_04707
|
|
void __cdecl InvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) {
|
|
EXCEPTION_RECORD record = {};
|
|
CONTEXT context = {};
|
|
EXCEPTION_POINTERS exceptionInfo = {&record, &context};
|
|
|
|
RtlCaptureContext(&context);
|
|
|
|
record.ExceptionCode = 0xC000000D;
|
|
record.NumberParameters = 3;
|
|
record.ExceptionInformation[0] = reinterpret_cast<ULONG_PTR>(expression);
|
|
record.ExceptionInformation[1] = reinterpret_cast<ULONG_PTR>(file);
|
|
record.ExceptionInformation[2] = static_cast<ULONG_PTR>(line);
|
|
|
|
TopLevelExceptionFilter(&exceptionInfo);
|
|
abort();
|
|
}
|
|
|
|
// OFFSET: 0x00635E90
|
|
// FUNCTION: fn_04708
|
|
void __cdecl PurecallHandler() {
|
|
EXCEPTION_RECORD record = {};
|
|
CONTEXT context = {};
|
|
EXCEPTION_POINTERS exceptionInfo = {&record, &context};
|
|
|
|
RtlCaptureContext(&context);
|
|
|
|
record.ExceptionCode = 0xC0000025;
|
|
|
|
TopLevelExceptionFilter(&exceptionInfo);
|
|
abort();
|
|
}
|
|
|
|
// OFFSET: 0x00635F00
|
|
// FUNCTION: fn_04709
|
|
LONG WINAPI TopLevelExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) {
|
|
PCONTEXT context = exceptionInfo->ContextRecord;
|
|
HMODULE dbgHelp = LoadLibraryA("dbghelp.dll");
|
|
|
|
if (dbgHelp != nullptr) {
|
|
auto pStackWalk = reinterpret_cast<BOOL(WINAPI*)(
|
|
DWORD, HANDLE, HANDLE, LPSTACKFRAME, PVOID, PREAD_PROCESS_MEMORY_ROUTINE, PFUNCTION_TABLE_ACCESS_ROUTINE, PGET_MODULE_BASE_ROUTINE,
|
|
PTRANSLATE_ADDRESS_ROUTINE
|
|
)>(GetProcAddress(dbgHelp, "StackWalk"));
|
|
|
|
auto pGetTimestamp = reinterpret_cast<DWORD(WINAPI*)(HMODULE)>(GetProcAddress(dbgHelp, "GetTimestampForLoadedLibrary"));
|
|
|
|
auto pEnumerateLoadedModules =
|
|
reinterpret_cast<BOOL(WINAPI*)(HANDLE, PENUMLOADED_MODULES_CALLBACK, PVOID)>(GetProcAddress(dbgHelp, "EnumerateLoadedModules"));
|
|
|
|
HMODULE module = GetModuleHandleA(nullptr);
|
|
CHAR filename[256] = {};
|
|
GetModuleFileNameA(module, filename, sizeof(filename));
|
|
|
|
unsigned int timestamp = pGetTimestamp ? pGetTimestamp(module) : 0;
|
|
sprintf(&g_crashString[strlen(g_crashString)], "Module Name: %s\r\n", filename);
|
|
|
|
__time32_t timestampTime;
|
|
std::memcpy(×tampTime, ×tamp, sizeof(timestampTime));
|
|
const char* timeString = _ctime32(×tampTime);
|
|
sprintf(&g_crashString[strlen(g_crashString)], "Time Stamp: 0x%08x - %s\r\n", timestamp, timeString);
|
|
sprintf(&g_crashString[strlen(g_crashString)], "Version: 0x%08x\r\n", g_clientVersion);
|
|
sprintf(&g_crashString[strlen(g_crashString)], "\r\n");
|
|
sprintf(&g_crashString[strlen(g_crashString)], "Exception Type: 0x%08x\r\n", static_cast<unsigned int>(exceptionInfo->ExceptionRecord->ExceptionCode));
|
|
sprintf(&g_crashString[strlen(g_crashString)], "\r\n");
|
|
|
|
HANDLE process = GetCurrentProcess();
|
|
HANDLE thread = GetCurrentThread();
|
|
|
|
STACKFRAME frame = {};
|
|
frame.AddrPC.Offset = context->Eip;
|
|
frame.AddrPC.Mode = AddrModeFlat;
|
|
frame.AddrStack.Offset = context->Esp;
|
|
frame.AddrStack.Mode = AddrModeFlat;
|
|
frame.AddrFrame.Offset = context->Ebp;
|
|
frame.AddrFrame.Mode = AddrModeFlat;
|
|
|
|
sprintf(&g_crashString[strlen(g_crashString)], "\r\n");
|
|
sprintf(
|
|
&g_crashString[strlen(g_crashString)], "eax: 0x%08x\tebx: 0x%08x\r\n", static_cast<unsigned int>(context->Eax), static_cast<unsigned int>(context->Ebx)
|
|
);
|
|
sprintf(
|
|
&g_crashString[strlen(g_crashString)], "ecx: 0x%08x\tedx: 0x%08x\r\n", static_cast<unsigned int>(context->Ecx), static_cast<unsigned int>(context->Edx)
|
|
);
|
|
sprintf(
|
|
&g_crashString[strlen(g_crashString)], "esi: 0x%08x\tedi: 0x%08x\r\n", static_cast<unsigned int>(context->Esi), static_cast<unsigned int>(context->Edi)
|
|
);
|
|
sprintf(
|
|
&g_crashString[strlen(g_crashString)], "ebp: 0x%08x\tesp: 0x%08x\r\n", static_cast<unsigned int>(context->Ebp), static_cast<unsigned int>(context->Esp)
|
|
);
|
|
sprintf(&g_crashString[strlen(g_crashString)], "\r\n");
|
|
|
|
for (int i = 0; i < 512; ++i) {
|
|
if (frame.AddrPC.Offset == 0) {
|
|
break;
|
|
}
|
|
|
|
if (!pStackWalk(IMAGE_FILE_MACHINE_I386, process, thread, &frame, context, nullptr, nullptr, nullptr, nullptr)) {
|
|
break;
|
|
}
|
|
|
|
sprintf(&g_crashString[strlen(g_crashString)], "0x%08x\t", static_cast<unsigned int>(frame.AddrPC.Offset));
|
|
pEnumerateLoadedModules(process, EnumerateLoadedModulesCallback, &frame.AddrPC.Offset);
|
|
sprintf(&g_crashString[strlen(g_crashString)], "\r\n");
|
|
}
|
|
|
|
const auto* stack = reinterpret_cast<const unsigned char*>(context->Esp);
|
|
sprintf(&g_crashString[strlen(g_crashString)], "\r\n");
|
|
sprintf(
|
|
&g_crashString[strlen(g_crashString)], "stack %08x - %08x\r\n", static_cast<unsigned int>(reinterpret_cast<uintptr_t>(stack)),
|
|
static_cast<unsigned int>(reinterpret_cast<uintptr_t>(stack + 1024))
|
|
);
|
|
|
|
for (int i = 0; i < 16; ++i) {
|
|
sprintf(&g_crashString[strlen(g_crashString)], "%08X : ", static_cast<unsigned int>(reinterpret_cast<uintptr_t>(stack)));
|
|
|
|
for (int j = 0; j < 16; ++j) {
|
|
sprintf(&g_crashString[strlen(g_crashString)], "%02X ", *stack++);
|
|
}
|
|
|
|
sprintf(&g_crashString[strlen(g_crashString)], "\r\n");
|
|
}
|
|
|
|
sprintf(&g_crashString[strlen(g_crashString)], "\r\nLaunch Info \r\n");
|
|
int launchIndex = 0;
|
|
|
|
for (int k = 0; k < 4; ++k) {
|
|
for (int m = 0; m < 8; ++m) {
|
|
sprintf(&g_crashString[strlen(g_crashString)], "%04X ", g_launchInfo[launchIndex++]);
|
|
}
|
|
|
|
sprintf(&g_crashString[strlen(g_crashString)], "\r\n");
|
|
}
|
|
|
|
int jobId = ReadPlayerJob();
|
|
const char* jobName = ReadPlayerJobName(jobId);
|
|
sprintf(&g_crashString[strlen(g_crashString)], "\r\nJob : %s\r\n", jobName);
|
|
|
|
const char* mapName = ReadCurrentMapName();
|
|
if (mapName != nullptr) {
|
|
sprintf(&g_crashString[strlen(g_crashString)], "%s %d %d\r\n", mapName, g_playerX, g_playerY);
|
|
}
|
|
|
|
sprintf(&g_crashString[strlen(g_crashString)], "\r\n%I64d - %I64d", g_startTimeTicks / 10000000LL, g_loginTimeTicks / 10000000LL);
|
|
|
|
int64_t reportTime = _Xtime_get_ticks() / 10000000LL;
|
|
PROCESS_MEMORY_COUNTERS memCounters = {};
|
|
double workingSet = 0.0;
|
|
double pagefile = 0.0;
|
|
|
|
if (K32GetProcessMemoryInfo(process, &memCounters, sizeof(memCounters))) {
|
|
workingSet = static_cast<double>(static_cast<uint32_t>(memCounters.WorkingSetSize >> 10));
|
|
pagefile = static_cast<double>(static_cast<uint32_t>(memCounters.PagefileUsage >> 10));
|
|
}
|
|
|
|
sprintf(&g_crashString[strlen(g_crashString)], "\r\nReport Time: %I64d(%.0lf/%.0lf)\r\n", reportTime, workingSet, pagefile);
|
|
FreeLibrary(dbgHelp);
|
|
} else {
|
|
sprintf(&g_crashString[strlen(g_crashString)], "Can't find DbgHelp.DLL !!!");
|
|
}
|
|
|
|
DialogBoxParamA(GetModuleHandleA(nullptr), MAKEINTRESOURCEA(117), nullptr, DialogFunc, 0);
|
|
|
|
if (g_unhandledExceptionCallback != nullptr) {
|
|
g_unhandledExceptionCallback();
|
|
}
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|