diff options
author | Davide Beatrici <git@davidebeatrici.dev> | 2022-06-05 03:48:47 +0300 |
---|---|---|
committer | Davide Beatrici <git@davidebeatrici.dev> | 2022-06-05 03:48:47 +0300 |
commit | 799926b8a34883d39d8108f99804f822776ddda1 (patch) | |
tree | b4cc97e884f020ba837a442274bd177e2cb2c313 /plugins | |
parent | 444094d2de28dee98fbeb033810ad4c5d668c140 (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.cpp | 139 | ||||
-rw-r--r-- | plugins/gtav/Game.h | 36 | ||||
-rw-r--r-- | plugins/gtav/gtav.cpp | 69 | ||||
-rw-r--r-- | plugins/gtav/structs.h | 224 |
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 ®ions = 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 |