New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Source Engine positional audio plugin #3771
Add Source Engine positional audio plugin #3771
Conversation
fc5319e
to
28792ae
Compare
I compiled the plugin for Mumble 1.3: Mumble_Source_Engine_Plugin_28792ae7e0801a4dd4267eed8dd8e9b8c6902714.zip |
This is a pretty clever way to deal with the plugins getting out-of-date problem. Do you think coding such an evergreen plugin is possible for every game? Also what tools did you use to reverse engineer this? I might try something like this in the future as well. |
Definitely. However, unless the game has an interface system or something you can use to access the classes/functions you need, you have to create a signature pattern for the specific assembly part you're looking for and then iterate through the process' memory until you find it.
I used IDA for the most part, but now that Ghidra is available and open-source I recommend it instead. https://github.com/VSES/SourceEngine2007 was also helpful. |
Please let me know whether the plugin (#3771 (comment)) works and/or there are any issues. |
Just tried it, left/right detection works really well. Not sure if a front/back detection is possible/ever worked, but there's no noticable difference for me. |
Thank you very much for testing it! Front/back detection is not implemented in the current mixer code, the audio source is emitted at 360 degrees. |
4ea944f
to
fa55245
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general I'd appreciate if you added some more documentation to the individual functions (a small Doxygen comment stating what this function is for would be neough) :D
fa55245
to
21b656a
Compare
This pull request introduces 2 alerts when merging 21b656a into 72e1f47 - view on LGTM.com new alerts:
|
d44e60d
to
20f8eb6
Compare
20f8eb6
to
a42e3ae
Compare
Rebased against |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, except that the commits don't follow our new commit guidelines
And imo the commits could be grouped into 2 commits:
- Additions to the plugin utility files
- Added Source-engine plugin
In the description of those commits you can then list all changes in detail (e.g. all added functions and the fact that the L4D plugin got redacted due to it being replaced by the SourceEngine one)
I'm not sure. The description of 6bbde07 is pretty long. |
Shouldn't really matter as there is to limit to the size of the commit message (afaik) But if you think it is getting too long, you can also keep that commit separate while merging the others together :) |
a42e3ae
to
3c02adc
Compare
Done! |
3c02adc
to
801920a
Compare
…o plugins - peekProcString(): reads the specified amount of data at the specified address and returns it as std::string. An empty std::string is returned in case of error. If length is 0, the function reads one byte at a time and stops when either '\0' is found or 3 seconds have passed. The successfully read data is returned, also in case of error. - peekProcVector(): can be used to read a dynamic array (size known at runtime) from the process' memory. Dynamic arrays are part of C99, which should be supported by most compilers nowadays, but std::vector provides many advantages (e.g. easy resize) over a raw array. - getVirtualFunction(): gets the address of a virtual function given its index and the class object's base address. A macro called GET_POINTER_SIZE is added because "is64Bit ? 8 : 4" is used in two places now. Also, it's useful and intuitive for plugin(s) programmers. - sinCos(): calculates sine and cosine of the specified value. On Linux the calculation is guaranteed to be simultaneous. - degreesToRadians(): converts degrees to radians. - isBigEndian(): returns whether the architecture is big-endian, by checking how a 4-byte value is stored in memory. - networkToHost() converts from network byte order to host byte order. I decided to create our own function because ntohs() is part of winsock(2).h on Windows, which means we would have to link to "ws2_32". This commit also adds and makes use of the new "OS_WINDOWS" and "OS_LINUX" definitions. The reason behind their existence is the "_WIN32" and "__linux__" definitions not being intuitive, mainly because they don't follow the same naming convention.
801920a
to
9f3d7c3
Compare
…lugins This function is of critical importance for the Source Engine plugin, let me elaborate on why. Most games consist in an executable and maybe one or more libraries, but they don't have particular exported symbols we can use to easily access (existing) resources inside the process, which means we have to rely on hardcoded offsets and hex pattern scanning. Source Engine games are special: the executable is nothing more than a manager that takes care of loading the core libraries, which are engine(.dll|.so) and client(.dll|.so) (or server(.dll|.so) in the case of a dedicated server). Those libraries have a common exported symbol, which is CreateInterface(). The function takes the interface's name as argument (char *), creates the interface object if it doesn't exist yet and returns a pointer (void *) to it. The interfaces objects are stored in a list called s_pInterfaceList, which is usually an exported symbol on Linux. getExportedSymbol() allows us to get the address to CreateInterface() (different for each loaded library) inside the process and read the function's assembly code in order to reach s_pInterfaceList. If s_pInterfaceList is exported we can get its address with getExportedSymbol(), without the need of CreateInterface().
…gins 5df2bb2 explains how we gain access to the game's interfaces. Once we have access to the interfaces, we retrieve the address of the variables' we're interested in by reading the assembly of one or more functions accessing them. The process for each function is documented in the code.
9f3d7c3
to
501072e
Compare
This is the first special plugin (aside from Link): offsets are not hardcoded, they are retrieved dynamically from the game; unless the functions we gather data from change (unlikely to happen), the plugin is evergreen.
Each commit has a detailed explanation about what it implements and assembly code we parse is provided as comment for each function we gather data from.
For now only Left 4 Dead and Left 4 Dead 2 (both Windows and Linux versions) are supported.
The plugin also works with Wine.
Fixes #3493.