Skip to content

Releases: tkhquang/DetourModKit

DetourModKit v4.0.0

Choose a tag to compare

@tkhquang tkhquang released this 19 Aug 08:22
17c2c4d

Release Title: Clean-break C++23 surface, unified errors, resilient signatures
Version: 4.0.0

Changelog:

Major release. v4.0.0 is a deliberate clean break from the v3.x line: the entire public surface is reshaped into a modern C++23 idiom with zero legacy spellings, followed by a broad correctness- and lifecycle-hardening pass. It gathers everything merged since v3.9.0 (PR #137 through #308). Every distinct v3 capability survives in a clean v4 form. See the v3-to-v4 migration guide. This is a breaking release: rebuild and port your mod against the new headers.

API and errors

  • One unified error channel: every fallible entry point returns Result<T> (std::expected<T, Error>) over a single category-tagged ErrorCode, replacing the per-domain error enums. Best-effort and query surfaces keep documented bool / std::optional returns under an explicit two-tier policy. (#139, #153, #193)
  • Clean-break reshape of every module (scan, memory, hook, config, input, logger, session, rtti, anchor) to consistent v4 naming with encapsulated headers, Address / Region value vocabulary, and API-discipline labels (Callback-safe, Setup/control-plane only) on every public docblock. (#137, #141 to #147, #149, #295)

Hooks

  • Caller-owned RAII handles replace the HookManager singleton: hook::inline_at / hook::mid_at / hook::vmt_for return move-only Hook / VmtHook handles that unhook on drop, plus a declarative hook::install_all table. (#143)
  • Two-step install transaction: inline and mid hooks are created disabled and armed by Hook::enable(), so a detour can never run before its handle is published. (#208)
  • Byte-witnessed hook state: target bytes, not backend results, decide the published state through a four-state patch witness (Original / OwnedPatch / Foreign / Indeterminate), with distinct refusal codes and contained backend exceptions. (#246, #250, #252)
  • Layer-aware hook ledger: per-instance duplicate detection, newest-first layer authority with LayerConflict refusals, and a HookStack for guaranteed newest-first teardown. (#209, #152, #179)
  • Mid-hook adapter pool with DMK-owned exception containment, callback rundown, routed-frame unwind metadata, and bounded retained-chain accounting; the XMM0-15 preservation contract is stated on the surface. (#211, #265, #273)
  • Fail-closed teardown: when quiescence or a restore witness is unprovable (loader lock, foreign bytes, self-destruction from a callback), teardown pins the backend and books the leak on diagnostics counters instead of corrupting the target. (#210, #259, #250)
  • Per-method typed VMT hooking (hook_method / original / remove_method) with fault-contained publication and snapshot safety. (#152, #205, #207)
  • SafetyHook backend sourcing: the submodule pins an upstream commit and DMK reapplies one vendored patch at configure time, with a CI byte-equality gate. (#233)

Scanning and signatures

  • One resolver surface: scan::resolve / scan::resolve_batch over a compiled scan::Pattern, replacing the v3 cascade family; adds bounded-jump pattern syntax, a unified DSL parser, and fail-closed rejection of out-of-range policy enums. (#140, #168, #229)
  • Typed truncation: NoMatch, IncompleteScan, BudgetExceeded, and NotAuthoritative are distinct answers, so a truncated or query-visible sweep can never certify absence or uniqueness. One-pass uniqueness, UTF-16 string transcode, and query-storage exclusion harden the string-xref and readable-scope paths. (#222, #263)
  • Signature resilience: editable manifest over anchors with a collision-closed grammar and mutation-strict trust on content-bearing winning evidence, typed result domains, deterministic N-of-M quorum voting with pairwise-independence gates, offline signature-health analysis, and a drift-telemetry gate. (#162, #163, #164, #166, #167, #224, #226, #240, #247)
  • New anchor tiers: ExportName resolution through the PE export table without entering the loader, and semantic witnesses with immutable RIP evidence and a decode-epoch contract for code constants. (#195, #223, #255, #277)
  • Enclosing-function resolution via .pdata, a fail-closed prologue-fallback identity gate (WarnOnly / RequireIdentity with witness), stolen-span recovery, and section-aware image identity. (#183, #184, #223, #262)

Memory

  • Guarded-memory surface with fault containment, truthful write outcomes (WriteFaulted means nothing written, WriteMayBePartial names the indeterminate prefix), a strict write_in_place tier that never changes protection, and a multi-region pointer-chain walk. (#142, #203, #254)
  • Representation-safe typed reads: an explicit compile-time domain for read<T> (checked read_bool, opt-in aggregates), plus wrap-safe overlap rejection on every copy primitive. (#244, #278)
  • Protection-cache coherence: one key model for reads, storage, and invalidation, serialized cache lifecycle, exact range invalidation, and seam-exact per-region protection changes and restores. (#158, #172, #180, #202)

Config and input

  • Config reshape with watcher IRP safety, exception-safe workers, correct watcher/reload lifecycle, and reload-quiesce ordering. (#144, #173, #174, #217)
  • Input engine hardening: transactional poll cycles, generation-safe removal and rebind, single-owner interception with an owner epoch, witnessed raw XInput teardown, WndProc exchange reconciliation, and an explicit bounded consume table with occupancy reporting. (#161, #214, #219, #237, #253)
  • Typed Logic-DLL unload drains: prepare_logic_dll_unload* returns a typed status, and only SafeToUnload / Drained authorize an unmap; the drain destroys gate-owned callables while the provider is still mapped. (#241, #248)

Logging, session, and lifecycle

  • Value-facade logger with AsyncLogger demoted to an internal transport, single-owner shutdown, loader-lock abandon, allocation-free drain progress, inert fail-closed first use under OOM, and honest callback-safety bounds on the hot path. ([#145](https://github....
Read more

DetourModKit v3.9.0

Choose a tag to compare

@tkhquang tkhquang released this 21 Jun 12:11
eedadd8

Release Title: Guarded writes, cascade tiers, and hardening
Version: 3.9.0

Changelog:
Minor release on top of v3.8.2, spanning three PRs (#134, #135, #136). It adds purely additive public API -- a guarded per-frame write family, new scanner cascade resilience tiers, and config input fusions -- plus a release-readiness hardening pass. Every v3.8.2 API compiles and links unchanged; the bump is MINOR because nothing was removed or changed.

Memory

  • Guarded per-frame write primitives: new seh_write / seh_write_bytes / seh_write_chain / seh_write_chain_bytes, the write counterpart of the seh_read_* family. They write already-writable game memory under the same fault guard with no page-protection change, instruction-cache flush, or cache invalidation, so a stale chain fails closed instead of faulting the host. write_bytes stays the setup/patch-only primitive (it flips protection, flushes the i-cache, and invalidates the cache range). (#136)

Scanner

  • Cascade name/string resilience tiers: AddrCandidate rows can now resolve by a mangled MSVC type name (ResolveMode::RttiVtable) or a string literal (ResolveMode::StringXref), layered above the byte-pattern tiers so a signature survives a patch that moves the bytes. Both backends are unique-only and host-module-scoped by default. (#135)

Config and input

  • Hold-combo fusion and consume facet: register_hold_combo is the hold-mode mirror of register_press_combo (and returns an InputBindingGuard), and register_consume_flag adds a per-binding <key>.Consume input-suppression facet. A new register_atomic overload infers the INI default from the atomic's current value. (#134)

Release hardening

  • on_dll_attach is now noexcept, so an allocation failure during attach can never cross the DllMain loader lock, and the MinGW x64 fault guard now also covers the string-xref window scans (matching the scanner). Includes assorted RTTI self-heal, config, scanner, and input hardening with no changes to existing API or behavior. (#136)

Backwards compatibility

  • All v3.8.2 APIs are unchanged; the release only adds symbols, so existing mods compile and link without edits.
  • Because DetourModKit ships as a static library, rebuild against this release rather than reusing a prebuilt v3.8.2 archive.

Full Changelog: v3.8.2...v3.9.0

Artifacts:
This release includes packages for different build environments. Both packages ensure library names are prefixed with lib for consistency (e.g., libDetourModKit.a, libDetourModKit.lib).

  • DetourModKit_MinGW_v3.9.0.zip: Built with MinGW (g++). Contains libDetourModKit.a, libsafetyhook.a, libZydis.a, libZycore.a, headers, and CMake config files. find_package links the transitive static chain via the DetourModKit::deps imported target.
  • DetourModKit_MSVC_v3.9.0.zip: Built with MSVC (Visual Studio 2022). Contains libDetourModKit.lib, safetyhook.lib, Zydis.lib, Zycore.lib, headers, and CMake config files. Same DetourModKit::deps wiring as the MinGW package.

These packages are ready to be used by other CMake projects via find_package(DetourModKit).
See README for detailed usage instructions.

DetourModKit v3.8.2

Choose a tag to compare

@tkhquang tkhquang released this 15 Jun 11:08
bad300d

Release Title: Dependency refresh and CMake 3.28 floor
Version: 3.8.2

Changelog:
Patch release on top of v3.8.1, a single PR (#133). It refreshes the vendored and fetched dependencies to their latest stable releases and raises the CMake minimum to match. No DetourModKit source or API changes: every v3.8.1 API compiles and links unchanged. A mod sees no behavior change beyond the bundled dependency fixes.

Dependency updates

  • SafetyHook v0.6.9 -> v0.7.0: public hooking API (InlineHook, MidHook, VmtHook, create_*, Allocator) is unchanged. Carries the only Windows-relevant runtime fix since the prior pin: an access violation when disable is called during atexit. The opt-in C++20 module interface stays off (DetourModKit never sets SAFETYHOOK_USE_CXXMODULES / SAFETYHOOK_BUILD_MODULE). Still bundles Zydis v4.1.0. (#133)
  • SimpleIni -> v4.26: drop-in. The narrow CSimpleIniA (SI_CONVERT_WIN32) path DetourModKit uses for config parsing is unchanged. (#133)
  • DirectXMath -> jun2026 (3.21b): clean fast-forward of the header-only library re-exported for consumers; DetourModKit calls none of it directly. (#133)
  • GoogleTest v1.14.0 -> v1.17.0: test-only build dependency, not shipped in the installed package. (#133)
  • Zydis held at v4.1.0: bundled transitively by SafetyHook; the only upstream release since is a Meson-build patch with no relevance to this build. (#133)

Build requirements

  • CMake minimum raised 3.25 -> 3.28 to match SafetyHook's existing requirement (the floor was already enforced by the vendored submodule). Applies to the root and tests builds and CMakePresets.json. Consumers that add DetourModKit via add_subdirectory must now declare cmake_minimum_required(VERSION 3.28); find_package consumers of a prebuilt package are unaffected.

Backwards compatibility

  • All v3.8.1 APIs unchanged. This is a dependency and build-tooling refresh, not a source change.
  • Because DetourModKit ships as a static library, rebuild against this release rather than reusing a prebuilt v3.8.1 archive.

Full Changelog: v3.8.1...v3.8.2

Artifacts:
This release includes packages for different build environments. Both packages ensure library names are prefixed with lib for consistency (e.g., libDetourModKit.a, libDetourModKit.lib).

  • DetourModKit_MinGW_v3.8.2.zip: Built with MinGW (g++). Contains libDetourModKit.a, libsafetyhook.a, libZydis.a, libZycore.a, headers, and CMake config files. find_package links the transitive static chain via the DetourModKit::deps imported target.
  • DetourModKit_MSVC_v3.8.2.zip: Built with MSVC (Visual Studio 2022). Contains libDetourModKit.lib, safetyhook.lib, Zydis.lib, Zycore.lib, headers, and CMake config files. Same DetourModKit::deps wiring as the MinGW package.

These packages are ready to be used by other CMake projects via find_package(DetourModKit).
See README for detailed usage instructions.

DetourModKit v3.8.1

Choose a tag to compare

@tkhquang tkhquang released this 15 Jun 10:34

Release Title: Multiple-inheritance-safe offset healing
Version: 3.8.1

Changelog:
Patch release on top of v3.8.0, a single PR (#132). It closes a silent correctness trap in direct-object self-healing under multiple inheritance and adds an opt-in shape that avoids it. Strictly additive: a mod that does not opt in sees no behavior change, and all v3.8.0 APIs compile and link unchanged.

Reverse-RTTI self-heal

  • New Indirection::CompleteObject: matches only the primary subobject (COL.offset == 0), so a direct-object heal can no longer latch a multiple-inheritance secondary base vtable (whose COL still names the complete type) and report an offset shifted by the subobject delta. (#132)
  • HealHit::col_offset: surfaces the matched subobject delta so ObjectBase / Any callers can detect a secondary-base match (nonzero on a direct-object match means the offset is shifted). (#132)

Backwards compatibility

  • All v3.8.0 APIs unchanged. CompleteObject is opt-in; PointerToObject, ObjectBase, and Any behavior and their enum values are unchanged (pinned: Any stays 2, CompleteObject is 3).
  • Because DetourModKit ships as a static library, rebuild against this release rather than reusing a prebuilt v3.8.0 archive.

Full Changelog: v3.8.0...v3.8.1

Artifacts:
This release includes packages for different build environments. Both packages ensure library names are prefixed with lib for consistency (e.g., libDetourModKit.a, libDetourModKit.lib).

  • DetourModKit_MinGW_v3.8.1.zip: Built with MinGW (g++). Contains libDetourModKit.a, libsafetyhook.a, libZydis.a, libZycore.a, headers, and CMake config files. find_package links the transitive static chain via the DetourModKit::deps imported target.
  • DetourModKit_MSVC_v3.8.1.zip: Built with MSVC (Visual Studio 2022). Contains libDetourModKit.lib, safetyhook.lib, Zydis.lib, Zycore.lib, headers, and CMake config files. Same DetourModKit::deps wiring as the MinGW package.

These packages are ready to be used by other CMake projects via find_package(DetourModKit).
See README for detailed usage instructions.

DetourModKit v3.8.0

Choose a tag to compare

@tkhquang tkhquang released this 14 Jun 20:56

Release Title: Anchor Fingerprinting and Typed Diagnostics
Version: 3.8.0

Changelog:
Additive feature release on top of v3.7.0, spanning 3 PRs (#129 through #131). It adds an address-independent anchor fingerprint for drift diffing, typed diagnostics event streams, and a one-call diagnostics snapshot, on top of fail-closed hardening of the scanner and reverse-RTTI paths and a behavior-preserving namespace-block migration. Every addition is opt-in and strictly additive: a mod that uses none of it sees no behavior change, and all v3.7.x APIs compile and link unchanged.

Update-resilience: anchor fingerprinting

  • Address-independent anchor fingerprint (Anchors::anchor_fingerprint): a stable 64-bit FNV-1a hash over an anchor's declarative resolution evidence, excluding the resolved address, so a manifest diff distinguishes expected drift ("same evidence, new address") from a rewritten signature ("new evidence path"). (#131)

Diagnostics

  • Typed diagnostics event streams: Diagnostics::scanner_faults() and Diagnostics::hook_lifecycle() publish ScannerFaultEvent / HookLifecycleEvent to subscribers instead of log-only. (#131)
  • One-call diagnostics snapshot (diagnostics_dump.hpp, Diagnostics::collect): aggregate intentional-leak counters, hook counts, anchor quality, and drift heal/fail tallies into a single value Snapshot. (#131)

Robustness and internals

  • Fail-closed hardening of the scanner and reverse-RTTI resolution paths: validate before dereference and reject malformed vtable/RTTI input. (#130)
  • Namespace-block migration across the core sources (file-scope using namespace replaced with namespace DetourModKit { ... }), plus verified audit fixes; behavior-preserving. (#129)

Backwards compatibility

  • All v3.7.x APIs unchanged. Every item above is strictly additive or internal hardening, so this is a minor release.
  • Because DetourModKit ships as a static library, rebuild against this release rather than reusing a prebuilt v3.7.0 archive.

Full Changelog: v3.7.0...v3.8.0

Artifacts:
This release includes packages for different build environments. Both packages ensure library names are prefixed with lib for consistency (e.g., libDetourModKit.a, libDetourModKit.lib).

  • DetourModKit_MinGW_v3.8.0.zip: Built with MinGW (g++). Contains libDetourModKit.a, libsafetyhook.a, libZydis.a, libZycore.a, headers, and CMake config files. find_package links the transitive static chain via the DetourModKit::deps imported target.
  • DetourModKit_MSVC_v3.8.0.zip: Built with MSVC (Visual Studio 2022). Contains libDetourModKit.lib, safetyhook.lib, Zydis.lib, Zycore.lib, headers, and CMake config files. Same DetourModKit::deps wiring as the MinGW package.

These packages are ready to be used by other CMake projects via find_package(DetourModKit).
See README for detailed usage instructions.

DetourModKit v3.7.0

Choose a tag to compare

@tkhquang tkhquang released this 13 Jun 18:55

Release Title: Declarative Anchors, Update-Resilience, and Parallel Scanning
Version: 3.7.0

Changelog:
Additive feature release on top of v3.6.1, spanning 31 PRs (#97 through #128). It adds a declarative self-healing anchor registry, a string-reference xref resolver, durable drift manifests and per-subsystem diagnostics, parallel batch scanning and cascade resolution, and a large round of correctness, concurrency, and performance hardening. Every addition is opt-in and strictly additive: a mod that uses none of it sees no behavior change, and all v3.6.x APIs compile and link unchanged.

Update-resilience: anchors, xref, drift manifests

  • Declarative anchor registry (anchors.hpp) over the vtable / cascade / code-constant / string-xref / manual backends, with two-signal quorum corroboration and optional post-resolve validators. (#97, #100)
  • String-reference xref resolver (Scanner::find_string_xref): locate an immutable literal, then its unique RIP-relative reference (load site, enclosing function, or cached global pointer slot), with an opt-in Zydis broad sweep. (#98, #99)
  • Drift manifest (drift_manifest.hpp) to serialize and diff self-heal reports across game versions, per-subsystem diagnostics counters (diagnostics.hpp), and a per-game scan profile (profile.hpp). (#101, #123)

Scanner: parallel resolution and tiered SIMD

  • Parallel batch scanning and resolution over a transient worker pool: scan_regions_batch, scan_module_batch, and resolve_cascade_batch. (#125, #126)
  • Tiered SIMD prefilter and verify (SSE2/AVX2 at runtime, opt-in AVX-512), plus E9/FF25 prologue-recovery fallback for cascade resolution. (#119, #112, #123)

Hooks

  • VMT hook pre-flight config and a bulk enable/disable hook API; layered inline/mid hooks now unwind newest-first. (#101, #109, #110)

Input

  • Generation-checked BindingToken handles let a high-frequency consumer query a binding without the per-call name hash, failing closed on any reshape. (#125)

Performance and robustness

  • Allocation-free async-logger hot path with prompt writer wakeup, a MinGW VEH fault guard for seh_read, SRWLOCK-backed reader/writer locks (srw_shared_mutex.hpp), and memory cache-line hygiene. (#108, #117, #118, #119)
  • A broad sweep of correctness and concurrency hardening across the cache, hooks, input, config, and lifecycle paths, including the v3.7.0 release audit fixes. (#105, #106, #113, #115, #116, #127, #128)

Backwards compatibility

  • All v3.6.x APIs unchanged. Every item above is strictly additive or internal hardening, so this is a minor release.
  • Because DetourModKit ships as a static library, rebuild against this release rather than reusing a prebuilt v3.6.1 archive.

Full Changelog: v3.6.1...v3.7.0

Artifacts:
This release includes packages for different build environments. Both packages ensure library names are prefixed with lib for consistency (e.g., libDetourModKit.a, libDetourModKit.lib).

  • DetourModKit_MinGW_v3.7.0.zip: Built with MinGW (g++). Contains libDetourModKit.a, libsafetyhook.a, libZydis.a, libZycore.a, headers, and CMake config files. find_package links the transitive static chain via the DetourModKit::deps imported target.
  • DetourModKit_MSVC_v3.7.0.zip: Built with MSVC (Visual Studio 2022). Contains libDetourModKit.lib, safetyhook.lib, Zydis.lib, Zycore.lib, headers, and CMake config files. Same DetourModKit::deps wiring as the MinGW package.

These packages are ready to be used by other CMake projects via find_package(DetourModKit).
See README for detailed usage instructions.

DetourModKit v3.6.1

Choose a tag to compare

@tkhquang tkhquang released this 09 Jun 03:03
84ec4b8

Release Title: Gamepad Consume Leading-Edge Hardening
Version: 3.6.1

Changelog:
Patch release on top of v3.6.0. It closes a leading-edge race in the gamepad passthrough suppression shipped in v3.6.0: a consume chord can no longer leak a single trigger frame to the game when the modifier and trigger are pressed within one poll interval. The fix is internal and behavioral -- there are no public API changes, and a mod that does not set InputBinding::consume on a gamepad binding sees no difference. All v3.6.0 APIs compile and link unchanged.

Input: Close the Gamepad Consume Leading-Edge Window

The v3.6.0 consume feature hides a claimed gamepad button from the state the game reads, via the XInputGetState detour. The suppression mask was published reactively by the poll thread, so it trailed the physical controller by up to one poll cycle. A game that polls XInput faster than the mod's poll loop (the usual case) could read the trigger's leading edge in that gap, before the mask caught up -- a one-frame leak of an otherwise-suppressed press. Two changes close the window:

  • Pre-arm. The poll loop now claims a consume chord's digital gamepad trigger as soon as the chord's modifiers are held, before the trigger is physically pressed, so the published mask is already up when the trigger arrives. Masking a button that is not yet down is a no-op against the game's state, and the consume-until-release latch still trails the trigger, so the trailing edge and the modifier-released-before-trigger guarantee are unchanged.

  • Detour-side chord evaluation. A consume chord the detour can evaluate from XINPUT_GAMEPAD.wButtons alone (its modifiers and masked triggers are all digital gamepad buttons) is reduced to a compact rule and published to the detour behind a seqlock. The detour evaluates the rule against the exact button snapshot the game is about to read, so a modifier and trigger that arrive in the same sub-poll-interval snapshot are masked on the very frame the game reads them, not a cycle later. The rule path reproduces the poll loop's strict-match decision exactly -- a chord is rejected when a modifier belonging to a different chord is held -- so the reactive and detour paths never disagree. When any known modifier is not a digital gamepad button (a keyboard or analog modifier the detour cannot observe), no rules are published and the reactive pre-arm alone covers the chord.

Behavior and safety

  • Detour-side rule masking shares the reactive mask's time-to-live and is gated to focused-and-connected poll cycles, so a stalled or backgrounded poll thread stops all masking and the game regains its input -- the same self-healing guarantee the reactive mask already had.
  • The rule list is published by the binding-mutation path under the input bindings lock and read lock-free by the game's XInput threads. A torn or mid-update read masks nothing for that frame and resolves on the next game poll, microseconds later. Rules change only on a binding rebuild, so a torn read never coincides with steady gameplay input.

Backwards compatibility

  • No public API changes. The fix lives entirely in the internal active-input layer; the InputBinding::consume, InputManager::set_consume / InputPoller::set_consume, and Config::register_consume_flag surface from v3.6.0 is unchanged.
  • Because DetourModKit ships as a static library, rebuild against this release rather than reusing a prebuilt v3.6.0 archive.

Full Changelog: v3.6.0...v3.6.1

Artifacts:
This release includes packages for different build environments. Both packages ensure library names are prefixed with lib for consistency (e.g., libDetourModKit.a, libDetourModKit.lib).

  • DetourModKit_MinGW_v3.6.1.zip: Built with MinGW (g++). Contains libDetourModKit.a, libsafetyhook.a, headers, CMake config files. (Zydis & Zycore are statically linked into libsafetyhook.a).
  • DetourModKit_MSVC_v3.6.1.zip: Built with MSVC (Visual Studio 2022). Contains libDetourModKit.lib, libsafetyhook.lib (or safetyhook.lib if SafetyHook doesn't get the prefix), headers, CMake config files. (Zydis & Zycore are statically linked into libsafetyhook.lib/safetyhook.lib).

These packages are ready to be used by other CMake projects via find_package(DetourModKit).
See README for detailed usage instructions.

DetourModKit v3.6.0

Choose a tag to compare

@tkhquang tkhquang released this 08 Jun 16:59
97b055c

Release Title: Active Input Layer and Reverse RTTI Dissection
Version: 3.6.0

Changelog:
Additive feature release on top of v3.5.0 across two independent areas. The input system gains an opt-in active layer (mouse-wheel bindings and gamepad passthrough suppression), and a new reverse-direction RTTI facility answers the slot-first questions a mod asks against a drifting game binary (identify the type behind a pointer, label a struct's slots, and self-heal a field offset after a patch shifts the layout). Every addition is opt-in and strictly additive: a mod that uses none of it sees no behavior change, installs no hooks, and keeps the input system purely observational. All v3.5.x APIs compile and link unchanged.

Input: Mouse-Wheel Capture and Gamepad Passthrough Suppression (#93)

  • Mouse-wheel bindings. InputSource::MouseWheel and the WheelCode namespace (Up, Down, Left, Right) with the mouse_wheel(code) factory. parse_input_name resolves "WheelUp", "WheelDown", "WheelLeft", "WheelRight", and input_code_to_name / format_input_code round-trip them. Wheel codes are trigger-only, Press-mode inputs: each notch surfaces as exactly one Press edge. The poll loop lazily installs a window-procedure subclass on the process main window only when a wheel binding exists, and the subclass self-heals across a window recreation (for example a fullscreen toggle) by re-subclassing on WM_NCDESTROY.

  • Gamepad passthrough suppression. InputBinding::consume opt-in flag, plus InputManager::set_consume(name, consume) and InputPoller::set_consume(name, consume) to toggle it on a named binding at runtime. When set, the binding's trigger is hidden from the game: digital gamepad buttons via an XInputGetState (and ordinal-100 XInputGetStateEx) inline hook, and the mouse wheel via the window-procedure hook. A consume-until-release latch keeps a trigger masked until the physical button is released plus a short grace window, so releasing a modifier a frame before the trigger cannot leak a bare trigger to the game. Analog triggers, stick directions, and keyboard/mouse buttons cannot be masked.

  • Config-driven consume. Config::register_consume_flag(section, ini_key, log_name, binding_name, default) drives the consume flag from an INI key, so the choice lives next to the combo it suppresses.

Input behavior and safety

  • The interception hooks are process-global and single-instance: one XInput hook bound to one gamepad index, one window-procedure subclass, and one suppression mask. The InputManager singleton is the intended owner; running two pollers that both opt in is unsupported.
  • A stalled or torn-down poll thread cannot latch the game's input off. The suppression mask carries a time-to-live, and the wheel-swallow flag is re-driven every poll cycle, so removing the last consume binding (or a hot-reload reset via clear_bindings) disarms masking on a later cycle even though the subclass and XInput hook stay installed until shutdown.

RTTI: Reverse Dissection and Self-Healing Offsets (#95)

A new header, DetourModKit/rtti_dissect.hpp, adds the DetourModKit::Rtti reverse-direction facility. The existing forward walker answers "what type is the object behind this vtable?"; this answers the inverse, slot-first questions. Scope is x64 MSVC.

  • Per-slot identification. Rtti::identify_pointee_type(slot_addr, out) reads the pointer-sized slot at slot_addr and identifies the RTTI type of the object it refers to, accepting both shapes (a pointer-to-object, dereferenced once, or a direct object base). On success it fills a PointeeType with the resolved vtable / COL / TypeDescriptor coordinates, the object and complete-object bases, and an inline copy of the MSVC mangled name (PointeeType::name()), so the result holds no pointer into transient buffers.

  • Struct labeling. Rtti::reverse_scan_block(start, slot_count, out, stride) and the reverse_scan_block_bytes(start, byte_len, out, stride) overload sweep a block of pointer slots and append a LabeledSlot for each one that resolves to a real RTTI type -- the dump/triage face of the feature. These allocate (they grow out) and run the syscall-heavy prelude per slot, so they are init-time and tooling only, never a hot path.

  • Self-healing offsets. A Rtti::Landmark is a consumer-owned, serializable record that a field of a known RTTI type lives near a known offset within a struct. After a game patch shifts the layout, Rtti::heal_landmark(lm) (full detail) or Rtti::heal_offset(lm) (just the corrected std::ptrdiff_t, ready to feed a std::span<const std::ptrdiff_t> pointer-chain API) re-locates the field: it checks the nominal offset first (an unchanged offset short-circuits), then scans a +/- window grid (capped at MAX_HEAL_WINDOW = 4096 bytes per side, stepping by stride) for the uniquely nearest slot whose most-derived mangled name byte-matches the expected type and satisfies the requested Indirection. The base address is resolved fresh each session (from a Scanner cascade/AOB anchor or a live object pointer) and is never persisted.

  • Rigid multi-field recovery. Rtti::solve_fingerprint(base, fp, window_bytes) finds the single uniform delta that satisfies a whole template of co-moving landmarks at once, structurally disambiguating dense neighborhoods of same-typed slots that would defeat a single-field heal. FingerprintHit carries the recovered delta and match counts.

RTTI behavior and safety

  • Every entry point is noexcept and fails closed. All reads flow through the same SEH-guarded, module-bound-checked prelude as the forward walker, so an unmapped page, a forged complete-object locator, or an ambiguous match is a clean error return (HealError::BadDescriptor / NoMatch / Ambiguous, with heal_error_to_string for diagnostics), never a fault and never a silently-wrong offset. Matching compares MSVC mangled bytes exactly; no UnDecorateSymbolName runs on any path.
  • The heal entry points allocate nothing (they reuse a single stack PointeeType); reverse_scan_block is the only allocating path. Both are init-time or re-heal-on-miss work, not per-frame: each probe runs the prelude up to twice, and the window cap bounds the worst case.
  • Anchor a landmark to a type that is stable across patches (a base/engine type, not a game-specific most-derived subtype). Matching is byte-exact on the most-derived name, so a subtype rename defeats healing and fails closed via NoMatch.

Backwards compatibility

  • All v3.5.x APIs unchanged. Both areas are strictly additive: the new InputSource enumerator, WheelCode namespace, InputBinding::consume field, the set_consume / register_consume_flag entry points, and the entire new DetourModKit/rtti_dissect.hpp header extend the existing surface without changing any existing signature or behavior.
  • Because DetourModKit ships as a static library, rebuild against this release rather than reusing a prebuilt v3.5.0 archive.

Full Changelog: v3.5.0...v3.6.0

Artifacts:
This release includes packages for different build environments. Both packages ensure library names are prefixed with lib for consistency (e.g., libDetourModKit.a, libDetourModKit.lib).

  • DetourModKit_MinGW_v3.6.0.zip: Built with MinGW (g++). Contains libDetourModKit.a, libsafetyhook.a, headers, CMake config files. (Zydis & Zycore are statically linked into libsafetyhook.a).
  • DetourModKit_MSVC_v3.6.0.zip: Built with MSVC (Visual Studio 2022). Contains libDetourModKit.lib, libsafetyhook.lib (or safetyhook.lib if SafetyHook doesn't get the prefix), headers, CMake config files. (Zydis & Zycore are statically linked into libsafetyhook.lib/safetyhook.lib).

These packages are ready to be used by other CMake projects via find_package(DetourModKit).
See README for detailed usage instructions.

DetourModKit v3.5.0

Choose a tag to compare

@tkhquang tkhquang released this 07 Jun 21:19

Release Title: Module-Scoped Cascade Resolvers
Version: 3.5.0

Changelog:
Additive feature release on top of v3.4.0. Adds module-scoped cascade resolvers with a per-candidate unique-match guard, so a signature can be resolved within a single module's mapped range and an ambiguous (multi-site) match is rejected instead of silently resolving to the first hit. All v3.4.x APIs compile and link unchanged.

New API

  • Scanner::resolve_cascade_in_module and resolve_cascade_in_module_with_prologue_fallback (#92). Module-scoped variants of resolve_cascade / resolve_cascade_with_prologue_fallback: the AOB cascade is searched only inside a caller-supplied module range rather than the whole process. Both return std::expected<ResolveHit, ResolveError>. An invalid range returns ResolveError::InvalidRange and never falls back; no candidate matching returns ResolveError::NoMatch. Successful resolution logs at debug level.

  • Per-candidate require_unique guard (#92). AddrCandidate::require_unique defaults to true: a candidate whose pattern matches more than once in the scanned scope is treated as ambiguous and skipped (logged at debug) rather than resolving to the lowest-address match. Set require_unique = false to opt back into first-match behavior for patterns you know are intentionally non-unique. As with resolve_cascade, an in-module match should still be paired with is_likely_function_prologue() where a .rdata/.data false-match is possible.

  • ResolveError::InvalidRange (#92). New error enumerator returned by the module-scoped resolvers when the supplied range is empty or malformed.

Backwards compatibility

  • All v3.4.x APIs unchanged. Strictly additive: the new resolvers and the new ResolveError enumerator extend the existing surface; resolve_cascade / resolve_cascade_with_prologue_fallback keep their signatures and whole-process behavior.
  • Because DetourModKit ships as a static library, rebuild against this release rather than reusing a prebuilt v3.4.0 archive.

Full Changelog: v3.4.0...v3.5.0

Artifacts:
This release includes packages for different build environments. Both packages ensure library names are prefixed with lib for consistency (e.g., libDetourModKit.a, libDetourModKit.lib).

  • DetourModKit_MinGW_v3.5.0.zip: Built with MinGW (g++). Contains libDetourModKit.a, libsafetyhook.a, headers, CMake config files. (Zydis & Zycore are statically linked into libsafetyhook.a).
  • DetourModKit_MSVC_v3.5.0.zip: Built with MSVC (Visual Studio 2022). Contains libDetourModKit.lib, libsafetyhook.lib (or safetyhook.lib if SafetyHook doesn't get the prefix), headers, CMake config files. (Zydis & Zycore are statically linked into libsafetyhook.lib/safetyhook.lib).

These packages are ready to be used by other CMake projects via find_package(DetourModKit).
See README for detailed usage instructions.

DetourModKit v3.4.0

Choose a tag to compare

@tkhquang tkhquang released this 29 May 23:05
b6b7b1c

Release Title: Pointer-Chain Reads & Data-Section Scanning
Version: 3.4.0

Changelog:
Additive feature release on top of v3.3.0. Adds single-fault-guard pointer-chain read primitives for per-frame game-state reads and data-section AOB scanning, with scan-path and teardown hardening. All v3.3.x APIs compile and link unchanged.

New API

  • DetourModKit::Memory pointer-chain primitives (#87). Resolve and read a whole multi-level pointer chain under one fault guard: seh_resolve_chain, seh_read_chain<T> / seh_read_chain_bytes, plus plausible_userspace_ptr (pure-arithmetic pointer screen) and read_ptr_unchecked (fastest dereference with low-address guards only). One out-of-line guarded call instead of N, with each intermediate link kept in a register and pre-screened. Zero allocation, lock-free, and crash-safe on MSVC. Intended for reading player/camera state every frame from a hook callback.

  • Scanner::scan_readable_regions and ScannerKind (#89). AOB scanning across committed, readable data sections, not just executable regions. ScannerKind selects the region class and is threaded through resolve_cascade via a trailing defaulted parameter.

Fixes and hardening

  • ConfigWatcher stop-path drain is now bounded (#90). On stop, an in-flight ReadDirectoryChangesW is cancelled and drained with a bounded, escalating wait so a deleted watched directory can no longer hang teardown or write into freed I/O state.

  • Scan-path and teardown hardening (#91). Scan-time displacement and prologue probes now read under an SEH fault guard instead of is_readable plus a raw dereference, closing a time-of-check/time-of-use window that could fault the host if a page changed mid-scan. Input teardown no longer fires hold-release callbacks on the loader-lock detach path and now keeps the poller alive until its detached poll thread exits (closing a use-after-free). Loader-lock detection validates the PEB critical-section pointer and biases to a safe result, removing a deadlock risk during FreeLibrary.

Internal

  • Hot-path memory microbenchmark refactor (#88). Isolates validation-predicate cost from direct SEH-guarded reads in bench_memory. Benchmark only; no API change.

  • register_atomic is now a single constrained template (#91), replacing in-class explicit specializations, and the const Logger accessors are noexcept and [[nodiscard]]. Same supported types (int, bool, float); no API change.

Backwards compatibility

  • All v3.3.x APIs unchanged. Strictly additive.
  • resolve_cascade gained a trailing defaulted ScannerKind parameter. Source-compatible; because DetourModKit ships as a static library, rebuild against this release rather than reusing a prebuilt v3.3.0 archive.

Full Changelog: v3.3.0...v3.4.0

Artifacts:
This release includes packages for different build environments. Both packages ensure library names are prefixed with lib for consistency (e.g., libDetourModKit.a, libDetourModKit.lib).

  • DetourModKit_MinGW_v3.4.0.zip: Built with MinGW (g++). Contains libDetourModKit.a, libsafetyhook.a, headers, CMake config files. (Zydis & Zycore are statically linked into libsafetyhook.a).
  • DetourModKit_MSVC_v3.4.0.zip: Built with MSVC (Visual Studio 2022). Contains libDetourModKit.lib, libsafetyhook.lib (or safetyhook.lib if SafetyHook doesn't get the prefix), headers, CMake config files. (Zydis & Zycore are statically linked into libsafetyhook.lib/safetyhook.lib).

These packages are ready to be used by other CMake projects via find_package(DetourModKit).
See README for detailed usage instructions.