Skip to content

Commit

Permalink
FEAT(positional-audio): Grounded uses findPattern
Browse files Browse the repository at this point in the history
I was finishing this change this morning, and had been testing on the
two most recent versions, 1.4.3.4578 and 1.4.4.4634. And then steam
starts a download for the game. It turns out they shipped version
1.4.5.4679 as I happened to finish my changes to the plugin to better
support future game updates.  So I booted up the update and it just
worked. Pretty ding dang epic, right?

Anyway, I'm pretty sure the coordinate mapping in
mumble_fetchPositionalData is not quite right. The game is left-hand
z-up and mumble is left-hand y-up. But when I implemented this and
tested it a while back it sounded like it was working. I think it might
be mirroring one or two positional axis the wrong way and then flipping
the directional vectors upside down or something. So it all ends up
working out. But I can't be sure because I haven't tested this in
multiplayer in a while. And the tooling available right now doesn't give
me much confidence that trying to fix it won't just make it worse.  So
if it sounds off to someone, that coordiante mapping might be the reason
why.
  • Loading branch information
sqwishy committed Oct 1, 2024
1 parent c2e2576 commit 89be65b
Showing 1 changed file with 101 additions and 41 deletions.
142 changes: 101 additions & 41 deletions plugins/grounded/grounded.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,31 +123,54 @@ uint8_t mumble_initPositionalData(const char *const *programNames, const uint64_
continue;
}

/* Look up camera positional data through pointer chain */

procptr_t p = iter->second.baseAddress();

if (!(p = proc.peekPtr(p + 0x614C3C8))) {
// An address pointing to the start our pointer chain is moved
// into r10. findPattern and peekRIP read executable regions of
// memory and give us the address that would be copied to r10.
//
// This address is in executable code and points to a data
// page in the executable. It's not moving around between
// program launches. Instead, we use findPattern to support
// different versions of the game, hopefully future versions.
// If a new executable is shipped with a game update, and the
// start of our pointer chain in the data page moves, we can
// find it again by finding this code pattern and seeing what
// it points to.
//
// 4C 8B 15 ?? ?? ?? ?? mov r10,qword ptr ds:[?? ?? ?? ??]
// 44 8D 4A FF lea r9d,qword ptr ds:[rdx-1]
// 49 63 C1 movsxd rax,r9d
// 49 8B 34 C2 mov rsi,qword ptr ds:[r10+rax*8]
const std::vector< uint8_t > pattern = {
0x4C, 0x8B, 0x15, '?', '?', '?', '?', /**/
0x44, 0x8D, 0x4A, 0xFF, /**/
0x49, 0x63, 0xC1, /**/
0x49, 0x8B, 0x34, 0xC2, /**/
};

procptr_t addr, ok;

if (!(addr = proc.findPattern(pattern, iter->second))) {
continue;
}

if (!(p = proc.peekPtr(p + 0x0))) {
if (!(addr = proc.peekRIP(addr + 0x3))) {
continue;
}

if (!(p = proc.peekPtr(p + 0x8))) {
continue;
}
/* Only test that we can read the memory in the address we got.
*
* Normally, the memory at that address is another pointer to
* the start of our pointer chain. But, if we're at the main
* menu and haven't loaded in to any world yet, the pointer at
* this address will be null. We should be able to read this
* memory early on, but expect to read out a null pointer if we
* haven't loaded into the world yet. */

p += 0x700;

GroundedCam _cam;

if (!(proc.peek(p, _cam))) {
if (!(proc.peek(addr, ok))) {
continue;
}

handle = std::make_unique< GroundedHandle >(std::move(proc), p);
handle = std::make_unique< GroundedHandle >(std::move(proc), addr);

return MUMBLE_PDEC_OK;
}
Expand All @@ -159,44 +182,81 @@ void mumble_shutdownPositionalData() {
handle.reset();
}

enum FollowChain {
CHAIN_OK = 0,
CHAIN_LATER,
CHAIN_BAD,
};

enum FollowChain followPointerChain(const ProcessWindows &proc, const procptr_t start, GroundedCam &cam) {
procptr_t chain;

/* If we can't read the starting address, the program probably quit;
* we should shut down our positional audio */
if (!proc.peek< procptr_t >(start, chain)) {
return CHAIN_BAD;
}

if (chain == 0) {
/* Probably at the main menu. We expect this to point to a
* valid address later. */
return CHAIN_LATER;
}

if (!(chain = proc.peekPtr(chain))) {
return CHAIN_BAD;
}

if (!(chain = proc.peekPtr(chain + 0x8))) {
return CHAIN_BAD;
}

if (!proc.peek(chain + 0x700, cam)) {
return CHAIN_BAD;
}

return CHAIN_OK;
}

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);

const ProcessWindows &proc = std::get< 0 >(*handle);
const procptr_t start = std::get< 1 >(*handle);
GroundedCam cam;
auto result = followPointerChain(proc, start, 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);
switch (result) {
case CHAIN_OK:
/* We expect top and front to be unit vectors in the game. */
assert(float3_is_unit(cam.top));
assert(float3_is_unit(cam.front));

std::fill_n(cameraPos, 3, 0.f);
std::fill_n(cameraDir, 3, 0.f);
std::fill_n(cameraAxis, 3, 0.f);
avatarAxis[0] = cameraAxis[0] = -cam.top[0];
avatarAxis[1] = cameraAxis[1] = cam.top[2];
avatarAxis[2] = cameraAxis[2] = -cam.top[1];

return false;
}

/* We expect top and front to be unit vectors in the game. */
assert(float3_is_unit(cam.top));
assert(float3_is_unit(cam.front));
avatarDir[0] = cameraDir[0] = -cam.front[0];
avatarDir[1] = cameraDir[1] = cam.front[2];
avatarDir[2] = cameraDir[2] = -cam.front[1];

avatarAxis[0] = cameraAxis[0] = -cam.top[0];
avatarAxis[1] = cameraAxis[1] = cam.top[2];
avatarAxis[2] = cameraAxis[2] = -cam.top[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;

avatarDir[0] = cameraDir[0] = -cam.front[0];
avatarDir[1] = cameraDir[1] = cam.front[2];
avatarDir[2] = cameraDir[2] = -cam.front[1];
default:
std::fill_n(avatarPos, 3, 0.f);
std::fill_n(avatarDir, 3, 0.f);
std::fill_n(avatarAxis, 3, 0.f);

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]);
std::fill_n(cameraPos, 3, 0.f);
std::fill_n(cameraDir, 3, 0.f);
std::fill_n(cameraAxis, 3, 0.f);

return true;
return result == CHAIN_LATER;
}
}

0 comments on commit 89be65b

Please sign in to comment.