102 Patches: Detours to the Rescue
C reference for DttR maintainers and modders.
Loading...
Searching...
No Matches
util_pkg_walk.c
Go to the documentation of this file.
1#ifndef DTTR_SDK_ENABLE_UNSTABLE
2#define DTTR_SDK_ENABLE_UNSTABLE
3#endif
4
5#include <stdint.h>
6#include <string.h>
7
9
10enum {
12};
13
15 DTTR_Util_PkgWalkResult result = {0};
16
17 result.status = status;
18
19 return result;
20}
21
23 DTTR_Util_PkgWalkOptions options = {0};
24
25 options.struct_size = sizeof(options);
27 options.max_depth = 64;
28 options.load_entries = true;
30
31 return options;
32}
33
35 switch (status) {
37 return "ok";
39 return "invalid argument";
41 return "unresolved symbol";
43 return "load failed";
45 return "unknown entry kind";
47 return "decode unsupported";
49 return "bounds invalid";
50 default:
51 return "unknown status";
52 }
53}
54
57 int32_t toc_index,
58 const DTTR_PCDOGS_T_PKG_TOCEntry *entry,
59 void *userdata,
60 void **out_entry,
61 size_t *out_size,
62 DTTR_Util_PkgVisitStatus *out_status
63) {
64 if (out_entry) {
65 *out_entry = NULL;
66 }
67
68 if (out_size) {
69 *out_size = 0;
70 }
71
72 if (!entry || !out_entry || !out_size || toc_index < 0) {
73 if (out_status) {
75 }
76
77 return false;
78 }
79
80 void *loaded = NULL;
81 if (!DTTR_ResultOK(DTTR_PCDOGS_F_PKG_LoadEntry->Call(ctx, toc_index, NULL, &loaded))
82 || !loaded) {
83 if (out_status) {
85 }
86
87 return false;
88 }
89
90 *out_entry = loaded;
91 *out_size = entry->size;
92
93 if (out_status) {
94 *out_status = DTTR_UTIL_PKG_STATUS_OK;
95 }
96
97 return true;
98}
99
101 const DTTR_Core_Context *ctx,
102 int32_t toc_index,
103 const DTTR_PCDOGS_T_PKG_TOCEntry *entry,
104 void *entry_data,
105 void *userdata
106) {
107 if (!entry_data) {
108 return;
109 }
110
111 BOOL ignored = FALSE;
112 DTTR_PCDOGS_F_PKG_FreeResourceData->Call(ctx, entry_data, &ignored);
113}
114
115static bool resolve_toc(
116 const DTTR_Core_Context *ctx,
117 const DTTR_Util_PkgWalkOptions *options,
118 const DTTR_PCDOGS_T_PKG_TOCEntry **out_entries,
119 uint32_t *out_count
120) {
121 if (!out_entries || !out_count) {
122 return false;
123 }
124
125 *out_entries = NULL;
126 *out_count = 0;
127
128 uint32_t requested_count = options->toc_count ? options->toc_count
130
131 if (options->toc_entries) {
132 *out_entries = options->toc_entries;
133 *out_count = requested_count;
134 return true;
135 }
136
137 uintptr_t pkg_load_entry_addr = 0;
139 ctx,
141 &pkg_load_entry_addr
142 );
143 if (!DTTR_ResultOK(resolved) || !pkg_load_entry_addr) {
144 return false;
145 }
146
147 uint32_t toc_addr = 0;
148 const uintptr_t toc_operand_addr = pkg_load_entry_addr
150 memcpy(&toc_addr, (const void *)toc_operand_addr, sizeof(toc_addr));
151 if (!toc_addr) {
152 return false;
153 }
154
155 *out_entries = (const DTTR_PCDOGS_T_PKG_TOCEntry *)(uintptr_t)toc_addr;
156 *out_count = requested_count;
157
158 return true;
159}
160
163 DTTR_Util_PkgVisitor visitor,
164 void *visitor_userdata,
165 const DTTR_Util_PkgVisit *visit
166) {
167 if (result) {
168 result->visited_count++;
169 result->failed_count += visit && visit->status != DTTR_UTIL_PKG_STATUS_OK;
170 }
171
172 return visitor ? visitor(visit, visitor_userdata) : DTTR_UTIL_PKG_VISIT_CONTINUE;
173}
174
176 const DTTR_Util_PkgWalkOptions *options,
179 uint32_t depth,
180 int32_t toc_index,
181 const DTTR_PCDOGS_T_PKG_TOCEntry *toc_entry,
182 const void *ptr,
183 const void *loaded_entry_base,
184 size_t loaded_entry_size,
185 const DTTR_Util_PkgVisit *parent
186) {
187 DTTR_Util_PkgVisit visit = {0};
188
189 visit.struct_size = sizeof(visit);
190 visit.kind = kind;
191 visit.status = status;
192 visit.depth = depth;
193 visit.toc_index = toc_index;
194
195 if (toc_entry) {
196 visit.pkg_offset = toc_entry->offset;
197 visit.pkg_size = toc_entry->size;
198 }
199
200 visit.ptr = ptr;
201 visit.parent = parent;
202 visit.toc_entry = toc_entry;
203 visit.loaded_entry_base = loaded_entry_base;
204 visit.loaded_entry_size = loaded_entry_size;
205 visit.userdata = options ? options->io_userdata : NULL;
206
207 return visit;
208}
209
211 return action == DTTR_UTIL_PKG_VISIT_RECURSE
213}
214
215static bool should_load(
216 const DTTR_Util_PkgWalkOptions *options,
218) {
219 if (!(options->domains & DTTR_UTIL_PKG_DOMAIN_ENTRY)) {
220 return false;
221 }
222
224 return true;
225 }
226
227 return options->load_entries && action == DTTR_UTIL_PKG_VISIT_RECURSE;
228}
229
232 const DTTR_Util_PkgWalkOptions *options,
233 DTTR_Util_PkgVisitor visitor,
234 void *visitor_userdata,
235 const DTTR_Util_PkgVisit *loaded_visit
236) {
238 || loaded_visit->depth + 1 > options->max_depth) {
239 return false;
240 }
241
242 DTTR_Util_PkgVisit unsupported_visit = make_visit(
243 options,
246 loaded_visit->depth + 1,
247 loaded_visit->toc_index,
248 loaded_visit->toc_entry,
249 loaded_visit->ptr,
250 loaded_visit->loaded_entry_base,
251 loaded_visit->loaded_entry_size,
252 loaded_visit
253 );
255 action = emit_visit(result, visitor, visitor_userdata, &unsupported_visit);
256 if (action == DTTR_UTIL_PKG_VISIT_STOP) {
257 result->stopped = true;
258 return true;
259 }
260
261 return false;
262}
263
265 const DTTR_Core_Context *ctx,
266 const DTTR_Util_PkgWalkOptions *options,
267 DTTR_Util_PkgVisitor visitor,
268 void *userdata
269) {
271 if (!options) {
272 options = &defaults;
273 }
274
275 if (!visitor) {
277 }
278
280 if (!(options->domains & DTTR_UTIL_PKG_DOMAIN_TOC)) {
281 return result;
282 }
283
284 const DTTR_PCDOGS_T_PKG_TOCEntry *toc_entries = NULL;
285 uint32_t toc_count = 0;
286 if (!resolve_toc(ctx, options, &toc_entries, &toc_count)) {
288 }
289
290 DTTR_Util_PkgLoadEntryFn load_entry = options->load_entry ? options->load_entry
292 DTTR_Util_PkgFreeEntryFn free_entry = options->free_entry ? options->free_entry
294 uint32_t max_depth = options->max_depth;
295
296 for (uint32_t i = 0; i < toc_count; ++i) {
297 const DTTR_PCDOGS_T_PKG_TOCEntry *toc_entry = &toc_entries[i];
298
299 DTTR_Util_PkgVisit toc_visit = make_visit(
300 options,
303 0,
304 (int32_t)i,
305 toc_entry,
306 toc_entry,
307 NULL,
308 0,
309 NULL
310 );
311
313 toc_action = emit_visit(&result, visitor, userdata, &toc_visit);
314
315 if (toc_action == DTTR_UTIL_PKG_VISIT_STOP) {
316 result.stopped = true;
317 break;
318 }
319
320 if (toc_action == DTTR_UTIL_PKG_VISIT_SKIP_SUBTREE || max_depth < 1
321 || !should_load(options, toc_action)) {
322 continue;
323 }
324
325 void *loaded = NULL;
326 size_t loaded_size = 0;
328
329 if (!load_entry(
330 ctx,
331 (int32_t)i,
332 toc_entry,
333 options->io_userdata,
334 &loaded,
335 &loaded_size,
336 &load_status
337 )
338 || !loaded) {
339
340 if (load_status == DTTR_UTIL_PKG_STATUS_OK) {
342 }
343
344 DTTR_Util_PkgVisit failed_visit = make_visit(
345 options,
347 load_status,
348 1,
349 (int32_t)i,
350 toc_entry,
351 NULL,
352 NULL,
353 0,
354 &toc_visit
355 );
356
358 failed_action = emit_visit(&result, visitor, userdata, &failed_visit);
359
360 if (failed_action == DTTR_UTIL_PKG_VISIT_STOP) {
361 result.stopped = true;
362 break;
363 }
364
365 continue;
366 }
367
368 result.loaded_count++;
369
370 DTTR_Util_PkgVisit loaded_visit = make_visit(
371 options,
374 1,
375 (int32_t)i,
376 toc_entry,
377 loaded,
378 loaded,
379 loaded_size,
380 &toc_visit
381 );
382
384 loaded_action = emit_visit(&result, visitor, userdata, &loaded_visit);
385
386 if (loaded_action == DTTR_UTIL_PKG_VISIT_STOP) {
387 result.stopped = true;
388 free_entry(ctx, (int32_t)i, toc_entry, loaded, options->io_userdata);
389 break;
390 }
391
392 if (should_recurse(loaded_action)
393 && emit_decode_boundary(&result, options, visitor, userdata, &loaded_visit)) {
394 free_entry(ctx, (int32_t)i, toc_entry, loaded, options->io_userdata);
395 break;
396 }
397
398 free_entry(ctx, (int32_t)i, toc_entry, loaded, options->io_userdata);
399 }
400
401 return result;
402}
DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 void void void void DWORD f BOOL
void void * ctx
DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 void void void void DWORD f FALSE
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void NULL
DTTR_PCDOGS_API const struct dttr_pcdogs_function_accessor_PKG_LoadEntry *const DTTR_PCDOGS_F_PKG_LoadEntry
Accessor object for PKG_LoadEntry.
@ DTTR_PCDOGS_FUNCTION_PKG_LOAD_ENTRY
Not yet documented.
DTTR_PCDOGS_API const struct dttr_pcdogs_function_accessor_PKG_FreeResourceData *const DTTR_PCDOGS_F_PKG_FreeResourceData
Accessor object for PKG_FreeResourceData.
bool DTTR_ResultOK(DTTR_Result result)
Definition core.c:78
DTTR_Util_PkgVisitAction(* DTTR_Util_PkgVisitor)(const DTTR_Util_PkgVisit *visit, void *userdata)
DTTR_Util_PkgVisitKind
Object kind reported to a package visitor.
@ DTTR_UTIL_PKG_VISIT_TOC_ENTRY
@ DTTR_UTIL_PKG_VISIT_LOADED_ENTRY
@ DTTR_UTIL_PKG_VISIT_UNSUPPORTED
DTTR_Util_PkgVisitAction
Visitor return value controlling traversal below the current visit.
@ DTTR_UTIL_PKG_VISIT_LOAD_AND_RECURSE
@ DTTR_UTIL_PKG_VISIT_RECURSE
@ DTTR_UTIL_PKG_VISIT_SKIP_SUBTREE
@ DTTR_UTIL_PKG_VISIT_STOP
@ DTTR_UTIL_PKG_VISIT_CONTINUE
bool(* DTTR_Util_PkgLoadEntryFn)(const DTTR_Core_Context *ctx, int32_t toc_index, const DTTR_PCDOGS_T_PKG_TOCEntry *entry, void *userdata, void **out_entry, size_t *out_size, DTTR_Util_PkgVisitStatus *out_status)
#define DTTR_UTIL_PKG_DEFAULT_TOC_COUNT
@ DTTR_UTIL_PKG_DOMAIN_ALL_KNOWN
@ DTTR_UTIL_PKG_DOMAIN_TOC
@ DTTR_UTIL_PKG_DOMAIN_ENTRY
@ DTTR_UTIL_PKG_DOMAIN_KNOWN_CHILDREN
void(* DTTR_Util_PkgFreeEntryFn)(const DTTR_Core_Context *ctx, int32_t toc_index, const DTTR_PCDOGS_T_PKG_TOCEntry *entry, void *entry_data, void *userdata)
DTTR_Util_PkgVisitStatus
Per-visit and whole-walk status values.
@ DTTR_UTIL_PKG_STATUS_UNRESOLVED_SYMBOL
@ DTTR_UTIL_PKG_STATUS_OK
@ DTTR_UTIL_PKG_STATUS_INVALID_ARGUMENT
@ DTTR_UTIL_PKG_STATUS_LOAD_FAILED
@ DTTR_UTIL_PKG_STATUS_DECODE_UNSUPPORTED
@ DTTR_UTIL_PKG_STATUS_UNKNOWN_ENTRY_KIND
@ DTTR_UTIL_PKG_STATUS_BOUNDS_INVALID
DTTR_Result DTTR_PCDOGS_FunctionResolve(const DTTR_Core_Context *ctx, DTTR_PCDOGS_T_Function_ID id, uintptr_t *out_addr)
Package TOC entry used by the package table. Size-lane aliases share this storage.
uint32_t offset
Offset 0x0.
uint32_t size
Offset 0x4.
A single callback frame. Pointers are visit-lifetime unless documented by PCDOGS.
DTTR_Util_PkgVisitKind kind
const void * loaded_entry_base
const DTTR_Util_PkgVisit * parent
DTTR_Util_PkgVisitStatus status
const DTTR_PCDOGS_T_PKG_TOCEntry * toc_entry
Walker configuration. Use DTTR_Util_PkgWalk_DefaultOptions() for defaults.
const DTTR_PCDOGS_T_PKG_TOCEntry * toc_entries
DTTR_Util_PkgLoadEntryFn load_entry
DTTR_Util_PkgFreeEntryFn free_entry
Aggregate walk result. Non-fatal per-entry failures keep traversal moving.
DTTR_Util_PkgVisitStatus status
static bool should_load(const DTTR_Util_PkgWalkOptions *options, DTTR_Util_PkgVisitAction action)
const char * DTTR_Util_PkgVisitStatusName(DTTR_Util_PkgVisitStatus status)
static bool emit_decode_boundary(DTTR_Util_PkgWalkResult *result, const DTTR_Util_PkgWalkOptions *options, DTTR_Util_PkgVisitor visitor, void *visitor_userdata, const DTTR_Util_PkgVisit *loaded_visit)
static bool default_load_entry(const DTTR_Core_Context *ctx, int32_t toc_index, const DTTR_PCDOGS_T_PKG_TOCEntry *entry, void *userdata, void **out_entry, size_t *out_size, DTTR_Util_PkgVisitStatus *out_status)
DTTR_Util_PkgWalkOptions DTTR_Util_PkgWalk_DefaultOptions()
static bool should_recurse(DTTR_Util_PkgVisitAction action)
DTTR_Util_PkgWalkResult DTTR_Util_PkgWalk(const DTTR_Core_Context *ctx, const DTTR_Util_PkgWalkOptions *options, DTTR_Util_PkgVisitor visitor, void *userdata)
static DTTR_Util_PkgVisitAction emit_visit(DTTR_Util_PkgWalkResult *result, DTTR_Util_PkgVisitor visitor, void *visitor_userdata, const DTTR_Util_PkgVisit *visit)
static void default_free_entry(const DTTR_Core_Context *ctx, int32_t toc_index, const DTTR_PCDOGS_T_PKG_TOCEntry *entry, void *entry_data, void *userdata)
static bool resolve_toc(const DTTR_Core_Context *ctx, const DTTR_Util_PkgWalkOptions *options, const DTTR_PCDOGS_T_PKG_TOCEntry **out_entries, uint32_t *out_count)
static DTTR_Util_PkgVisit make_visit(const DTTR_Util_PkgWalkOptions *options, DTTR_Util_PkgVisitKind kind, DTTR_Util_PkgVisitStatus status, uint32_t depth, int32_t toc_index, const DTTR_PCDOGS_T_PKG_TOCEntry *toc_entry, const void *ptr, const void *loaded_entry_base, size_t loaded_entry_size, const DTTR_Util_PkgVisit *parent)
static DTTR_Util_PkgWalkResult make_result(DTTR_Util_PkgVisitStatus status)
@ PKG_LOAD_ENTRY_TOC_OPERAND_OFFSET