minor fixes
[ia32rtools.git] / tools / protoparse.h
CommitLineData
7637b6cc 1/*
2 * ia32rtools
3 * (C) notaz, 2013,2014
4 *
5 * This work is licensed under the terms of 3-clause BSD license.
6 * See COPYING file in the top-level directory.
7 */
c36e914d 8
39b168b8 9struct parsed_proto;
10
3ebea2cf 11struct parsed_type {
12 char *name;
13 unsigned int is_array:1;
14 unsigned int is_ptr:1;
a652aa9f 15 unsigned int is_struct:1; // split for args
5f70a34f 16 unsigned int is_retreg:1; // register to return to caller
17 unsigned int is_va_list:1;
2c31fb4c 18 unsigned int is_64bit:1;
b62264bc 19 unsigned int is_float:1; // float, double
3ebea2cf 20};
21
39b168b8 22struct parsed_proto_arg {
23 char *reg;
3ebea2cf 24 struct parsed_type type;
93b5bd18 25 struct parsed_proto *pp; // fptr or struct
8c83cc48 26 unsigned int is_saved:1; // not set here, for tool use
77d7983a 27 void **push_refs;
28 int push_ref_cnt;
39b168b8 29};
30
c36e914d 31struct parsed_proto {
39b168b8 32 char name[256];
3ebea2cf 33 union {
34 struct parsed_type ret_type;
35 struct parsed_type type;
36 };
57b1b785 37 struct parsed_proto_arg arg[32];
c36e914d 38 int argc;
39 int argc_stack;
40 int argc_reg;
06c5d854 41 unsigned int is_func:1;
39b168b8 42 unsigned int is_stdcall:1;
c0050df6 43 unsigned int is_fastcall:1;
efea2951 44 unsigned int is_vararg:1; // vararg func
39b168b8 45 unsigned int is_fptr:1;
1fe8d40e 46 unsigned int is_import:1; // data import
e56ab892 47 unsigned int is_noreturn:1;
89ff3147 48 unsigned int is_unresolved:1;
1fe8d40e 49 unsigned int is_guessed:1; // for extra checking
1f84f6b3 50 unsigned int is_userstack:1;
61e29183 51 unsigned int is_include:1; // not from top-level header
52 unsigned int is_osinc:1; // OS/system library func
ebc4dc43 53 unsigned int is_cinc:1; // crt library func
179b79a9 54 unsigned int is_arg:1; // declared in some func arg
a652aa9f 55 unsigned int has_structarg:1;
1f84f6b3 56 unsigned int has_retreg:1;
c36e914d 57};
58
865f1aca 59struct parsed_struct {
60 char name[256];
61 struct {
62 int offset;
63 struct parsed_proto pp;
64 } members[64];
65 int member_count;
66};
67
c36e914d 68static const char *hdrfn;
69static int hdrfline = 0;
70
a652aa9f 71static void pp_copy_arg(struct parsed_proto_arg *d,
72 const struct parsed_proto_arg *s);
73
61e29183 74static int b_pp_c_handler(char *proto, const char *fname,
ebc4dc43 75 int is_include, int is_osinc, int is_cinc);
865f1aca 76static int struct_handler(FILE *fhdr, char *proto, int *line);
bd96f656 77
61e29183 78static int do_protostrs(FILE *fhdr, const char *fname, int is_include)
c36e914d 79{
06c5d854 80 const char *finc_name;
bd96f656 81 const char *hdrfn_saved;
82 char protostr[256];
36595fd2 83 char path[256];
84 char fname_inc[256];
61e29183 85 int is_osinc;
ebc4dc43 86 int is_cinc;
39b168b8 87 FILE *finc;
c36e914d 88 int line = 0;
39b168b8 89 int ret;
c36e914d 90 char *p;
91
bd96f656 92 hdrfn_saved = hdrfn;
93 hdrfn = fname;
39b168b8 94
ebc4dc43 95 is_cinc = strstr(fname, "stdc.hlist") != NULL;
edeadc28 96 is_osinc = is_cinc || strstr(fname, "win32.hlist") != NULL;
179b79a9 97
bd96f656 98 while (fgets(protostr, sizeof(protostr), fhdr))
c36e914d 99 {
100 line++;
bd96f656 101 if (strncmp(protostr, "//#include ", 11) == 0) {
102 finc_name = protostr + 11;
06c5d854 103 p = strpbrk(finc_name, "\r\n ");
39b168b8 104 if (p != NULL)
105 *p = 0;
106
36595fd2 107 path[0] = 0;
108 p = strrchr(hdrfn_saved, '/');
109 if (p) {
110 memcpy(path, hdrfn_saved,
111 p - hdrfn_saved + 1);
112 path[p - hdrfn_saved + 1] = 0;
113 }
114 snprintf(fname_inc, sizeof(fname_inc), "%s%s",
115 path, finc_name);
116 finc = fopen(fname_inc, "r");
39b168b8 117 if (finc == NULL) {
118 printf("%s:%d: can't open '%s'\n",
36595fd2 119 fname_inc, line, finc_name);
39b168b8 120 continue;
121 }
61e29183 122 ret = do_protostrs(finc, finc_name, 1);
39b168b8 123 fclose(finc);
bd96f656 124 if (ret < 0)
39b168b8 125 break;
126 continue;
127 }
bd96f656 128 if (strncmp(sskip(protostr), "//", 2) == 0)
129 continue;
130
131 p = protostr + strlen(protostr);
132 for (p--; p >= protostr && my_isblank(*p); --p)
133 *p = 0;
134 if (p < protostr)
06c5d854 135 continue;
39b168b8 136
bd96f656 137 hdrfline = line;
138
865f1aca 139 if (!strncmp(protostr, "struct", 6)
140 && strchr(protostr, '{') != NULL)
141 ret = struct_handler(fhdr, protostr, &line);
142 else
143 ret = b_pp_c_handler(protostr, hdrfn,
144 is_include, is_osinc, is_cinc);
bd96f656 145 if (ret < 0)
c36e914d 146 break;
147 }
c36e914d 148
bd96f656 149 hdrfn = hdrfn_saved;
c36e914d 150
bd96f656 151 if (feof(fhdr))
152 return 0;
c36e914d 153
bd96f656 154 return -1;
c36e914d 155}
156
1f84f6b3 157static int get_regparm(char *dst, size_t dlen, char *p, int *retreg)
c36e914d 158{
1f84f6b3 159 int i = 0, o;
160
161 *retreg = 0;
c36e914d 162
163 if (*p != '<')
164 return 0;
165
1f84f6b3 166 i++;
167 if (p[i] == '*') {
168 *retreg = 1;
169 i++;
170 }
171
172 for (o = 0; o < dlen; i++) {
c36e914d 173 if (p[i] == 0)
174 return 0;
175 if (p[i] == '>')
176 break;
177 dst[o++] = p[i];
178 }
179 dst[o] = 0;
180 return i + 1;
181}
182
183// hmh..
64c59faf 184static const char *known_type_mod[] = {
185 "const",
186 "signed",
187 "unsigned",
3ebea2cf 188 "enum",
840257f6 189 "CONST",
92804a48 190 "volatile",
64c59faf 191};
192
3ebea2cf 193static const char *known_ptr_types[] = {
da87ae38 194 "FARPROC",
92804a48 195 "WNDPROC",
bf2471e9 196 "LINECALLBACK",
e56ab892 197 "HACCEL",
c36e914d 198 "HANDLE",
e56ab892 199 "HBITMAP",
79af284c 200 "HBRUSH",
bf2471e9 201 "HCALL",
e56ab892 202 "HCURSOR",
3ebea2cf 203 "HDC",
a652aa9f 204 "HFONT",
3ebea2cf 205 "HGDIOBJ",
e56ab892 206 "HGLOBAL",
79af284c 207 "HHOOK",
da87ae38 208 "HICON",
e56ab892 209 "HINSTANCE",
c7ed83dd 210 "HIMC", // DWORD in mingw, ptr in wine..
bf2471e9 211 "HLINE",
212 "HLINEAPP",
213 "HLOCAL",
e56ab892 214 "HMODULE",
da87ae38 215 "HPALETTE",
e56ab892 216 "HRGN",
217 "HRSRC",
218 "HKEY",
360cca21 219 "HKL",
e56ab892 220 "HMENU",
383dff16 221 "HMONITOR",
7aca4698 222 "HWAVEOUT",
e56ab892 223 "HWND",
b62264bc 224 "PAPPBARDATA",
ef6b315d 225 "PBYTE",
a2c1d768 226 "PCRITICAL_SECTION",
b62264bc 227 "PDEVMODEA",
3ebea2cf 228 "PDWORD",
92804a48 229 "PFILETIME",
bf2471e9 230 "PLARGE_INTEGER",
79af284c 231 "PHANDLE",
94d447fb 232 "PHKEY",
233 "PLONG",
a2c1d768 234 "PMEMORY_BASIC_INFORMATION",
46411e6c 235 "PUINT",
f9327ad4 236 "PULARGE_INTEGER",
237 "PULONG_PTR",
3ebea2cf 238 "PVOID",
239 "PCVOID",
ef6b315d 240 "PWORD",
79af284c 241 "REFCLSID",
ead38b9e 242 "REFGUID",
79af284c 243 "REFIID",
354f5504 244 "SC_HANDLE",
245 "SERVICE_STATUS_HANDLE",
79af284c 246 "HOOKPROC",
e56ab892 247 "DLGPROC",
cdfaeed7 248 "TIMERPROC",
249 "WNDENUMPROC",
4f12f671 250 "va_list",
251 "__VALIST",
3ebea2cf 252};
253
254static const char *ignored_keywords[] = {
255 "extern",
75b4a70d 256 "static",
3ebea2cf 257 "WINBASEAPI",
258 "WINUSERAPI",
4f12f671 259 "WINGDIAPI",
260 "WINADVAPI",
c36e914d 261};
262
3ebea2cf 263static int typecmp(const char *n, const char *t)
39b168b8 264{
265 for (; *t != 0; n++, t++) {
266 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
267 n++;
268 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
269 t++;
270 if (*n != *t)
3ebea2cf 271 return *n - *t;
39b168b8 272 }
273
3ebea2cf 274 return 0;
39b168b8 275}
276
3ebea2cf 277static const char *skip_type_mod(const char *n)
c36e914d 278{
64c59faf 279 int len;
39b168b8 280 int i;
c36e914d 281
64c59faf 282 for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
283 len = strlen(known_type_mod[i]);
284 if (strncmp(n, known_type_mod[i], len) != 0)
285 continue;
bd96f656 286 if (!my_isblank(n[len]))
287 continue;
64c59faf 288
289 n += len;
290 while (my_isblank(*n))
291 n++;
292 i = 0;
293 }
294
3ebea2cf 295 return n;
296}
297
298static int check_type(const char *name, struct parsed_type *type)
299{
300 const char *n, *n1;
301 int ret = -1;
302 int i;
303
304 n = skip_type_mod(name);
305
865f1aca 306 if (!strncmp(n, "struct", 6) && my_isblank(n[6])) {
307 type->is_struct = 1;
308
309 n += 6;
310 while (my_isblank(*n))
311 n++;
312 }
313
3ebea2cf 314 for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
315 if (typecmp(n, known_ptr_types[i]))
64c59faf 316 continue;
317
3ebea2cf 318 type->is_ptr = 1;
319 break;
c36e914d 320 }
321
a652aa9f 322 if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
3ebea2cf 323 type->is_ptr = 1;
324
325 // assume single word
326 while (!my_isblank(*n) && !my_issep(*n))
327 n++;
328
329 while (1) {
330 n1 = n;
331 while (my_isblank(*n))
332 n++;
333 if (*n == '*') {
334 type->is_ptr = 1;
335 n++;
336 continue;
337 }
338 break;
339 }
340
341 ret = n1 - name;
342 type->name = strndup(name, ret);
5f70a34f 343 if (IS(type->name, "__VALIST") || IS(type->name, "va_list"))
344 type->is_va_list = 1;
a2c1d768 345 if (IS(type->name, "VOID"))
346 memcpy(type->name, "void", 4);
347
3ebea2cf 348 return ret;
c36e914d 349}
350
351/* args are always expanded to 32bit */
352static const char *map_reg(const char *reg)
353{
354 const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
355 const char *regs_w[] = { "ax", "bx", "cx", "dx", "si", "di" };
356 const char *regs_b[] = { "al", "bl", "cl", "dl" };
357 int i;
358
359 for (i = 0; i < ARRAY_SIZE(regs_w); i++)
360 if (IS(reg, regs_w[i]))
361 return regs_f[i];
362
363 for (i = 0; i < ARRAY_SIZE(regs_b); i++)
364 if (IS(reg, regs_b[i]))
365 return regs_f[i];
366
367 return reg;
368}
369
a652aa9f 370static int check_struct_arg(struct parsed_proto_arg *arg)
371{
372 if (IS(arg->type.name, "POINT"))
373 return 2 - 1;
374
375 return 0;
376}
377
93b5bd18 378static int parse_protostr(char *protostr, struct parsed_proto *pp);
379
380static int parse_arg(char **p_, struct parsed_proto_arg *arg, int xarg)
381{
382 char buf[256];
383 char *p = *p_;
384 char *pe;
385 int ret;
386
387 arg->pp = calloc(1, sizeof(*arg->pp));
388 my_assert_not(arg->pp, NULL);
389 arg->pp->is_arg = 1;
390
391 pe = p;
392 while (1) {
393 pe = strpbrk(pe, ",()");
394 if (pe == NULL)
395 return -1;
396 if (*pe == ',' || *pe == ')')
397 break;
398 pe = strchr(pe, ')');
399 if (pe == NULL)
400 return -1;
401 pe++;
402 }
403
404 if (pe - p > sizeof(buf) - 1)
405 return -1;
406 memcpy(buf, p, pe - p);
407 buf[pe - p] = 0;
408
409 ret = parse_protostr(buf, arg->pp);
410 if (ret < 0)
411 return -1;
412
1fe8d40e 413 if (IS_START(arg->pp->name, "guess"))
414 arg->pp->is_guessed = 1;
415
93b5bd18 416 // we don't use actual names right now...
417 snprintf(arg->pp->name, sizeof(arg->pp->name), "a%d", xarg);
418
419 if (!arg->type.is_struct)
420 // we'll treat it as void * for non-calls
421 arg->type.name = strdup("void *");
422 arg->type.is_ptr = 1;
423
424 p += ret;
425 *p_ = p;
426 return 0;
427}
428
c36e914d 429static int parse_protostr(char *protostr, struct parsed_proto *pp)
430{
39b168b8 431 struct parsed_proto_arg *arg;
c36e914d 432 char regparm[16];
433 char buf[256];
434 char cconv[32];
1f84f6b3 435 int is_retreg;
39b168b8 436 char *p, *p1;
93b5bd18 437 int xarg = 0;
3ebea2cf 438 int i, l;
c36e914d 439 int ret;
c36e914d 440
06c5d854 441 p = sskip(protostr);
c36e914d 442 if (p[0] == '/' && p[1] == '/') {
06c5d854 443 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
c36e914d 444 p = sskip(p + 2);
445 }
446
865f1aca 447 // allow start of line comment
448 if (p[0] == '/' && p[1] == '*') {
449 p = strstr(p + 2, "*/");
450 if (p == NULL) {
451 printf("%s:%d: multiline comments unsupported\n",
452 hdrfn, hdrfline);
453 return -1;
454 }
455 p = sskip(p + 2);
456 }
457
458 // we need remaining hints in comments, so strip / *
06c5d854 459 for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
460 if ((p1[0] == '/' && p1[1] == '*')
461 || (p1[0] == '*' && p1[1] == '/'))
462 p1[0] = p1[1] = ' ';
463 }
464
e56ab892 465 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
466 pp->is_noreturn = 1;
467 p = sskip(p + 18);
468 }
469
3ebea2cf 470 for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
471 l = strlen(ignored_keywords[i]);
472 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
473 p = sskip(p + l + 1);
474 }
06c5d854 475
1fe8d40e 476 if (IS_START(p, "DECL_IMPORT ")) {
477 pp->is_import = 1;
478 p = sskip(p + 12);
479 }
480
3ebea2cf 481 ret = check_type(p, &pp->ret_type);
482 if (ret <= 0) {
63df67be 483 printf("%s:%d:%zd: unhandled return in '%s'\n",
c36e914d 484 hdrfn, hdrfline, (p - protostr) + 1, protostr);
39b168b8 485 return -1;
c36e914d 486 }
64c59faf 487 p = sskip(p + ret);
c36e914d 488
da87ae38 489 if (!strncmp(p, "noreturn ", 9)) {
490 pp->is_noreturn = 1;
491 p = sskip(p + 9);
492 }
493
06c5d854 494 if (!strchr(p, ')')) {
495 p = next_idt(buf, sizeof(buf), p);
496 p = sskip(p);
93b5bd18 497 if (!pp->is_arg && buf[0] == 0) {
498 printf("%s:%d:%zd: var name is missing\n",
06c5d854 499 hdrfn, hdrfline, (p - protostr) + 1);
500 return -1;
501 }
502 strcpy(pp->name, buf);
503
504 p1 = strchr(p, ']');
505 if (p1 != NULL) {
506 p = p1 + 1;
3ebea2cf 507 pp->ret_type.is_array = 1;
06c5d854 508 }
509 return p - protostr;
510 }
511
512 pp->is_func = 1;
513
39b168b8 514 if (*p == '(') {
515 pp->is_fptr = 1;
516 p = sskip(p + 1);
517 }
518
c36e914d 519 p = next_word(cconv, sizeof(cconv), p);
520 p = sskip(p);
521 if (cconv[0] == 0) {
63df67be 522 printf("%s:%d:%zd: cconv missing\n",
c36e914d 523 hdrfn, hdrfline, (p - protostr) + 1);
39b168b8 524 return -1;
c36e914d 525 }
526 if (IS(cconv, "__cdecl"))
527 pp->is_stdcall = 0;
528 else if (IS(cconv, "__stdcall"))
529 pp->is_stdcall = 1;
c0050df6 530 else if (IS(cconv, "__fastcall")) {
531 pp->is_fastcall = 1;
532 pp->is_stdcall = 1; // sort of..
533 }
c36e914d 534 else if (IS(cconv, "__thiscall"))
535 pp->is_stdcall = 1;
536 else if (IS(cconv, "__userpurge"))
de50b98b 537 pp->is_stdcall = 1; // IDA
c36e914d 538 else if (IS(cconv, "__usercall"))
de50b98b 539 pp->is_stdcall = 0; // IDA
1f84f6b3 540 else if (IS(cconv, "__userstack")) {
541 pp->is_stdcall = 0; // custom
542 pp->is_userstack = 1;
543 }
79af284c 544 else if (IS(cconv, "WINAPI") || IS(cconv, "PASCAL"))
64c59faf 545 pp->is_stdcall = 1;
c36e914d 546 else {
63df67be 547 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
c36e914d 548 hdrfn, hdrfline, (p - protostr) + 1, cconv);
39b168b8 549 return -1;
550 }
551
552 if (pp->is_fptr) {
553 if (*p != '*') {
63df67be 554 printf("%s:%d:%zd: '*' expected\n",
39b168b8 555 hdrfn, hdrfline, (p - protostr) + 1);
556 return -1;
557 }
bd96f656 558 p++;
559 // XXX: skipping extra asterisks, for now
560 while (*p == '*')
561 p++;
562 p = sskip(p);
c36e914d 563 }
564
565 p = next_idt(buf, sizeof(buf), p);
566 p = sskip(p);
567 if (buf[0] == 0) {
de50b98b 568 //printf("%s:%d:%zd: func name missing\n",
569 // hdrfn, hdrfline, (p - protostr) + 1);
570 //return -1;
c36e914d 571 }
39b168b8 572 strcpy(pp->name, buf);
c36e914d 573
1f84f6b3 574 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
c36e914d 575 if (ret > 0) {
576 if (!IS(regparm, "eax") && !IS(regparm, "ax")
2b43685d 577 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
c36e914d 578 {
63df67be 579 printf("%s:%d:%zd: bad regparm: %s\n",
c36e914d 580 hdrfn, hdrfline, (p - protostr) + 1, regparm);
39b168b8 581 return -1;
c36e914d 582 }
583 p += ret;
584 p = sskip(p);
585 }
586
39b168b8 587 if (pp->is_fptr) {
bd96f656 588 if (*p == '[') {
589 // not really ret_type is array, but ohwell
590 pp->ret_type.is_array = 1;
591 p = strchr(p + 1, ']');
592 if (p == NULL) {
593 printf("%s:%d:%zd: ']' expected\n",
594 hdrfn, hdrfline, (p - protostr) + 1);
595 return -1;
596 }
597 p = sskip(p + 1);
598 }
39b168b8 599 if (*p != ')') {
63df67be 600 printf("%s:%d:%zd: ')' expected\n",
39b168b8 601 hdrfn, hdrfline, (p - protostr) + 1);
602 return -1;
603 }
604 p = sskip(p + 1);
605 }
606
c36e914d 607 if (*p != '(') {
63df67be 608 printf("%s:%d:%zd: '(' expected, got '%c'\n",
c36e914d 609 hdrfn, hdrfline, (p - protostr) + 1, *p);
39b168b8 610 return -1;
c36e914d 611 }
612 p++;
613
39b168b8 614 // check for x(void)
615 p = sskip(p);
06c5d854 616 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
617 && *sskip(p + 4) == ')')
39b168b8 618 p += 4;
619
c36e914d 620 while (1) {
621 p = sskip(p);
39b168b8 622 if (*p == ')') {
623 p++;
c36e914d 624 break;
39b168b8 625 }
840257f6 626 if (xarg > 0) {
627 if (*p != ',') {
628 printf("%s:%d:%zd: ',' expected\n",
629 hdrfn, hdrfline, (p - protostr) + 1);
630 return -1;
631 }
c36e914d 632 p = sskip(p + 1);
840257f6 633 }
c36e914d 634
7ba45c34 635 if (!strncmp(p, "...", 3)) {
636 pp->is_vararg = 1;
637 p = sskip(p + 3);
638 if (*p == ')') {
639 p++;
640 break;
641 }
63df67be 642 printf("%s:%d:%zd: ')' expected\n",
7ba45c34 643 hdrfn, hdrfline, (p - protostr) + 1);
644 return -1;
645 }
646
57b1b785 647 if (xarg >= ARRAY_SIZE(pp->arg)) {
648 printf("%s:%d:%zd: too many args\n",
649 hdrfn, hdrfline, (p - protostr) + 1);
650 return -1;
651 }
652
39b168b8 653 arg = &pp->arg[xarg];
c36e914d 654 xarg++;
655
39b168b8 656 p1 = p;
3ebea2cf 657 ret = check_type(p, &arg->type);
658 if (ret <= 0) {
63df67be 659 printf("%s:%d:%zd: unhandled type for arg%d\n",
c36e914d 660 hdrfn, hdrfline, (p - protostr) + 1, xarg);
39b168b8 661 return -1;
c36e914d 662 }
64c59faf 663 p = sskip(p + ret);
c36e914d 664
93b5bd18 665 if (*p == '(' || arg->type.is_struct) {
666 // func ptr or struct
667 ret = parse_arg(&p1, arg, xarg);
39b168b8 668 if (ret < 0) {
63df67be 669 printf("%s:%d:%zd: funcarg parse failed\n",
39b168b8 670 hdrfn, hdrfline, p1 - protostr);
671 return -1;
672 }
93b5bd18 673 p = p1;
39b168b8 674 }
675
c36e914d 676 p = next_idt(buf, sizeof(buf), p);
677 p = sskip(p);
678#if 0
679 if (buf[0] == 0) {
63df67be 680 printf("%s:%d:%zd: idt missing for arg%d\n",
c36e914d 681 hdrfn, hdrfline, (p - protostr) + 1, xarg);
39b168b8 682 return -1;
c36e914d 683 }
684#endif
39b168b8 685 arg->reg = NULL;
c36e914d 686
1f84f6b3 687 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
c36e914d 688 if (ret > 0) {
689 p += ret;
690 p = sskip(p);
691
39b168b8 692 arg->reg = strdup(map_reg(regparm));
1f84f6b3 693 arg->type.is_retreg = is_retreg;
694 pp->has_retreg |= is_retreg;
c36e914d 695 }
a652aa9f 696
b62264bc 697 if (IS(arg->type.name, "float")
698 || IS(arg->type.name, "double"))
699 {
700 arg->type.is_float = 1;
701 }
702
f9327ad4 703 if (!arg->type.is_ptr && (strstr(arg->type.name, "int64")
704 || IS(arg->type.name, "double")))
7e50b291 705 {
2c31fb4c 706 arg->type.is_64bit = 1;
7e50b291 707 // hack..
7e50b291 708 pp_copy_arg(&pp->arg[xarg], arg);
2c31fb4c 709 arg = &pp->arg[xarg];
7e50b291 710 xarg++;
2c31fb4c 711 free(arg->type.name);
712 arg->type.name = strdup("dummy");
7e50b291 713 }
714
a652aa9f 715 ret = check_struct_arg(arg);
716 if (ret > 0) {
717 pp->has_structarg = 1;
718 arg->type.is_struct = 1;
719 free(arg->type.name);
720 arg->type.name = strdup("int");
721 for (l = 0; l < ret; l++) {
722 pp_copy_arg(&pp->arg[xarg], arg);
723 xarg++;
724 }
725 }
c36e914d 726 }
727
728 if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
729 if (pp->arg[0].reg != NULL) {
730 printf("%s:%d: %s with arg1 spec %s?\n",
731 hdrfn, hdrfline, cconv, pp->arg[0].reg);
732 }
733 pp->arg[0].reg = strdup("ecx");
734 }
735
736 if (xarg > 1 && IS(cconv, "__fastcall")) {
737 if (pp->arg[1].reg != NULL) {
738 printf("%s:%d: %s with arg2 spec %s?\n",
739 hdrfn, hdrfline, cconv, pp->arg[1].reg);
740 }
741 pp->arg[1].reg = strdup("edx");
742 }
743
744 pp->argc = xarg;
745
746 for (i = 0; i < pp->argc; i++) {
747 if (pp->arg[i].reg == NULL)
748 pp->argc_stack++;
749 else
750 pp->argc_reg++;
751 }
752
c0050df6 753 if (pp->argc == 1 && pp->arg[0].reg != NULL
754 && IS(pp->arg[0].reg, "ecx"))
755 {
756 pp->is_fastcall = 1;
757 }
758 else if (pp->argc_reg == 2
759 && pp->arg[0].reg != NULL && IS(pp->arg[0].reg, "ecx")
760 && pp->arg[1].reg != NULL && IS(pp->arg[1].reg, "edx"))
761 {
762 pp->is_fastcall = 1;
763 }
764
765 if (pp->is_vararg && (pp->is_stdcall || pp->is_fastcall)) {
766 printf("%s:%d: vararg %s?\n", hdrfn, hdrfline, cconv);
767 return -1;
768 }
769
39b168b8 770 return p - protostr;
c36e914d 771}
772
bd96f656 773static int pp_name_cmp(const void *p1, const void *p2)
774{
775 const struct parsed_proto *pp1 = p1, *pp2 = p2;
776 return strcmp(pp1->name, pp2->name);
777}
778
865f1aca 779static int ps_name_cmp(const void *p1, const void *p2)
780{
781 const struct parsed_struct *ps1 = p1, *ps2 = p2;
782 return strcmp(ps1->name, ps2->name);
783}
784
785// parsed struct cache
786static struct parsed_struct *ps_cache;
787static int ps_cache_size;
788static int ps_cache_alloc;
789
790static int struct_handler(FILE *fhdr, char *proto, int *line)
791{
792 struct parsed_struct *ps;
793 char lstr[256], *p;
794 int offset = 0;
795 int m = 0;
796 int ret;
797
798 if (ps_cache_size >= ps_cache_alloc) {
799 ps_cache_alloc = ps_cache_alloc * 2 + 64;
800 ps_cache = realloc(ps_cache, ps_cache_alloc
801 * sizeof(ps_cache[0]));
802 my_assert_not(ps_cache, NULL);
803 memset(ps_cache + ps_cache_size, 0,
804 (ps_cache_alloc - ps_cache_size)
805 * sizeof(ps_cache[0]));
806 }
807
808 ps = &ps_cache[ps_cache_size++];
809 ret = sscanf(proto, "struct %255s {", ps->name);
810 if (ret != 1) {
811 printf("%s:%d: struct parse failed\n", hdrfn, *line);
812 return -1;
813 }
814
815 while (fgets(lstr, sizeof(lstr), fhdr))
816 {
817 (*line)++;
818
819 p = sskip(lstr);
820 if (p[0] == '/' && p[1] == '/')
821 continue;
822 if (p[0] == '}')
823 break;
824
825 if (m >= ARRAY_SIZE(ps->members)) {
826 printf("%s:%d: too many struct members\n",
827 hdrfn, *line);
828 return -1;
829 }
830
831 hdrfline = *line;
832 ret = parse_protostr(p, &ps->members[m].pp);
833 if (ret < 0) {
834 printf("%s:%d: struct member #%d/%02x "
835 "doesn't parse\n", hdrfn, *line,
836 m, offset);
837 return -1;
838 }
839 ps->members[m].offset = offset;
840 offset += 4;
841 m++;
842 }
843
844 ps->member_count = m;
845
846 return 0;
847}
848
849// parsed proto cache
bd96f656 850static struct parsed_proto *pp_cache;
851static int pp_cache_size;
852static int pp_cache_alloc;
853
61e29183 854static int b_pp_c_handler(char *proto, const char *fname,
ebc4dc43 855 int is_include, int is_osinc, int is_cinc)
bd96f656 856{
857 int ret;
858
859 if (pp_cache_size >= pp_cache_alloc) {
860 pp_cache_alloc = pp_cache_alloc * 2 + 64;
861 pp_cache = realloc(pp_cache, pp_cache_alloc
862 * sizeof(pp_cache[0]));
863 my_assert_not(pp_cache, NULL);
864 memset(pp_cache + pp_cache_size, 0,
865 (pp_cache_alloc - pp_cache_size)
866 * sizeof(pp_cache[0]));
867 }
868
869 ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
870 if (ret < 0)
871 return -1;
872
61e29183 873 pp_cache[pp_cache_size].is_include = is_include;
874 pp_cache[pp_cache_size].is_osinc = is_osinc;
ebc4dc43 875 pp_cache[pp_cache_size].is_cinc = is_cinc;
bd96f656 876 pp_cache_size++;
877 return 0;
878}
879
865f1aca 880static void build_caches(FILE *fhdr)
c36e914d 881{
1f906263 882 long pos;
c36e914d 883 int ret;
884
1f906263 885 pos = ftell(fhdr);
bd96f656 886 rewind(fhdr);
887
61e29183 888 ret = do_protostrs(fhdr, hdrfn, 0);
bd96f656 889 if (ret < 0)
890 exit(1);
891
892 qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
865f1aca 893 qsort(ps_cache, ps_cache_size, sizeof(ps_cache[0]), ps_name_cmp);
1f906263 894 fseek(fhdr, pos, SEEK_SET);
bd96f656 895}
896
36595fd2 897static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
898 int quiet)
bd96f656 899{
900 const struct parsed_proto *pp_ret;
901 struct parsed_proto pp_search;
7aca4698 902 char *p;
bd96f656 903
904 if (pp_cache == NULL)
865f1aca 905 build_caches(fhdr);
bd96f656 906
f9327ad4 907 // ugh...
908 if (sym[0] == '_' && !IS_START(sym, "__W"))
bd96f656 909 sym++;
c36e914d 910
bd96f656 911 strcpy(pp_search.name, sym);
7aca4698 912 p = strchr(pp_search.name, '@');
913 if (p != NULL)
914 *p = 0;
915
bd96f656 916 pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
917 sizeof(pp_cache[0]), pp_name_cmp);
36595fd2 918 if (pp_ret == NULL && !quiet)
c36e914d 919 printf("%s: sym '%s' is missing\n", hdrfn, sym);
bd96f656 920
921 return pp_ret;
922}
923
865f1aca 924static const struct parsed_proto *proto_lookup_struct(FILE *fhdr,
925 const char *type, int offset)
926{
927 struct parsed_struct ps_search, *ps;
928 int m;
929
930 if (pp_cache == NULL)
931 build_caches(fhdr);
932 if (ps_cache_size == 0)
933 return NULL;
934
935 while (my_isblank(*type))
936 type++;
937 if (!strncmp(type, "struct", 6) && my_isblank(type[6]))
938 type += 7;
939
940 if (sscanf(type, "%255s", ps_search.name) != 1)
941 return NULL;
942
943 ps = bsearch(&ps_search, ps_cache, ps_cache_size,
944 sizeof(ps_cache[0]), ps_name_cmp);
945 if (ps == NULL) {
946 printf("%s: struct '%s' is missing\n",
947 hdrfn, ps_search.name);
948 return NULL;
949 }
950
951 for (m = 0; m < ps->member_count; m++) {
952 if (ps->members[m].offset == offset)
953 return &ps->members[m].pp;
954 }
955
956 return NULL;
957}
958
a652aa9f 959static void pp_copy_arg(struct parsed_proto_arg *d,
960 const struct parsed_proto_arg *s)
961{
962 memcpy(d, s, sizeof(*d));
963
964 if (s->reg != NULL) {
965 d->reg = strdup(s->reg);
966 my_assert_not(d->reg, NULL);
967 }
968 if (s->type.name != NULL) {
969 d->type.name = strdup(s->type.name);
970 my_assert_not(d->type.name, NULL);
971 }
93b5bd18 972 if (s->pp != NULL) {
973 d->pp = malloc(sizeof(*d->pp));
974 my_assert_not(d->pp, NULL);
975 memcpy(d->pp, s->pp, sizeof(*d->pp));
a652aa9f 976 }
977}
978
bd96f656 979struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
980{
981 struct parsed_proto *pp;
982 int i;
983
984 pp = malloc(sizeof(*pp));
985 my_assert_not(pp, NULL);
986 memcpy(pp, pp_c, sizeof(*pp)); // lazy..
987
988 // do the actual deep copy..
a652aa9f 989 for (i = 0; i < pp_c->argc; i++)
990 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
bd96f656 991 if (pp_c->ret_type.name != NULL)
992 pp->ret_type.name = strdup(pp_c->ret_type.name);
c36e914d 993
bd96f656 994 return pp;
c36e914d 995}
996
27ebfaed 997
998static inline int pp_cmp_func(const struct parsed_proto *pp1,
999 const struct parsed_proto *pp2)
1000{
1001 int i;
1002
1003 if (pp1->argc != pp2->argc || pp1->argc_reg != pp2->argc_reg)
1004 return 1;
17ed469e 1005 if (pp1->is_stdcall != pp2->is_stdcall)
1006 return 1;
1007
1008 // because of poor void return detection, return is not
1009 // checked for now to avoid heaps of false positives
1010
1011 for (i = 0; i < pp1->argc; i++) {
1012 if ((pp1->arg[i].reg != NULL) != (pp2->arg[i].reg != NULL))
1013 return 1;
1014
1015 if ((pp1->arg[i].reg != NULL)
1016 && !IS(pp1->arg[i].reg, pp2->arg[i].reg))
1017 {
1018 return 1;
27ebfaed 1019 }
1020 }
1021
1022 return 0;
1023}
1024
17ed469e 1025static inline int pp_compatible_func(
1026 const struct parsed_proto *pp_site,
1027 const struct parsed_proto *pp_callee)
1028{
1029 if (pp_cmp_func(pp_site, pp_callee) == 0)
1030 return 1;
1031
1032 if (pp_site->argc_stack == 0 && pp_site->is_fastcall
1033 && pp_callee->argc_stack == 0
1034 && (pp_callee->is_fastcall || pp_callee->argc_reg == 0)
1035 && pp_site->argc_reg > pp_callee->argc_reg)
1036 /* fascall compatible callee doesn't use all args -> ok */
1037 return 1;
1038
1039 return 0;
1040}
1041
b74c31e3 1042static inline void pp_print(char *buf, size_t buf_size,
1043 const struct parsed_proto *pp)
1044{
1045 size_t l;
1046 int i;
1047
1048 snprintf(buf, buf_size, "%s %s(", pp->ret_type.name, pp->name);
1049 l = strlen(buf);
1050
1051 for (i = 0; i < pp->argc_reg; i++) {
1052 snprintf(buf + l, buf_size - l, "%s%s",
1053 i == 0 ? "" : ", ", pp->arg[i].reg);
1054 l = strlen(buf);
1055 }
1056 if (pp->argc_stack > 0) {
1057 snprintf(buf + l, buf_size - l, "%s{%d stack}",
1058 i == 0 ? "" : ", ", pp->argc_stack);
1059 l = strlen(buf);
1060 }
1061 snprintf(buf + l, buf_size - l, ")");
1062}
1063
bd96f656 1064static inline void proto_release(struct parsed_proto *pp)
c36e914d 1065{
1066 int i;
1067
1068 for (i = 0; i < pp->argc; i++) {
77d7983a 1069 free(pp->arg[i].reg);
1070 free(pp->arg[i].type.name);
1071 free(pp->arg[i].pp);
1072 free(pp->arg[i].push_refs);
c36e914d 1073 }
3ebea2cf 1074 if (pp->ret_type.name != NULL)
1075 free(pp->ret_type.name);
bd96f656 1076 free(pp);
865f1aca 1077
1078 (void)proto_lookup_struct;
c36e914d 1079}