// sacredK.dll entry point. Loaded into sacred.exe by the launcher before the // game's own entry point runs, so every hook is in place from the first // instruction of game code. #include #include #include #include "re/config.h" #include "re/crash.h" #include "re/hook.h" #include "re/log.h" #include "re/patch.h" namespace { // The only build we support: Sacred Gold 2.28, sacred.exe // sha256 fe774c6f985b02c9ce03103c2e55e5d853258c0cc3c737ccd211219c74460cb9. // Every hook address in this project is specific to it. constexpr DWORD kExpectedTimestamp = 0x451BBE74; constexpr DWORD kExpectedSizeOfImage = 0x01999000; std::wstring ModuleDir(HMODULE module) { wchar_t path[MAX_PATH]; DWORD length = GetModuleFileNameW(module, path, MAX_PATH); std::wstring dir(path, length); return dir.substr(0, dir.find_last_of(L"\\/") + 1); } bool IsSupportedGameBuild() { auto base = reinterpret_cast(GetModuleHandleW(nullptr)); auto dos = reinterpret_cast(base); auto nt = reinterpret_cast(base + dos->e_lfanew); re::Log("game image: timestamp 0x%08lx, size 0x%08lx", nt->FileHeader.TimeDateStamp, nt->OptionalHeader.SizeOfImage); return nt->FileHeader.TimeDateStamp == kExpectedTimestamp && nt->OptionalHeader.SizeOfImage == kExpectedSizeOfImage; } void Startup(HMODULE module) { std::wstring dir = ModuleDir(module); re::OpenLog((dir + L"sacredK.log").c_str()); re::SetConfigPath(dir + L"sacredK.ini"); re::Log("sacredK loaded"); re::InstallCrashHandler(dir); if (!IsSupportedGameBuild()) { re::Log("unsupported sacred.exe build, no hooks installed"); MessageBoxW(nullptr, L"This sacred.exe is not the supported Sacred Gold 2.28 build.\n" L"sacredK will not patch it; the game runs unmodified.", L"sacredK", MB_OK | MB_ICONWARNING); return; } if (re::ConfigInt("debug", "WaitForDebugger", 0)) { re::Log("waiting up to 60 s for a debugger to attach"); for (int i = 0; i < 600 && !IsDebuggerPresent(); ++i) { Sleep(100); } re::Log(IsDebuggerPresent() ? "debugger attached" : "no debugger, continuing"); } re::InstallHooks(); re::RunStartupFunctions(); } } // namespace BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID) { if (DetourIsHelperProcess()) { return TRUE; } if (reason == DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(module); DetourRestoreAfterWith(); Startup(module); } return TRUE; }