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