c36e914d |
1 | |
39b168b8 |
2 | struct parsed_proto; |
3 | |
4 | struct parsed_proto_arg { |
5 | char *reg; |
64c59faf |
6 | char *type; |
39b168b8 |
7 | struct parsed_proto *fptr; |
8 | void *datap; |
9 | }; |
10 | |
c36e914d |
11 | struct parsed_proto { |
39b168b8 |
12 | char name[256]; |
64c59faf |
13 | char *ret_type; |
39b168b8 |
14 | struct parsed_proto_arg arg[16]; |
c36e914d |
15 | int argc; |
16 | int argc_stack; |
17 | int argc_reg; |
06c5d854 |
18 | unsigned int is_func:1; |
39b168b8 |
19 | unsigned int is_stdcall:1; |
20 | unsigned int is_fptr:1; |
06c5d854 |
21 | unsigned int is_array:1; |
c36e914d |
22 | }; |
23 | |
24 | static const char *hdrfn; |
25 | static int hdrfline = 0; |
26 | |
39b168b8 |
27 | static int find_protostr(char *dst, size_t dlen, FILE *fhdr, |
28 | const char *fname, const char *sym_) |
c36e914d |
29 | { |
39b168b8 |
30 | const char *sym = sym_; |
06c5d854 |
31 | const char *finc_name; |
39b168b8 |
32 | FILE *finc; |
33 | int symlen; |
c36e914d |
34 | int line = 0; |
39b168b8 |
35 | int ret; |
c36e914d |
36 | char *p; |
37 | |
39b168b8 |
38 | if (sym[0] == '_' && strncmp(fname, "stdc", 4) == 0) |
39 | sym++; |
40 | symlen = strlen(sym); |
41 | |
c36e914d |
42 | rewind(fhdr); |
43 | |
44 | while (fgets(dst, dlen, fhdr)) |
45 | { |
46 | line++; |
06c5d854 |
47 | if (strncmp(dst, "//#include ", 11) == 0) { |
48 | finc_name = dst + 11; |
49 | p = strpbrk(finc_name, "\r\n "); |
39b168b8 |
50 | if (p != NULL) |
51 | *p = 0; |
52 | |
06c5d854 |
53 | finc = fopen(finc_name, "r"); |
39b168b8 |
54 | if (finc == NULL) { |
55 | printf("%s:%d: can't open '%s'\n", |
06c5d854 |
56 | fname, line, finc_name); |
39b168b8 |
57 | continue; |
58 | } |
59 | ret = find_protostr(dst, dlen, finc, |
06c5d854 |
60 | finc_name, sym_); |
39b168b8 |
61 | fclose(finc); |
62 | if (ret == 0) |
63 | break; |
64 | continue; |
65 | } |
06c5d854 |
66 | if (strncmp(sskip(dst), "//", 2) == 0) |
67 | continue; |
39b168b8 |
68 | |
69 | p = strstr(dst, sym); |
06c5d854 |
70 | if (p != NULL && p > dst |
71 | && (my_isblank(p[-1]) || my_issep(p[-1])) |
39b168b8 |
72 | && (my_isblank(p[symlen]) || my_issep(p[symlen]))) |
c36e914d |
73 | break; |
74 | } |
75 | hdrfline = line; |
76 | |
77 | if (feof(fhdr)) |
78 | return -1; |
79 | |
80 | p = dst + strlen(dst); |
81 | for (p--; p > dst && my_isblank(*p); --p) |
82 | *p = 0; |
83 | |
84 | return 0; |
85 | } |
86 | |
87 | static int get_regparm(char *dst, size_t dlen, char *p) |
88 | { |
89 | int i, o; |
90 | |
91 | if (*p != '<') |
92 | return 0; |
93 | |
94 | for (o = 0, i = 1; o < dlen; i++) { |
95 | if (p[i] == 0) |
96 | return 0; |
97 | if (p[i] == '>') |
98 | break; |
99 | dst[o++] = p[i]; |
100 | } |
101 | dst[o] = 0; |
102 | return i + 1; |
103 | } |
104 | |
105 | // hmh.. |
64c59faf |
106 | static const char *known_type_mod[] = { |
107 | "const", |
108 | "signed", |
109 | "unsigned", |
110 | }; |
111 | |
c36e914d |
112 | static const char *known_types[] = { |
c36e914d |
113 | "void *", |
114 | "char *", |
115 | "FILE *", |
f3d05b09 |
116 | "int *", |
64c59faf |
117 | "__int8 *", |
118 | "__int16 *", |
c36e914d |
119 | "char", |
120 | "__int8", |
121 | "__int16", |
64c59faf |
122 | "__int32", |
c36e914d |
123 | "int", |
124 | "bool", |
125 | "void", |
126 | "BYTE", |
127 | "WORD", |
64c59faf |
128 | "BOOL", |
c36e914d |
129 | "DWORD", |
91977a1c |
130 | "_DWORD", |
c36e914d |
131 | "HMODULE", |
132 | "HANDLE", |
133 | "HWND", |
64c59faf |
134 | "LANGID", |
c36e914d |
135 | "LPCSTR", |
136 | "size_t", |
137 | }; |
138 | |
64c59faf |
139 | // returns ptr to char after type ends |
140 | static const char *typecmp(const char *n, const char *t) |
39b168b8 |
141 | { |
142 | for (; *t != 0; n++, t++) { |
143 | while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*')) |
144 | n++; |
145 | while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*')) |
146 | t++; |
147 | if (*n != *t) |
64c59faf |
148 | return NULL; |
39b168b8 |
149 | } |
150 | |
64c59faf |
151 | return n; |
39b168b8 |
152 | } |
153 | |
64c59faf |
154 | static char *check_type(const char *name, int *len_out) |
c36e914d |
155 | { |
64c59faf |
156 | const char *n = name, *n1; |
157 | int len; |
39b168b8 |
158 | int i; |
c36e914d |
159 | |
64c59faf |
160 | *len_out = 0; |
161 | |
162 | for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) { |
163 | len = strlen(known_type_mod[i]); |
164 | if (strncmp(n, known_type_mod[i], len) != 0) |
165 | continue; |
166 | |
167 | n += len; |
168 | while (my_isblank(*n)) |
169 | n++; |
170 | i = 0; |
171 | } |
172 | |
c36e914d |
173 | for (i = 0; i < ARRAY_SIZE(known_types); i++) { |
64c59faf |
174 | n1 = typecmp(n, known_types[i]); |
175 | if (n1 == NULL) |
176 | continue; |
177 | |
178 | *len_out = n1 - name; |
179 | return strndup(name, *len_out); |
c36e914d |
180 | } |
181 | |
182 | return NULL; |
183 | } |
184 | |
185 | /* args are always expanded to 32bit */ |
186 | static const char *map_reg(const char *reg) |
187 | { |
188 | const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" }; |
189 | const char *regs_w[] = { "ax", "bx", "cx", "dx", "si", "di" }; |
190 | const char *regs_b[] = { "al", "bl", "cl", "dl" }; |
191 | int i; |
192 | |
193 | for (i = 0; i < ARRAY_SIZE(regs_w); i++) |
194 | if (IS(reg, regs_w[i])) |
195 | return regs_f[i]; |
196 | |
197 | for (i = 0; i < ARRAY_SIZE(regs_b); i++) |
198 | if (IS(reg, regs_b[i])) |
199 | return regs_f[i]; |
200 | |
201 | return reg; |
202 | } |
203 | |
204 | static int parse_protostr(char *protostr, struct parsed_proto *pp) |
205 | { |
39b168b8 |
206 | struct parsed_proto_arg *arg; |
c36e914d |
207 | char regparm[16]; |
208 | char buf[256]; |
209 | char cconv[32]; |
64c59faf |
210 | char *kt; |
c36e914d |
211 | int xarg = 0; |
39b168b8 |
212 | char *p, *p1; |
c36e914d |
213 | int ret; |
c36e914d |
214 | int i; |
215 | |
06c5d854 |
216 | p = sskip(protostr); |
c36e914d |
217 | if (p[0] == '/' && p[1] == '/') { |
06c5d854 |
218 | printf("%s:%d: commented out?\n", hdrfn, hdrfline); |
c36e914d |
219 | p = sskip(p + 2); |
220 | } |
221 | |
06c5d854 |
222 | // strip unneeded stuff |
223 | for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) { |
224 | if ((p1[0] == '/' && p1[1] == '*') |
225 | || (p1[0] == '*' && p1[1] == '/')) |
226 | p1[0] = p1[1] = ' '; |
227 | } |
228 | |
229 | if (strncmp(p, "extern ", 7) == 0) |
230 | p = sskip(p + 7); |
64c59faf |
231 | if (strncmp(p, "WINBASEAPI ", 11) == 0) |
232 | p = sskip(p + 11); |
06c5d854 |
233 | |
64c59faf |
234 | kt = check_type(p, &ret); |
c36e914d |
235 | if (kt == NULL) { |
236 | printf("%s:%d:%ld: unhandled return in '%s'\n", |
237 | hdrfn, hdrfline, (p - protostr) + 1, protostr); |
39b168b8 |
238 | return -1; |
c36e914d |
239 | } |
240 | pp->ret_type = kt; |
64c59faf |
241 | p = sskip(p + ret); |
c36e914d |
242 | |
06c5d854 |
243 | if (!strchr(p, ')')) { |
244 | p = next_idt(buf, sizeof(buf), p); |
245 | p = sskip(p); |
246 | if (buf[0] == 0) { |
247 | printf("%s:%d:%ld: var name missing\n", |
248 | hdrfn, hdrfline, (p - protostr) + 1); |
249 | return -1; |
250 | } |
251 | strcpy(pp->name, buf); |
252 | |
253 | p1 = strchr(p, ']'); |
254 | if (p1 != NULL) { |
255 | p = p1 + 1; |
256 | pp->is_array = 1; |
257 | } |
258 | return p - protostr; |
259 | } |
260 | |
261 | pp->is_func = 1; |
262 | |
39b168b8 |
263 | if (*p == '(') { |
264 | pp->is_fptr = 1; |
265 | p = sskip(p + 1); |
266 | } |
267 | |
c36e914d |
268 | p = next_word(cconv, sizeof(cconv), p); |
269 | p = sskip(p); |
270 | if (cconv[0] == 0) { |
271 | printf("%s:%d:%ld: cconv missing\n", |
272 | hdrfn, hdrfline, (p - protostr) + 1); |
39b168b8 |
273 | return -1; |
c36e914d |
274 | } |
275 | if (IS(cconv, "__cdecl")) |
276 | pp->is_stdcall = 0; |
277 | else if (IS(cconv, "__stdcall")) |
278 | pp->is_stdcall = 1; |
279 | else if (IS(cconv, "__fastcall")) |
280 | pp->is_stdcall = 1; |
281 | else if (IS(cconv, "__thiscall")) |
282 | pp->is_stdcall = 1; |
283 | else if (IS(cconv, "__userpurge")) |
284 | pp->is_stdcall = 1; // in all cases seen.. |
285 | else if (IS(cconv, "__usercall")) |
286 | pp->is_stdcall = 0; // ..or is it? |
64c59faf |
287 | else if (IS(cconv, "WINAPI")) |
288 | pp->is_stdcall = 1; |
c36e914d |
289 | else { |
290 | printf("%s:%d:%ld: unhandled cconv: '%s'\n", |
291 | hdrfn, hdrfline, (p - protostr) + 1, cconv); |
39b168b8 |
292 | return -1; |
293 | } |
294 | |
295 | if (pp->is_fptr) { |
296 | if (*p != '*') { |
297 | printf("%s:%d:%ld: '*' expected\n", |
298 | hdrfn, hdrfline, (p - protostr) + 1); |
299 | return -1; |
300 | } |
301 | p = sskip(p + 1); |
c36e914d |
302 | } |
303 | |
304 | p = next_idt(buf, sizeof(buf), p); |
305 | p = sskip(p); |
306 | if (buf[0] == 0) { |
307 | printf("%s:%d:%ld: func name missing\n", |
39b168b8 |
308 | hdrfn, hdrfline, (p - protostr) + 1); |
309 | return -1; |
c36e914d |
310 | } |
39b168b8 |
311 | strcpy(pp->name, buf); |
c36e914d |
312 | |
313 | ret = get_regparm(regparm, sizeof(regparm), p); |
314 | if (ret > 0) { |
315 | if (!IS(regparm, "eax") && !IS(regparm, "ax") |
316 | && !IS(regparm, "al")) |
317 | { |
318 | printf("%s:%d:%ld: bad regparm: %s\n", |
319 | hdrfn, hdrfline, (p - protostr) + 1, regparm); |
39b168b8 |
320 | return -1; |
c36e914d |
321 | } |
322 | p += ret; |
323 | p = sskip(p); |
324 | } |
325 | |
39b168b8 |
326 | if (pp->is_fptr) { |
327 | if (*p != ')') { |
328 | printf("%s:%d:%ld: ')' expected\n", |
329 | hdrfn, hdrfline, (p - protostr) + 1); |
330 | return -1; |
331 | } |
332 | p = sskip(p + 1); |
333 | } |
334 | |
c36e914d |
335 | if (*p != '(') { |
336 | printf("%s:%d:%ld: '(' expected, got '%c'\n", |
337 | hdrfn, hdrfline, (p - protostr) + 1, *p); |
39b168b8 |
338 | return -1; |
c36e914d |
339 | } |
340 | p++; |
341 | |
39b168b8 |
342 | // check for x(void) |
343 | p = sskip(p); |
06c5d854 |
344 | if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4)) |
345 | && *sskip(p + 4) == ')') |
39b168b8 |
346 | p += 4; |
347 | |
c36e914d |
348 | while (1) { |
349 | p = sskip(p); |
39b168b8 |
350 | if (*p == ')') { |
351 | p++; |
c36e914d |
352 | break; |
39b168b8 |
353 | } |
c36e914d |
354 | if (*p == ',') |
355 | p = sskip(p + 1); |
356 | |
39b168b8 |
357 | arg = &pp->arg[xarg]; |
c36e914d |
358 | xarg++; |
359 | |
39b168b8 |
360 | p1 = p; |
64c59faf |
361 | kt = check_type(p, &ret); |
c36e914d |
362 | if (kt == NULL) { |
363 | printf("%s:%d:%ld: unhandled type for arg%d\n", |
364 | hdrfn, hdrfline, (p - protostr) + 1, xarg); |
39b168b8 |
365 | return -1; |
c36e914d |
366 | } |
39b168b8 |
367 | arg->type = kt; |
64c59faf |
368 | p = sskip(p + ret); |
c36e914d |
369 | |
39b168b8 |
370 | if (*p == '(') { |
371 | // func ptr |
372 | arg->fptr = calloc(1, sizeof(*arg->fptr)); |
373 | ret = parse_protostr(p1, arg->fptr); |
374 | if (ret < 0) { |
375 | printf("%s:%d:%ld: funcarg parse failed\n", |
376 | hdrfn, hdrfline, p1 - protostr); |
377 | return -1; |
378 | } |
379 | // we'll treat it as void * for non-calls |
380 | arg->type = "void *"; |
381 | |
382 | p = p1 + ret; |
383 | } |
384 | |
c36e914d |
385 | p = next_idt(buf, sizeof(buf), p); |
386 | p = sskip(p); |
387 | #if 0 |
388 | if (buf[0] == 0) { |
389 | printf("%s:%d:%ld: idt missing for arg%d\n", |
390 | hdrfn, hdrfline, (p - protostr) + 1, xarg); |
39b168b8 |
391 | return -1; |
c36e914d |
392 | } |
393 | #endif |
39b168b8 |
394 | arg->reg = NULL; |
c36e914d |
395 | |
396 | ret = get_regparm(regparm, sizeof(regparm), p); |
397 | if (ret > 0) { |
398 | p += ret; |
399 | p = sskip(p); |
400 | |
39b168b8 |
401 | arg->reg = strdup(map_reg(regparm)); |
c36e914d |
402 | } |
403 | } |
404 | |
405 | if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) { |
406 | if (pp->arg[0].reg != NULL) { |
407 | printf("%s:%d: %s with arg1 spec %s?\n", |
408 | hdrfn, hdrfline, cconv, pp->arg[0].reg); |
409 | } |
410 | pp->arg[0].reg = strdup("ecx"); |
411 | } |
412 | |
413 | if (xarg > 1 && IS(cconv, "__fastcall")) { |
414 | if (pp->arg[1].reg != NULL) { |
415 | printf("%s:%d: %s with arg2 spec %s?\n", |
416 | hdrfn, hdrfline, cconv, pp->arg[1].reg); |
417 | } |
418 | pp->arg[1].reg = strdup("edx"); |
419 | } |
420 | |
421 | pp->argc = xarg; |
422 | |
423 | for (i = 0; i < pp->argc; i++) { |
424 | if (pp->arg[i].reg == NULL) |
425 | pp->argc_stack++; |
426 | else |
427 | pp->argc_reg++; |
428 | } |
429 | |
39b168b8 |
430 | return p - protostr; |
c36e914d |
431 | } |
432 | |
433 | static int proto_parse(FILE *fhdr, const char *sym, struct parsed_proto *pp) |
434 | { |
435 | char protostr[256]; |
436 | int ret; |
437 | |
438 | memset(pp, 0, sizeof(*pp)); |
439 | |
39b168b8 |
440 | ret = find_protostr(protostr, sizeof(protostr), fhdr, hdrfn, sym); |
c36e914d |
441 | if (ret != 0) { |
442 | printf("%s: sym '%s' is missing\n", hdrfn, sym); |
443 | return ret; |
444 | } |
445 | |
39b168b8 |
446 | return parse_protostr(protostr, pp) < 0 ? -1 : 0; |
c36e914d |
447 | } |
448 | |
449 | static void proto_release(struct parsed_proto *pp) |
450 | { |
451 | int i; |
452 | |
453 | for (i = 0; i < pp->argc; i++) { |
39b168b8 |
454 | if (pp->arg[i].reg != NULL) |
c36e914d |
455 | free(pp->arg[i].reg); |
64c59faf |
456 | if (pp->arg[i].type != NULL) |
457 | free(pp->arg[i].type); |
39b168b8 |
458 | if (pp->arg[i].fptr != NULL) |
459 | free(pp->arg[i].fptr); |
c36e914d |
460 | } |
64c59faf |
461 | if (pp->ret_type != NULL) |
462 | free(pp->ret_type); |
c36e914d |
463 | } |