Files
sacred2_k/CLAUDE.md
T
khallaandClaude Opus 5.5 8a7bdfd99f Framework for Sacred 2 Gold, game log mirror, borderless fullscreen
Ported from sacredK with module-relative addresses, hooks by exported
name and a SHA-256 check of the six game modules (Community Patch
2.65.1). Borderless windowed at monitor size replaces exclusive
fullscreen, whose focus loss during loading made the game quit and
crash on exit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 01:39:44 +01:00

4.9 KiB

CLAUDE.md

Guidance for agents working in this repository. Read README.md (what the project is, build, run, layout), docs/modules.md (the game's modules and address convention) and tools/README.md (RE tools, Ghidra project) first; this file adds how we work and what we learned.

What this is

sacred2K.dll is injected into Sacred 2 Gold with Community Patch 2.65.1 (system\sacred2.exe plus orcSystem, s2core, s2logic, s2logicai, s2render DLLs; 2009, 32-bit, MSVC 8 + STLport) by the launcher sacred2K.exe. It hooks and patches the running game: functions are rewritten and mods change behaviour on top of the original. The game stays playable at every step; the user plays it to test. Sister project: sacredK (Sacred Gold, ../sacred_k), whose framework and working rules this one follows.

  • Target files: the user's local installation (see README.md, Supported build). The game folder is given to the launcher once (--game-dir) and kept in build\<preset>\sacred2K.ini, [paths] GameDir. Never hard-code a local path in code, docs or configs.
  • Game texts are German (locale\de_DE); keep the game's names when talking about features.
  • The user's monitor is 3440x1440.

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, in player terms. 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 anything that takes over the desktop (starting the game, autotests).
  • Commit only what the user confirmed, or when asked. End commit messages with the attribution line given by the harness.

Principles

  • Measure before imitating. Log the original's runtime behaviour instead of guessing from decompiles. Temporary diagnostics: log lines start with diag, code is marked TEMPORARY, both 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 into shared headers or tables.
  • Name as you go. Exported functions are named already (decorated names; prefer hooking them with RE_HOOK_EXPORT). Name what we identify ourselves in tools/names.txt (module 0xADDR Name). No bare hex in logic: named re::Address constants with a comment saying where they come from.
  • Data-driven over id lists. Prefer the game's own data tables and scripts over hard-coded ids.
  • Document each mod in docs/<topic>.md: the request (with date), what was measured, addresses, how the mod works. docs/todo.md for deferred items.

Build and run

.\build.ps1                           # x86-debug -> build\x86-debug\
build\x86-debug\sacred2K.exe          # start the game with the DLL

New source files must be added to CMakeLists.txt. Output to check: build\x86-debug\sacred2K.log (written anew each start: module check with running bases, hooks, the game's Orc::Log messages as [game:<level>]). Crashes: sacred2K_crash.txt / .dmp next to the DLL (module+offset; the offset is from the running base, add the preferred base for Ghidra).

Code map

  • src/re/ — framework: module.h (re::Module, re::Address{module, va}, GameFunction<Fn>, build check by SHA-256), RE_HOOK(address, fn) / RE_HOOK_EXPORT(module, "?decorated@@", fn) / RE_HOOK_API (Detours; define fn_original, null until installed), RE_STARTUP(name, fn), patch helpers in patch.h (all take re::Address and verify the original bytes first), config, log, crash handler.
  • src/game/<module or subsystem>/ — the game's structures and functions: orc/string.h (Orc::String), orc/log.cpp (logger hook), render/dx9base.h (cDX9Base, sInitParams).
  • src/mods/ — one file per mod; the top comment explains the request, the measured behaviour and the approach. borderless.cpp: always borderless windowed at monitor size (docs/borderless.md).

Reverse-engineering workflow

  • Find by name first: node tools/exports.mjs <module> "<regex>"; most classes of the DLLs are exported (?method@Class@@...).
  • Decompile: .\tools\decompile.ps1 <module> 0xADDR|symbol -ReadOnly.
  • Disassemble: node tools/disasm.mjs <module> 0xVA [n], or objdump -d -M intel --start-address=.. --stop-address=.. <module>.
  • Verify calling conventions before hooking or calling: decorated names tell them (QAE/UAE = __thiscall, QAA/UAA = __cdecl with this on the stack, SA = static __cdecl, SG = static __stdcall); still check ret N. Use __fastcall(self, edx, ...) for __thiscall.
  • Absolute addresses inside code were relocated with their module: compare and patch them as re::Address, never as raw Ghidra VAs.