ifs_layeredfs/src_injector/dllmain.cpp
2025-10-15 16:28:06 +10:00

180 lines
6.0 KiB
C++

#include <stdio.h>
// 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 <windows.h>. So instead, include these
// lesser-used actual imports
#include <errhandlingapi.h>
#include <libloaderapi.h>
#include <sysinfoapi.h>
#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;