Releases: tkhquang/DetourModKit
Release list
DetourModKit v4.0.0
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-taggedErrorCode, replacing the per-domain error enums. Best-effort and query surfaces keep documentedbool/std::optionalreturns 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/Regionvalue 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
HookManagersingleton:hook::inline_at/hook::mid_at/hook::vmt_forreturn move-onlyHook/VmtHookhandles that unhook on drop, plus a declarativehook::install_alltable. (#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
LayerConflictrefusals, and aHookStackfor 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_batchover a compiledscan::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, andNotAuthoritativeare 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:
ExportNameresolution 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/RequireIdentitywith witness), stolen-span recovery, and section-aware image identity. (#183, #184, #223, #262)
Memory
- Guarded-memory surface with fault containment, truthful write outcomes (
WriteFaultedmeans nothing written,WriteMayBePartialnames the indeterminate prefix), a strictwrite_in_placetier 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>(checkedread_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 onlySafeToUnload/Drainedauthorize an unmap; the drain destroys gate-owned callables while the provider is still mapped. (#241, #248)
Logging, session, and lifecycle
- Value-facade logger with
AsyncLoggerdemoted 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....
DetourModKit v3.9.0
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 theseh_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_bytesstays the setup/patch-only primitive (it flips protection, flushes the i-cache, and invalidates the cache range). (#136)
Scanner
- Cascade name/string resilience tiers:
AddrCandidaterows 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_combois the hold-mode mirror ofregister_press_combo(and returns anInputBindingGuard), andregister_consume_flagadds a per-binding<key>.Consumeinput-suppression facet. A newregister_atomicoverload infers the INI default from the atomic's current value. (#134)
Release hardening
on_dll_attachis nownoexcept, 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++). ContainslibDetourModKit.a,libsafetyhook.a,libZydis.a,libZycore.a, headers, and CMake config files.find_packagelinks the transitive static chain via theDetourModKit::depsimported target.DetourModKit_MSVC_v3.9.0.zip: Built with MSVC (Visual Studio 2022). ContainslibDetourModKit.lib,safetyhook.lib,Zydis.lib,Zycore.lib, headers, and CMake config files. SameDetourModKit::depswiring 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
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 whendisableis called duringatexit. The opt-in C++20 module interface stays off (DetourModKit never setsSAFETYHOOK_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 viaadd_subdirectorymust now declarecmake_minimum_required(VERSION 3.28);find_packageconsumers 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++). ContainslibDetourModKit.a,libsafetyhook.a,libZydis.a,libZycore.a, headers, and CMake config files.find_packagelinks the transitive static chain via theDetourModKit::depsimported target.DetourModKit_MSVC_v3.8.2.zip: Built with MSVC (Visual Studio 2022). ContainslibDetourModKit.lib,safetyhook.lib,Zydis.lib,Zycore.lib, headers, and CMake config files. SameDetourModKit::depswiring 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
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 soObjectBase/Anycallers 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.
CompleteObjectis opt-in;PointerToObject,ObjectBase, andAnybehavior and their enum values are unchanged (pinned:Anystays 2,CompleteObjectis 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++). ContainslibDetourModKit.a,libsafetyhook.a,libZydis.a,libZycore.a, headers, and CMake config files.find_packagelinks the transitive static chain via theDetourModKit::depsimported target.DetourModKit_MSVC_v3.8.1.zip: Built with MSVC (Visual Studio 2022). ContainslibDetourModKit.lib,safetyhook.lib,Zydis.lib,Zycore.lib, headers, and CMake config files. SameDetourModKit::depswiring 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
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()andDiagnostics::hook_lifecycle()publishScannerFaultEvent/HookLifecycleEventto 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 valueSnapshot. (#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 namespacereplaced withnamespace 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++). ContainslibDetourModKit.a,libsafetyhook.a,libZydis.a,libZycore.a, headers, and CMake config files.find_packagelinks the transitive static chain via theDetourModKit::depsimported target.DetourModKit_MSVC_v3.8.0.zip: Built with MSVC (Visual Studio 2022). ContainslibDetourModKit.lib,safetyhook.lib,Zydis.lib,Zycore.lib, headers, and CMake config files. SameDetourModKit::depswiring 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
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, andresolve_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
BindingTokenhandles 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++). ContainslibDetourModKit.a,libsafetyhook.a,libZydis.a,libZycore.a, headers, and CMake config files.find_packagelinks the transitive static chain via theDetourModKit::depsimported target.DetourModKit_MSVC_v3.7.0.zip: Built with MSVC (Visual Studio 2022). ContainslibDetourModKit.lib,safetyhook.lib,Zydis.lib,Zycore.lib, headers, and CMake config files. SameDetourModKit::depswiring 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
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.wButtonsalone (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, andConfig::register_consume_flagsurface 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++). ContainslibDetourModKit.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). ContainslibDetourModKit.lib,libsafetyhook.lib(orsafetyhook.libif 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
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::MouseWheeland theWheelCodenamespace (Up,Down,Left,Right) with themouse_wheel(code)factory.parse_input_nameresolves"WheelUp","WheelDown","WheelLeft","WheelRight", andinput_code_to_name/format_input_coderound-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 onWM_NCDESTROY. -
Gamepad passthrough suppression.
InputBinding::consumeopt-in flag, plusInputManager::set_consume(name, consume)andInputPoller::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 anXInputGetState(and ordinal-100XInputGetStateEx) 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
InputManagersingleton 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 atslot_addrand 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 aPointeeTypewith 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 thereverse_scan_block_bytes(start, byte_len, out, stride)overload sweep a block of pointer slots and append aLabeledSlotfor each one that resolves to a real RTTI type -- the dump/triage face of the feature. These allocate (they growout) 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::Landmarkis 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) orRtti::heal_offset(lm)(just the correctedstd::ptrdiff_t, ready to feed astd::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+/- windowgrid (capped atMAX_HEAL_WINDOW= 4096 bytes per side, stepping bystride) for the uniquely nearest slot whose most-derived mangled name byte-matches the expected type and satisfies the requestedIndirection. 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.FingerprintHitcarries the recovered delta and match counts.
RTTI behavior and safety
- Every entry point is
noexceptand 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, withheal_error_to_stringfor diagnostics), never a fault and never a silently-wrong offset. Matching compares MSVC mangled bytes exactly; noUnDecorateSymbolNameruns on any path. - The heal entry points allocate nothing (they reuse a single stack
PointeeType);reverse_scan_blockis 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
InputSourceenumerator,WheelCodenamespace,InputBinding::consumefield, theset_consume/register_consume_flagentry points, and the entire newDetourModKit/rtti_dissect.hppheader 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++). ContainslibDetourModKit.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). ContainslibDetourModKit.lib,libsafetyhook.lib(orsafetyhook.libif 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
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_moduleandresolve_cascade_in_module_with_prologue_fallback(#92). Module-scoped variants ofresolve_cascade/resolve_cascade_with_prologue_fallback: the AOB cascade is searched only inside a caller-supplied module range rather than the whole process. Both returnstd::expected<ResolveHit, ResolveError>. An invalid range returnsResolveError::InvalidRangeand never falls back; no candidate matching returnsResolveError::NoMatch. Successful resolution logs at debug level. -
Per-candidate
require_uniqueguard (#92).AddrCandidate::require_uniquedefaults totrue: 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. Setrequire_unique = falseto opt back into first-match behavior for patterns you know are intentionally non-unique. As withresolve_cascade, an in-module match should still be paired withis_likely_function_prologue()where a.rdata/.datafalse-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
ResolveErrorenumerator extend the existing surface;resolve_cascade/resolve_cascade_with_prologue_fallbackkeep 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++). ContainslibDetourModKit.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). ContainslibDetourModKit.lib,libsafetyhook.lib(orsafetyhook.libif 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
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::Memorypointer-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, plusplausible_userspace_ptr(pure-arithmetic pointer screen) andread_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_regionsandScannerKind(#89). AOB scanning across committed, readable data sections, not just executable regions.ScannerKindselects the region class and is threaded throughresolve_cascadevia a trailing defaulted parameter.
Fixes and hardening
-
ConfigWatcher stop-path drain is now bounded (#90). On stop, an in-flight
ReadDirectoryChangesWis 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_readableplus 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 duringFreeLibrary.
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_atomicis now a single constrained template (#91), replacing in-class explicit specializations, and theconstLogger accessors arenoexceptand[[nodiscard]]. Same supported types (int,bool,float); no API change.
Backwards compatibility
- All v3.3.x APIs unchanged. Strictly additive.
resolve_cascadegained a trailing defaultedScannerKindparameter. 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++). ContainslibDetourModKit.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). ContainslibDetourModKit.lib,libsafetyhook.lib(orsafetyhook.libif 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.