Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
147 lines
7.9 KiB
Markdown
147 lines
7.9 KiB
Markdown
# CLAUDE.md
|
|
|
|
Guidance for agents working in this repository. Read `README.md` (what the
|
|
project is, build, run, layout) and `tools/README.md` (RE tools, Ghidra
|
|
project, autotest) first; this file adds how we work and what we learned.
|
|
|
|
## What this is
|
|
|
|
`sacredK.dll` is injected into the original **Sacred Gold 2.28**
|
|
`sacred.exe` (2006, 32-bit, MSVC 6) by the launcher `sacredK.exe`. It
|
|
hooks and patches the running game: some functions are rewritten, most of
|
|
the work so far is **mods** — behaviour changes on top of the original
|
|
(widescreen, HUD layout, 7 weapon sets / art slots, key bindings, WASD,
|
|
auto-attack, skill reworks, respec, map fixes, ...). The game stays playable
|
|
at every step; the user plays it to test.
|
|
|
|
- Target binary: the original `sacred.exe` in `D:\Gaming\Sacred Gold\`
|
|
(path in `build\x86-debug\sacredK.ini`, `[paths] GameDir`). Ignore the
|
|
`SacredReborn*` files in that folder.
|
|
- Game texts are German (Fertigkeiten, Attribute, Blitzschlag, ...); keep the
|
|
game's names when talking about features.
|
|
- The user's monitor is 3440x1440; the game renders about 1835x768 scaled up.
|
|
Widescreen goal: same on-screen size as the original, view extended
|
|
horizontally only. Only 16:9 and wider are supported (no 4:3).
|
|
|
|
## How the user works (important)
|
|
|
|
- **The user tests in-game** and reports back ("1: works, 2: ..."). After a
|
|
change, say exactly what to test, numbered. Don't claim something works
|
|
until the user (or a test you ran) confirmed it.
|
|
- **Their game start builds the DLL.** A link error `LNK1168` (DLL locked)
|
|
means the game is running; that's expected, not a failure to fix.
|
|
- **Ask before running the autotest** — it takes over mouse, keyboard and
|
|
screen. Keep tests fast (log markers, not sleeps). Save row 2 (`-Save 2`)
|
|
is the battlemage, the usual test character.
|
|
- **Commit only what the user confirmed**, or when asked. End commit
|
|
messages with the attribution line given by the harness.
|
|
- Feature requests often arrive mid-task as change-request lists; note them,
|
|
finish the piece in hand, then take them in order.
|
|
|
|
## Principles
|
|
|
|
- **Measure before imitating.** Log the original's runtime behaviour (orders,
|
|
fields, distances, draw calls) instead of guessing from decompiles. Many
|
|
wrong turns in this project came from trusting a decompile; most fixes came
|
|
from a one-off log. Temporary diagnostics: log lines start with `diag`,
|
|
code is marked `TEMPORARY`, and both are removed before committing.
|
|
- **No vanilla preservation.** Once a mod works, delete the old path, toggles
|
|
and fallbacks. Keep only migrations (e.g. old save formats).
|
|
- **Refactor duplicates while writing.** Repeated operations go into shared
|
|
headers (`src/game/engine/*.h`, `src/game/ui/*.h`) or tables
|
|
(`re::PatchAll`, `re::ReplaceAll`), not copies.
|
|
- **Name as you go.** Functions, globals (`g_`), fields and constants get
|
|
readable names when identified; add function names to `tools/names.txt`
|
|
(applied to Ghidra with `tools/rename.ps1`). No bare hex in logic: named
|
|
constants with a comment saying where they come from.
|
|
- **Data-driven over id lists.** Prefer the game's own data (art definition
|
|
tables, weapon class, launch type, the game's own behaviour) over per-art
|
|
id constants. Hard-coded art ids don't scale.
|
|
- Document each mod in `docs/<topic>.md`: the request (with date), what was
|
|
measured, addresses, how the mod works. Keep `docs/todo.md` for noted but
|
|
deferred requests.
|
|
|
|
## Build and run
|
|
|
|
```powershell
|
|
.\build.ps1 # x86-debug -> build\x86-debug\
|
|
build\x86-debug\sacredK.exe log # start the game with the DLL
|
|
```
|
|
|
|
New source files must be added to `CMakeLists.txt`. Output to check:
|
|
`build\x86-debug\sacredK.log` (written anew each start; our lines plus the
|
|
game's own log). Crashes: `sacredK_crash.txt` / `.dmp` next to the DLL
|
|
(exception, module+offset, ebp chain); Windows also records the faulting
|
|
offset in the Application event log (`Get-WinEvent`, provider "Application
|
|
Error"). The debug build's run-time checks turn calling-convention mistakes
|
|
into a breakpoint exception (0x80000003, "ESP was not properly saved").
|
|
|
|
## Code map
|
|
|
|
- `src/re/` — framework: `RE_HOOK(address, fn)` (Detours; defines
|
|
`fn_original`), `RE_HOOK_API`, `RE_STARTUP(name, fn)` (runs after hooks are
|
|
installed), patch helpers in `patch.h` (`PatchInstructionDword`,
|
|
`PatchAll`, `RedirectCall`, `RedirectJcc`, `RedirectOperand`,
|
|
`ReplaceCode`/`ReplaceAll`; all verify the original bytes first), config,
|
|
log, crash handler.
|
|
- `src/game/` — the game's structures and functions by subsystem, as inline
|
|
wrappers with the address and what is known:
|
|
`engine/orders.h` (creature orders, `OnCreatureOrder`, creature fields and
|
|
actions, `IsHostileTo`, `DistanceTo`, `ContactDistance`, `WeaponClass`,
|
|
`NearestHostile`), `engine/arts.h` (art definitions, cooldowns, launch
|
|
types), `engine/objects.h` (object manager, nearby creatures, player hero),
|
|
`engine/fx.h` (FX objects and events), `engine/events.h` (engine tick, save
|
|
loaded), `ui/` (UI rects, fonts, events), `addresses.h` (shared tables).
|
|
- `src/mods/` — one file per mod; the top comment explains the request, the
|
|
measured behaviour and the approach. `hud.cpp` is the HUD layer: 1024-wide
|
|
windows are drawn shifted (`Anchor::Center/Right`, or `Screen` for windows
|
|
laid out across the full width), mouse events and the cursor shifted to
|
|
match; other mods use `hud::RenderCentred` / `HandleCentred` /
|
|
`DrawOffsetX` instead of hooking the same function again.
|
|
- `docs/` — notes per topic; `function-map.md` lists known functions.
|
|
|
|
## Reverse-engineering workflow
|
|
|
|
- Decompile: `.\tools\decompile.ps1 0xADDR [...] -Out ghidra\x.c -ReadOnly`
|
|
(persistent Ghidra project in `ghidra/`; large functions can time out —
|
|
fall back to disassembly).
|
|
- Disassemble: `objdump -d --no-show-raw-insn --start-address=.. --stop-address=..`
|
|
on `sacred.exe` (Git Bash has mingw objdump), or `tools/disasm.mjs`.
|
|
A full `.text` dump grepped for immediates/calls is often the fastest way to
|
|
find all users of a constant or function; `tools/find-imm.mjs` does the same.
|
|
- File offset ↔ VA: `.text` VA = offset + 0x400000; `.rdata` 0x88e000 ↔
|
|
0x48e000; `.data` 0x8e5000 ↔ 0x4e5000. Vtables via RTTI
|
|
(`tools/rtti.mjs`, `Class::vfNN` names in Ghidra).
|
|
- **Verify calling conventions before hooking or calling:** check the
|
|
function's `ret N` (callee-popped bytes) and every argument, including
|
|
structs passed by value. A wrong count corrupts the caller's stack. Use
|
|
`__fastcall(self, edx, ...)` as the stand-in for `__thiscall`.
|
|
- Register-passing call sites are handled with naked thunks
|
|
(`__declspec(naked)`, push the registers, call C++, `ret N`) installed with
|
|
`RedirectCall`/`RedirectJcc`.
|
|
|
|
## Pitfalls learned
|
|
|
|
- Two `RE_HOOK`s on the same address don't stack; extend the existing hook
|
|
(or expose a helper from the mod that owns it).
|
|
- UI windows are laid out for 1024x768 and render even while hidden; check a
|
|
control's visible flag (+0x10 bit 0) before drawing extras.
|
|
- The hero's attributes/skills (cCreatureArts, hero +0x3a8) are mirrored
|
|
xor-ed into a protected page (0x182cddc); change them only through the
|
|
game's own functions (they refresh the copy), never by writing fields.
|
|
- `GetKeyState` is per-thread message state: Alt reads as held after Alt+Tab
|
|
until pressed again in the game. Don't gate key handling on it.
|
|
- Hard-coded 1024/768, 512/384, 0x3ff/0x2ff constants appear throughout the
|
|
UI code; widening a window means finding all of them (see `docs/hud.md`,
|
|
`docs/widescreen.md`). Shared drawing routines (e.g. the map's zoomed view)
|
|
may serve windows with different offsets.
|
|
- The game's log messages (`[game] ...` in our log) are useful landmarks,
|
|
e.g. `cEngine End and exit constructor` marks a finished save-game load.
|
|
|
|
## Memory
|
|
|
|
Project memory lives in
|
|
`C:\Users\torha\.claude\projects\D--Projekte-sacred-k\memory\`
|
|
(`project-status.md`: what is done and confirmed, what is open). Update it
|
|
after commits and before long pauses.
|