diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 9cd3216f82b..0343cace06b 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -57,6 +57,7 @@ if(WIN32 OR (UNIX AND CMAKE_SYSTEM_NAME STREQUAL "Linux")) "ffxiv" "ffxiv_x64" "gmod" + "grounded" "gtaiv" "gtasa" "gtav" diff --git a/plugins/HostWindows.cpp b/plugins/HostWindows.cpp index e05e2cf86fe..156002e40c0 100644 --- a/plugins/HostWindows.cpp +++ b/plugins/HostWindows.cpp @@ -20,6 +20,10 @@ HostWindows::~HostWindows() { } } +HostWindows::HostWindows(HostWindows &&other) noexcept + : m_pid(other.m_pid), m_handle(std::exchange(other.m_handle, nullptr)) { +} + bool HostWindows::peek(const procptr_t address, void *dst, const size_t size) const { SIZE_T read; const auto ok = ReadProcessMemory(m_handle, reinterpret_cast< void * >(address), dst, size, &read); diff --git a/plugins/HostWindows.h b/plugins/HostWindows.h index bf3e6e3fff1..0662ffe86c3 100644 --- a/plugins/HostWindows.h +++ b/plugins/HostWindows.h @@ -20,6 +20,8 @@ class HostWindows { Modules modules() const; HostWindows(const procid_t pid); + HostWindows(const HostWindows &) = delete; + HostWindows(HostWindows &&other) noexcept; virtual ~HostWindows(); }; diff --git a/plugins/ProcessBase.h b/plugins/ProcessBase.h index 5493e0d28d1..0078ee1a055 100644 --- a/plugins/ProcessBase.h +++ b/plugins/ProcessBase.h @@ -110,6 +110,7 @@ class ProcessBase : public Host { static procid_t find(const std::string &name, const std::multimap< std::wstring, unsigned long long int > &pids); ProcessBase(const procid_t id, const std::string &name); + ProcessBase(ProcessBase &&other) = default; virtual ~ProcessBase(); }; diff --git a/plugins/ProcessWindows.h b/plugins/ProcessWindows.h index 44dba7a7892..7af1182210a 100644 --- a/plugins/ProcessWindows.h +++ b/plugins/ProcessWindows.h @@ -14,6 +14,7 @@ class ProcessWindows : public ProcessBase { procptr_t exportedSymbol(const std::string &symbol, const procptr_t module) const override; ProcessWindows(const procid_t id, const std::string &name); + ProcessWindows(ProcessWindows &&other) = default; virtual ~ProcessWindows(); }; diff --git a/plugins/grounded/CMakeLists.txt b/plugins/grounded/CMakeLists.txt new file mode 100644 index 00000000000..56938599ed8 --- /dev/null +++ b/plugins/grounded/CMakeLists.txt @@ -0,0 +1,18 @@ + +# 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 . + +add_library(grounded SHARED + "grounded.cpp" + + "../Module.cpp" + "../ProcessBase.cpp" + "../ProcessWindows.cpp") + +if(WIN32) + target_sources(grounded PRIVATE "../HostWindows.cpp") +else() + target_sources(grounded PRIVATE "../HostLinux.cpp") +endif() diff --git a/plugins/grounded/grounded.cpp b/plugins/grounded/grounded.cpp new file mode 100644 index 00000000000..951f316d625 --- /dev/null +++ b/plugins/grounded/grounded.cpp @@ -0,0 +1,177 @@ +#include "ProcessWindows.h" +#define MUMBLE_PLUGIN_NO_DEFAULT_FUNCTION_DEFINITIONS +#include "MumblePlugin.h" +#undef MUMBLE_PLUGIN_NO_DEFAULT_FUNCTION_DEFINITIONS + +#include "mumble_positional_audio_utils.h" + + +using GroundedHandle = std::tuple< ProcessWindows, procptr_t >; + +static std::unique_ptr< GroundedHandle > handle; + +/* the indexes of these float[3] from the game are + * 0: south low, north high + * 1: west low, east high + * 2: altitude, high toward sky, goes up when you jump */ +struct GroundedCam { + /* 940 */ float top[3]; + uint8_t _unused1[4]; + /* 950 */ float front[3]; + uint8_t _unused2[4 * 40]; + /* 9fc */ float pos[3]; +}; + +#define unreal_to_mumble_units(unreal) (unreal / 100.f) + +static_assert(sizeof(struct GroundedCam) == 200, ""); + + +mumble_error_t mumble_init(uint32_t) { + return MUMBLE_STATUS_OK; +} + +void mumble_shutdown() { +} + +MumbleStringWrapper mumble_getName() { + static const char name[] = "Grounded"; + + MumbleStringWrapper wrapper; + wrapper.data = name; + wrapper.size = strlen(name); + wrapper.needsReleasing = false; + + return wrapper; +} + +MumbleStringWrapper mumble_getDescription() { + static const char description[] = "Positional audio support for Grounded. Steam release version 1.4.3.4578."; + + MumbleStringWrapper wrapper; + wrapper.data = description; + wrapper.size = strlen(description); + wrapper.needsReleasing = false; + + return wrapper; +} + +MumbleStringWrapper mumble_getAuthor() { + static const char author[] = "MumbleDevelopers"; + + MumbleStringWrapper wrapper; + wrapper.data = author; + wrapper.size = strlen(author); + wrapper.needsReleasing = false; + + return wrapper; +} + +mumble_version_t mumble_getAPIVersion() { + return MUMBLE_PLUGIN_API_VERSION; +} + +void mumble_registerAPIFunctions(void *) { +} + +void mumble_releaseResource(const void *) { +} + +mumble_version_t mumble_getVersion() { + return { 1, 0, 0 }; +} + +uint32_t mumble_getFeatures() { + return MUMBLE_FEATURE_POSITIONAL; +} + +uint8_t mumble_initPositionalData(const char *const *programNames, const uint64_t *programPIDs, size_t programCount) { + for (size_t i = 0; i < programCount; ++i) { + if (strcmp(programNames[i], "Maine-Win64-Shipping.exe") != 0) { + continue; + } + + ProcessWindows proc(programPIDs[i], programNames[i]); + + if (!proc.isOk()) { + continue; + } + + const Modules &modules = proc.modules(); + const auto iter = modules.find("Maine-Win64-Shipping.exe"); + + if (iter == modules.cend()) { + continue; + } + + procptr_t p = iter->second.baseAddress(); + + if (!(p = proc.peekPtr(p + 0x0612cb38))) { + continue; + } + + if (!(p = proc.peekPtr(p + 0x0))) { + continue; + } + + if (!(p = proc.peekPtr(p + 0x8))) { + continue; + } + + p += 0x8c0; + + GroundedCam _cam; + + if (!(proc.peek(p, _cam))) { + continue; + } + + handle = std::make_unique< GroundedHandle >(GroundedHandle{ std::move(proc), p }); + + return MUMBLE_PDEC_OK; + } + + return MUMBLE_PDEC_ERROR_TEMP; +} + +void mumble_shutdownPositionalData() { + handle.reset(); +} + +bool mumble_fetchPositionalData(float *avatarPos, float *avatarDir, float *avatarAxis, float *cameraPos, + float *cameraDir, float *cameraAxis, const char **contextPtr, + const char **identityPtr) { + *contextPtr = ""; + *identityPtr = ""; + + ProcessWindows &proc = std::get< 0 >(*handle); + procptr_t camAddr = std::get< 1 >(*handle); + + GroundedCam cam; + + if (!proc.peek< GroundedCam >(camAddr, cam)) { + 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); + + return false; + } + + avatarAxis[0] = cameraAxis[0] = -cam.top[0]; + avatarAxis[1] = cameraAxis[1] = cam.top[2]; + avatarAxis[2] = cameraAxis[2] = -cam.top[1]; + + avatarDir[0] = cameraDir[0] = -cam.front[0]; + avatarDir[1] = cameraDir[1] = cam.front[2]; + avatarDir[2] = cameraDir[2] = -cam.front[1]; + + avatarPos[0] = cameraPos[0] = unreal_to_mumble_units(cam.pos[0]); + avatarPos[1] = cameraPos[1] = unreal_to_mumble_units(cam.pos[2]); + avatarPos[2] = cameraPos[2] = unreal_to_mumble_units(cam.pos[1]); + + return true; +}