sacred_re.dll/.exe/.ini/.log/_crash files are now sacredK.*; inventory saves with the old "sacred_re.inventory" format id still load. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
80 lines
2.6 KiB
C++
80 lines
2.6 KiB
C++
// 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 <string>
|
|
|
|
#include <windows.h>
|
|
#include <detours.h>
|
|
|
|
#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<const BYTE*>(GetModuleHandleW(nullptr));
|
|
auto dos = reinterpret_cast<const IMAGE_DOS_HEADER*>(base);
|
|
auto nt = reinterpret_cast<const IMAGE_NT_HEADERS32*>(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;
|
|
}
|