Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mumble-voip/mumble.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavide Beatrici <git@davidebeatrici.dev>2022-06-05 03:48:47 +0300
committerDavide Beatrici <git@davidebeatrici.dev>2022-06-05 03:48:47 +0300
commit799926b8a34883d39d8108f99804f822776ddda1 (patch)
treeb4cc97e884f020ba837a442274bd177e2cb2c313 /plugins
parent444094d2de28dee98fbeb033810ad4c5d668c140 (diff)
FEAT(positional-audio): Rewrite GTAV plugin to use signatures and game structs
It should now work flawlessly with all variants (Rockstar, Steam, Epic Games).
Diffstat (limited to 'plugins')
-rw-r--r--plugins/gtav/Game.cpp139
-rw-r--r--plugins/gtav/Game.h36
-rw-r--r--plugins/gtav/gtav.cpp69
-rw-r--r--plugins/gtav/structs.h224
4 files changed, 354 insertions, 114 deletions
diff --git a/plugins/gtav/Game.cpp b/plugins/gtav/Game.cpp
index 0b7c06990..7f98d39a2 100644
--- a/plugins/gtav/Game.cpp
+++ b/plugins/gtav/Game.cpp
@@ -5,11 +5,32 @@
#include "Game.h"
-#include "mumble_positional_audio_utils.h"
+#include <sstream>
-#include <cstring>
+Game::Game(const uint64_t id, const std::string &name) : m_proc(static_cast< procid_t >(id), name) {
+}
+
+bool Game::setupPointers(const Module &module) {
+ // 48 8B 0D ?? ?? ?? ?? mov rcx, cs:off_????????
+ const std::vector< uint8_t > playerMgrPattern = { 0x48, 0x8B, 0x0D, '?', '?', '?', '?', 0x8A, 0xD3, 0x48, 0x8B,
+ 0x01, 0xFF, 0x50, '?', 0x4C, 0x8B, 0x07, 0x48, 0x8B, 0xCF };
+
+ m_playerMgr = m_proc.findPattern(playerMgrPattern, module);
+ if (!m_playerMgr) {
+ return false;
+ }
+
+ // 48 8B 05 ?? ?? ?? ?? mov rax, cs:qword_????????
+ const std::vector< uint8_t > cameraMgrPattern = { 0x48, 0x8B, 0x05, '?', '?', '?', '?', 0x4A, 0x8B, 0x1C, 0xF0 };
+ m_cameraMgr = m_proc.findPattern(cameraMgrPattern, module);
+ if (!m_cameraMgr) {
+ return false;
+ }
+
+ m_playerMgr = m_proc.peekPtr(m_proc.peekRIP(m_playerMgr + 3));
+ m_cameraMgr = m_proc.peekRIP(m_cameraMgr + 3);
-Game::Game(const procid_t id, const std::string &name) : m_proc((id), name) {
+ return true;
}
Mumble_PositionalDataErrorCode Game::init() {
@@ -22,96 +43,44 @@ Mumble_PositionalDataErrorCode Game::init() {
if (iter == modules.cend()) {
return MUMBLE_PDEC_ERROR_TEMP;
}
- // Find out the offset of the memory region
- const MemoryRegions &regions = iter->second.regions();
- const auto &r = regions.cbegin();
- if (r != regions.cend()) {
- m_moduleBase = r->address;
- }
- // Check if we can get meaningful data from it
- const std::string expected_name = "Grand Theft Auto V";
- if (m_proc.peekString(m_moduleBase + 0x192D120, expected_name.length()) == expected_name) {
- // Steam version
- m_stateAddr = m_moduleBase + 0x2970F70;
- m_avatarPosAddr = m_moduleBase + 0x1FDCD70;
- m_cameraPosAddr = m_moduleBase + 0x1FDCD80;
- m_avatarDirAddr = m_moduleBase + 0x20510E0;
- m_avatarAxisAddr = m_moduleBase + 0x20510D0;
- m_cameraDirAddr = m_moduleBase + 0x20510A0;
- m_cameraAxisAddr = m_moduleBase + 0x20510B0;
- m_playerAddr = m_moduleBase + 0x297E2F4;
- } else if (m_proc.peekString(0x180D4D8, expected_name.length()) == expected_name) {
- // Retail version
- m_stateAddr = m_moduleBase + 0x2733490;
- m_avatarPosAddr = m_moduleBase + 0x1F7EAA0;
- m_cameraPosAddr = m_moduleBase + 0x1C58630;
- m_avatarBaseAddr = m_moduleBase + 0x1B956C0;
- // Avatar pointer
- procptr_t avatarBase = m_proc.peekPtr(m_avatarBaseAddr);
- if (!avatarBase)
- return MUMBLE_PDEC_ERROR_TEMP;
- m_avatarDirAddr = avatarBase + 0x70;
- m_avatarAxisAddr = avatarBase + 0x80;
- m_cameraDirAddr = m_moduleBase + 0x1C5A0F0;
- m_cameraAxisAddr = m_moduleBase + 0x1F7D9F0;
- m_playerAddr = m_moduleBase + 0x273DBAC;
- } else {
- // Return permanent error on unknown version
- return MUMBLE_PDEC_ERROR_PERM;
+ if (!setupPointers(iter->second)) {
+ // TODO: Return MUMBLE_PDEC_ERROR_PERM.
+ // The game process is spawned by its launcher, but it takes at least a few seconds to unpack.
+ // We have to figure out a way to detect that state.
+ return MUMBLE_PDEC_ERROR_TEMP;
}
- // Check if we can get meaningful data from it
- float apos[3], afront[3], atop[3], cpos[3], cfront[3], ctop[3];
- const char *identity = "";
- const char *context = "";
+ if (!m_playerMgr || !m_cameraMgr) {
+ return MUMBLE_PDEC_ERROR_TEMP;
+ }
- return fetchPositionalData(apos, afront, atop, cpos, cfront, ctop, &context, &identity);
+ return MUMBLE_PDEC_OK;
}
-Mumble_PositionalDataErrorCode Game::fetchPositionalData(float *avatarPos, float *avatarDir, float *avatarAxis,
- float *cameraPos, float *cameraDir, float *cameraAxis,
- const char **contextPtr, const char **identityPtr) {
- for (uint8_t i = 0; i < 3; ++i) {
- avatarPos[i] = avatarDir[i] = avatarAxis[i] = cameraPos[i] = cameraDir[i] = cameraAxis[i] = 0.0f;
- }
+CPlayerAngles Game::playerAngles() const {
+ const auto gameCameraAngles = m_proc.peek< CGameCameraAngles >(m_cameraMgr);
+ const auto cameraManagerAngles = m_proc.peek< CCameraManagerAngles >(gameCameraAngles.cameraManagerAngles);
+ const auto cameraAngles = m_proc.peek< CCameraAngles >(cameraManagerAngles.cameraAngles);
- // Boolean value to check if game addresses retrieval is successful
- bool ok;
- // Char values for extra features
- char state, player[50];
-
- // Peekproc and assign game addresses to our containers, so we can retrieve positional data
- ok = m_proc.peek(m_stateAddr, &state, 1)
- && // Magical state value: 0 when in single player, 2 when online and 3 when in a lobby.
- m_proc.peek(m_avatarPosAddr, avatarPos, 12) && // Avatar Position values (X, Z and Y).
- m_proc.peek(m_cameraPosAddr, cameraPos, 12) && // Camera Position values (X, Z and Y).
- m_proc.peek(m_avatarDirAddr, avatarDir, 12) && // Avatar Front Vector values (X, Z and Y).
- m_proc.peek(m_avatarAxisAddr, avatarAxis, 12) && // Avatar Top Vector values (X, Z and Y).
- m_proc.peek(m_cameraDirAddr, cameraDir, 12) && // Camera Front Vector values (X, Z and Y).
- m_proc.peek(m_cameraAxisAddr, cameraAxis, 12) && // Camera Top Vector values (X, Z and Y).
- m_proc.peek(m_playerAddr, player); // Player nickname.
-
- // This prevents the plugin from linking to the game in case something goes wrong during values retrieval from
- // memory addresses.
- if (!ok)
- return MUMBLE_PDEC_ERROR_TEMP;
+ return m_proc.peek< CPlayerAngles >(cameraAngles.playerAngles);
+}
- // State
- if (state != 2) { // If not in-game
- *contextPtr = ""; // Clear contextPtr
- *identityPtr = ""; // Clear identityPtr
- return MUMBLE_PDEC_ERROR_TEMP;
- }
+const std::string &Game::identity(const CNetGamePlayer &player, const CPlayerInfo &info, const CPed &entity) {
+ std::ostringstream stream;
- // Set identity to playername
- escape(player, sizeof(player));
- if (strcmp(player, "") != 0) {
- m_identity = player;
- } else {
- m_identity = "null";
- }
- *identityPtr = m_identity.c_str();
+ stream << "ID: " << std::to_string(player.id) << '\n';
+ stream << "Name: " << info.netData.name << '\n';
+ stream << "Health: " << entity.health << '\n';
+ stream << "Max health: " << entity.maxHealth << '\n';
+ stream << "Armor: " << entity.armor << '\n';
+ stream << "Wanted level: " << info.wantedLevel << '\n';
+ stream << "State: "
+ << std::to_string(static_cast< std::underlying_type_t< decltype(info.gameState) > >(info.gameState)) << '\n';
+ stream << "Peer ID: " << std::to_string(info.netData.peerID) << '\n';
+ stream << "Host token: " << std::to_string(info.netData.hostToken);
- return MUMBLE_PDEC_OK;
+ m_identity = stream.str();
+
+ return m_identity;
}
diff --git a/plugins/gtav/Game.h b/plugins/gtav/Game.h
index 6beefc198..97a48aa0c 100644
--- a/plugins/gtav/Game.h
+++ b/plugins/gtav/Game.h
@@ -6,26 +6,40 @@
#ifndef GTAV_GAME_H_
#define GTAV_GAME_H_
+#include "structs.h"
+
#include "ProcessWindows.h"
#include "PluginComponents_v_1_0_x.h"
class Game {
public:
- Game(const procid_t id, const std::string &name);
-
Mumble_PositionalDataErrorCode init();
- Mumble_PositionalDataErrorCode fetchPositionalData(float *avatarPos, float *avatarDir, float *avatarAxis,
- float *cameraPos, float *cameraDir, float *cameraAxis,
- const char **contextPtr, const char **identityPtr);
+
+ static constexpr bool isMultiplayer(const CNetworkPlayerMgr &mgr) { return mgr.player; }
+
+ CNetworkPlayerMgr playerMgr() const { return m_proc.peek< CNetworkPlayerMgr >(m_playerMgr); }
+
+ CNetGamePlayer player(const CNetworkPlayerMgr &manager) const {
+ return m_proc.peek< CNetGamePlayer >(manager.player);
+ }
+
+ CPlayerInfo playerInfo(const CNetGamePlayer &player) const { return m_proc.peek< CPlayerInfo >(player.info); }
+
+ CPed playerEntity(const CPlayerInfo &info) const { return m_proc.peek< CPed >(info.ped); }
+
+ CPlayerAngles playerAngles() const;
+
+ const std::string &identity(const CNetGamePlayer &player, const CPlayerInfo &info, const CPed &entity);
+
+ Game(const uint64_t id, const std::string &name);
protected:
- ProcessWindows m_proc;
- procptr_t m_moduleBase = 0;
- std::string m_identity;
+ bool setupPointers(const Module &module);
- // Memory addresses
- procptr_t m_stateAddr, m_avatarPosAddr, m_cameraPosAddr, m_avatarBaseAddr, m_avatarDirAddr, m_avatarAxisAddr,
- m_cameraDirAddr, m_cameraAxisAddr, m_playerAddr;
+ ptr_t m_playerMgr;
+ ptr_t m_cameraMgr;
+ std::string m_identity;
+ ProcessWindows m_proc;
};
#endif
diff --git a/plugins/gtav/gtav.cpp b/plugins/gtav/gtav.cpp
index 5680f9193..6ad10b870 100644
--- a/plugins/gtav/gtav.cpp
+++ b/plugins/gtav/gtav.cpp
@@ -6,8 +6,8 @@
#include "Game.h"
#include "MumblePlugin_v_1_0_x.h"
-#include "mumble_positional_audio_utils.h"
+#include <algorithm>
#include <cstring>
#include <memory>
@@ -42,8 +42,7 @@ void mumble_releaseResource(const void *) {
}
mumble_version_t mumble_getVersion() {
- // we reuse the GTA version as plugin version
- return { 1, 59, 2612 };
+ return { 2, 0, 0 };
}
MumbleStringWrapper mumble_getAuthor() {
@@ -59,8 +58,7 @@ MumbleStringWrapper mumble_getAuthor() {
MumbleStringWrapper mumble_getDescription() {
static const char description[] = "Provides positional audio functionality for GTA V. "
- "Supports GTA V version 1.59 Build 2612 (Steam) and 1.38 (Retail). "
- "Also provides identity based on username.";
+ "Identity is provided.";
MumbleStringWrapper wrapper;
wrapper.data = description;
@@ -82,8 +80,16 @@ uint8_t mumble_initPositionalData(const char *const *programNames, const uint64_
continue;
}
- game = std::make_unique< Game >(static_cast< procid_t >(programPIDs[i]), programNames[i]);
- ret = game->init();
+ game = std::make_unique< Game >(programPIDs[i], programNames[i]);
+
+ ret = game->init();
+ if (ret == MUMBLE_PDEC_OK) {
+ const CNetworkPlayerMgr mgr = game->playerMgr();
+ if (!game->isMultiplayer(mgr)) {
+ ret = MUMBLE_PDEC_ERROR_TEMP;
+ }
+ }
+
if (ret != MUMBLE_PDEC_OK) {
game.reset();
}
@@ -101,22 +107,49 @@ void mumble_shutdownPositionalData() {
bool mumble_fetchPositionalData(float *avatarPos, float *avatarDir, float *avatarAxis, float *cameraPos,
float *cameraDir, float *cameraAxis, const char **contextPtr,
const char **identityPtr) {
- if (game->fetchPositionalData(avatarPos, avatarDir, avatarAxis, cameraPos, cameraDir, cameraAxis, contextPtr,
- identityPtr)
- != MUMBLE_PDEC_OK) {
+ std::fill_n(avatarPos, 3, 0.f);
+ std::fill_n(avatarDir, 3, 0.f);
+ std::fill_n(avatarAxis, 3, 0.f);
+
+ std::fill_n(cameraPos, 3, 0.f);
+ std::fill_n(cameraDir, 3, 0.f);
+ std::fill_n(cameraAxis, 3, 0.f);
+
+ const CNetworkPlayerMgr mgr = game->playerMgr();
+
+ if (!game->isMultiplayer(mgr)) {
return false;
}
- /*
- Mumble | Game
- X | X
- Y | Z
- Z | Y
- */
- for (auto vec : { avatarPos, avatarDir, avatarAxis, cameraPos, cameraDir, cameraAxis }) {
- // Swap Y with Z component
+ const CNetGamePlayer player = game->player(mgr);
+
+ const CPlayerInfo info = game->playerInfo(player);
+ if (info.gameState != GameState::Playing) {
+ return true;
+ }
+
+ const CPed ent = game->playerEntity(info);
+
+ std::copy(ent.position.cbegin(), ent.position.cend(), avatarPos);
+ std::copy(ent.forward.cbegin(), ent.forward.cend(), avatarDir);
+ std::copy(ent.up.cbegin(), ent.up.cend(), avatarAxis);
+
+ const CPlayerAngles cam = game->playerAngles();
+
+ std::copy(cam.position.cbegin(), cam.position.cend(), cameraPos);
+ std::copy(cam.forward.cbegin(), cam.forward.cend(), cameraDir);
+ std::copy(cam.up.cbegin(), cam.up.cend(), cameraAxis);
+
+ // Mumble | Game
+ // X | X
+ // Y | Z
+ // Z | Y
+ for (auto &vec : { avatarPos, avatarDir, avatarAxis, cameraPos, cameraDir, cameraAxis }) {
std::swap(vec[1], vec[2]);
}
+ *contextPtr = "";
+ *identityPtr = game->identity(player, info, ent).c_str();
+
return true;
}
diff --git a/plugins/gtav/structs.h b/plugins/gtav/structs.h
new file mode 100644
index 000000000..2829dc11e
--- /dev/null
+++ b/plugins/gtav/structs.h
@@ -0,0 +1,224 @@
+// Copyright 2022 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 GTAV_STRUCTS
+#define GTAV_STRUCTS
+
+#include <array>
+#include <cstdint>
+
+// Reference: https://github.com/Yimura/GTAV-Classes
+
+using ptr_t = uint64_t;
+
+enum class GameState : int32_t {
+ Invalid = -1,
+ Playing,
+ Died,
+ Arrested,
+ FailedMission,
+ LeftGame,
+ Respawn,
+ InMPCutscene
+};
+
+enum class ModelType : uint8_t { Unk0, Object, Unk1, Unk2, Weapon, Vehicle, Ped, Plant = 129 };
+
+// Rockstar Advanced Game Engine (RAGE).
+namespace rage {
+template< typename T > using vector3 = std::array< T, 3 >;
+
+using fvector3 = vector3< float >;
+
+struct netAddress {
+ uint8_t field4;
+ uint8_t field3;
+ uint8_t field2;
+ uint8_t field1;
+};
+
+struct netPlayerData {
+ uint8_t pad1[8];
+ uint64_t rockstarID;
+ uint8_t pad2[52];
+ netAddress relayIP;
+ uint16_t relayPort;
+ uint8_t pad3[2];
+ netAddress externalIP;
+ uint16_t externalPort;
+ uint8_t pad4[2];
+ netAddress internalIP;
+ uint16_t internalPort;
+ uint8_t pad5[6];
+ uint64_t hostToken;
+ uint64_t peerID;
+ uint64_t rockstarID2;
+ uint8_t pad6[12];
+ char name[20];
+};
+
+#pragma pack(push, 4)
+struct fwEntity {
+ uint8_t pad1[32];
+ ptr_t modelInfo; // CBaseModelInfo *
+ uint8_t pad2[1];
+ int8_t entityType;
+ uint8_t pad3[2];
+ uint8_t invisible;
+ uint8_t pad4[3];
+ ptr_t navigation; // CNavigation *
+ uint8_t pad5[16];
+ ptr_t drawData; // rage::fwDrawData *
+ uint8_t pad6[16];
+ rage::fvector3 right;
+ uint8_t pad7[4];
+ rage::fvector3 forward;
+ uint8_t pad8[4];
+ rage::fvector3 up;
+ uint8_t pad9[4];
+ rage::fvector3 position;
+ uint8_t pad10[52];
+ ptr_t netObject; // rage::netObject *
+ uint8_t pad11[176];
+ uint32_t damageBits;
+};
+#pragma pack(pop)
+} // namespace rage
+
+#pragma pack(push, 4)
+struct CPed : public rage::fwEntity {
+ uint8_t hostility;
+ uint8_t pad1[243];
+ float health;
+ uint8_t pad2[28];
+ float maxHealth;
+ uint8_t pad3[124];
+ rage::fvector3 velocity;
+ uint8_t pad4[2564];
+ ptr_t vehicle; // CAutomobile *
+ uint8_t pad5[912];
+ ptr_t playerInfo; // CPlayerInfo *
+ uint8_t pad6[8];
+ ptr_t weaponManager; // CPedWeaponManager *
+ uint8_t pad7[907];
+ uint8_t pedTaskFlag;
+ uint8_t pad8[196];
+ float armor;
+};
+#pragma pack(pop)
+
+struct CGameCameraAngles {
+ ptr_t cameraManagerAngles; // CCameraManagerAngles *
+ uint8_t pad[56];
+};
+
+struct CCameraManagerAngles {
+ ptr_t cameraAngles; // CCameraAngles *
+};
+
+struct CCameraAngles {
+ uint8_t pad1[960];
+ ptr_t playerAngles; // CPlayerAngles *
+ uint8_t pad2[60];
+};
+
+struct CPlayerAngles {
+ uint8_t pad1[16];
+ ptr_t camData; // CPlayerCameraData *
+ uint8_t pad2[24];
+ rage::fvector3 right;
+ uint8_t pad3[4];
+ rage::fvector3 forward;
+ uint8_t pad4[4];
+ rage::fvector3 up;
+ uint8_t pad5[4];
+ rage::fvector3 position;
+ uint8_t pad6[36];
+};
+
+struct CPlayerInfo {
+ uint8_t pad1[32];
+ rage::netPlayerData netData;
+ uint8_t pad2[184];
+ float swimSpeed;
+ uint8_t pad3[20];
+ uint32_t waterProof;
+ uint8_t pad4[76];
+ GameState gameState;
+ uint8_t pad5[12];
+ ptr_t ped; // CPed *
+ uint8_t pad6[40];
+ uint32_t frameFlags;
+ uint8_t pad7[52];
+ uint32_t playerControls;
+ uint8_t pad8[1256];
+ float wantedCanChange;
+ uint8_t pad9[304];
+ uint32_t npcIgnore;
+ uint8_t pad10[12];
+ bool isWanted;
+ uint8_t pad11[7];
+ uint32_t wantedLevel;
+ uint32_t wantedLevelDisplay;
+ uint8_t pad12[1120];
+ float runSpeed;
+ float stamina;
+ float staminaRegen;
+ uint8_t pad13[16];
+ float weaponDamageMult;
+ float weaponDefenceMult;
+ uint8_t pad14[4];
+ float meleeWeaponDamageMult;
+ float meleeDamageMult;
+ float meleeDefenceMult;
+ uint8_t pad15[8];
+ float meleeWeaponDefenceMult;
+};
+
+#pragma pack(push, 1)
+struct CNetGamePlayer {
+ ptr_t vtable;
+ uint8_t pad1[8];
+ ptr_t nonPhysicalData; // CNonPhysicalPlayerData *
+ uint32_t msgID;
+ uint8_t pad2[4];
+ uint8_t activeID;
+ uint8_t id;
+ uint8_t pad3[3];
+ uint16_t complaints;
+ uint8_t pad4[17];
+ ptr_t unknownList[10]; // CNetGamePlayer *
+ uint8_t pad5[24];
+ ptr_t info; // CPlayerInfo *
+};
+#pragma pack(pop)
+
+#pragma pack(push, 2)
+struct CNetworkPlayerMgr {
+ ptr_t vtable;
+ uint8_t pad1[224];
+ ptr_t player; // CNetGamePlayer *
+ uint8_t pad2[144];
+ ptr_t players[32]; // CNetGamePlayer *
+ uint16_t playersMax;
+ uint8_t pad_0282[10];
+ uint16_t playersCount;
+};
+#pragma pack(pop)
+
+static_assert(sizeof(rage::netAddress) == 0x4, "");
+static_assert(sizeof(rage::netPlayerData) == 0x98, "");
+static_assert(sizeof(rage::fwEntity) == 0x18C, "");
+
+static_assert(sizeof(CPed) == 0x1534, "");
+static_assert(sizeof(CGameCameraAngles) == 0x40, "");
+static_assert(sizeof(CCameraManagerAngles) == 0x8, "");
+static_assert(sizeof(CCameraAngles) == 0x408, "");
+static_assert(sizeof(CPlayerAngles) == 0x90, "");
+static_assert(sizeof(CPlayerInfo) == 0xD30, "");
+static_assert(sizeof(CNetGamePlayer) == 0xA8, "");
+static_assert(sizeof(CNetworkPlayerMgr) == 0x28E, "");
+
+#endif