# 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/.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-decompile\memory\` (`project-status.md`: what is done and confirmed, what is open). Update it after commits and before long pauses.