6 unsigned int is_array:1;
8 unsigned int is_struct:1; // split for args
11 struct parsed_proto_arg {
13 struct parsed_type type;
14 struct parsed_proto *fptr;
21 struct parsed_type ret_type;
22 struct parsed_type type;
24 struct parsed_proto_arg arg[16];
28 unsigned int is_func:1;
29 unsigned int is_stdcall:1;
30 unsigned int is_vararg:1;
31 unsigned int is_fptr:1;
32 unsigned int is_noreturn:1;
33 unsigned int is_unresolved:1;
34 unsigned int has_structarg:1;
37 static const char *hdrfn;
38 static int hdrfline = 0;
40 static void pp_copy_arg(struct parsed_proto_arg *d,
41 const struct parsed_proto_arg *s);
43 static int b_pp_c_handler(char *proto, const char *fname);
45 static int do_protostrs(FILE *fhdr, const char *fname)
47 const char *finc_name;
48 const char *hdrfn_saved;
60 while (fgets(protostr, sizeof(protostr), fhdr))
63 if (strncmp(protostr, "//#include ", 11) == 0) {
64 finc_name = protostr + 11;
65 p = strpbrk(finc_name, "\r\n ");
70 p = strrchr(hdrfn_saved, '/');
72 memcpy(path, hdrfn_saved,
74 path[p - hdrfn_saved + 1] = 0;
76 snprintf(fname_inc, sizeof(fname_inc), "%s%s",
78 finc = fopen(fname_inc, "r");
80 printf("%s:%d: can't open '%s'\n",
81 fname_inc, line, finc_name);
84 ret = do_protostrs(finc, finc_name);
90 if (strncmp(sskip(protostr), "//", 2) == 0)
93 p = protostr + strlen(protostr);
94 for (p--; p >= protostr && my_isblank(*p); --p)
101 ret = b_pp_c_handler(protostr, hdrfn);
114 static int get_regparm(char *dst, size_t dlen, char *p)
121 for (o = 0, i = 1; o < dlen; i++) {
133 static const char *known_type_mod[] = {
142 static const char *known_ptr_types[] = {
163 "PMEMORY_BASIC_INFORMATION",
174 static const char *ignored_keywords[] = {
182 // returns ptr to char after type ends
183 static int typecmp(const char *n, const char *t)
185 for (; *t != 0; n++, t++) {
186 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
188 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
197 static const char *skip_type_mod(const char *n)
202 for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
203 len = strlen(known_type_mod[i]);
204 if (strncmp(n, known_type_mod[i], len) != 0)
206 if (!my_isblank(n[len]))
210 while (my_isblank(*n))
218 static int check_type(const char *name, struct parsed_type *type)
224 n = skip_type_mod(name);
226 for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
227 if (typecmp(n, known_ptr_types[i]))
234 if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
237 // assume single word
238 while (!my_isblank(*n) && !my_issep(*n))
243 while (my_isblank(*n))
254 type->name = strndup(name, ret);
255 if (IS(type->name, "VOID"))
256 memcpy(type->name, "void", 4);
261 /* args are always expanded to 32bit */
262 static const char *map_reg(const char *reg)
264 const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
265 const char *regs_w[] = { "ax", "bx", "cx", "dx", "si", "di" };
266 const char *regs_b[] = { "al", "bl", "cl", "dl" };
269 for (i = 0; i < ARRAY_SIZE(regs_w); i++)
270 if (IS(reg, regs_w[i]))
273 for (i = 0; i < ARRAY_SIZE(regs_b); i++)
274 if (IS(reg, regs_b[i]))
280 static int check_struct_arg(struct parsed_proto_arg *arg)
282 if (IS(arg->type.name, "POINT"))
288 static int parse_protostr(char *protostr, struct parsed_proto *pp)
290 struct parsed_proto_arg *arg;
300 if (p[0] == '/' && p[1] == '/') {
301 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
305 // strip unneeded stuff
306 for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
307 if ((p1[0] == '/' && p1[1] == '*')
308 || (p1[0] == '*' && p1[1] == '/'))
312 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
316 else if (!strncmp(p, "noreturn ", 9)) {
321 for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
322 l = strlen(ignored_keywords[i]);
323 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
324 p = sskip(p + l + 1);
327 ret = check_type(p, &pp->ret_type);
329 printf("%s:%d:%zd: unhandled return in '%s'\n",
330 hdrfn, hdrfline, (p - protostr) + 1, protostr);
335 if (!strchr(p, ')')) {
336 p = next_idt(buf, sizeof(buf), p);
339 printf("%s:%d:%zd: var name missing\n",
340 hdrfn, hdrfline, (p - protostr) + 1);
343 strcpy(pp->name, buf);
348 pp->ret_type.is_array = 1;
360 p = next_word(cconv, sizeof(cconv), p);
363 printf("%s:%d:%zd: cconv missing\n",
364 hdrfn, hdrfline, (p - protostr) + 1);
367 if (IS(cconv, "__cdecl"))
369 else if (IS(cconv, "__stdcall"))
371 else if (IS(cconv, "__fastcall"))
373 else if (IS(cconv, "__thiscall"))
375 else if (IS(cconv, "__userpurge"))
376 pp->is_stdcall = 1; // IDA
377 else if (IS(cconv, "__usercall"))
378 pp->is_stdcall = 0; // IDA
379 else if (IS(cconv, "WINAPI"))
382 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
383 hdrfn, hdrfline, (p - protostr) + 1, cconv);
389 printf("%s:%d:%zd: '*' expected\n",
390 hdrfn, hdrfline, (p - protostr) + 1);
394 // XXX: skipping extra asterisks, for now
400 p = next_idt(buf, sizeof(buf), p);
403 //printf("%s:%d:%zd: func name missing\n",
404 // hdrfn, hdrfline, (p - protostr) + 1);
407 strcpy(pp->name, buf);
409 ret = get_regparm(regparm, sizeof(regparm), p);
411 if (!IS(regparm, "eax") && !IS(regparm, "ax")
412 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
414 printf("%s:%d:%zd: bad regparm: %s\n",
415 hdrfn, hdrfline, (p - protostr) + 1, regparm);
424 // not really ret_type is array, but ohwell
425 pp->ret_type.is_array = 1;
426 p = strchr(p + 1, ']');
428 printf("%s:%d:%zd: ']' expected\n",
429 hdrfn, hdrfline, (p - protostr) + 1);
435 printf("%s:%d:%zd: ')' expected\n",
436 hdrfn, hdrfline, (p - protostr) + 1);
443 printf("%s:%d:%zd: '(' expected, got '%c'\n",
444 hdrfn, hdrfline, (p - protostr) + 1, *p);
451 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
452 && *sskip(p + 4) == ')')
463 printf("%s:%d:%zd: ',' expected\n",
464 hdrfn, hdrfline, (p - protostr) + 1);
470 if (!strncmp(p, "...", 3)) {
477 printf("%s:%d:%zd: ')' expected\n",
478 hdrfn, hdrfline, (p - protostr) + 1);
482 arg = &pp->arg[xarg];
486 ret = check_type(p, &arg->type);
488 printf("%s:%d:%zd: unhandled type for arg%d\n",
489 hdrfn, hdrfline, (p - protostr) + 1, xarg);
496 arg->fptr = calloc(1, sizeof(*arg->fptr));
497 ret = parse_protostr(p1, arg->fptr);
499 printf("%s:%d:%zd: funcarg parse failed\n",
500 hdrfn, hdrfline, p1 - protostr);
503 // we'll treat it as void * for non-calls
504 arg->type.name = strdup("void *");
505 arg->type.is_ptr = 1;
510 p = next_idt(buf, sizeof(buf), p);
514 printf("%s:%d:%zd: idt missing for arg%d\n",
515 hdrfn, hdrfline, (p - protostr) + 1, xarg);
521 ret = get_regparm(regparm, sizeof(regparm), p);
526 arg->reg = strdup(map_reg(regparm));
529 ret = check_struct_arg(arg);
531 pp->has_structarg = 1;
532 arg->type.is_struct = 1;
533 free(arg->type.name);
534 arg->type.name = strdup("int");
535 for (l = 0; l < ret; l++) {
536 pp_copy_arg(&pp->arg[xarg], arg);
542 if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
543 if (pp->arg[0].reg != NULL) {
544 printf("%s:%d: %s with arg1 spec %s?\n",
545 hdrfn, hdrfline, cconv, pp->arg[0].reg);
547 pp->arg[0].reg = strdup("ecx");
550 if (xarg > 1 && IS(cconv, "__fastcall")) {
551 if (pp->arg[1].reg != NULL) {
552 printf("%s:%d: %s with arg2 spec %s?\n",
553 hdrfn, hdrfline, cconv, pp->arg[1].reg);
555 pp->arg[1].reg = strdup("edx");
558 if (pp->is_vararg && pp->is_stdcall) {
559 printf("%s:%d: vararg stdcall?\n", hdrfn, hdrfline);
565 for (i = 0; i < pp->argc; i++) {
566 if (pp->arg[i].reg == NULL)
575 static int pp_name_cmp(const void *p1, const void *p2)
577 const struct parsed_proto *pp1 = p1, *pp2 = p2;
578 return strcmp(pp1->name, pp2->name);
581 static struct parsed_proto *pp_cache;
582 static int pp_cache_size;
583 static int pp_cache_alloc;
585 static int b_pp_c_handler(char *proto, const char *fname)
589 if (pp_cache_size >= pp_cache_alloc) {
590 pp_cache_alloc = pp_cache_alloc * 2 + 64;
591 pp_cache = realloc(pp_cache, pp_cache_alloc
592 * sizeof(pp_cache[0]));
593 my_assert_not(pp_cache, NULL);
594 memset(pp_cache + pp_cache_size, 0,
595 (pp_cache_alloc - pp_cache_size)
596 * sizeof(pp_cache[0]));
599 ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
607 static void build_pp_cache(FILE *fhdr)
613 ret = do_protostrs(fhdr, hdrfn);
617 qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
620 static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
623 const struct parsed_proto *pp_ret;
624 struct parsed_proto pp_search;
626 if (pp_cache == NULL)
627 build_pp_cache(fhdr);
629 if (sym[0] == '_') // && strncmp(fname, "stdc", 4) == 0)
632 strcpy(pp_search.name, sym);
633 pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
634 sizeof(pp_cache[0]), pp_name_cmp);
635 if (pp_ret == NULL && !quiet)
636 printf("%s: sym '%s' is missing\n", hdrfn, sym);
641 static void pp_copy_arg(struct parsed_proto_arg *d,
642 const struct parsed_proto_arg *s)
644 memcpy(d, s, sizeof(*d));
646 if (s->reg != NULL) {
647 d->reg = strdup(s->reg);
648 my_assert_not(d->reg, NULL);
650 if (s->type.name != NULL) {
651 d->type.name = strdup(s->type.name);
652 my_assert_not(d->type.name, NULL);
654 if (s->fptr != NULL) {
655 d->fptr = malloc(sizeof(*d->fptr));
656 my_assert_not(d->fptr, NULL);
657 memcpy(d->fptr, s->fptr, sizeof(*d->fptr));
661 struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
663 struct parsed_proto *pp;
666 pp = malloc(sizeof(*pp));
667 my_assert_not(pp, NULL);
668 memcpy(pp, pp_c, sizeof(*pp)); // lazy..
670 // do the actual deep copy..
671 for (i = 0; i < pp_c->argc; i++)
672 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
673 if (pp_c->ret_type.name != NULL)
674 pp->ret_type.name = strdup(pp_c->ret_type.name);
679 static inline void proto_release(struct parsed_proto *pp)
683 for (i = 0; i < pp->argc; i++) {
684 if (pp->arg[i].reg != NULL)
685 free(pp->arg[i].reg);
686 if (pp->arg[i].type.name != NULL)
687 free(pp->arg[i].type.name);
688 if (pp->arg[i].fptr != NULL)
689 free(pp->arg[i].fptr);
691 if (pp->ret_type.name != NULL)
692 free(pp->ret_type.name);