6 unsigned int is_array:1;
10 struct parsed_proto_arg {
12 struct parsed_type type;
13 struct parsed_proto *fptr;
20 struct parsed_type ret_type;
21 struct parsed_type type;
23 struct parsed_proto_arg arg[16];
27 unsigned int is_func:1;
28 unsigned int is_stdcall:1;
29 unsigned int is_vararg:1;
30 unsigned int is_fptr:1;
31 unsigned int is_noreturn:1;
34 static const char *hdrfn;
35 static int hdrfline = 0;
37 static int b_pp_c_handler(char *proto, const char *fname);
39 static int do_protostrs(FILE *fhdr, const char *fname)
41 const char *finc_name;
42 const char *hdrfn_saved;
52 while (fgets(protostr, sizeof(protostr), fhdr))
55 if (strncmp(protostr, "//#include ", 11) == 0) {
56 finc_name = protostr + 11;
57 p = strpbrk(finc_name, "\r\n ");
61 finc = fopen(finc_name, "r");
63 printf("%s:%d: can't open '%s'\n",
64 fname, line, finc_name);
67 ret = do_protostrs(finc, finc_name);
73 if (strncmp(sskip(protostr), "//", 2) == 0)
76 p = protostr + strlen(protostr);
77 for (p--; p >= protostr && my_isblank(*p); --p)
84 ret = b_pp_c_handler(protostr, hdrfn);
97 static int get_regparm(char *dst, size_t dlen, char *p)
104 for (o = 0, i = 1; o < dlen; i++) {
116 static const char *known_type_mod[] = {
124 static const char *known_ptr_types[] = {
148 static const char *ignored_keywords[] = {
156 // returns ptr to char after type ends
157 static int typecmp(const char *n, const char *t)
159 for (; *t != 0; n++, t++) {
160 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
162 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
171 static const char *skip_type_mod(const char *n)
176 for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
177 len = strlen(known_type_mod[i]);
178 if (strncmp(n, known_type_mod[i], len) != 0)
180 if (!my_isblank(n[len]))
184 while (my_isblank(*n))
192 static int check_type(const char *name, struct parsed_type *type)
198 n = skip_type_mod(name);
200 for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
201 if (typecmp(n, known_ptr_types[i]))
208 if (n[0] == 'L' && n[1] == 'P')
211 // assume single word
212 while (!my_isblank(*n) && !my_issep(*n))
217 while (my_isblank(*n))
228 type->name = strndup(name, ret);
232 /* args are always expanded to 32bit */
233 static const char *map_reg(const char *reg)
235 const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
236 const char *regs_w[] = { "ax", "bx", "cx", "dx", "si", "di" };
237 const char *regs_b[] = { "al", "bl", "cl", "dl" };
240 for (i = 0; i < ARRAY_SIZE(regs_w); i++)
241 if (IS(reg, regs_w[i]))
244 for (i = 0; i < ARRAY_SIZE(regs_b); i++)
245 if (IS(reg, regs_b[i]))
251 static int parse_protostr(char *protostr, struct parsed_proto *pp)
253 struct parsed_proto_arg *arg;
263 if (p[0] == '/' && p[1] == '/') {
264 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
268 // strip unneeded stuff
269 for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
270 if ((p1[0] == '/' && p1[1] == '*')
271 || (p1[0] == '*' && p1[1] == '/'))
275 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
280 for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
281 l = strlen(ignored_keywords[i]);
282 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
283 p = sskip(p + l + 1);
286 ret = check_type(p, &pp->ret_type);
288 printf("%s:%d:%zd: unhandled return in '%s'\n",
289 hdrfn, hdrfline, (p - protostr) + 1, protostr);
294 if (!strchr(p, ')')) {
295 p = next_idt(buf, sizeof(buf), p);
298 printf("%s:%d:%zd: var name missing\n",
299 hdrfn, hdrfline, (p - protostr) + 1);
302 strcpy(pp->name, buf);
307 pp->ret_type.is_array = 1;
319 p = next_word(cconv, sizeof(cconv), p);
322 printf("%s:%d:%zd: cconv missing\n",
323 hdrfn, hdrfline, (p - protostr) + 1);
326 if (IS(cconv, "__cdecl"))
328 else if (IS(cconv, "__stdcall"))
330 else if (IS(cconv, "__fastcall"))
332 else if (IS(cconv, "__thiscall"))
334 else if (IS(cconv, "__userpurge"))
335 pp->is_stdcall = 1; // IDA
336 else if (IS(cconv, "__usercall"))
337 pp->is_stdcall = 0; // IDA
338 else if (IS(cconv, "WINAPI"))
341 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
342 hdrfn, hdrfline, (p - protostr) + 1, cconv);
348 printf("%s:%d:%zd: '*' expected\n",
349 hdrfn, hdrfline, (p - protostr) + 1);
353 // XXX: skipping extra asterisks, for now
359 p = next_idt(buf, sizeof(buf), p);
362 //printf("%s:%d:%zd: func name missing\n",
363 // hdrfn, hdrfline, (p - protostr) + 1);
366 strcpy(pp->name, buf);
368 ret = get_regparm(regparm, sizeof(regparm), p);
370 if (!IS(regparm, "eax") && !IS(regparm, "ax")
371 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
373 printf("%s:%d:%zd: bad regparm: %s\n",
374 hdrfn, hdrfline, (p - protostr) + 1, regparm);
383 // not really ret_type is array, but ohwell
384 pp->ret_type.is_array = 1;
385 p = strchr(p + 1, ']');
387 printf("%s:%d:%zd: ']' expected\n",
388 hdrfn, hdrfline, (p - protostr) + 1);
394 printf("%s:%d:%zd: ')' expected\n",
395 hdrfn, hdrfline, (p - protostr) + 1);
402 printf("%s:%d:%zd: '(' expected, got '%c'\n",
403 hdrfn, hdrfline, (p - protostr) + 1, *p);
410 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
411 && *sskip(p + 4) == ')')
423 if (!strncmp(p, "...", 3)) {
430 printf("%s:%d:%zd: ')' expected\n",
431 hdrfn, hdrfline, (p - protostr) + 1);
435 arg = &pp->arg[xarg];
439 ret = check_type(p, &arg->type);
441 printf("%s:%d:%zd: unhandled type for arg%d\n",
442 hdrfn, hdrfline, (p - protostr) + 1, xarg);
449 arg->fptr = calloc(1, sizeof(*arg->fptr));
450 ret = parse_protostr(p1, arg->fptr);
452 printf("%s:%d:%zd: funcarg parse failed\n",
453 hdrfn, hdrfline, p1 - protostr);
456 // we'll treat it as void * for non-calls
457 arg->type.name = "void *";
458 arg->type.is_ptr = 1;
463 p = next_idt(buf, sizeof(buf), p);
467 printf("%s:%d:%zd: idt missing for arg%d\n",
468 hdrfn, hdrfline, (p - protostr) + 1, xarg);
474 ret = get_regparm(regparm, sizeof(regparm), p);
479 arg->reg = strdup(map_reg(regparm));
483 if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
484 if (pp->arg[0].reg != NULL) {
485 printf("%s:%d: %s with arg1 spec %s?\n",
486 hdrfn, hdrfline, cconv, pp->arg[0].reg);
488 pp->arg[0].reg = strdup("ecx");
491 if (xarg > 1 && IS(cconv, "__fastcall")) {
492 if (pp->arg[1].reg != NULL) {
493 printf("%s:%d: %s with arg2 spec %s?\n",
494 hdrfn, hdrfline, cconv, pp->arg[1].reg);
496 pp->arg[1].reg = strdup("edx");
499 if (pp->is_vararg && pp->is_stdcall) {
500 printf("%s:%d: vararg stdcall?\n", hdrfn, hdrfline);
506 for (i = 0; i < pp->argc; i++) {
507 if (pp->arg[i].reg == NULL)
516 static int pp_name_cmp(const void *p1, const void *p2)
518 const struct parsed_proto *pp1 = p1, *pp2 = p2;
519 return strcmp(pp1->name, pp2->name);
522 static struct parsed_proto *pp_cache;
523 static int pp_cache_size;
524 static int pp_cache_alloc;
526 static int b_pp_c_handler(char *proto, const char *fname)
530 if (pp_cache_size >= pp_cache_alloc) {
531 pp_cache_alloc = pp_cache_alloc * 2 + 64;
532 pp_cache = realloc(pp_cache, pp_cache_alloc
533 * sizeof(pp_cache[0]));
534 my_assert_not(pp_cache, NULL);
535 memset(pp_cache + pp_cache_size, 0,
536 (pp_cache_alloc - pp_cache_size)
537 * sizeof(pp_cache[0]));
540 ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
548 static void build_pp_cache(FILE *fhdr)
554 ret = do_protostrs(fhdr, hdrfn);
558 qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
561 static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym)
563 const struct parsed_proto *pp_ret;
564 struct parsed_proto pp_search;
566 if (pp_cache == NULL)
567 build_pp_cache(fhdr);
569 if (sym[0] == '_') // && strncmp(fname, "stdc", 4) == 0)
572 strcpy(pp_search.name, sym);
573 pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
574 sizeof(pp_cache[0]), pp_name_cmp);
576 printf("%s: sym '%s' is missing\n", hdrfn, sym);
581 struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
583 struct parsed_proto *pp;
586 pp = malloc(sizeof(*pp));
587 my_assert_not(pp, NULL);
588 memcpy(pp, pp_c, sizeof(*pp)); // lazy..
590 // do the actual deep copy..
591 for (i = 0; i < pp_c->argc; i++) {
592 if (pp_c->arg[i].reg != NULL) {
593 pp->arg[i].reg = strdup(pp_c->arg[i].reg);
594 my_assert_not(pp->arg[i].reg, NULL);
596 if (pp_c->arg[i].type.name != NULL) {
597 pp->arg[i].type.name = strdup(pp_c->arg[i].type.name);
598 my_assert_not(pp->arg[i].type.name, NULL);
600 if (pp_c->arg[i].fptr != NULL) {
601 pp->arg[i].fptr = malloc(sizeof(*pp->arg[i].fptr));
602 my_assert_not(pp->arg[i].fptr, NULL);
603 memcpy(pp->arg[i].fptr, pp_c->arg[i].fptr,
604 sizeof(*pp->arg[i].fptr));
607 if (pp_c->ret_type.name != NULL)
608 pp->ret_type.name = strdup(pp_c->ret_type.name);
613 static inline void proto_release(struct parsed_proto *pp)
617 for (i = 0; i < pp->argc; i++) {
618 if (pp->arg[i].reg != NULL)
619 free(pp->arg[i].reg);
620 if (pp->arg[i].type.name != NULL)
621 free(pp->arg[i].type.name);
622 if (pp->arg[i].fptr != NULL)
623 free(pp->arg[i].fptr);
625 if (pp->ret_type.name != NULL)
626 free(pp->ret_type.name);