Files
gMod/CLAUDE.md
T
2026-06-08 16:24:05 +00:00

4.9 KiB

gMod

A 32-bit Windows DLL that hooks Direct3D 9 to load texture mods for Guild Wars. Continuation of uMod. Integrated with GW Launcher and Daybreak.

Project structure

  • header/ — header files (D3D9Hooks, D3D9State, Defines, Error, Main, Utils)
  • source/ — implementation files (D3D9Hooks.cpp, dll_main.cpp, Error.cpp)
  • modules/ — C++23 module files (ModfileLoader, ModfileLoader_TpfReader, TextureClient, TextureFunction)
  • TpfConvert/ — standalone utility to fix broken .tpf files
  • cmake/ — CMake helper scripts
  • build/ — CMake build tree (generated, ignored)
  • bin/ — compiled output (generated, ignored)

Build

Requires Visual Studio 2022, CMake 3.29+, and vcpkg — all via the VS 2022 Developer PowerShell.

cmake --preset=vcpkg
cmake --open build
# then compile inside Visual Studio, or:
cmake --build build --config Release
  • Target: 32-bit only (-A Win32 / CMAKE_GENERATOR_PLATFORM=win32)
  • Standard: C++23
  • 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
  • libzippp / libzip / zlib — reading .tpf/.zip mod files
  • directxtex — texture format conversion
  • Microsoft.GSL — Guidelines Support Library

How it works

  1. dll_main.cpp — DLL entry point, hooks d3d9.dll via MinHook
  2. D3D9Hooks.cpp — intercepts Direct3DCreate9 and IDirect3DDevice9 methods
  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
  • Reshade > 5.0.1 causes glitches; recommend 5.0.1 or 4.9.1
  • modlist.txt contains full paths to .tpf/.zip mod files, one per line