// Copyright 2020-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 . #include "Game.h" #include "mumble_positional_audio_utils.h" Game::Game(const uint64_t id, const std::string name) : m_proc(static_cast< procid_t >(id), name) { } Mumble_PositionalDataErrorCode Game::init() { if (!m_proc.isOk()) { return MUMBLE_PDEC_ERROR_TEMP; } const auto &modules = m_proc.modules(); const auto iter = modules.find("GameAssembly.dll"); if (iter == modules.cend()) { return MUMBLE_PDEC_ERROR_TEMP; } // 75 58 jnz short loc_???????? // A1 ?? ?? ?? ?? mov eax, AmongUsClient_c ** // 8B 40 5C mov eax, [eax+5Ch] const std::vector< uint8_t > clientPattern = { 0x75, 0x58, 0xA1, '?', '?', '?', '?', 0x8B, 0x40, 0x5C }; m_client = m_proc.findPattern(clientPattern, iter->second); if (!m_client) { return MUMBLE_PDEC_ERROR_PERM; } // +3 in order to skip to the memory address we actually care about m_client = m_proc.peekPtr(m_proc.peekPtr(m_client + 3)); if (!m_client) { return MUMBLE_PDEC_ERROR_TEMP; } const auto clientC = m_proc.peek< AmongUsClient_c >(m_client); if (!clientC.staticFields) { return MUMBLE_PDEC_ERROR_TEMP; } const auto clientStaticFields = m_proc.peek< AmongUsClient_StaticFields >(clientC.staticFields); if (!clientStaticFields.instance) { return MUMBLE_PDEC_ERROR_TEMP; } m_client = clientStaticFields.instance; const auto fields = clientFields(); const auto playerControlO = m_proc.peek< PlayerControl_o >(fields.playerPrefab); if (!playerControlO.klass) { return MUMBLE_PDEC_ERROR_TEMP; } const auto playerControlC = m_proc.peek< PlayerControl_c >(playerControlO.klass); if (!playerControlC.staticFields) { return MUMBLE_PDEC_ERROR_TEMP; } m_playerControlStaticFields = playerControlC.staticFields; return MUMBLE_PDEC_OK; } PlayerControl_Fields Game::playerControlFields() { const auto playerControlStaticFields = m_proc.peek< PlayerControl_StaticFields >(m_playerControlStaticFields); if (playerControlStaticFields.localPlayer) { return m_proc.peek< PlayerControl_o >(playerControlStaticFields.localPlayer).fields; } return {}; } GameData_PlayerOutfit_Fields Game::playerOutfitFields(const GameData_PlayerInfo_Fields &fields) { const auto dictFields = m_proc.peek< Dictionary_o >(fields.outfits).fields; if (!dictFields.entries || dictFields.count < 1) { return {}; } const auto array = m_proc.peek< Dictionary_Array >(dictFields.entries); return m_proc.peek< GameData_PlayerOutfit_o >(array.items[0].value).fields; } std::string Game::string(const procptr_t address) { const auto object = m_proc.peek< String_o >(address); std::u16string string; string.resize(object.fields.length); if (m_proc.peek(address + sizeof(object), &string[0], sizeof(char16_t) * string.size())) { return utf16ToUtf8(string.data()); } return {}; } const std::string &Game::context(const AmongUsClient_Fields &fields) { std::ostringstream stream; stream << " {\"Game ID\": " << fields.gameId << "}"; m_context = stream.str(); return m_context; } const std::string &Game::identity(const AmongUsClient_Fields &fields, const PlayerControl_Fields &controlFields) { const GameData_PlayerInfo_Fields infoFields = playerInfoFields(controlFields); const GameData_PlayerOutfit_Fields outfitFields = playerOutfitFields(infoFields); std::ostringstream stream; stream << "ID: " << std::to_string(infoFields.playerId) << '\n'; stream << "Name: " << string(outfitFields.playerName) << '\n'; stream << "Level: " << std::to_string(infoFields.playerLevel + 1) << '\n'; stream << "Color ID: " << std::to_string(outfitFields.colorId) << '\n'; stream << "Skin: " << string(outfitFields.skinId) << '\n'; stream << "Hat: " << string(outfitFields.hatId) << '\n'; stream << "Visor: " << string(outfitFields.visorId) << '\n'; stream << "Pet: " << string(outfitFields.petId) << '\n'; stream << "Nameplate: " << string(outfitFields.namePlateId) << '\n'; stream << "Dead: " << (infoFields.isDead ? "true" : "false") << '\n'; stream << "Client ID: " << std::to_string(fields.clientId) << '\n'; stream << "Host ID: " << std::to_string(fields.hostId) << '\n'; stream << "Public game: " << (fields.isGamePublic ? "true" : "false"); m_identity = stream.str(); return m_identity; }