From 18d650a4221eb86e0e7ce1bab9da9229b373746e Mon Sep 17 00:00:00 2001 From: Marc Date: Mon, 8 Jun 2026 16:24:05 +0000 Subject: [PATCH] Expand CLAUDE.md with build constraints, hooking model, and conventions Co-Authored-By: Claude Opus 4.8 (1M context) --- CLAUDE.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 1556402..710c013 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,6 +28,12 @@ cmake --build build --config Release - Output: `bin/` (gMod.dll, TpfConvert.exe) - Version: set in `CMakeLists.txt` (`VERSION_MAJOR/MINOR/PATCH/TWEAK`) +> **Cannot be built or run in this agent environment** (Linux, no MSVC, no 32-bit +> D3D9, no Guild Wars to inject into). Don't burn turns attempting `cmake`/builds +> here — validate changes by reading and matching existing patterns, and lean on +> Windows CI (`ci.yaml`) for the real build. When an actual compile/test is needed, +> ask the user to do it in Visual Studio rather than trying locally. + ## Key dependencies (via vcpkg) - `minhook` — D3D9 function hooking @@ -42,6 +48,47 @@ cmake --build build --config Release 3. `ModfileLoader` — reads `modlist.txt`, loads .tpf/.zip mod archives 4. `TextureClient` / `TextureFunction` — matches and replaces textures by hash at runtime +### Hooking model (read before touching D3D9 code) + +gMod uses **MinHook vtable hooks**, not proxy/subclass wrappers (the old uMod model +was replaced — see `git log` "Replace D3D9 proxy wrappers with vtable hooks"). The +game keeps the **real** `IDirect3D9` / `IDirect3DDevice9` / texture objects byte-for-byte +untouched; gMod patches only the individual vtable slots it cares about (CreateDevice, +CreateTexture/Volume/Cube, UpdateTexture, BeginScene, Set/GetTexture, Release). + +- Per-texture side-state lives **out of band** in `header/D3D9State.h` (`TexState`, + keyed by the real texture pointer), so reverting the hooks fully detaches gMod. +- `RemoveAllD3D9Hooks()` must leave the host process pristine — preserve that invariant. +- A vtable is shared by all instances of a class, so each slot is hooked once; texture + Release slots are hooked lazily on first instance. Don't double-hook. + +## Conventions + +- **Formatting is CI-enforced** by clang-format `22.1.5` (`.clang-format`, applied by + `lint.yaml` on push to `dev`). Don't hand-reformat or "tidy" whole files — it just + churns the diff. Notable rules: `ColumnLimit: 0` (never reflow long D3D signatures / + vtable typedefs), `SortIncludes: Never` (include order is load-bearing in Windows + headers — never reorder), 4-space indent, braces on their own line after functions. +- **C++23 named modules** (`modules/*.ixx`). Editing a module *interface* triggers wide + rebuilds; prefer changing implementation over interface when possible. +- **Public exported API is a fixed surface** — four `extern "C" __declspec(dllexport)` + functions in `dll_main.cpp`: `SetDevice`, `AddFile`, `RemoveFile`, `GetFiles`. These + are consumed by GW Launcher and Daybreak; do **not** change their names, signatures, + or calling convention (`__cdecl`) without coordinating downstream. +- **Commits**: short imperative summaries, lowercase, no trailing period + (e.g. "Keep loaded_files in load order so GetFiles returns priority order"). +- **Branching**: `dev` is the integration branch; PRs target `master`. CI builds on + push to `dev` and on PRs to `master`. + +### Where things live (by size / how often they change) + +- `modules/TextureClient.ixx` (~670 lines) — texture match/replace bookkeeping, hot +- `source/D3D9Hooks.cpp` (~580) — the vtable detours and install/remove +- `modules/TextureFunction.ixx` (~380) — texture creation/conversion helpers +- `source/dll_main.cpp` (~345) — entry point + exported C API +- `modules/ModfileLoader*.ixx` — .tpf/.zip parsing +- `header/Defines.h` — shared structs/constants (`TextureFileStruct`, `HashTuple`, etc.) + ## Notes - Must be injected before d3d9.dll loads, or renamed to d3d9.dll in the GW folder