- Launcher (sacred_re.exe) starts sacred.exe with sacred_re.dll injected via Detours; the DLL verifies the Sacred Gold 2.28 build before hooking. - Hook framework: RE_HOOK for game addresses, RE_HOOK_API for Windows APIs, per-hook enable/disable in sacred_re.ini, sacred_re.log. - Game code: Log (0x66f1c0) hooked, CmdLineHasFlag (0x813140) rewritten. - Borderless mod: forces windowed mode, covers the monitor, scales the 1024x768 image aspect-correct, remaps mouse input, keeps running in the background with keyboard input hidden while unfocused. - CMake/Ninja/MSVC x86 build, VS Code setup, disasm tool, function map. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
93 lines
3.2 KiB
Markdown
93 lines
3.2 KiB
Markdown
# sacred_re
|
|
|
|
An incremental reimplementation of **Sacred Gold 2.28** (`sacred.exe`, 2006).
|
|
|
|
`sacred_re.dll` is injected into the original game at startup and replaces game
|
|
functions with rewritten C++ one by one. Everything not yet rewritten keeps
|
|
running as original machine code, so the game stays playable at every step and
|
|
each rewrite can be tested in-game immediately. Once enough is rewritten, the
|
|
original executable can be dropped.
|
|
|
|
No game assets or original code are included; you need your own installed copy
|
|
of Sacred Gold.
|
|
|
|
## Supported build
|
|
|
|
Only this executable is supported (all hook addresses are specific to it):
|
|
|
|
| | |
|
|
|---|---|
|
|
| File | `sacred.exe`, 12,103,680 bytes |
|
|
| SHA-256 | `fe774c6f985b02c9ce03103c2e55e5d853258c0cc3c737ccd211219c74460cb9` |
|
|
| PE timestamp | `0x451BBE74` (2006-09-28) |
|
|
|
|
The DLL checks the timestamp and image size at startup and leaves any other
|
|
build unpatched.
|
|
|
|
## Building
|
|
|
|
Requirements: Visual Studio 2022 Build Tools ("Desktop development with C++",
|
|
MSVC x86 + Windows SDK), CMake ≥ 3.24, Ninja, git (Detours is fetched at
|
|
configure time).
|
|
|
|
```powershell
|
|
.\build.ps1 # x86-debug -> build\x86-debug\
|
|
.\build.ps1 x86-release # x86-release -> build\x86-release\
|
|
```
|
|
|
|
Everything is 32-bit x86: the code runs inside the 32-bit game process. In an
|
|
IDE (CLion, VS Code), select an MSVC toolchain with **x86** architecture and use
|
|
the CMake presets.
|
|
|
|
## Running
|
|
|
|
```powershell
|
|
build\x86-debug\sacred_re.exe --game-dir "D:\Gaming\Sacred Gold" # first run; remembered in sacred_re.ini
|
|
build\x86-debug\sacred_re.exe log # later runs; extra args go to the game
|
|
```
|
|
|
|
`sacred_re.log` (next to the DLL) records what was hooked plus every message the
|
|
game sends to its internal logger, even when the game's own logging is off.
|
|
|
|
Hooks can be switched off individually in `sacred_re.ini` (next to the DLL)
|
|
to compare a rewrite against the original:
|
|
|
|
```ini
|
|
[hooks]
|
|
CmdLineHasFlag=0
|
|
```
|
|
|
|
## Layout
|
|
|
|
```
|
|
src/re/ hook framework, injection-side startup, logging, config
|
|
src/launcher/ sacred_re.exe — starts sacred.exe with the DLL injected
|
|
src/game/ rewritten game code and original-layout structs, by subsystem
|
|
src/mods/ behaviour changes on top of the original (e.g. borderless.cpp)
|
|
docs/ reverse-engineering notes; function-map.md tracks progress
|
|
```
|
|
|
|
## Adding a rewritten function
|
|
|
|
```cpp
|
|
namespace game {
|
|
bool __cdecl CmdLineHasFlag(const char* cmdline, const char* flag, bool quiet);
|
|
RE_HOOK(0x00813140, CmdLineHasFlag); // also defines CmdLineHasFlag_original
|
|
bool __cdecl CmdLineHasFlag(const char* cmdline, const char* flag, bool quiet) { ... }
|
|
}
|
|
```
|
|
|
|
The signature and calling convention must match the original exactly. Add the
|
|
file to `CMakeLists.txt` and a row to `docs/function-map.md`.
|
|
|
|
## Portability rules (for an eventual 64-bit build)
|
|
|
|
The game is 32-bit and must stay so while we hook into it, but new code should
|
|
not make that permanent:
|
|
|
|
- use fixed-width types (`uint32_t`, not `long`/`DWORD`) for anything with a
|
|
fixed layout;
|
|
- never store pointers in, or cast pointers to, 32-bit integers;
|
|
- keep file and network formats behind explicit serialization code instead of
|
|
`fwrite`-ing structs.
|