#include // because GCC's exports aren't as powerful as MSVC's pragma(comment), we need // to name our functions identically to the real ones, which actually collides // with the real ones if we include . So instead, include these // lesser-used actual imports #include #include #include #define DLL_TO_HOOK "ifs_hook.dll" // #define DO_LOG // leave a bunch of room in the string so aspiring modders can just hex edit the filename #define HELP "\0Hello hex editor - The DLL name + this message have 256 bytes allocated for them. You can change the DLL name if you want!" const char DLL_TO_LOAD[256] = DLL_TO_HOOK HELP; #if defined(USE_D3D9) // Anything that uses D3d9.dll, which is most games #define DLL_NAME L"d3d9.dll" #define FOREACH_D3D_FUNC(X) \ X(20, "", Direct3DCreate9On12) \ X(21, "", Direct3DCreate9On12Ex) \ X(24, "", Direct3DShaderValidatorCreate9) \ X(25, "", PSGPError) \ X(26, "", PSGPSampleTexture) \ X(27, "", D3DPERF_BeginEvent) \ X(28, "", D3DPERF_EndEvent) \ X(29, "", D3DPERF_GetStatus) \ X(30, "", D3DPERF_QueryRepeatFrame) \ X(31, "", D3DPERF_SetMarker) \ X(32, "", D3DPERF_SetOptions) \ X(33, "", D3DPERF_SetRegion) \ X(34, "", DebugSetLevel) \ X(35, "", DebugSetMute) \ X(36, "", Direct3D9EnableMaximizedWindowedModeShim) \ X(37, "", Direct3DCreate9) \ X(38, "", Direct3DCreate9Ex) \ #elif defined(USE_DXGI) // Anything newer will include dxgi transitively, so D3d10/11/12 are all covered #define DLL_NAME L"dxgi.dll" #define FOREACH_D3D_FUNC(X) \ X(1, "", ApplyCompatResolutionQuirking) \ X(2, "", CompatString) \ X(3, "", CompatValue) \ X(4, "", DXGIDumpJournal) \ X(5, "", PIXBeginCapture) \ X(6, "", PIXEndCapture) \ X(7, "", PIXGetCaptureState) \ X(8, "", SetAppCompatStringPointer) \ X(9, "", UpdateHMDEmulationStatus) \ X(10, "", CreateDXGIFactory) \ X(11, "", CreateDXGIFactory1) \ X(12, "", CreateDXGIFactory2) \ X(13, "", DXGID3D10CreateDevice) \ X(14, "", DXGID3D10CreateLayeredDevice) \ X(15, "", DXGID3D10GetLayeredDeviceSize) \ X(16, "", DXGID3D10RegisterLayers) \ X(17, "", DXGIDeclareAdapterRemovalSupport) \ X(18, "", DXGIGetDebugInterface1) \ X(19, "", DXGIReportAdapterConfiguration) \ #elif defined(USE_OGL) // OpenGL used by jubeat, older gfdm #define DLL_NAME L"opengl32.dll" // this one has tons of defs so put it in a separate file #include "opengl_defs.h" #else #error choose a valid DLL to hook #endif // Notes on register preservation and safety etc // cdecl/stdcall both push args on stack, on x64 also uses registers. // // On x86, with at least 1 arg in the wrapped function, the compiler // correctly generates stack preservation code. // // Note! If the wrapped function is cdecl instead of stdcall, it'll // need arg propagation. Having it be stdcall makes it a jmp. // // On x64, things are more complex because stdcall isn't used. // 0-arg functions are fine, but because args are placed in registers // (RCX, RDX, R8, R9) and only then overflowed into stack, they need anti-clobber // code. // // Thus, by making the wrapped function have 4 args and stdcall, we // generate correct register preservation for both x86 and x64 targets. // If the function isn't stdcall or cdecl, you're out of luck, but I've // never seen a DLL do that so it Should Totes Be Fine Yo (TM). #define DECLARE_ORIGINAL(ordinal, noname, name) void (WINAPI *real_ ## name)(void* a, void* b, void* c, void* d); FOREACH_D3D_FUNC(DECLARE_ORIGINAL); #ifdef DO_LOG FILE* logfile; #define LOG(...) if (logfile) fprintf(logfile, __VA_ARGS__); fflush(logfile); #else #define LOG(...) #endif void __cdecl onetime_setup(const char *fn_name) { static bool setup_complete = false; LOG("%s\n", fn_name); if (setup_complete) return; #ifdef DO_LOG fopen_s(&logfile, "d3d_hook.log", "w"); LOG("Init via %s\n", fn_name); #endif #ifdef BOMBERGIRL_BULLSHIT // it rewrites the path loader and needs "_dxgi.dll" copied out of system32 // since the loader doesn't deal with full paths properly wchar_t path[] = L"_" DLL_NAME; HMODULE orig = LoadLibraryW(path); #else wchar_t path[MAX_PATH]; if (GetSystemDirectoryW(path, sizeof(path)) == 0) { LOG("Couldn't GetSystemDirectoryW, enjoy your crash\n"); return; } if (wcslen(path) + wcslen(L"\\" DLL_NAME) >= MAX_PATH) { LOG("Original DLL path length exceeds MAX_PATH, enjoy your crash\n"); return; } // can't use wcscat_s, not available on XP wcscat(path, L"\\" DLL_NAME); HMODULE orig = LoadLibraryW(path); #endif if (!orig) { LOG("Couldn't load original dll at %ls, errno %d, enjoy your crash\n", path, (int)GetLastError()); } #define LOAD_ORIGINAL(ordinal, noname, name) \ real_ ## name = (decltype(real_ ## name))GetProcAddress( \ orig, strlen(noname) == 0 ? #name : (const char*)ordinal); \ if(!real_ ## name) \ LOG("Couldn't load " #name " , enjoy your crash\n"); FOREACH_D3D_FUNC(LOAD_ORIGINAL); // off we go LoadLibraryA(DLL_TO_LOAD); setup_complete = true; LOG("Hook init done\n"); } #define REPLICATE_FUNC(ordinal, noname, name) \ extern "C" void __stdcall name(void* a, void* b, void* c, void* d) { \ /* Using this avoids name mangling for stdcall functions (which happens even with extern "C"!) */ \ /* It also removes the need for a .def file */ \ /* ...but only for MSVC. GCC's asm(".section .drectve") and -export directive does not work on ordinals, nor on mangled names */ \ /*__pragma(comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__ ",@" #ordinal noname))*/ \ onetime_setup(__FUNCTION__); \ return real_ ## name(a,b,c,d); \ } FOREACH_D3D_FUNC(REPLICATE_FUNC); BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { return TRUE; } extern "C" __declspec(dllexport) const char __layeredfs_version[] = VER_STRING;