c36e914d |
1 | |
39b168b8 |
2 | struct parsed_proto; |
3 | |
4 | struct parsed_proto_arg { |
5 | char *reg; |
6 | const char *type; |
7 | struct parsed_proto *fptr; |
8 | void *datap; |
9 | }; |
10 | |
c36e914d |
11 | struct parsed_proto { |
39b168b8 |
12 | char name[256]; |
c36e914d |
13 | const 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.. |
106 | static const char *known_types[] = { |
107 | "const void *", |
108 | "void *", |
109 | "char *", |
110 | "FILE *", |
f3d05b09 |
111 | "int *", |
c36e914d |
112 | "unsigned __int8", |
113 | "unsigned __int16", |
114 | "unsigned int", |
115 | "signed int", |
116 | "char", |
117 | "__int8", |
118 | "__int16", |
119 | "int", |
120 | "bool", |
121 | "void", |
122 | "BYTE", |
123 | "WORD", |
124 | "DWORD", |
91977a1c |
125 | "_DWORD", |
c36e914d |
126 | "HMODULE", |
127 | "HANDLE", |
128 | "HWND", |
129 | "LPCSTR", |
130 | "size_t", |
131 | }; |
132 | |
39b168b8 |
133 | static int typecmp(const char *n, const char *t) |
134 | { |
135 | for (; *t != 0; n++, t++) { |
136 | while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*')) |
137 | n++; |
138 | while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*')) |
139 | t++; |
140 | if (*n != *t) |
141 | return *n - *t; |
142 | } |
143 | |
144 | return 0; |
145 | } |
146 | |
c36e914d |
147 | static const char *check_type(const char *name) |
148 | { |
39b168b8 |
149 | int i; |
c36e914d |
150 | |
151 | for (i = 0; i < ARRAY_SIZE(known_types); i++) { |
39b168b8 |
152 | if (typecmp(name, known_types[i]) == 0) |
c36e914d |
153 | return known_types[i]; |
154 | } |
155 | |
156 | return NULL; |
157 | } |
158 | |
159 | /* args are always expanded to 32bit */ |
160 | static const char *map_reg(const char *reg) |
161 | { |
162 | const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" }; |
163 | const char *regs_w[] = { "ax", "bx", "cx", "dx", "si", "di" }; |
164 | const char *regs_b[] = { "al", "bl", "cl", "dl" }; |
165 | int i; |
166 | |
167 | for (i = 0; i < ARRAY_SIZE(regs_w); i++) |
168 | if (IS(reg, regs_w[i])) |
169 | return regs_f[i]; |
170 | |
171 | for (i = 0; i < ARRAY_SIZE(regs_b); i++) |
172 | if (IS(reg, regs_b[i])) |
173 | return regs_f[i]; |
174 | |
175 | return reg; |
176 | } |
177 | |
178 | static int parse_protostr(char *protostr, struct parsed_proto *pp) |
179 | { |
39b168b8 |
180 | struct parsed_proto_arg *arg; |
c36e914d |
181 | char regparm[16]; |
182 | char buf[256]; |
183 | char cconv[32]; |
184 | const char *kt; |
185 | int xarg = 0; |
39b168b8 |
186 | char *p, *p1; |
c36e914d |
187 | int ret; |
c36e914d |
188 | int i; |
189 | |
06c5d854 |
190 | p = sskip(protostr); |
c36e914d |
191 | if (p[0] == '/' && p[1] == '/') { |
06c5d854 |
192 | printf("%s:%d: commented out?\n", hdrfn, hdrfline); |
c36e914d |
193 | p = sskip(p + 2); |
194 | } |
195 | |
06c5d854 |
196 | // strip unneeded stuff |
197 | for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) { |
198 | if ((p1[0] == '/' && p1[1] == '*') |
199 | || (p1[0] == '*' && p1[1] == '/')) |
200 | p1[0] = p1[1] = ' '; |
201 | } |
202 | |
203 | if (strncmp(p, "extern ", 7) == 0) |
204 | p = sskip(p + 7); |
205 | |
c36e914d |
206 | kt = check_type(p); |
207 | if (kt == NULL) { |
208 | printf("%s:%d:%ld: unhandled return in '%s'\n", |
209 | hdrfn, hdrfline, (p - protostr) + 1, protostr); |
39b168b8 |
210 | return -1; |
c36e914d |
211 | } |
212 | pp->ret_type = kt; |
213 | p += strlen(kt); |
214 | p = sskip(p); |
215 | |
06c5d854 |
216 | if (!strchr(p, ')')) { |
217 | p = next_idt(buf, sizeof(buf), p); |
218 | p = sskip(p); |
219 | if (buf[0] == 0) { |
220 | printf("%s:%d:%ld: var name missing\n", |
221 | hdrfn, hdrfline, (p - protostr) + 1); |
222 | return -1; |
223 | } |
224 | strcpy(pp->name, buf); |
225 | |
226 | p1 = strchr(p, ']'); |
227 | if (p1 != NULL) { |
228 | p = p1 + 1; |
229 | pp->is_array = 1; |
230 | } |
231 | return p - protostr; |
232 | } |
233 | |
234 | pp->is_func = 1; |
235 | |
39b168b8 |
236 | if (*p == '(') { |
237 | pp->is_fptr = 1; |
238 | p = sskip(p + 1); |
239 | } |
240 | |
c36e914d |
241 | p = next_word(cconv, sizeof(cconv), p); |
242 | p = sskip(p); |
243 | if (cconv[0] == 0) { |
244 | printf("%s:%d:%ld: cconv missing\n", |
245 | hdrfn, hdrfline, (p - protostr) + 1); |
39b168b8 |
246 | return -1; |
c36e914d |
247 | } |
248 | if (IS(cconv, "__cdecl")) |
249 | pp->is_stdcall = 0; |
250 | else if (IS(cconv, "__stdcall")) |
251 | pp->is_stdcall = 1; |
252 | else if (IS(cconv, "__fastcall")) |
253 | pp->is_stdcall = 1; |
254 | else if (IS(cconv, "__thiscall")) |
255 | pp->is_stdcall = 1; |
256 | else if (IS(cconv, "__userpurge")) |
257 | pp->is_stdcall = 1; // in all cases seen.. |
258 | else if (IS(cconv, "__usercall")) |
259 | pp->is_stdcall = 0; // ..or is it? |
260 | else { |
261 | printf("%s:%d:%ld: unhandled cconv: '%s'\n", |
262 | hdrfn, hdrfline, (p - protostr) + 1, cconv); |
39b168b8 |
263 | return -1; |
264 | } |
265 | |
266 | if (pp->is_fptr) { |
267 | if (*p != '*') { |
268 | printf("%s:%d:%ld: '*' expected\n", |
269 | hdrfn, hdrfline, (p - protostr) + 1); |
270 | return -1; |
271 | } |
272 | p = sskip(p + 1); |
c36e914d |
273 | } |
274 | |
275 | p = next_idt(buf, sizeof(buf), p); |
276 | p = sskip(p); |
277 | if (buf[0] == 0) { |
278 | printf("%s:%d:%ld: func name missing\n", |
39b168b8 |
279 | hdrfn, hdrfline, (p - protostr) + 1); |
280 | return -1; |
c36e914d |
281 | } |
39b168b8 |
282 | strcpy(pp->name, buf); |
c36e914d |
283 | |
284 | ret = get_regparm(regparm, sizeof(regparm), p); |
285 | if (ret > 0) { |
286 | if (!IS(regparm, "eax") && !IS(regparm, "ax") |
287 | && !IS(regparm, "al")) |
288 | { |
289 | printf("%s:%d:%ld: bad regparm: %s\n", |
290 | hdrfn, hdrfline, (p - protostr) + 1, regparm); |
39b168b8 |
291 | return -1; |
c36e914d |
292 | } |
293 | p += ret; |
294 | p = sskip(p); |
295 | } |
296 | |
39b168b8 |
297 | if (pp->is_fptr) { |
298 | if (*p != ')') { |
299 | printf("%s:%d:%ld: ')' expected\n", |
300 | hdrfn, hdrfline, (p - protostr) + 1); |
301 | return -1; |
302 | } |
303 | p = sskip(p + 1); |
304 | } |
305 | |
c36e914d |
306 | if (*p != '(') { |
307 | printf("%s:%d:%ld: '(' expected, got '%c'\n", |
308 | hdrfn, hdrfline, (p - protostr) + 1, *p); |
39b168b8 |
309 | return -1; |
c36e914d |
310 | } |
311 | p++; |
312 | |
39b168b8 |
313 | // check for x(void) |
314 | p = sskip(p); |
06c5d854 |
315 | if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4)) |
316 | && *sskip(p + 4) == ')') |
39b168b8 |
317 | p += 4; |
318 | |
c36e914d |
319 | while (1) { |
320 | p = sskip(p); |
39b168b8 |
321 | if (*p == ')') { |
322 | p++; |
c36e914d |
323 | break; |
39b168b8 |
324 | } |
c36e914d |
325 | if (*p == ',') |
326 | p = sskip(p + 1); |
327 | |
39b168b8 |
328 | arg = &pp->arg[xarg]; |
c36e914d |
329 | xarg++; |
330 | |
39b168b8 |
331 | p1 = p; |
c36e914d |
332 | kt = check_type(p); |
333 | if (kt == NULL) { |
334 | printf("%s:%d:%ld: unhandled type for arg%d\n", |
335 | hdrfn, hdrfline, (p - protostr) + 1, xarg); |
39b168b8 |
336 | return -1; |
c36e914d |
337 | } |
39b168b8 |
338 | arg->type = kt; |
c36e914d |
339 | p += strlen(kt); |
340 | p = sskip(p); |
341 | |
39b168b8 |
342 | if (*p == '(') { |
343 | // func ptr |
344 | arg->fptr = calloc(1, sizeof(*arg->fptr)); |
345 | ret = parse_protostr(p1, arg->fptr); |
346 | if (ret < 0) { |
347 | printf("%s:%d:%ld: funcarg parse failed\n", |
348 | hdrfn, hdrfline, p1 - protostr); |
349 | return -1; |
350 | } |
351 | // we'll treat it as void * for non-calls |
352 | arg->type = "void *"; |
353 | |
354 | p = p1 + ret; |
355 | } |
356 | |
c36e914d |
357 | p = next_idt(buf, sizeof(buf), p); |
358 | p = sskip(p); |
359 | #if 0 |
360 | if (buf[0] == 0) { |
361 | printf("%s:%d:%ld: idt missing for arg%d\n", |
362 | hdrfn, hdrfline, (p - protostr) + 1, xarg); |
39b168b8 |
363 | return -1; |
c36e914d |
364 | } |
365 | #endif |
39b168b8 |
366 | arg->reg = NULL; |
c36e914d |
367 | |
368 | ret = get_regparm(regparm, sizeof(regparm), p); |
369 | if (ret > 0) { |
370 | p += ret; |
371 | p = sskip(p); |
372 | |
39b168b8 |
373 | arg->reg = strdup(map_reg(regparm)); |
c36e914d |
374 | } |
375 | } |
376 | |
377 | if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) { |
378 | if (pp->arg[0].reg != NULL) { |
379 | printf("%s:%d: %s with arg1 spec %s?\n", |
380 | hdrfn, hdrfline, cconv, pp->arg[0].reg); |
381 | } |
382 | pp->arg[0].reg = strdup("ecx"); |
383 | } |
384 | |
385 | if (xarg > 1 && IS(cconv, "__fastcall")) { |
386 | if (pp->arg[1].reg != NULL) { |
387 | printf("%s:%d: %s with arg2 spec %s?\n", |
388 | hdrfn, hdrfline, cconv, pp->arg[1].reg); |
389 | } |
390 | pp->arg[1].reg = strdup("edx"); |
391 | } |
392 | |
393 | pp->argc = xarg; |
394 | |
395 | for (i = 0; i < pp->argc; i++) { |
396 | if (pp->arg[i].reg == NULL) |
397 | pp->argc_stack++; |
398 | else |
399 | pp->argc_reg++; |
400 | } |
401 | |
39b168b8 |
402 | return p - protostr; |
c36e914d |
403 | } |
404 | |
405 | static int proto_parse(FILE *fhdr, const char *sym, struct parsed_proto *pp) |
406 | { |
407 | char protostr[256]; |
408 | int ret; |
409 | |
410 | memset(pp, 0, sizeof(*pp)); |
411 | |
39b168b8 |
412 | ret = find_protostr(protostr, sizeof(protostr), fhdr, hdrfn, sym); |
c36e914d |
413 | if (ret != 0) { |
414 | printf("%s: sym '%s' is missing\n", hdrfn, sym); |
415 | return ret; |
416 | } |
417 | |
39b168b8 |
418 | return parse_protostr(protostr, pp) < 0 ? -1 : 0; |
c36e914d |
419 | } |
420 | |
421 | static void proto_release(struct parsed_proto *pp) |
422 | { |
423 | int i; |
424 | |
425 | for (i = 0; i < pp->argc; i++) { |
39b168b8 |
426 | if (pp->arg[i].reg != NULL) |
c36e914d |
427 | free(pp->arg[i].reg); |
39b168b8 |
428 | if (pp->arg[i].fptr != NULL) |
429 | free(pp->arg[i].fptr); |
c36e914d |
430 | } |
431 | } |