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#define DTTR_SDK_ENABLE_UNSTABLE
2
3#include <stdbool.h>
4#include <stdint.h>
5#include <string.h>
6#include <windows.h>
7
8#include <dttr_core.h>
9#include <dttr_pcdogs.h>
10#include <dttr_runtime.h>
11#include <dttr_test_support.h>
12
13static uint8_t sig_target[] = {0x55, 0x8B, 0xEC, 0x90, 0x90, 0xC3, 0x33, 0xC0};
14static uint8_t patch_target[4];
15static uint8_t group_patch_target[2];
16
17static uintptr_t sigscan_bytes(
18 const uint8_t *bytes,
19 size_t size,
20 const char *sig,
21 const char *mask
22) {
23 const size_t len = strlen(mask);
24 if (!len || len > size) {
25 return 0;
26 }
27
28 for (size_t offset = 0; offset + len <= size; offset++) {
29 bool matched = true;
30 for (size_t i = 0; i < len; i++) {
31 if (mask[i] == 'x' && (uint8_t)sig[i] != bytes[offset + i]) {
32 matched = false;
33 break;
34 }
35 }
36
37 if (matched) {
38 return (uintptr_t)&bytes[offset];
39 }
40 }
41
42 return 0;
43}
44
45static uintptr_t sigscan(HMODULE mod, const char *sig, const char *mask) {
46 return sigscan_bytes(sig_target, sizeof(sig_target), sig, mask);
47}
48
49static const DTTR_Core_API RUNTIME = {
50 .sigscan = sigscan,
51 .hook_function = DTTR_Core_HookAttachFunction,
52 .hook_pointer = DTTR_Core_HookAttachPointer,
53 .patch_bytes = DTTR_Core_HookPatchBytes,
54 .unhook = DTTR_Core_HookDetach,
55 .hook_is_active = DTTR_Core_HookIsActive,
56 .unhook_checked = DTTR_Core_HookDetachChecked,
57};
58
60 return (DTTR_Core_Context){
61 .game_module = (HMODULE)1,
62 .api = &RUNTIME,
63 };
64}
65
68 uintptr_t addr = 0;
69
70 DTTR_Result result = DTTR_Core_AOBFind(&ctx, "55 8B EC ??", &addr);
71 assert_true(DTTR_ResultOK(result));
72 assert_ptr_equal((void *)addr, sig_target);
73
74 addr = 0;
75 result = DTTR_Core_SignatureFind(&ctx, "\x8B\xEC", "xx", &addr);
76 assert_true(DTTR_ResultOK(result));
77 assert_ptr_equal((void *)addr, &sig_target[1]);
78
79 result = DTTR_Core_AOBFind(&ctx, "55 8", &addr);
80 assert_int_equal(result.status, DTTR_ERR_INVALID_ARGUMENT);
81}
82
87 const uint8_t first_original[] = {0x10, 0x20, 0x30, 0x40};
88 const uint8_t first_patch[] = {0xA0, 0xB0, 0xC0, 0xD0};
89 const uint8_t added_original[] = {0x01, 0x02};
90 const uint8_t added_patch[] = {0x90, 0x90};
91 DTTR_Core_TargetReport report = {0};
92
93 memcpy(patch_target, first_original, sizeof(first_original));
94 memcpy(group_patch_target, added_original, sizeof(added_original));
95
97 assert_true(DTTR_ResultOK(result));
99 group,
100 (uintptr_t)patch_target,
101 first_patch,
102 sizeof(first_patch),
103 NULL
104 );
105 assert_true(DTTR_ResultOK(result));
106
107 const DTTR_Core_TargetSpec targets[] = {
108 {
110 .required = true,
111 .address = (uintptr_t)group_patch_target,
112 .patch_bytes = added_patch,
113 .patch_size = sizeof(added_patch),
114 },
115 {
116 .kind = DTTR_TARGET_AOB_PATCH,
117 .required = true,
118 .aob = "AA BB CC DD",
119 .patch_bytes = added_patch,
120 .patch_size = sizeof(added_patch),
121 },
122 };
123
125 group,
126 targets,
127 DTTR_TEST_ARRAY_COUNT(targets),
128 &report
129 );
130 assert_int_equal(result.status, DTTR_ERR_NOT_FOUND);
131 assert_int_equal(report.failed_index, 1);
132 assert_memory_equal(patch_target, first_patch, sizeof(first_patch));
133 assert_memory_equal(group_patch_target, added_original, sizeof(added_original));
134
136 assert_memory_equal(patch_target, first_original, sizeof(first_original));
138}
139
141 const char *filename,
142 const char *mode
143) {
144 return NULL;
145}
146
148 return TRUE;
149}
150
151static HRESULT __stdcall pcdogs_ddraw_create_ex_detour(
153 void **ddraw_out,
156) {
157 return S_OK;
158}
159
162 void *movie_key_state_original = NULL;
163 void *const replacement = (void *)0x33333333u;
164
165 assert_int_equal(
168 );
170 true,
172 &ddraw_original
173 );
174 assert_int_equal(ddraw.kind, DTTR_PCDOGS_PATCH_FUNCTION_HOOK);
175 assert_true(ddraw.required);
176 assert_int_equal(ddraw.function, DTTR_PCDOGS_FUNCTION_DDRAW_CREATE_EX);
177 assert_ptr_equal(ddraw.detour, pcdogs_ddraw_create_ex_detour);
178 assert_ptr_equal(ddraw.out_original, &ddraw_original);
179
180 assert_int_equal(
183 );
186 ->PatchSpec(false, replacement, &movie_key_state_original);
187 assert_int_equal(movie_key_state.kind, DTTR_PCDOGS_PATCH_DATA_POINTER_HOOK);
188 assert_false(movie_key_state.required);
189 assert_int_equal(
190 movie_key_state.global,
192 );
193 assert_ptr_equal(movie_key_state.new_value, replacement);
194 assert_ptr_equal(movie_key_state.out_original, &movie_key_state_original);
195}
196
198 DTTR_PCDOGS_F_File_Open_proto file_open_original = NULL;
200
202 true,
204 &file_open_original
205 );
206 assert_int_equal(file_open.kind, DTTR_PCDOGS_PATCH_FUNCTION_HOOK);
207 assert_true(file_open.required);
208 assert_int_equal(file_open.function, DTTR_PCDOGS_FUNCTION_FILE_OPEN);
209 assert_ptr_equal(file_open.detour, pcdogs_file_open_detour);
210 assert_ptr_equal(file_open.out_original, &file_open_original);
211
213 ->PatchSpec(
214 true,
216 &cleanup_original
217 );
218 assert_int_equal(cleanup.kind, DTTR_PCDOGS_PATCH_FUNCTION_HOOK);
219 assert_true(cleanup.required);
220 assert_int_equal(
221 cleanup.function,
223 );
224 assert_ptr_equal(cleanup.detour, pcdogs_cleanup_title_resources_detour);
225 assert_ptr_equal(cleanup.out_original, &cleanup_original);
226}
227
229 void **state
230) {
231 void *handle1_original = NULL;
232 void *handle0_original = NULL;
233 void *const replacement1 = (void *)0x11111111u;
234 void *const replacement0 = (void *)0x22222222u;
235
237 true,
238 replacement1,
239 &handle1_original
240 );
241 assert_int_equal(handle1.kind, DTTR_PCDOGS_PATCH_DATA_POINTER_HOOK);
242 assert_true(handle1.required);
243 assert_int_equal(handle1.global, DTTR_PCDOGS_DATA_TITLE_RESOURCE_HANDLE1);
244 assert_ptr_equal(handle1.new_value, replacement1);
245 assert_ptr_equal(handle1.out_original, &handle1_original);
246
248 false,
249 replacement0,
250 &handle0_original
251 );
252 assert_int_equal(handle0.kind, DTTR_PCDOGS_PATCH_DATA_POINTER_HOOK);
253 assert_false(handle0.required);
254 assert_int_equal(handle0.global, DTTR_PCDOGS_DATA_TITLE_RESOURCE_HANDLE0);
255 assert_ptr_equal(handle0.new_value, replacement0);
256 assert_ptr_equal(handle0.out_original, &handle0_original);
257}
258
259static const DTTR_TestCase TEST_CASES[] = {
260 {"core-sdk-signatures", test_signature_helpers_resolve_aob_patterns},
261 {"core-sdk-patch-group-rollback",
263 {"pcdogs-generated-function-patch-specs",
265 {"pcdogs-generated-title-resource-patch-specs",
267 {"pcdogs-unstable-patch-specs", test_pcdogs_unstable_patch_specs_match_stable_shape},
268};
269
static void cleanup(DTTR_BackendState *state)
return S_OK
DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 void void void void DWORD f BOOL
const DTTR_BackendState * state
void void * ctx
const DWORD size
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void NULL
static const DTTR_TestCase TEST_CASES[]
Definition core.c:17
@ DTTR_TARGET_ADDRESS_PATCH
Definition dttr_core.h:29
@ DTTR_TARGET_AOB_PATCH
Definition dttr_core.h:30
struct DTTR_Core_PatchGroup DTTR_Core_PatchGroup
Definition dttr_core.h:25
DTTR_PCDOGS_API const struct dttr_pcdogs_function_accessor_DDraw_CreateEx *const DTTR_PCDOGS_F_DDraw_CreateEx
Accessor object for DDraw_CreateEx.
DTTR_PCDOGS_API const struct dttr_pcdogs_function_accessor_File_Open *const DTTR_PCDOGS_F_File_Open
Accessor object for File_Open.
GUID DTTR_PCDOGS_T_Win32_GUID
Win32 GUID value used by DirectDraw import entries.
@ DTTR_PCDOGS_DATA_TITLE_RESOURCE_HANDLE1
@ DTTR_PCDOGS_DATA_TITLE_RESOURCE_HANDLE0
@ DTTR_PCDOGS_DATA_VIDEO_PLAY_MOVIE_LOOP_GET_ASYNC_KEY_STATE_THUNK
BOOL(* DTTR_PCDOGS_F_Title_CleanupScreenResources_proto)()
HRESULT(* DTTR_PCDOGS_F_DDraw_CreateEx_proto)(DTTR_PCDOGS_T_Win32_GUID *lp_guid, void **lplp_dd, DTTR_PCDOGS_T_Win32_GUID *iid, DTTR_PCDOGS_T_COM_IUnknown *p_unk_outer)
DTTR_PCDOGS_API const struct DTTR_PCDOGS_D_Title_ResourceHandle1_type *const DTTR_PCDOGS_D_Title_ResourceHandle1
@ DTTR_PCDOGS_PATCH_DATA_POINTER_HOOK
@ DTTR_PCDOGS_PATCH_FUNCTION_HOOK
DTTR_PCDOGS_API const struct DTTR_PCDOGS_D_Video_PlayMovieLoop_GetAsyncKeyStateThunk_type *const DTTR_PCDOGS_D_Video_PlayMovieLoop_GetAsyncKeyStateThunk
DTTR_PCDOGS_API const struct DTTR_PCDOGS_D_Title_ResourceHandle0_type *const DTTR_PCDOGS_D_Title_ResourceHandle0
@ DTTR_PCDOGS_FUNCTION_FILE_OPEN
@ DTTR_PCDOGS_FUNCTION_TITLE_CLEANUP_SCREEN_RESOURCES
@ DTTR_PCDOGS_FUNCTION_DDRAW_CREATE_EX
DTTR_PCDOGS_API const struct dttr_pcdogs_function_accessor_Title_CleanupScreenResources *const DTTR_PCDOGS_F_Title_CleanupScreenResources
Accessor object for Title_CleanupScreenResources.
DTTR_PCDOGS_T_File_Handle *(* DTTR_PCDOGS_F_File_Open_proto)(char const *filename, char const *mode)
struct DTTR_PCDOGS_T_COM_IUnknown DTTR_PCDOGS_T_COM_IUnknown
@ DTTR_ERR_INVALID_ARGUMENT
Definition dttr_result.h:15
@ DTTR_ERR_NOT_FOUND
Definition dttr_result.h:16
DTTR_Core_Hook * DTTR_Core_HookAttachPointer(uintptr_t addr, void *new_value, void **out_original)
bool DTTR_Core_HookIsActive(DTTR_Core_Hook *hook)
DTTR_Core_Hook * DTTR_Core_HookAttachFunction(uintptr_t addr, int prologue_size, void *handler, void **out_original)
DTTR_Core_Hook * DTTR_Core_HookPatchBytes(uintptr_t addr, const uint8_t *bytes, size_t size)
void DTTR_Core_HookCleanupAll()
Detach all remaining hooks and free the sigscan cache.
void DTTR_Core_HookDetach(DTTR_Core_Hook *hook)
bool DTTR_Core_HookDetachChecked(DTTR_Core_Hook *hook)
#define DTTR_TEST_ARRAY_COUNT(TESTS)
#define DTTR_TEST_MAIN(TESTS)
DTTR_Result DTTR_Core_PatchGroupDestroy(DTTR_Core_PatchGroup *group)
Definition core.c:642
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_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
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_PatchGroupInstallTargets(DTTR_Core_PatchGroup *group, const DTTR_Core_TargetSpec *targets, size_t target_count, DTTR_Core_TargetReport *out_report)
Definition core.c:826
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 void test_patch_group_target_failure_rolls_back_only_new_entries(void **state)
Definition core.c:83
static void test_pcdogs_unstable_patch_specs_match_stable_shape(void **state)
Definition core.c:160
static uintptr_t sigscan(HMODULE mod, const char *sig, const char *mask)
Definition core.c:45
static uint8_t group_patch_target[2]
Definition core.c:15
static uint8_t sig_target[]
Definition core.c:13
static uintptr_t sigscan_bytes(const uint8_t *bytes, size_t size, const char *sig, const char *mask)
Definition core.c:17
static HRESULT pcdogs_ddraw_create_ex_detour(DTTR_PCDOGS_T_Win32_GUID *guid, void **ddraw_out, DTTR_PCDOGS_T_Win32_GUID *iid, DTTR_PCDOGS_T_COM_IUnknown *outer)
Definition core.c:151
static uint8_t patch_target[4]
Definition core.c:14
static const DTTR_Core_API RUNTIME
Definition core.c:49
static void test_pcdogs_generated_function_patch_specs_name_current_hooks(void **state)
Definition core.c:197
static void test_signature_helpers_resolve_aob_patterns(void **state)
Definition core.c:66
static DTTR_PCDOGS_T_File_Handle * pcdogs_file_open_detour(const char *filename, const char *mode)
Definition core.c:140
static BOOL pcdogs_cleanup_title_resources_detour()
Definition core.c:147
static DTTR_Core_Context runtime_context()
Definition core.c:59
static void test_pcdogs_generated_title_resource_patch_specs_use_current_names(void **state)
Definition core.c:228
CRT-compatible file handle layout, used by package and asset loading streams.
DTTR_PCDOGS_T_Data_ID global
DTTR_PCDOGS_T_Function_ID function
DTTR_PCDOGS_T_Patch_Kind kind
DTTR_Status status
Definition dttr_result.h:37