Last active
March 1, 2023 05:36
-
-
Save NeKzor/7dd99825ac924f365516adf4bb999a41 to your computer and use it in GitHub Desktop.
Really simple hack that enables jumping in The Stanley Parable.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 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
Do you have a pre-compiled version? Where would I place this file?