diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-06 16:19:25 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-06 16:19:25 +0100 |
| commit | 9f0724dcd25a06c5674d30d045dc485a341253b1 (patch) | |
| tree | e7587dd03c0b164113d3b879b18d81fccf332c05 /src/hooks.cpp | |
| parent | Chams: leave engine-forced material passes alone (diff) | |
| download | hl2h-9f0724dcd25a06c5674d30d045dc485a341253b1.tar.gz hl2h-9f0724dcd25a06c5674d30d045dc485a341253b1.zip | |
Hooks: central vtable install with restore support
Move vtable patching into hooks.cpp: one call clones the table, validates every
slot, records the originals and installs the clone. Installs happen from the
setup, game and paint threads, so the bookkeeping is mutex-protected.
Client, CInput, IVModelRender, IStudioRender and ISurface all use it, and
hooks_restore_all() can now put every interface back (previously impossible).
Diffstat (limited to '')
| -rw-r--r-- | src/hooks.cpp | 59 |
1 files changed, 59 insertions, 0 deletions
diff --git a/src/hooks.cpp b/src/hooks.cpp new file mode 100644 index 0000000..215ecfd --- /dev/null +++ b/src/hooks.cpp | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | // hooks.cpp — vtable hook installation and teardown bookkeeping. | ||
| 2 | #include "hl2h.h" | ||
| 3 | |||
| 4 | #include <mutex> | ||
| 5 | #include <vector> | ||
| 6 | |||
| 7 | namespace { | ||
| 8 | |||
| 9 | struct Installed { | ||
| 10 | void *iface; | ||
| 11 | void **old_vt; | ||
| 12 | }; | ||
| 13 | |||
| 14 | std::mutex g_hook_mu; | ||
| 15 | std::vector<Installed> g_installed; | ||
| 16 | |||
| 17 | } // namespace | ||
| 18 | |||
| 19 | bool hooks_install_vtable(void *iface, size_t slots, const VtablePatch *patches, | ||
| 20 | int count) { | ||
| 21 | if (!iface || !patches || count <= 0) | ||
| 22 | return false; | ||
| 23 | void **old = *(void ***)iface; | ||
| 24 | if (!old) | ||
| 25 | return false; | ||
| 26 | for (int i = 0; i < count; i++) { | ||
| 27 | if (patches[i].slot < 0 || (size_t)patches[i].slot >= slots) { | ||
| 28 | logf("hooks: slot %d out of range (table has %u)", patches[i].slot, | ||
| 29 | (unsigned)slots); | ||
| 30 | return false; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | void **clone = vtable_clone(iface, slots); | ||
| 35 | if (!clone) | ||
| 36 | return false; | ||
| 37 | for (int i = 0; i < count; i++) { | ||
| 38 | if (patches[i].original) | ||
| 39 | *patches[i].original = old[patches[i].slot]; | ||
| 40 | clone[patches[i].slot] = patches[i].replacement; | ||
| 41 | } | ||
| 42 | |||
| 43 | std::lock_guard<std::mutex> lock(g_hook_mu); | ||
| 44 | *(void ***)iface = clone; | ||
| 45 | g_installed.push_back({iface, old}); | ||
| 46 | return true; | ||
| 47 | } | ||
| 48 | |||
| 49 | void hooks_restore_all() { | ||
| 50 | std::lock_guard<std::mutex> lock(g_hook_mu); | ||
| 51 | for (auto it = g_installed.rbegin(); it != g_installed.rend(); ++it) | ||
| 52 | *(void ***)it->iface = it->old_vt; | ||
| 53 | g_installed.clear(); | ||
| 54 | } | ||
| 55 | |||
| 56 | int hooks_installed_count() { | ||
| 57 | std::lock_guard<std::mutex> lock(g_hook_mu); | ||
| 58 | return (int)g_installed.size(); | ||
| 59 | } | ||
