Skip to content

Instantly share code, notes, and snippets.

@NeKzor
Last active March 1, 2023 05:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NeKzor/7dd99825ac924f365516adf4bb999a41 to your computer and use it in GitHub Desktop.
Save NeKzor/7dd99825ac924f365516adf4bb999a41 to your computer and use it in GitHub Desktop.
Really simple hack that enables jumping in The Stanley Parable.
// cl /O2 /MD /LD dllmain.cpp /Fe:tsp-bhop
#include <cstring>
#include <memory>
#include <Windows.h>
#include <Psapi.h>
// client.dll Build 5454
#define IN_JUMP_OFFSET 0xF52670 // /src/game/client/in_main.cpp
#define KEYDOWN_OFFSET 0x165EF // /src/game/client/in_main.cpp
#define KEYUP_OFFSET 0x17305 // /src/game/client/in_main.cpp
#define CC_CTOR_OFFSET 0x986C00 // /src/tier1/convar.cpp
struct ConCommandBase {
void* VTable;
ConCommandBase* Next;
bool Registered;
const char* Name;
const char* HelpString;
int Flags;
};
struct ConCommand : ConCommandBase {
union {
void* CommandCallbackV1;
void* CommandCallback;
void* CommandCallbackInterface;
};
union {
void* CompletionCallback;
void* CommandCompletionCallback;
};
bool HasCompletionCallback : 1;
bool UsingNewCommandCallback : 1;
bool UsingCommandCallbackInterface : 1;
};
struct CCommand {
enum {
COMMAND_MAX_ARGC = 64,
COMMAND_MAX_LENGTH = 512,
};
int ArgC;
int ArgV0Size;
char ArgSBuffer[COMMAND_MAX_LENGTH];
char ArgVBuffer[COMMAND_MAX_LENGTH];
const char* ArgV[COMMAND_MAX_ARGC];
int count() const {
return this->ArgC;
}
const char* at(int index) const {
return this->ArgV[index];
}
};
using _KeyDown = int(__cdecl*)(void* b, const char* c);
using _KeyUp = int(__cdecl*)(void* b, const char* c);
void* in_jump;
_KeyDown KeyDown;
_KeyUp KeyUp;
void IN_BhopDown(const CCommand& args)
{
KeyDown(in_jump, (args.count() > 1) ? args.at(1) : NULL);
}
void IN_BhopUp(const CCommand& args)
{
KeyUp(in_jump, (args.count() > 1) ? args.at(1) : NULL);
}
struct Command {
ConCommand* ptr;
std::unique_ptr<uint8_t[]> data;
Command() {
auto size = sizeof(ConCommand);
data = std::make_unique<uint8_t[]>(size);
ptr = reinterpret_cast<ConCommand*>(data.get());
std::memset(ptr, 0, size);
}
};
static Command startbhop;
static Command endbhop;
unsigned __stdcall Main(void* args)
{
using _Msg = void(__cdecl*)(const char* pMsgFormat, ...);
using _Warning = void(__cdecl*)(const char* pMsgFormat, ...);
using _CommandCallback = void(*)(const CCommand& args);
using _ConCommandCtor = void(__thiscall*)(void* thisptr, const char* name, void* callback, const char* helpstr, int flags, void* compfunc);
auto tier0 = GetModuleHandleA("tier0.dll");
auto client = GetModuleHandleA("client.dll");
auto Msg = reinterpret_cast<_Msg>(GetProcAddress(tier0, "Msg"));
auto Warning = reinterpret_cast<_Warning>(GetProcAddress(tier0, "Warning"));
auto info = MODULEINFO();
if (GetModuleInformation(GetCurrentProcess(), client, &info, sizeof(MODULEINFO))) {
in_jump = reinterpret_cast<void*>((uintptr_t)info.lpBaseOfDll + IN_JUMP_OFFSET);
KeyDown = reinterpret_cast<_KeyDown>((uintptr_t)info.lpBaseOfDll + KEYDOWN_OFFSET);
KeyUp = reinterpret_cast<_KeyUp>((uintptr_t)info.lpBaseOfDll + KEYUP_OFFSET);
auto ConCommandCtor = reinterpret_cast<_ConCommandCtor>((uintptr_t)info.lpBaseOfDll + CC_CTOR_OFFSET);
auto CreateCommand = [ConCommandCtor, Msg](const char* name, _CommandCallback callback, const char* helpstr = "", int flags = 0)
{
auto ret = Command();
ConCommandCtor(ret.ptr, name, callback, helpstr, flags, nullptr);
Msg("[tsp-bhop] Created ConCommand %s at %p\n", name, ret.ptr);
return ret;
};
startbhop = CreateCommand("+bhop", IN_BhopDown);
endbhop = CreateCommand("-bhop", IN_BhopUp);
Msg("--- Loaded tsp-bhop v1.0 (by NeKz) ---\n");
return 0;
}
Warning("--- Failed to load tsp-bhop! ---\n");
return 1;
}
BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved)
{
if (reason == DLL_PROCESS_ATTACH) {
DisableThreadLibraryCalls(module);
CreateThread(0, 0, LPTHREAD_START_ROUTINE(Main), 0, 0, 0);
}
return TRUE;
}
@nardholio
Copy link

Do you have a pre-compiled version? Where would I place this file?

@NeKzor
Copy link
Author

NeKzor commented Jun 29, 2020

@nardholio You probably want to compile this yourself with MSVC, I even included the build command in the very first line. Although I would not recommend trying this anymore since a DLL injector is needed to load the module into the game's process. I ported this code mostly into SourceAutoRecord which is a generic Source Engine plugin that supports lots of games. Last release was a while ago and nobody gave me any feedback for The Stanley Parable but you can try it out here. Don't forget to read the installation section. The command +bhop is still the same which you can bind to any key; for example: bind mwheelup +bhop and/or bind mwheeldown +bhop. Also feel free to open new issues there (if any occurred).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment