182 lines
5.5 KiB
C++
182 lines
5.5 KiB
C++
// Copyright 2020-2023 The Mumble Developers. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file at the root of the
|
|
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
|
|
|
|
#ifndef MUMBLE_MUMBLE_PLUGIN_SE_CLIENT_
|
|
#define MUMBLE_MUMBLE_PLUGIN_SE_CLIENT_
|
|
|
|
struct TypeDescription {
|
|
uint32_t fieldType;
|
|
uint32_t fieldName;
|
|
int32_t fieldOffset;
|
|
uint16_t fieldSize;
|
|
int16_t flags;
|
|
uint32_t externalName;
|
|
uint32_t saveRestoreOps;
|
|
uint32_t inputFunc;
|
|
uint32_t td;
|
|
int32_t fieldSizeInBytes;
|
|
uint32_t overrideField;
|
|
int32_t overrideCount;
|
|
float fieldTolerance;
|
|
int32_t flatOffset[2];
|
|
uint16_t flatGroup;
|
|
};
|
|
|
|
struct DataMap {
|
|
uint32_t dataDesc;
|
|
int32_t dataNumFields;
|
|
uint32_t dataClassName;
|
|
uint32_t baseMap;
|
|
int32_t nPackedSize;
|
|
uint32_t optimizedDataMap;
|
|
};
|
|
|
|
struct EntityCacheInfo {
|
|
uint32_t networkable;
|
|
uint16_t baseEntitiesIndex;
|
|
uint16_t isDormant;
|
|
};
|
|
|
|
static int32_t getDataVar(const std::string &name, procptr_t predMap) {
|
|
while (predMap) {
|
|
DataMap dataMap;
|
|
if (!proc->peek(predMap, dataMap)) {
|
|
return 0;
|
|
}
|
|
|
|
// The structure is 4 bytes bigger on Linux.
|
|
const size_t realStructSize = isWin32 ? sizeof(TypeDescription) : sizeof(TypeDescription) + 4;
|
|
|
|
const auto descs = proc->peekVector< TypeDescription >(dataMap.dataDesc, dataMap.dataNumFields, realStructSize);
|
|
for (const auto &desc : descs) {
|
|
if (!desc.fieldName) {
|
|
continue;
|
|
}
|
|
|
|
const auto fieldName = proc->peekString(desc.fieldName);
|
|
|
|
if (fieldName == name) {
|
|
return desc.fieldOffset;
|
|
}
|
|
|
|
if (desc.td) {
|
|
const auto offset = getDataVar(name, desc.td);
|
|
if (offset) {
|
|
return offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
predMap = dataMap.baseMap;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int32_t getDataVarFromEntity(const std::string &name, const procptr_t entity) {
|
|
procptr_t GetPredDescMap;
|
|
|
|
// Brute-force virtual function index.
|
|
for (uint8_t i = 20; i > 16; --i) {
|
|
GetPredDescMap = proc->virtualFunction(entity, i);
|
|
|
|
if (proc->peek< uint8_t >(GetPredDescMap + (isWin32 ? 0 : 1)) == 0xB8) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Windows:
|
|
// B8 ?? ?? ?? ?? mov eax, offset dword_????????
|
|
// C3 retn
|
|
//
|
|
// Linux:
|
|
// 55 push ebp
|
|
// B8 ?? ?? ?? ?? mov eax, offset dword_????????
|
|
// 89 E5 mov ebp, esp
|
|
// 5D pop ebp
|
|
// C3 retn
|
|
return getDataVar(name, proc->peek< uint32_t >(GetPredDescMap + (isWin32 ? 1 : 2)));
|
|
}
|
|
|
|
static procptr_t getLocalPlayer(const procptr_t localClient, procptr_t clientEntityList, const procptr_t engineClient) {
|
|
const auto GetLocalPlayer = proc->virtualFunction(engineClient, 12);
|
|
|
|
// Windows:
|
|
// 6A FF push 0FFFFFFFFh
|
|
// E8 ?? ?? ?? ?? call GetLocalClient
|
|
// 8B 80 ?? ?? ?? ?? mov eax, [eax+?]
|
|
// 83 C4 04 add esp, 4
|
|
// 40 inc eax
|
|
// C3 retn
|
|
//
|
|
// Linux:
|
|
// 55 push ebp
|
|
// 89 E5 mov ebp, esp
|
|
// 83 EC 18 sub esp, 18h
|
|
// C7 04 24 FF FF FF FF mov dword ptr [esp], 0FFFFFFFFh
|
|
// E8 ?? ?? ?? ?? call GetLocalClient
|
|
// 8B 80 ?? ?? ?? ?? mov eax, [eax+?]
|
|
// C9 leave
|
|
// 83 C0 01 add eax, 1
|
|
// C3 retn
|
|
const auto localPlayerIndexOffset = proc->peek< int32_t >(GetLocalPlayer + (isWin32 ? 9 : 20));
|
|
const auto localPlayerIndex = proc->peek< uint32_t >(localClient + localPlayerIndexOffset) + 1;
|
|
|
|
auto GetClientNetworkable = proc->virtualFunction(clientEntityList, 0);
|
|
|
|
// Left 4 Dead:
|
|
// 8B 44 24 04 mov eax, [esp+arg_0]
|
|
// 8B 44 C1 ?? mov eax, [ecx+eax*8+?]
|
|
// C2 04 00 retn 4
|
|
|
|
// Windows:
|
|
// 55 push ebp
|
|
// 8B EC mov ebp, esp
|
|
// 8B 45 08 mov eax, [ebp+arg_0]
|
|
// 3D ?? ?? ?? ?? cmp eax, ?
|
|
// 77 08 ja short 08
|
|
// 8B 44 C1 ?? mov eax, [ecx+eax*8+?]
|
|
// 5D pop ebp
|
|
// C2 04 00 retn 4
|
|
//
|
|
// Linux:
|
|
// 81 6C 24 04 ?? ?? ?? ?? sub [esp+arg_0], ????????
|
|
// EB ?? jmp short GetClientNetworkable
|
|
if (!isWin32) {
|
|
clientEntityList -= proc->peek< int32_t >(GetClientNetworkable + 4);
|
|
GetClientNetworkable = GetClientNetworkable + 10 + proc->peek< int8_t >(GetClientNetworkable + 9);
|
|
|
|
// 55 push ebp
|
|
// 89 E5 mov ebp, esp
|
|
// 8B 45 0C mov eax, [ebp+arg_4]
|
|
// 3D ?? ?? ?? ?? cmp eax, ?
|
|
// 77 13 ja short 13
|
|
// 8B 55 08 mov edx, [ebp+arg_0]
|
|
// 5D pop ebp
|
|
// 8B 84 C2 ?? ?? ?? ?? mov eax, [edx+eax*8+?]
|
|
// C3 retn
|
|
}
|
|
|
|
procptr_t entityCacheInfo;
|
|
|
|
if (isWin32) {
|
|
if (proc->peek< uint8_t >(GetClientNetworkable + 6) == 0xC1) {
|
|
// Left 4 Dead
|
|
entityCacheInfo = clientEntityList + proc->peek< int8_t >(GetClientNetworkable + 7);
|
|
} else {
|
|
entityCacheInfo = clientEntityList + proc->peek< int8_t >(GetClientNetworkable + 16);
|
|
}
|
|
} else {
|
|
entityCacheInfo = clientEntityList + proc->peek< int32_t >(GetClientNetworkable + 20);
|
|
}
|
|
|
|
const auto entity = proc->peek< EntityCacheInfo >(entityCacheInfo + sizeof(EntityCacheInfo) * localPlayerIndex);
|
|
|
|
// We subtract 8 bytes in order to cast from IClientNetworkable to IClientEntity.
|
|
return entity.networkable ? (entity.networkable - 8) : 0;
|
|
}
|
|
|
|
#endif
|