mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
Revert "Remove deprecated WoW plugin."
This reverts commit 8c197b374e.
Updated WoW plugin to WoW 3.2.2. Attaches to process, returns sane values in test program. Attaches to process and activates positional audio in mumble, but has not influence to audio but make the sound softer as if both actors are >15m from each other. (which they where not in out tests)
translate wow coordinate system to right handed coordinate system add resync after stuck coordinates (maybe needed, could realiably reproduce) add realm as context
add more checks for sane values add utility functions for memory reading add identity reading
add camera reading instead of copied avatar
385 lines
9.1 KiB
C++
385 lines
9.1 KiB
C++
/* stdint.h */
|
|
typedef int uint32_t;
|
|
typedef long long uint64_t;
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <windows.h>
|
|
#include <tlhelp32.h>
|
|
#include <math.h>
|
|
|
|
#include "../mumble_plugin.h"
|
|
|
|
HANDLE h;
|
|
uint32_t p_playerBase;
|
|
uint64_t g_playerGUID;
|
|
|
|
#define STATIC_REALMNAME 0x0127046E
|
|
|
|
|
|
static DWORD getProcess(const wchar_t *exename) {
|
|
PROCESSENTRY32 pe;
|
|
DWORD pid = 0;
|
|
|
|
pe.dwSize = sizeof(pe);
|
|
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
if (hSnap != INVALID_HANDLE_VALUE) {
|
|
BOOL ok = Process32First(hSnap, &pe);
|
|
|
|
while (ok) {
|
|
if (wcscmp(pe.szExeFile, exename)==0) {
|
|
pid = pe.th32ProcessID;
|
|
break;
|
|
}
|
|
ok = Process32Next(hSnap, &pe);
|
|
}
|
|
CloseHandle(hSnap);
|
|
}
|
|
return pid;
|
|
}
|
|
|
|
static bool peekProc(VOID *base, VOID *dest, SIZE_T len) {
|
|
SIZE_T r;
|
|
BOOL ok=ReadProcessMemory(h, base, dest, len, &r);
|
|
return (ok && (r == len));
|
|
}
|
|
|
|
static void about(HWND h) {
|
|
::MessageBox(h, L"Reads audio position information from World of Warcraft", L"Mumble WoW Plugin", MB_OK);
|
|
}
|
|
|
|
uint32_t getInt32(uint32_t ptr)
|
|
{
|
|
uint32_t result;
|
|
SIZE_T r;
|
|
BOOL ok=ReadProcessMemory(h, (void *)ptr, &result, sizeof(uint32_t), &r);
|
|
if (ok && (r == sizeof (uint32_t))) {
|
|
return result;
|
|
} else {
|
|
return 0xffffffff;
|
|
}
|
|
}
|
|
|
|
uint64_t getInt64(uint32_t ptr)
|
|
{
|
|
uint64_t result;
|
|
SIZE_T r;
|
|
BOOL ok=ReadProcessMemory(h, (void *)ptr, &result, sizeof(uint64_t), &r);
|
|
if (ok && (r == sizeof (uint64_t))) {
|
|
return result;
|
|
} else {
|
|
return 0xffffffffffffffff;
|
|
}
|
|
}
|
|
|
|
float getFloat (uint32_t ptr)
|
|
{
|
|
float result;
|
|
SIZE_T r;
|
|
BOOL ok=ReadProcessMemory(h, (void *)ptr, &result, sizeof(float), &r);
|
|
if (ok && (r == sizeof (float))) {
|
|
return result;
|
|
} else {
|
|
return (float)0xffffffff;
|
|
}
|
|
}
|
|
|
|
int getCStringN (uint32_t ptr, char *buffer, size_t buffersize)
|
|
{
|
|
SIZE_T r;
|
|
BOOL ok=ReadProcessMemory(h, (void *)ptr, buffer, buffersize, &r);
|
|
buffer[buffersize-1]='\0';
|
|
|
|
if (ok && (r == buffersize)) {
|
|
return strlen(buffer);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int getString (uint32_t ptr, std::string &buffer)
|
|
{
|
|
return getCStringN (ptr, (char *)buffer.data(), buffer.capacity());
|
|
}
|
|
int getWString (uint32_t ptr, std::wstring &buffer)
|
|
{
|
|
char buf[1024];
|
|
return getCStringN (ptr, (char *)buffer.data(), buffer.capacity());
|
|
}
|
|
|
|
|
|
void getDebug16(uint32_t ptr) {
|
|
#ifdef _DEBUG
|
|
unsigned char buf[16];
|
|
SIZE_T r;
|
|
BOOL ok=ReadProcessMemory(h, (void *)ptr, &buf, sizeof(buf), &r);
|
|
if (ok && (r == sizeof (buf))) {
|
|
printf ("%08x: %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n",
|
|
ptr,
|
|
buf[0], buf[1], buf[2], buf[3],
|
|
buf[4], buf[5], buf[6], buf[7],
|
|
buf[8], buf[9], buf[10], buf[11],
|
|
buf[12], buf[13], buf[14], buf[15]
|
|
);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
uint32_t getPlayerBase()
|
|
{
|
|
uint32_t gClientConnection;
|
|
uint32_t sCurMgr;
|
|
uint32_t curObj;
|
|
uint64_t playerGUID;
|
|
uint32_t playerBase;
|
|
|
|
uint32_t nextObj;
|
|
uint64_t GUID;
|
|
|
|
playerBase=0;
|
|
|
|
gClientConnection=getInt32(0x012705B0);
|
|
sCurMgr=getInt32(gClientConnection + 0x2d94);
|
|
if (sCurMgr != 0) {
|
|
playerGUID=getInt64(sCurMgr+0xC0);
|
|
if (playerGUID != 0) {
|
|
g_playerGUID = playerGUID;
|
|
curObj=getInt32(sCurMgr+0xAC);
|
|
while (curObj != 0) {
|
|
nextObj=getInt32(curObj + 0x3C);
|
|
GUID=getInt64(curObj + 0x30);
|
|
if (playerGUID == GUID) {
|
|
playerBase = curObj;
|
|
break;
|
|
} else if (curObj == nextObj) {
|
|
break;
|
|
} else {
|
|
curObj = nextObj;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return playerBase;
|
|
}
|
|
|
|
static const unsigned long nameStorePtr = 0x12542D8 + 0x8; // 0x012541C8, 0x011AE3D0+8 , 0x00D29BA8+8 , Player name database
|
|
static const unsigned long nameMaskOffset = 0x024; // Offset for the mask used with GUID to select a linked list
|
|
static const unsigned long nameBaseOffset = 0x01c; // Offset for the start of the name linked list
|
|
static const unsigned long nameStringOffset = 0x020; // Offset to the C string in a name structure
|
|
|
|
void getPlayerName(std::wstring &identity)
|
|
{
|
|
unsigned long mask, base, offset, current, shortGUID, testGUID;
|
|
|
|
if (identity.capacity() < 40) {
|
|
identity.resize(40);
|
|
}
|
|
|
|
mask = getInt32 (nameStorePtr + nameMaskOffset);
|
|
base = getInt32 (nameStorePtr + nameBaseOffset);
|
|
|
|
shortGUID = g_playerGUID & 0xffffffff; // Only half the guid is used to check for a hit
|
|
if (mask == 0xffffffff) {identity.clear(); return;}
|
|
offset = 12 * (mask & shortGUID); // select the appropriate linked list
|
|
current=getInt32 (base + offset + 8); // ptr to lower half of GUID of first element
|
|
offset = getInt32(base + offset); // this plus 4 is the offset for the next element
|
|
if ((current == 0) || (current & 0x1)) {
|
|
identity.clear();
|
|
return;
|
|
}
|
|
testGUID=getInt32(current);
|
|
|
|
while (testGUID != shortGUID)
|
|
{
|
|
current=getInt32(current + offset + 4);
|
|
if ((current == 0) || (current & 0x1)) {
|
|
identity.clear();
|
|
return;
|
|
}
|
|
testGUID=getInt32(current);
|
|
}
|
|
getWString(current + nameStringOffset, identity);
|
|
}
|
|
|
|
void getRealmName(std::string &context)
|
|
{
|
|
if (context.capacity() < 40) {
|
|
context.resize(40);
|
|
}
|
|
getString (STATIC_REALMNAME, context);
|
|
}
|
|
|
|
void getCamera(float camera_pos[3], float camera_front[3], float camera_top[3])
|
|
{
|
|
uint32_t ptr1, ptr2;
|
|
float buf[4][3];
|
|
|
|
ptr1 = getInt32(0x10e1824);
|
|
ptr2 = getInt32(ptr1+0x7da0);
|
|
|
|
peekProc((BYTE *) ptr2+0x08, buf, sizeof(buf));
|
|
|
|
/* camera postition */
|
|
camera_pos[0] = -buf[0][1];
|
|
camera_pos[1] = buf[0][2];
|
|
camera_pos[2] = buf[0][0];
|
|
|
|
/* camera front vector */
|
|
camera_front[0] = -buf[1][1];
|
|
camera_front[1] = buf[1][2];
|
|
camera_front[2] = buf[1][0];
|
|
|
|
/* camera right vector */
|
|
#if 0 /* gets calculated in mumble client */
|
|
camera_right[0] = -buf[2][1];
|
|
camera_right[1] = buf[2][2];
|
|
camera_right[2] = buf[2][0];
|
|
#endif
|
|
|
|
/* camera top vector */
|
|
camera_top[0] = -buf[3][1];
|
|
camera_top[1] = buf[3][2];
|
|
camera_top[2] = buf[3][0];
|
|
}
|
|
|
|
|
|
static int fetch(float *avatar_pos, float *avatar_front, float *avatar_top, float *camera_pos, float *camera_front, float *camera_top, std::string &context, std::wstring &identity) {
|
|
static float lastpos[3]={0.0,0.0,0.0}, lastheading=0.0;
|
|
static int seenpos=0;
|
|
getRealmName (context);
|
|
getPlayerName (identity);
|
|
|
|
BOOL ok = true;
|
|
|
|
// Wow stores as
|
|
// North/South (+ North)
|
|
// East/West (+ West)
|
|
// Up/Down (+Up)
|
|
// ... which isn't a right-hand coordinate system.
|
|
|
|
float pos[3];
|
|
ok = ok && peekProc((BYTE *) p_playerBase + 0x798, pos, sizeof(float)*3);
|
|
/* convert wow -> right hand coordinate system */
|
|
avatar_pos[0] = -pos[1];
|
|
avatar_pos[1] = pos[2];
|
|
avatar_pos[2] = pos[0];
|
|
|
|
float heading=0.0;
|
|
ok = ok && peekProc((BYTE *) p_playerBase + 0x7A8, &heading, sizeof(heading));
|
|
|
|
if ((lastpos[0] == pos[0]) &&
|
|
(lastpos[1] == pos[1]) &&
|
|
(lastpos[2] == pos[2]) &&
|
|
(lastheading == heading)) {
|
|
seenpos++;
|
|
} else {
|
|
seenpos = 0;
|
|
}
|
|
|
|
/* we have seen the exactly same position for n times, check if we're still on the right record */
|
|
if (seenpos > 100) {
|
|
p_playerBase=getPlayerBase();
|
|
}
|
|
|
|
/* FIXME sin/cos (heading) is right from the numbers, but (-heading) is right from the sound position */
|
|
avatar_front[0]=-sin(heading);
|
|
avatar_front[1]=0.0;
|
|
avatar_front[2]=cos(heading);
|
|
|
|
// Dummy top vector, can't tilt your head sideways in wow.
|
|
avatar_top[0]= 0.0;
|
|
avatar_top[1]= 1.0;
|
|
avatar_top[2]= 0.0;
|
|
|
|
getCamera (camera_pos, camera_front, camera_top);
|
|
|
|
if (! ok)
|
|
return false;
|
|
|
|
// printf("P %f %f %f -- %f %f %f \n", avatar_pos[0], avatar_pos[1], avatar_pos[2], avatar_front[0], avatar_front[1], avatar_front[2]);
|
|
|
|
// is it a unit length vector?
|
|
if (fabs((avatar_front[0]*avatar_front[0]+avatar_front[1]*avatar_front[1]+avatar_front[2]*avatar_front[2])-1.0)>0.5) {
|
|
// printf("F %f %f %f\n", front[0], front[1], front[2]);
|
|
return false;
|
|
}
|
|
|
|
// are we around 0/0/0
|
|
if ((fabs(avatar_pos[0])<0.1) && (fabs(avatar_pos[1])<0.1) && (fabs(avatar_pos[2])<0.1)) {
|
|
// printf("P %f %f %f\n", avatar_pos[0], avatar_pos[1], avatar_pos[2]);
|
|
return false;
|
|
}
|
|
|
|
lastpos[0] = pos[0];
|
|
lastpos[1] = pos[1];
|
|
lastpos[2] = pos[2];
|
|
lastheading = heading;
|
|
return true;
|
|
}
|
|
|
|
static int trylock() {
|
|
h = NULL;
|
|
|
|
DWORD pid=getProcess(L"Wow.exe");
|
|
if (!pid)
|
|
return false;
|
|
|
|
h=OpenProcess(PROCESS_VM_READ, false, pid);
|
|
if (!h) {
|
|
DWORD dwError;
|
|
wchar_t errBuf[256];
|
|
|
|
dwError = GetLastError();
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)errBuf, sizeof(errBuf),NULL);
|
|
printf ("Error in OpenProcess: %s\n", errBuf);
|
|
|
|
return false;
|
|
}
|
|
|
|
p_playerBase=getPlayerBase();
|
|
if (p_playerBase != 0) {
|
|
float apos[3], afront[3], atop[3], cpos[3], cfront[3], ctop[3];
|
|
std::string context;
|
|
std::wstring identity;
|
|
|
|
if (fetch(apos, afront, atop, cpos, cfront, ctop, context, identity))
|
|
return true;
|
|
}
|
|
|
|
CloseHandle(h);
|
|
h = NULL;
|
|
return false;
|
|
}
|
|
|
|
static void unlock() {
|
|
if (h) {
|
|
CloseHandle(h);
|
|
h = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static const std::wstring longdesc() {
|
|
return std::wstring(L"Supports World of Warcraft 3.2.2 (10505) (Release) (Euro). With context or identity support.");
|
|
}
|
|
|
|
static std::wstring description(L"World of Warcraft 3.2.2 (Euro)");
|
|
static std::wstring shortname(L"World of Warcraft");
|
|
|
|
static MumblePlugin wowplug = {
|
|
MUMBLE_PLUGIN_MAGIC,
|
|
description,
|
|
shortname,
|
|
about,
|
|
NULL,
|
|
trylock,
|
|
unlock,
|
|
longdesc,
|
|
fetch
|
|
};
|
|
|
|
extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() {
|
|
return &wowplug;
|
|
}
|