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