102 Patches: Detours to the Rescue
C reference for DttR maintainers and modders.
Loading...
Searching...
No Matches
core.c
Go to the documentation of this file.
1#include "core_internal.h"
2
3#include <kvec.h>
4
5#include <ctype.h>
6#include <limits.h>
7#include <stdlib.h>
8#include <string.h>
9
14
15typedef kvec_t(install_entry) install_entry_vec;
16
19 install_entry_vec entries;
20};
21
22// Return stable status tokens for logs and tests that compare SDK failures by name.
23const char *DTTR_StatusName(DTTR_Status status) {
24 switch (status) {
25 case DTTR_OK:
26 return "DTTR_OK";
28 return "DTTR_ERR_INVALID_ARGUMENT";
30 return "DTTR_ERR_NOT_FOUND";
32 return "DTTR_ERR_UNSUPPORTED";
34 return "DTTR_ERR_ALREADY_INSTALLED";
36 return "DTTR_ERR_NOT_INSTALLED";
38 return "DTTR_ERR_MEMORY_PROTECTION";
40 return "DTTR_ERR_RUNTIME_UNAVAILABLE";
42 return "DTTR_ERR_ABI_MISMATCH";
44 return "DTTR_ERR_OUT_OF_MEMORY";
46 return "DTTR_ERR_HOOK_CHAIN_UNSUPPORTED";
48 return "DTTR_ERR_MISSING_SYMBOL";
50 return "DTTR_ERR_UNRESOLVED";
52 return "DTTR_ERR_NOT_CALLABLE";
54 return "DTTR_ERR_READ_FAILED";
56 return "DTTR_ERR_WRITE_FAILED";
58 return "DTTR_ERR_POLICY_MISMATCH";
60 return "DTTR_ERR_UNSUPPORTED_LAYOUT";
62 return "DTTR_ERR_UNSUPPORTED_CONTRACT";
64 return "DTTR_ERR_PROVENANCE_UNSAFE";
65 default:
66 return "DTTR_ERR_UNKNOWN";
67 }
68}
69
71 return status == DTTR_OK;
72}
73
75 return !DTTR_StatusOK(status);
76}
77
79 return DTTR_StatusOK(result.status);
80}
81
83 return ctx && ctx->game_module && ctx->api;
84}
85
86static int hex_value(char ch) {
87 if (ch >= '0' && ch <= '9') {
88 return ch - '0';
89 }
90
91 if (ch >= 'a' && ch <= 'f') {
92 return 10 + ch - 'a';
93 }
94
95 if (ch >= 'A' && ch <= 'F') {
96 return 10 + ch - 'A';
97 }
98
99 return -1;
100}
101
103 char *sig,
104 char *mask,
105 DTTR_Status status,
106 const char *message
107) {
108 free(sig);
109 free(mask);
110 return dttr_core_result(status, message);
111}
112
113// Convert user-facing AOB text into the signature and mask strings consumed by sigscan.
114static DTTR_Result parse_aob(const char *aob, char **out_sig, char **out_mask) {
115 if (!aob || !out_sig || !out_mask) {
116 return dttr_core_result(DTTR_ERR_INVALID_ARGUMENT, "invalid AOB parser arguments");
117 }
118
119 *out_sig = NULL;
120 *out_mask = NULL;
121
122 size_t cap = strlen(aob) / 2u + 2u;
123 char *sig = (char *)calloc(cap, 1u);
124 char *mask = (char *)calloc(cap, 1u);
125
126 if (!sig || !mask) {
127 return parse_aob_fail(
128 sig,
129 mask,
131 "failed to allocate AOB buffers"
132 );
133 }
134
135 size_t count = 0;
136 const char *p = aob;
137
138 while (*p) {
139 while (*p && isspace((unsigned char)*p)) {
140 p++;
141 }
142
143 if (!*p) {
144 break;
145 }
146
147 if (count + 1u >= cap) {
148 return parse_aob_fail(
149 sig,
150 mask,
152 "AOB pattern is malformed"
153 );
154 }
155
156 if (*p == '?') {
157 p++;
158
159 if (*p == '?') {
160 p++;
161 }
162
163 sig[count] = '?';
164 mask[count] = '?';
165 count++;
166 continue;
167 }
168
169 int hi = hex_value(p[0]);
170 int lo = p[1] ? hex_value(p[1]) : -1;
171
172 if (hi < 0 || lo < 0) {
173 return parse_aob_fail(
174 sig,
175 mask,
177 "AOB pattern contains a non-hex byte"
178 );
179 }
180
181 sig[count] = (char)((hi << 4) | lo);
182 mask[count] = 'x';
183 count++;
184 p += 2;
185
186 if (*p && !isspace((unsigned char)*p)) {
187 return parse_aob_fail(
188 sig,
189 mask,
191 "AOB bytes must be separated by spaces"
192 );
193 }
194 }
195
196 if (!count) {
197 return parse_aob_fail(
198 sig,
199 mask,
201 "AOB pattern is empty"
202 );
203 }
204
205 sig[count] = '\0';
206 mask[count] = '\0';
207 *out_sig = sig;
208 *out_mask = mask;
209 return dttr_core_result(DTTR_OK, "ok");
210}
211
213 HMODULE mod,
215 const char *aob,
216 uintptr_t *out_addr
217) {
218 char *sig = NULL;
219 char *mask = NULL;
220 DTTR_Result parsed = parse_aob(aob, &sig, &mask);
221
222 if (!DTTR_ResultOK(parsed)) {
223 return parsed;
224 }
225
226 const uintptr_t addr = sigscan(mod, sig, mask);
227 free(sig);
228 free(mask);
229
230 if (!addr) {
231 return dttr_core_result(DTTR_ERR_NOT_FOUND, "AOB signature not found");
232 }
233
234 *out_addr = addr;
235 return dttr_core_result(DTTR_OK, "ok");
236}
237
238DTTR_Result DTTR_Core_AOBFindInModule(HMODULE mod, const char *aob, uintptr_t *out_addr) {
239 if (!out_addr) {
240 return dttr_core_result(DTTR_ERR_INVALID_ARGUMENT, "out_addr is NULL");
241 }
242
243 *out_addr = 0;
244 return aob_scan_with(mod, DTTR_Core_HookSigscan, aob, out_addr);
245}
246
248 const DTTR_Core_Context *ctx,
249 const char *aob,
250 uintptr_t *out_addr
251) {
252 if (!runtime_context_valid(ctx) || !out_addr) {
253 return dttr_core_result(DTTR_ERR_INVALID_ARGUMENT, "invalid AOB find arguments");
254 }
255
256 *out_addr = 0;
257 const DTTR_Core_API *runtime = ctx->api;
258
259 if (!runtime->sigscan) {
260 return dttr_core_result(
262 "runtime sigscan is unavailable"
263 );
264 }
265
266 return aob_scan_with(ctx->game_module, runtime->sigscan, aob, out_addr);
267}
268
270 const DTTR_Core_Context *ctx,
271 const char *sig,
272 const char *mask,
273 uintptr_t *out_addr
274) {
275 if (!runtime_context_valid(ctx) || !sig || !mask || !out_addr) {
276 return dttr_core_result(
278 "invalid signature find arguments"
279 );
280 }
281
282 *out_addr = 0;
283 const DTTR_Core_API *runtime = ctx->api;
284
285 if (!runtime->sigscan) {
286 return dttr_core_result(
288 "runtime sigscan is unavailable"
289 );
290 }
291
292 uintptr_t addr = runtime->sigscan(ctx->game_module, sig, mask);
293
294 if (!addr) {
295 return dttr_core_result(DTTR_ERR_NOT_FOUND, "signature not found");
296 }
297
298 *out_addr = addr;
299 return dttr_core_result(DTTR_OK, "ok");
300}
301
303 const DTTR_Core_Context *ctx,
304 uintptr_t address,
305 const uint8_t *bytes,
306 size_t size,
307 DTTR_Core_Patch **out_patch
308) {
309 if (out_patch) {
310 *out_patch = NULL;
311 }
312
313 if (!runtime_context_valid(ctx) || !address || !bytes || !size || !out_patch) {
314 return dttr_core_result(DTTR_ERR_INVALID_ARGUMENT, "invalid patch arguments");
315 }
316
317 const DTTR_Core_API *runtime = ctx->api;
318
319 if (!runtime->patch_bytes) {
320 return dttr_core_result(
322 "runtime byte patcher is unavailable"
323 );
324 }
325
326 DTTR_Core_Hook *hook = runtime->patch_bytes(address, bytes, size);
327
328 if (!hook) {
329 return dttr_core_result(DTTR_ERR_MEMORY_PROTECTION, "failed to patch bytes");
330 }
331
332 *out_patch = hook;
333 return dttr_core_result(DTTR_OK, "ok");
334}
335
337 const DTTR_Core_Context *ctx,
338 uintptr_t address,
339 int prologue_size,
340 void *detour,
341 void **out_original,
342 DTTR_Core_Hook **out_hook
343) {
344 if (out_hook) {
345 *out_hook = NULL;
346 }
347
348 if (!runtime_context_valid(ctx) || !address || !detour || !out_hook) {
349 return dttr_core_result(
351 "invalid function hook arguments"
352 );
353 }
354
355 const DTTR_Core_API *runtime = ctx->api;
356
357 if (!runtime->hook_function) {
358 return dttr_core_result(
360 "runtime function hooker is unavailable"
361 );
362 }
363
365 *hook = runtime->hook_function(address, prologue_size, detour, out_original);
366
367 if (!hook) {
369 if (hook_error.status == DTTR_ERR_HOOK_CHAIN_UNSUPPORTED) {
370 return hook_error;
371 }
372
373 return dttr_core_result(DTTR_ERR_MEMORY_PROTECTION, "failed to hook function");
374 }
375
376 *out_hook = hook;
377 return dttr_core_result(DTTR_OK, "ok");
378}
379
381 const DTTR_Core_Context *ctx,
382 const char *aob,
383 intptr_t offset,
384 int prologue_size,
385 void *detour,
386 void **out_original,
387 DTTR_Core_Hook **out_hook
388) {
389 uintptr_t match = 0;
390 DTTR_Result found = DTTR_Core_AOBFind(ctx, aob, &match);
391
392 if (!DTTR_ResultOK(found)) {
393 if (out_hook) {
394 *out_hook = NULL;
395 }
396
397 return found;
398 }
399
401 ctx,
402 (uintptr_t)((intptr_t)match + offset),
403 prologue_size,
404 detour,
405 out_original,
406 out_hook
407 );
408}
409
410static bool rel32_displacement(uintptr_t site, void *detour, int32_t *out_rel) {
411 const int64_t rel = (int64_t)(intptr_t)detour - (int64_t)(site + 5u);
412
413 if (rel < INT32_MIN || rel > INT32_MAX) {
414 return false;
415 }
416
417 *out_rel = (int32_t)rel;
418 return true;
419}
420
422 const DTTR_Core_Context *ctx,
423 uintptr_t address,
424 void *detour,
425 DTTR_Core_Patch **out_patch
426) {
427 if (out_patch) {
428 *out_patch = NULL;
429 }
430
431 if (!runtime_context_valid(ctx) || !address || !detour || !out_patch) {
432 return dttr_core_result(DTTR_ERR_INVALID_ARGUMENT, "invalid rel32 jump arguments");
433 }
434
435 uint8_t jmp[5] = {0xE9};
436 int32_t rel = 0;
437
438 if (!rel32_displacement(address, detour, &rel)) {
439 return dttr_core_result(DTTR_ERR_UNSUPPORTED, "rel32 jump target is out of range");
440 }
441
442 memcpy(jmp + 1, &rel, sizeof(rel));
443 return DTTR_Core_PatchBytes(ctx, address, jmp, sizeof(jmp), out_patch);
444}
445
447 const DTTR_Core_Context *ctx,
448 const char *aob,
449 intptr_t offset,
450 void *detour,
451 DTTR_Core_Patch **out_patch
452) {
453 if (out_patch) {
454 *out_patch = NULL;
455 }
456
457 uintptr_t match = 0;
458 DTTR_Result found = DTTR_Core_AOBFind(ctx, aob, &match);
459
460 if (!DTTR_ResultOK(found)) {
461 return found;
462 }
463
465 ctx,
466 (uintptr_t)((intptr_t)match + offset),
467 detour,
468 out_patch
469 );
470}
471
473 const DTTR_Core_Context *ctx,
474 uintptr_t address,
475 void *new_value,
476 void **out_original,
477 DTTR_Core_Hook **out_hook
478) {
479 if (out_hook) {
480 *out_hook = NULL;
481 }
482
483 if (!runtime_context_valid(ctx) || !address || !out_hook) {
484 return dttr_core_result(
486 "invalid pointer hook arguments"
487 );
488 }
489
490 const DTTR_Core_API *runtime = ctx->api;
491
492 if (!runtime->hook_pointer) {
493 return dttr_core_result(
495 "runtime pointer hooker is unavailable"
496 );
497 }
498
499 DTTR_Core_Hook *hook = runtime->hook_pointer(address, new_value, out_original);
500
501 if (!hook) {
502 return dttr_core_result(DTTR_ERR_MEMORY_PROTECTION, "failed to hook pointer");
503 }
504
505 *out_hook = hook;
506 return dttr_core_result(DTTR_OK, "ok");
507}
508
510 if (!patch || DTTR_Core_HookDetachChecked(patch)) {
511 return dttr_core_result(DTTR_OK, "ok");
512 }
513
514 return dttr_core_result(DTTR_ERR_MEMORY_PROTECTION, "failed to detach patch");
515}
516
518 if (!hook || DTTR_Core_HookDetachChecked(hook)) {
519 return dttr_core_result(DTTR_OK, "ok");
520 }
521
522 return dttr_core_result(DTTR_ERR_MEMORY_PROTECTION, "failed to detach hook");
523}
524
526 const DTTR_Core_Context *ctx,
528 DTTR_Core_Hook **out_handle
529);
530
531// Grow patch-group storage before installing another owned hook or patch.
533 if (kv_size(group->entries) < kv_max(group->entries)) {
534 return dttr_core_result(DTTR_OK, "ok");
535 }
536
537 const size_t new_cap = kv_max(group->entries) ? kv_max(group->entries) * 2u : 4u;
538
539 if (new_cap < kv_max(group->entries)
540 || new_cap > ((size_t)-1) / sizeof(install_entry)) {
541 return dttr_core_result(DTTR_ERR_OUT_OF_MEMORY, "patch group is too large");
542 }
543
544 install_entry *entries = (install_entry *)
545 realloc(group->entries.a, new_cap * sizeof(*entries));
546
547 if (!entries) {
548 return dttr_core_result(DTTR_ERR_OUT_OF_MEMORY, "failed to grow patch group");
549 }
550
551 group->entries.a = entries;
552 group->entries.m = new_cap;
553 return dttr_core_result(DTTR_OK, "ok");
554}
555
556// Validate a patch group and reserve storage for one more owned hook or patch.
558 if (!group) {
559 return dttr_core_result(DTTR_ERR_INVALID_ARGUMENT, "patch group is NULL");
560 }
561
562 return patch_group_reserve(group);
563}
564
565// Detach through the same runtime API that created the group-owned handle.
567 const DTTR_Core_PatchGroup *group,
568 DTTR_Core_Hook *hook
569) {
570 const DTTR_Core_API *runtime = group ? group->ctx.api : NULL;
571
572 if (runtime && runtime->unhook_checked) {
573 return runtime->unhook_checked(hook);
574 }
575
576 if (runtime && runtime->unhook) {
577 runtime->unhook(hook);
578 return true;
579 }
580
581 return DTTR_Core_HookDetachChecked(hook);
582}
583
584// Roll back patch-group entries after the requested keep count in reverse install order.
587 size_t keep_count
588) {
589 while (kv_size(group->entries) > keep_count) {
590 install_entry entry = kv_A(group->entries, kv_size(group->entries) - 1u);
591 if (!patch_group_detach_checked(group, entry.hook)) {
592 return dttr_core_result(
594 "failed to restore one or more patch group entries"
595 );
596 }
597
598 kv_pop(group->entries);
599 if (entry.out_original) {
600 *entry.out_original = NULL;
601 }
602 }
603
604 return dttr_core_result(DTTR_OK, "ok");
605}
606
608 const DTTR_Core_Context *ctx,
609 DTTR_Core_PatchGroup **out_group
610) {
611 if (out_group) {
612 *out_group = NULL;
613 }
614
615 if (!runtime_context_valid(ctx) || !out_group) {
616 return dttr_core_result(
618 "invalid patch group arguments"
619 );
620 }
621
622 DTTR_Core_PatchGroup *group = (DTTR_Core_PatchGroup *)calloc(1u, sizeof(*group));
623
624 if (!group) {
625 return dttr_core_result(DTTR_ERR_OUT_OF_MEMORY, "failed to allocate patch group");
626 }
627
628 group->ctx = *ctx;
629 kv_init(group->entries);
630 *out_group = group;
631 return dttr_core_result(DTTR_OK, "ok");
632}
633
635 if (!group) {
636 return dttr_core_result(DTTR_OK, "ok");
637 }
638
639 return patch_group_uninstall_from(group, 0u);
640}
641
643 if (!group) {
644 return dttr_core_result(DTTR_OK, "ok");
645 }
646
648 if (!DTTR_ResultOK(result)) {
649 return result;
650 }
651
652 kv_destroy(group->entries);
653 free(group);
654 return result;
655}
656
658 if (!group || !*group) {
659 return dttr_core_result(DTTR_OK, "ok");
660 }
661
663 if (!DTTR_ResultOK(result)) {
664 return result;
665 }
666
667 *group = NULL;
668 return result;
669}
670
671// Return the runtime context that backs a patch group for generated helper calls.
673 return group ? &group->ctx : NULL;
674}
675
676// Adopt a freshly installed handle into a patch group, or propagate the install failure.
679 DTTR_Result result,
680 DTTR_Core_Hook *hook,
681 void **out_original,
682 DTTR_Core_Hook **out_hook
683) {
684 if (!DTTR_ResultOK(result)) {
685 return result;
686 }
687
688 group->entries.a[group->entries.n++] = (install_entry){hook, out_original};
689
690 if (out_hook) {
691 *out_hook = hook;
692 }
693
694 return result;
695}
696
699 uintptr_t address,
700 const uint8_t *bytes,
701 size_t size,
702 DTTR_Core_Patch **out_patch
703) {
704 if (out_patch) {
705 *out_patch = NULL;
706 }
707
709
710 if (!DTTR_ResultOK(reserved)) {
711 return reserved;
712 }
713
714 DTTR_Core_Patch *patch = NULL;
715 DTTR_Result result = DTTR_Core_PatchBytes(&group->ctx, address, bytes, size, &patch);
716
718 group,
719 result,
720 patch,
721 NULL,
722 (DTTR_Core_Hook **)out_patch
723 );
724}
725
728 uintptr_t address,
729 int prologue_size,
730 void *detour,
731 void **out_original,
732 DTTR_Core_Hook **out_hook
733) {
734 if (out_hook) {
735 *out_hook = NULL;
736 }
737
739
740 if (!DTTR_ResultOK(reserved)) {
741 return reserved;
742 }
743
744 DTTR_Core_Hook *hook = NULL;
746 &group->ctx,
747 address,
748 prologue_size,
749 detour,
750 out_original,
751 &hook
752 );
753
754 return patch_group_finish_install(group, result, hook, out_original, out_hook);
755}
756
759 uintptr_t address,
760 void *new_value,
761 void **out_original,
762 DTTR_Core_Hook **out_hook
763) {
764 if (out_hook) {
765 *out_hook = NULL;
766 }
767
769
770 if (!DTTR_ResultOK(reserved)) {
771 return reserved;
772 }
773
774 DTTR_Core_Hook *hook = NULL;
776 &group->ctx,
777 address,
778 new_value,
779 out_original,
780 &hook
781 );
782
783 return patch_group_finish_install(group, result, hook, out_original, out_hook);
784}
785
788 uintptr_t address,
789 void *detour,
790 DTTR_Core_Patch **out_patch
791) {
792 if (out_patch) {
793 *out_patch = NULL;
794 }
795
797
798 if (!DTTR_ResultOK(reserved)) {
799 return reserved;
800 }
801
802 DTTR_Core_Patch *patch = NULL;
803 DTTR_Result result = DTTR_Core_PatchRel32Jump(&group->ctx, address, detour, &patch);
804
806 group,
807 result,
808 patch,
809 NULL,
810 (DTTR_Core_Hook **)out_patch
811 );
812}
813
815 switch (target->kind) {
819 return target->out_original;
820 default:
821 return NULL;
822 }
823}
824
825// Install a target list transactionally so required failures roll back new entries.
828 const DTTR_Core_TargetSpec *targets,
829 size_t target_count,
830 DTTR_Core_TargetReport *out_report
831) {
832 dttr_core_report_init(out_report);
833
834 if (!group || (!targets && target_count)) {
837 "invalid patch group target arguments"
838 );
839 dttr_core_report_fail(out_report, 0, result);
840 return result;
841 }
842
843 const size_t keep_count = kv_size(group->entries);
844
845 for (size_t i = 0; i < target_count; i++) {
846 if (out_report) {
847 out_report->attempted++;
848 }
849
850 DTTR_Result reserved = patch_group_reserve(group);
851
852 if (!DTTR_ResultOK(reserved)) {
853 DTTR_Result rollback = patch_group_uninstall_from(group, keep_count);
854 if (!DTTR_ResultOK(rollback)) {
855 dttr_core_report_fail(out_report, i, rollback);
856 return rollback;
857 }
858
859 dttr_core_report_fail(out_report, i, reserved);
860 return reserved;
861 }
862
863 DTTR_Core_Hook *handle = NULL;
864 DTTR_Result result = install_one_target(&group->ctx, &targets[i], &handle);
865
866 if (!DTTR_ResultOK(result)) {
867 if (!targets[i].required && result.status == DTTR_ERR_NOT_FOUND) {
868 if (out_report) {
869 out_report->skipped_optional++;
870 }
871
872 continue;
873 }
874
875 DTTR_Result rollback = patch_group_uninstall_from(group, keep_count);
876 if (!DTTR_ResultOK(rollback)) {
877 dttr_core_report_fail(out_report, i, rollback);
878 return rollback;
879 }
880
881 dttr_core_report_fail(out_report, i, result);
882 return result;
883 }
884
885 group->entries.a[group->entries.n++] = (install_entry){
886 handle,
887 target_original_slot(&targets[i])
888 };
889
890 if (out_report) {
891 out_report->installed++;
892 }
893 }
894
895 return dttr_core_result(DTTR_OK, "ok");
896}
897
899 if (!report) {
900 return;
901 }
902
903 report->attempted = 0;
904 report->installed = 0;
905 report->skipped_optional = 0;
906 report->failed_index = (size_t)-1;
907 report->status = DTTR_OK;
908 report->message = "ok";
909}
910
911// Record the first failing target so callers can diagnose partial install attempts.
914 size_t index,
915 DTTR_Result result
916) {
917 if (!report) {
918 return;
919 }
920
921 report->failed_index = index;
922 report->status = result.status;
923 report->message = result.message;
924}
925
926// Resolve direct and AOB target specs into concrete addresses before installation.
928 const DTTR_Core_Context *ctx,
930 uintptr_t *out_address
931) {
936 if (!target->address) {
937 return dttr_core_result(DTTR_ERR_INVALID_ARGUMENT, "target address is NULL");
938 }
939
940 *out_address = target->address;
941 return dttr_core_result(DTTR_OK, "ok");
942 }
943
944 uintptr_t match = 0;
945 DTTR_Result found = DTTR_Core_AOBFind(ctx, target->aob, &match);
946
947 if (!DTTR_ResultOK(found)) {
948 return found;
949 }
950
951 *out_address = (uintptr_t)((intptr_t)match + target->offset);
952 return dttr_core_result(DTTR_OK, "ok");
953}
954
955// Dispatch one target spec to the matching patch, hook, or jump primitive.
957 const DTTR_Core_Context *ctx,
959 DTTR_Core_Hook **out_handle
960) {
961 *out_handle = NULL;
962 uintptr_t address = 0;
963 DTTR_Result address_result = target_address(ctx, target, &address);
964
965 if (!DTTR_ResultOK(address_result)) {
966 return address_result;
967 }
968
969 switch (target->kind) {
973 ctx,
974 address,
975 target->patch_bytes,
976 target->patch_size,
977 (DTTR_Core_Patch **)out_handle
978 );
982 ctx,
983 address,
984 target->prologue_size,
985 target->detour,
986 target->out_original,
987 out_handle
988 );
991 ctx,
992 address,
993 target->new_value,
994 target->out_original,
995 out_handle
996 );
1000 ctx,
1001 address,
1002 target->detour,
1003 (DTTR_Core_Patch **)out_handle
1004 );
1005 default:
1006 return dttr_core_result(DTTR_ERR_UNSUPPORTED, "unsupported target kind");
1007 }
1008}
DTTR_Graphics_COM_Direct3DDevice7 void DWORD flags DWORD count
void void * ctx
void DWORD DWORD * free
const DWORD size
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void NULL
DTTR_Graphics_COM_DirectDrawSurface7 DWORD DWORD void void DWORD flags DTTR_Graphics_COM_DirectDrawSurface7 void void *cb void * target
static DTTR_Result dttr_core_result(DTTR_Status status, const char *message)
DTTR_Result dttr_core_hook_last_error()
DTTR_Core_Hook DTTR_Core_Patch
Definition dttr_core.h:26
@ DTTR_TARGET_ADDRESS_REL32_JMP
Definition dttr_core.h:34
@ DTTR_TARGET_ADDRESS_HOOK
Definition dttr_core.h:31
@ DTTR_TARGET_POINTER_HOOK
Definition dttr_core.h:33
@ DTTR_TARGET_ADDRESS_PATCH
Definition dttr_core.h:29
@ DTTR_TARGET_AOB_PATCH
Definition dttr_core.h:30
@ DTTR_TARGET_AOB_REL32_JMP
Definition dttr_core.h:35
@ DTTR_TARGET_AOB_HOOK
Definition dttr_core.h:32
struct DTTR_Core_PatchGroup DTTR_Core_PatchGroup
Definition dttr_core.h:25
DTTR_Status
Definition dttr_result.h:13
@ DTTR_ERR_HOOK_CHAIN_UNSUPPORTED
Definition dttr_result.h:24
@ DTTR_ERR_RUNTIME_UNAVAILABLE
Definition dttr_result.h:21
@ DTTR_ERR_WRITE_FAILED
Definition dttr_result.h:29
@ DTTR_ERR_NOT_INSTALLED
Definition dttr_result.h:19
@ DTTR_ERR_INVALID_ARGUMENT
Definition dttr_result.h:15
@ DTTR_ERR_UNSUPPORTED_LAYOUT
Definition dttr_result.h:31
@ DTTR_ERR_READ_FAILED
Definition dttr_result.h:28
@ DTTR_ERR_UNRESOLVED
Definition dttr_result.h:26
@ DTTR_ERR_NOT_FOUND
Definition dttr_result.h:16
@ DTTR_OK
Definition dttr_result.h:14
@ DTTR_ERR_UNSUPPORTED_CONTRACT
Definition dttr_result.h:32
@ DTTR_ERR_PROVENANCE_UNSAFE
Definition dttr_result.h:33
@ DTTR_ERR_OUT_OF_MEMORY
Definition dttr_result.h:23
@ DTTR_ERR_ABI_MISMATCH
Definition dttr_result.h:22
@ DTTR_ERR_NOT_CALLABLE
Definition dttr_result.h:27
@ DTTR_ERR_MEMORY_PROTECTION
Definition dttr_result.h:20
@ DTTR_ERR_MISSING_SYMBOL
Definition dttr_result.h:25
@ DTTR_ERR_POLICY_MISMATCH
Definition dttr_result.h:30
@ DTTR_ERR_ALREADY_INSTALLED
Definition dttr_result.h:18
@ DTTR_ERR_UNSUPPORTED
Definition dttr_result.h:17
uintptr_t(* DTTR_Core_SigscanFn)(HMODULE mod, const char *sig, const char *mask)
uintptr_t DTTR_Core_HookSigscan(HMODULE mod, const char *sig, const char *mask)
bool DTTR_Core_HookDetachChecked(DTTR_Core_Hook *hook)
static bool rel32_displacement(uintptr_t site, void *detour, int32_t *out_rel)
Definition core.c:410
const DTTR_Core_Context * dttr_core_patch_group_context(const DTTR_Core_PatchGroup *group)
Definition core.c:672
DTTR_Result DTTR_Core_PatchGroupDestroy(DTTR_Core_PatchGroup *group)
Definition core.c:642
void dttr_core_report_fail(DTTR_Core_TargetReport *report, size_t index, DTTR_Result result)
Definition core.c:912
void dttr_core_report_init(DTTR_Core_TargetReport *report)
Definition core.c:898
DTTR_Result DTTR_Core_HookFunction(const DTTR_Core_Context *ctx, uintptr_t address, int prologue_size, void *detour, void **out_original, DTTR_Core_Hook **out_hook)
Definition core.c:336
const char * DTTR_StatusName(DTTR_Status status)
Definition core.c:23
static int hex_value(char ch)
Definition core.c:86
static DTTR_Result patch_group_prepare_install(DTTR_Core_PatchGroup *group)
Definition core.c:557
DTTR_Result DTTR_Core_AOBFindInModule(HMODULE mod, const char *aob, uintptr_t *out_addr)
Definition core.c:238
static DTTR_Result patch_group_uninstall_from(DTTR_Core_PatchGroup *group, size_t keep_count)
Definition core.c:585
DTTR_Result DTTR_Core_SignatureFind(const DTTR_Core_Context *ctx, const char *sig, const char *mask, uintptr_t *out_addr)
Definition core.c:269
DTTR_Result DTTR_Core_PatchRel32Jump(const DTTR_Core_Context *ctx, uintptr_t address, void *detour, DTTR_Core_Patch **out_patch)
Definition core.c:421
static DTTR_Result patch_group_finish_install(DTTR_Core_PatchGroup *group, DTTR_Result result, DTTR_Core_Hook *hook, void **out_original, DTTR_Core_Hook **out_hook)
Definition core.c:677
DTTR_Result DTTR_Core_PatchGroupCreate(const DTTR_Core_Context *ctx, DTTR_Core_PatchGroup **out_group)
Definition core.c:607
bool DTTR_ResultOK(DTTR_Result result)
Definition core.c:78
static DTTR_Result parse_aob(const char *aob, char **out_sig, char **out_mask)
Definition core.c:114
DTTR_Result DTTR_Core_PatchAOBRel32Jump(const DTTR_Core_Context *ctx, const char *aob, intptr_t offset, void *detour, DTTR_Core_Patch **out_patch)
Definition core.c:446
static void ** target_original_slot(const DTTR_Core_TargetSpec *target)
Definition core.c:814
DTTR_Result DTTR_Core_PatchGroupPatchRel32Jump(DTTR_Core_PatchGroup *group, uintptr_t address, void *detour, DTTR_Core_Patch **out_patch)
Definition core.c:786
static DTTR_Result target_address(const DTTR_Core_Context *ctx, const DTTR_Core_TargetSpec *target, uintptr_t *out_address)
Definition core.c:927
DTTR_Result DTTR_Core_PatchGroupHookPointer(DTTR_Core_PatchGroup *group, uintptr_t address, void *new_value, void **out_original, DTTR_Core_Hook **out_hook)
Definition core.c:757
typedef kvec_t(install_entry)
Definition core.c:15
DTTR_Result DTTR_Core_PatchGroupRelease(DTTR_Core_PatchGroup **group)
Definition core.c:657
DTTR_Result DTTR_Core_Unpatch(DTTR_Core_Patch *patch)
Definition core.c:509
DTTR_Result DTTR_Core_HookAOB(const DTTR_Core_Context *ctx, const char *aob, intptr_t offset, int prologue_size, void *detour, void **out_original, DTTR_Core_Hook **out_hook)
Definition core.c:380
static DTTR_Result parse_aob_fail(char *sig, char *mask, DTTR_Status status, const char *message)
Definition core.c:102
bool DTTR_StatusOK(DTTR_Status status)
Definition core.c:70
bool DTTR_StatusFailed(DTTR_Status status)
Definition core.c:74
DTTR_Result DTTR_Core_PatchGroupUninstall(DTTR_Core_PatchGroup *group)
Definition core.c:634
DTTR_Result DTTR_Core_AOBFind(const DTTR_Core_Context *ctx, const char *aob, uintptr_t *out_addr)
Definition core.c:247
DTTR_Result DTTR_Core_PatchBytes(const DTTR_Core_Context *ctx, uintptr_t address, const uint8_t *bytes, size_t size, DTTR_Core_Patch **out_patch)
Definition core.c:302
DTTR_Result DTTR_Core_HookPointer(const DTTR_Core_Context *ctx, uintptr_t address, void *new_value, void **out_original, DTTR_Core_Hook **out_hook)
Definition core.c:472
DTTR_Result DTTR_Core_PatchGroupHookFunction(DTTR_Core_PatchGroup *group, uintptr_t address, int prologue_size, void *detour, void **out_original, DTTR_Core_Hook **out_hook)
Definition core.c:726
static DTTR_Result aob_scan_with(HMODULE mod, DTTR_Core_SigscanFn sigscan, const char *aob, uintptr_t *out_addr)
Definition core.c:212
static DTTR_Result patch_group_reserve(DTTR_Core_PatchGroup *group)
Definition core.c:532
static DTTR_Result install_one_target(const DTTR_Core_Context *ctx, const DTTR_Core_TargetSpec *target, DTTR_Core_Hook **out_handle)
Definition core.c:956
static bool patch_group_detach_checked(const DTTR_Core_PatchGroup *group, DTTR_Core_Hook *hook)
Definition core.c:566
DTTR_Result DTTR_Core_Unhook(DTTR_Core_Hook *hook)
Definition core.c:517
DTTR_Result DTTR_Core_PatchGroupInstallTargets(DTTR_Core_PatchGroup *group, const DTTR_Core_TargetSpec *targets, size_t target_count, DTTR_Core_TargetReport *out_report)
Definition core.c:826
static bool runtime_context_valid(const DTTR_Core_Context *ctx)
Definition core.c:82
DTTR_Result DTTR_Core_PatchGroupPatchBytes(DTTR_Core_PatchGroup *group, uintptr_t address, const uint8_t *bytes, size_t size, DTTR_Core_Patch **out_patch)
Definition core.c:697
static uintptr_t sigscan(HMODULE mod, const char *sig, const char *mask)
Definition core.c:45
DTTR_Core_UnhookCheckedFn unhook_checked
DTTR_Core_HookFunctionFn hook_function
DTTR_Core_UnhookFn unhook
DTTR_Core_HookPointerFn hook_pointer
DTTR_Core_SigscanFn sigscan
DTTR_Core_PatchBytesFn patch_bytes
const char * message
Definition dttr_core.h:58
const char * message
Optional static diagnostic text. May be NULL when status is enough.
Definition dttr_result.h:39
DTTR_Status status
Definition dttr_result.h:37
Definition core.c:10
void ** out_original
Definition core.c:12
DTTR_Core_Hook * hook
Definition core.c:11