hbmap: fix iterator truncation when size_t < 32bit
[rofl0r-agsutils.git] / Script.c
blobc28b8eef68a1b2c6a1327487695c041424e404f6
1 #define _GNU_SOURCE
2 #include "Script.h"
3 #include "Script_internal.h"
4 #include "endianness.h"
5 #include <string.h>
6 #include <stdlib.h>
7 #include <assert.h>
9 #define COMMENT(F, FMT, ...) fprintf(F, "; " FMT, __VA_ARGS__)
11 static int dump_sections(AF* a, FILE *f, size_t start, size_t count) {
12 if(count) {
13 AF_set_pos(a, start);
14 fprintf(f, ".sections\n");
15 char buf[300];
16 size_t i = 0;
17 for(; i < count; i++) {
18 if(!AF_read_string(a, buf, sizeof(buf))) return 0;
19 int off = AF_read_int(a);
20 fprintf(f, "\"%s\" = %d\n", buf, off);
22 fprintf(f, "\n");
24 return 1;
27 #include "StringEscape.h"
28 static int dump_strings(AF* a, FILE *f, struct strings *str) {
29 if(!str->count) return 1;
30 fprintf(f, ".%s\n", "strings");
31 char *p, escapebuf[4096];
32 size_t i;
33 for (i=0; i<str->count; ++i) {
34 escape(str->strings[i], escapebuf, sizeof(escapebuf));
35 fprintf(f, "\"%s\"\n", escapebuf);
37 return 1;
40 static void free_fixup_data(struct fixup_data *ret) {
41 size_t i;
42 for(i = 0; i <= FIXUP_MAX; i++) {
43 free(ret->codeindex_per[i]);
45 free(ret->codeindex);
46 free(ret->types);
49 /* fixup_data needs to be zeroed */
50 static int get_fixups(AF* a, size_t start, size_t count, struct fixup_data *ret) {
51 size_t i;
52 if(!(ret->types = malloc(count))) goto err;
53 if(!(ret->codeindex = malloc(count * sizeof(unsigned)))) goto err;
55 AF_set_pos(a, start);
57 if(count != (size_t) AF_read(a, ret->types, count)) goto err;
58 for(i = 0; i < count; i++) {
59 assert(ret->types[i]>=0 && ret->types[i]<=FIXUP_MAX);
60 ret->count[ret->types[i]]++;
61 ret->codeindex[i] = AF_read_uint(a);
63 for(i = 0; i <= FIXUP_MAX; i++) {
64 ret->codeindex_per[i] = malloc(ret->count[i] * sizeof(unsigned));
65 if(!ret->codeindex_per[i]) goto err;
66 ret->count[i] = 0; /* reset to 0 to use as index i.t. next loop */
69 for(i = 0; i < count; i++) {
70 ret->codeindex_per[ret->types[i]][ret->count[ret->types[i]]++] = ret->codeindex[i];
73 return 1;
74 err:
75 free_fixup_data(ret);
76 return 0;
79 static int dump_fixups(FILE *f, size_t count, struct fixup_data *fxd) {
80 static const char* typenames[] = {
81 [FIXUP_GLOBALDATA] = "FIXUP_GLOBALDATA",
82 [FIXUP_FUNCTION] = "FIXUP_FUNCTION",
83 [FIXUP_STRING] = "FIXUP_STRING",
84 [FIXUP_IMPORT] = "FIXUP_IMPORT",
85 [FIXUP_DATADATA] = "FIXUP_DATADATA",
86 [FIXUP_STACK] = "FIXUP_STACK",
89 fprintf(f, ".%ss\n", "fixup");
90 size_t i;
91 for(i = 0; i < count; i++) {
92 fprintf(f, "%s: %.12u\n", typenames[(int)fxd->types[i]], fxd->codeindex[i]);
94 return 1;
98 static int dump_import_export(AF* a, FILE *f, size_t start, size_t count, int import) {
99 static const char* secnames[2] = { "export", "import" };
100 const char* secname = secnames[import];
101 size_t i;
102 char buf[256]; /* arbitrarily chosen */
103 if(!count) return 1;
104 AF_set_pos(a, start);
105 fprintf(f, ".%ss\n", secname);
106 for(i = 0; i < count; i++) {
107 if(!AF_read_string(a, buf, sizeof(buf))) return 0;
108 fprintf(f, "%.12zu\"%s\"\n", i, buf);
109 if(!import) {
110 unsigned int addr = AF_read_uint(a);
111 fprintf(f, "%d:%.12u\n", addr >> 24, addr & 0x00FFFFFF);
114 return 1;
117 static struct export* get_exports(AF* a, size_t start, size_t count) {
118 if(!count) return 0;
119 struct export* fl = malloc(count * sizeof(struct export));
120 AF_set_pos(a, start);
121 char buf[4096]; size_t i;
122 for(i = 0; i < count; i++) {
123 if(!AF_read_string(a, buf, sizeof(buf))) return 0;
124 fl[i].fn = strdup(buf);
125 unsigned int addr = AF_read_uint(a);
126 fl[i].type = addr >> 24;
127 fl[i].instr = (addr & 0x00FFFFFF);
129 return fl;
132 static struct importlist get_imports(AF* a, size_t start, size_t count) {
133 struct importlist ret = {0};
134 if(!count) return ret;
135 ret.names = malloc(count * sizeof(char*));
136 if(!ret.names) return ret;
137 AF_set_pos(a, start);
138 char buf[4096]; size_t i;
139 for(i = 0; i < count; i++) {
140 if(!AF_read_string(a, buf, sizeof(buf))) {
141 free(ret.names);
142 return (struct importlist) {0};
144 ret.names[i] = strdup(buf);
146 return ret;
149 struct labels {
150 unsigned count;
151 unsigned *insno;
154 static int sort_comp(const void* xp, const void *yp) {
155 const unsigned *x = xp, *y = yp;
156 if(*x == *y) return 0;
157 else if(*x > *y) return 1;
158 else return -1;
161 #include "ags_cpu.h"
162 static struct labels get_labels(unsigned *code, size_t count) {
163 struct labels ret = {0, 0};
164 if(!count) return ret;
165 size_t capa = 0;
166 unsigned *p = 0;
167 size_t insno = 0;
168 unsigned insn;
169 while(insno < count) {
170 if(ret.count + 1 > capa) {
171 capa = capa ? capa * 2 : 16;
172 if(!(p = realloc(ret.insno, capa * sizeof(unsigned*)))) {
173 if(ret.insno) free(ret.insno);
174 ret.count = 0;
175 ret.insno = 0;
176 return ret;
177 } else ret.insno = p;
179 insn = code[insno] & 0x00ffffff;
180 insno++;
181 int isjmp = 0;
182 assert(insn < SCMD_MAX);
183 switch(insn) {
184 case SCMD_JZ: case SCMD_JMP: case SCMD_JNZ:
185 isjmp = 1;
186 default:
187 break;
189 size_t i = 0;
190 for(; i < opcodes[insn].argcount; i++) {
191 int val = code[insno];
192 insno++;
193 if(isjmp) {
194 if((int) insno + val < 0 || insno + val >= count || code[insno + val] > SCMD_MAX) {
195 fprintf(stderr, "error: label referenced from jump at %zu is out of bounds\n"
196 "or points to non-instruction start code.\n", insno);
197 assert(0);
199 ret.insno[ret.count] = insno + val;
200 ret.count++;
204 qsort(ret.insno, ret.count, sizeof(unsigned), sort_comp);
205 return ret;
208 static struct strings get_strings(AF* a, size_t start, size_t size) {
209 struct strings ret = {0,0,0};
210 int corrupt = 0;
211 if(!size) return ret;
212 if(!(ret.data = malloc(size))) return ret;
213 AF_set_pos(a, start);
214 if(size != (size_t) AF_read(a, ret.data, size)) {
215 free1:
216 free(ret.data);
217 retrn:
218 return ret;
220 size_t i, strcnt = 0;
221 for(i = 0; i < size; i++) {
222 if(!ret.data[i]) strcnt++;
223 else if(ret.data[i] < 9 || (unsigned char) ret.data[i] > 127
224 || (ret.data[i] > 13 && ret.data[i] < 32))
225 corrupt++;
227 if(corrupt) {
228 fprintf(stderr, "warning: %d unusual bytes in string data section, file may be corrupted\n", corrupt);
230 if(ret.data[size-1]) {
231 fprintf(stderr, "%s", "warning: string data section doesn't end with 0, data probably corrupted\n");
232 strcnt++;
233 ret.data[size-1] = 0;
235 if(!(ret.strings = malloc(strcnt * sizeof(char*)))) goto free1;
236 char* p = ret.data;
237 size_t l = 0;
238 strcnt = 0;
240 for(i = 0; i < size; i++) {
241 if(!ret.data[i]) {
242 ret.strings[strcnt] = p;
243 p += l + 1;
244 l = 0;
245 strcnt++;
246 } else
247 l++;
249 ret.count = strcnt;
250 goto retrn;
253 static char* get_varname(struct export* exp, size_t expcount, unsigned globaloffset) {
254 size_t i = 0;
255 for(; i < expcount; i++)
256 if(exp[i].type == EXPORT_DATA && exp[i].instr == globaloffset)
257 return exp[i].fn;
258 return 0;
261 unsigned *get_code(AF *a, size_t start, size_t count) {
262 unsigned *ret = malloc(count * sizeof(unsigned));
263 if(!ret) return 0;
264 AF_set_pos(a, start);
265 size_t i;
266 for(i=0; i<count; i++)
267 ret[i] = AF_read_uint(a);
268 return ret;
271 static unsigned get_varsize_from_instr(unsigned* code, size_t codecount, size_t index) {
272 assert(index < codecount);
273 switch(code[index]) {
274 case SCMD_MEMREADB: case SCMD_MEMWRITEB:
275 return 1;
276 case SCMD_MEMREADW: case SCMD_MEMWRITEW:
277 return 2;
278 case SCMD_MEMWRITEPTR:
279 case SCMD_MEMREADPTR:
280 case SCMD_MEMZEROPTR:
281 case SCMD_MEMINITPTR:
282 case SCMD_MEMREAD: case SCMD_MEMWRITE:
283 return 4;
284 default:
285 return 0;
289 struct fixup_resolved {
290 unsigned code;
291 unsigned offset;
294 static int fixup_cmp(const void *a, const void *b) {
295 const struct fixup_resolved *u1 = a;
296 const struct fixup_resolved *u2 = b;
297 return (u1->code - u2->code);
300 /* assumes list members are sorted. set iter to -1 for first call */
301 int find_next_match(struct fixup_resolved *list, size_t nel, unsigned value, size_t *iter) {
302 struct fixup_resolved comparer = {
303 .code = value
305 if(*iter == (size_t)-1) {
306 struct fixup_resolved* ret = bsearch(&comparer, list, nel, sizeof(list[0]), fixup_cmp);
307 if(!ret) return 0;
308 *iter = ret - list;
309 while(*iter>=1 && list[*iter-1].code == value)
310 --(*iter);
311 return 1;
312 } else {
313 ++(*iter);
314 if(*iter < nel && list[*iter].code == value)
315 return 1;
316 return 0;
320 static int has_datadata_fixup(unsigned gdoffset, struct fixup_data *fxd) {
321 size_t i;
322 for(i = 0; i < fxd->count[FIXUP_DATADATA]; i++)
323 if(fxd->codeindex_per[FIXUP_DATADATA][i] == gdoffset)
324 return 1;
325 return 0;
328 struct varinfo find_fixup_for_globaldata(FILE *f, size_t offset,
329 struct fixup_resolved *fxlist_resolved, size_t fxlist_cnt,
330 unsigned* code, size_t codecount)
333 size_t iter = (size_t)-1, x;
334 struct varinfo ret = {0,0};
335 while(find_next_match(fxlist_resolved, fxlist_cnt, offset, &iter)) {
336 x = fxlist_resolved[iter].offset;
337 if(1) {
338 assert(x + 1 < codecount);
339 if(1) {
340 ret.numrefs++;
341 unsigned oldvarsize = ret.varsize;
342 ret.varsize = get_varsize_from_instr(code, codecount, x+1);
343 if(!ret.varsize) switch(code[x+1]) {
344 case SCMD_REGTOREG:
345 // li mar, @Obj; mr ax, mar; callobj ax
346 if(x+5 < codecount &&
347 code[x+2] == AR_MAR &&
348 code[x+4] == SCMD_CALLOBJ &&
349 code[x+3] == code[x+5]) {
350 ret.varsize = 4;
352 break;
353 case SCMD_MUL:
354 // li mar, @var; muli dx, 4; ptrget mar; ptrassert; dynamicbounds dx
355 if(x+5 < codecount &&
356 code[x+2] != AR_MAR &&
357 code[x+4] == SCMD_MEMREADPTR &&
358 code[x+5] == AR_MAR)
359 ret.varsize = 4;
360 // muli is used as an array index like:
361 // muli REG, 4; add MAR, REG; PUSH/POP MAR; ...
362 else if(x+11 < codecount &&
363 code[x+4] == SCMD_ADDREG &&
364 code[x+5] == AR_MAR &&
365 code[x+6] == code[x+2] &&
366 code[x+7] == SCMD_PUSHREG &&
367 code[x+8] == AR_MAR &&
368 code[x+9] == SCMD_POPREG &&
369 code[x+10] == AR_MAR) {
370 ret.varsize = get_varsize_from_instr(code, codecount, x+11);
371 if(!ret.varsize &&
372 x+14 < codecount &&
373 code[x+11] == SCMD_ADDREG &&
374 code[x+12] == AR_MAR &&
375 code[x+13] != code[x+2]
377 /* this variation adds another reg to mar before calling ptrget */
378 ret.varsize = get_varsize_from_instr(code, codecount, x+14);
379 else if(!ret.varsize &&
380 x+13 < codecount &&
381 code[x+11] == SCMD_PUSHREG &&
382 code[x+12] == AR_AX)
383 /* this variation pushes ax on the stack before doing a ptrget ax, which overwrites ax */
384 ret.varsize = get_varsize_from_instr(code, codecount, x+13);
386 /* muli dx, 4; add mar, dx; add mar, cx; ptr... */
387 else if(x+10 < codecount &&
388 code[x+4] == SCMD_ADDREG &&
389 code[x+5] == AR_MAR &&
390 (code[x+6] == AR_CX || code[x+6] == AR_DX) &&
391 code[x+7] == SCMD_ADDREG &&
392 code[x+8] == AR_MAR &&
393 (code[x+9] == AR_CX || code[x+9] == AR_DX))
394 ret.varsize = get_varsize_from_instr(code, codecount, x+10);
395 else if(x+7 < codecount &&
396 code[x+4] == SCMD_ADDREG &&
397 code[x+5] == AR_MAR &&
398 code[x+6] == code[x+2]) {
399 ret.varsize = get_varsize_from_instr(code, codecount, x+7);
400 if(!ret.varsize &&
401 x + 9 < codecount &&
402 code[x+7] == SCMD_PUSHREG &&
403 code[x+8] == AR_AX)
404 ret.varsize = get_varsize_from_instr(code, codecount, x+9);
407 break;
408 case SCMD_ADDREG:
409 // addreg is typically used on an index register into an array
410 // followed by a byteread/store of the desired size
411 // the index register needs to be added to mar reg
412 assert(x+2 < codecount && code[x+2] == AR_MAR);
413 ret.varsize = get_varsize_from_instr(code, codecount, x+4);
414 // a typical method call
415 if(!ret.varsize && x+8 < codecount &&
416 code[x+4] == SCMD_REGTOREG &&
417 code[x+5] == AR_MAR &&
418 code[x+7] == SCMD_CALLOBJ &&
419 code[x+6] == code[x+8])
420 ret.varsize = 4;
421 break;
422 case SCMD_PUSHREG:
423 // ptrget and similar ops are typically preceded by push mar, pop mar
424 if(x+4 < codecount &&
425 code[x+2] == AR_MAR &&
426 code[x+3] == SCMD_POPREG &&
427 code[x+4] == AR_MAR) {
428 ret.varsize = get_varsize_from_instr(code, codecount, x+5);
429 if(!ret.varsize && x+7 < codecount &&
430 code[x+5] == SCMD_ADDREG &&
431 code[x+6] == AR_MAR)
432 ret.varsize = get_varsize_from_instr(code, codecount, x+8);
433 } else if(x+2 < codecount &&
434 code[x+2] == AR_AX)
435 /* ptrget is sometimes preceded by push ax */
436 ret.varsize = get_varsize_from_instr(code, codecount, x+3);
437 break;
439 if(!ret.varsize) {
440 if(oldvarsize) {
441 /* don't bother guessing the varsize if we already determined it */
442 ret.varsize = oldvarsize;
444 fprintf(stderr, "warning: '%s' globaldata fixup on insno %zu offset %zu\n",
445 opcodes[code[x+1]].mnemonic, x+1, offset);
446 COMMENT(f, "warning: '%s' globaldata fixup on insno %zu offset %zu\n",
447 opcodes[code[x+1]].mnemonic, x+1, offset);
449 if(oldvarsize != 0 && oldvarsize != ret.varsize)
450 assert(0);
454 return ret;
457 static int is_all_zeroes(const char* buf, int len) {
458 while(len--) {
459 if(*buf != 0) return 0;
460 buf++;
462 return 1;
465 static const char* get_varsize_typename(unsigned varsize) {
466 static const char typenames[][6] = {[0]="ERR", [1]="char", [2]="short", [4]="int"};
467 switch(varsize) {
468 case 0: case 1: case 2: case 4:
469 return typenames[varsize];
470 case 200:
471 return "string";
473 return 0;
476 static struct varinfo get_varinfo_from_code(
477 unsigned *code, size_t codesize,
478 size_t offset,
479 struct fixup_data *fxd,
480 struct fixup_resolved *gd_fixups_resolved,
481 FILE *f)
483 struct varinfo vi;
484 if(has_datadata_fixup(offset, fxd))
485 vi = (struct varinfo){0,4};
486 else {
487 vi = find_fixup_for_globaldata(f, offset, gd_fixups_resolved, fxd->count[FIXUP_GLOBALDATA], code, codesize);
488 if(vi.varsize == 0 && has_datadata_fixup(offset+200, fxd))
489 vi.varsize = 200;
491 return vi;
494 int get_varinfo_from_exports(size_t offs, struct export *exp, size_t expcount, struct varinfo *vi)
496 struct export *end = exp + expcount;
497 for(; exp < end; ++exp)
498 if(exp->instr == offs && exp->type == EXPORT_DATA) {
499 vi->varsize = 1; /* unfortunately no size info is available, so we need to default to char for safety */
500 return 1;
502 return 0;
505 static int dump_globaldata(AF *a, FILE *f, size_t start, size_t size,
506 struct export* exp, size_t expcount,
507 struct fixup_data *fxd,
508 unsigned *code, size_t codesize) {
509 if(!size) return 1;
511 size_t fxcount = fxd->count[FIXUP_GLOBALDATA];
512 struct fixup_resolved *gd_fixups_resolved = malloc(sizeof(struct fixup_resolved) * fxcount);
513 if(!gd_fixups_resolved) return 0;
514 size_t i;
515 for(i=0; i < fxcount; i++) {
516 unsigned x = fxd->codeindex_per[FIXUP_GLOBALDATA][i];
517 assert(x < codesize);
518 gd_fixups_resolved[i].code = code[x];
519 gd_fixups_resolved[i].offset = x;
521 qsort(gd_fixups_resolved, fxcount, sizeof(gd_fixups_resolved[0]), fixup_cmp);
523 fprintf(f, ".%s\n", "data");
524 AF_set_pos(a, start);
526 for(i = 0; i < size; ) {
527 struct varinfo vi;
528 vi = get_varinfo_from_code(code, codesize, i, fxd, gd_fixups_resolved, f);
529 if(vi.varsize == 0) get_varinfo_from_exports(i, exp, expcount, &vi);
530 int x;
531 char *comment = "";
532 int is_str = 0;
534 switch(vi.varsize) {
535 case 200:
537 off_t savepos = AF_get_pos(a);
538 if(i + 204 <= size) {
539 char buf[200];
540 assert(200 == AF_read(a, buf, 200));
541 /* read the datadata fixup content*/
542 x = AF_read_int(a);
543 if(x == i && is_all_zeroes(buf, 200)) {
544 x = 0;
545 AF_set_pos(a, savepos + 200);
546 is_str = 1;
547 break;
550 AF_set_pos(a, savepos);
551 vi.varsize = 0;
552 goto sw;
554 case 4:
555 x = AF_read_int(a);
556 break;
557 case 2:
558 x = AF_read_short(a);
559 break;
560 case 1:
561 x = ByteArray_readByte(a->b);
562 break;
563 case 0:
564 vi.varsize = 1;
565 x = ByteArray_readByte(a->b);
566 if(vi.numrefs) comment = " ; warning: couldn't determine varsize, default to 1";
567 else {
568 comment = " ; unreferenced variable, assuming char";
569 if(x) break;
570 struct varinfo vi2;
571 size_t j = i;
572 while(++j < size) {
573 vi2 = get_varinfo_from_code(code, codesize, j, fxd, gd_fixups_resolved, f);
574 if(vi2.varsize || vi.numrefs || get_varinfo_from_exports(j, exp, expcount, &vi2)) break;
575 x = ByteArray_readByte(a->b);
576 if(x) {
577 ByteArray_set_position_rel(a->b, -1);
578 x = 0;
579 break;
581 ++vi.varsize;
584 break;
586 char* vn = get_varname(exp, expcount, i), buf[32];
587 const char *tn = get_varsize_typename(vi.varsize);
588 if(!tn || (vi.varsize == 200 && !is_str)) {
589 snprintf(buf, sizeof buf, "char[%u]", vi.varsize);
590 tn = buf;
592 if(has_datadata_fixup(i, fxd)) {
593 if(vn) fprintf(f, "export %s %s = .data + %d%s\n", tn, vn, x, comment);
594 else fprintf(f, "%s var%.6zu = .data + %d%s\n", tn, i, x, comment);
595 } else {
596 if(vn) fprintf(f, "export %s %s = %d%s\n", tn, vn, x, comment);
597 else fprintf(f, "%s var%.6zu = %d%s\n", tn, i, x, comment);
599 i += vi.varsize;
601 free(gd_fixups_resolved);
602 return 1;
605 static int disassemble_code_and_data(AF* a, ASI* s, FILE *f, int flags, struct fixup_data *fxd, struct strings *str) {
606 int debugmode = getenv("AGSDEBUG") != 0;
607 size_t start = s->codestart;
608 size_t len = s->codesize * sizeof(unsigned);
610 unsigned *code = get_code(a, s->codestart, s->codesize);
612 struct export* fl = get_exports(a, s->exportstart, s->exportcount);
614 dump_globaldata(a, f, s->globaldatastart, s->globaldatasize, fl, s->exportcount, fxd, code, s->codesize);
616 if(!len) return 1; /* its valid for a scriptfile to have no code at all */
619 struct importlist il = get_imports(a, s->importstart, s->importcount);
621 struct labels lbl = get_labels(code, s->codesize);
623 AF_set_pos(a, start);
624 fprintf(f, ".%s\n", "text");
626 size_t currInstr = 0, currExp = 0, currFixup = 0, currLbl = 0;
627 char *curr_func = 0;
628 /* the data_data fixups appear to be glued separately onto the fixup logic,
629 * they are the only entries not sorted by instrucion number */
630 while(currFixup < s->fixupcount && fxd->types[currFixup] == FIXUP_DATADATA) currFixup++;
631 while(currInstr < s->codesize) {
632 if(flags & DISAS_DEBUG_OFFSETS) COMMENT(f, "offset: %llu (insno %zu)\n", (long long) AF_get_pos(a), currInstr);
633 unsigned regs, args, insn = AF_read_uint(a), op = insn & 0x00ffffff;
634 assert(op < SCMD_MAX);
635 while(currExp < s->exportcount && fl[currExp].type != EXPORT_FUNCTION)
636 currExp++;
637 if(currExp < s->exportcount && fl[currExp].instr == currInstr) {
638 /* new function starts here */
639 curr_func = fl[currExp].fn;
640 char comment[64], *p = strrchr(fl[currExp].fn, '$');
641 comment[0] = 0;
642 if(p) {
643 int n;
644 if((n = atoi(p+1)) >= 100)
645 sprintf(comment, " ; variadic, %d fixed args", n - 100);
646 else
647 sprintf(comment, " ; %d args", n);
649 fprintf(f, "\n%s:%s\n", curr_func, comment);
650 currExp++;
652 if(currLbl < lbl.count) {
653 if(lbl.insno[currLbl] == currInstr) {
654 size_t numrefs = 0;
655 while(currLbl < lbl.count && lbl.insno[currLbl] == currInstr) {
656 currLbl++; numrefs++;
658 fprintf(f, "label%.12zu: ", currInstr);
659 COMMENT(f, "inside %s, ", curr_func ? curr_func : "???");
660 COMMENT(f, "referenced by %zu spots\n", numrefs);
664 currInstr++;
666 regs = opcodes[op].regcount;
667 args = opcodes[op].argcount;
669 if(insn == SCMD_LINENUM && (flags & DISAS_SKIP_LINENO)) {
670 insn = AF_read_uint(a);
671 COMMENT(f, "line %u\n", insn);
672 currInstr++;
673 continue;
676 if(flags & DISAS_DEBUG_BYTECODE) {
677 unsigned char insbuf[16];
678 unsigned iblen = 0, val;
679 val = end_htole32(insn);
680 memcpy(insbuf+iblen, &val, 4); iblen += 4;
682 off_t currpos = AF_get_pos(a);
684 size_t l;
685 for(l = 0; l < args; l++) {
686 assert(iblen+4 <= sizeof(insbuf));
687 val = AF_read_uint(a);
688 val = end_htole32(val);
689 memcpy(insbuf+iblen, &val, 4); iblen += 4;
692 char printbuf[sizeof(insbuf)*2 + 1], *pb = printbuf;
693 for(l = 0; l < iblen; l++, pb+=2)
694 sprintf(pb, "%02x", (int) insbuf[l]);
695 COMMENT(f, "%s\n", printbuf);
697 AF_set_pos(a, currpos);
700 if(debugmode)
701 fprintf(f, "%.12zu""\t%s ", currInstr - 1, opcodes[op].mnemonic);
702 else
703 fprintf(f, /*"%.12zu"*/"\t%s ", /*currInstr - 1, */opcodes[op].mnemonic);
705 if(insn == SCMD_REGTOREG) {
706 /* the "mov" instruction differs from all others in that the source comes first
707 we do not want that. */
708 unsigned src, dst;
709 src = AF_read_uint(a);
710 currInstr++;
711 dst = AF_read_uint(a);
712 currInstr++;
713 fprintf(f, "%s, %s\n", regnames[dst], regnames[src]);
714 continue;
716 size_t l;
717 for (l = 0; l < args; l++) {
718 char escapebuf[4096];
719 if(l) fprintf(f, ", ");
720 insn = AF_read_uint(a);
721 currInstr++;
722 if((!l && regs) || (l == 1 && regs == 2))
723 fprintf(f, "%s", regnames[insn]);
724 else {
725 while(currFixup < s->fixupcount && fxd->types[currFixup] == FIXUP_DATADATA)
726 currFixup++; /* DATADATA fixups are unrelated to the code */
727 if(currFixup < s->fixupcount && fxd->codeindex[currFixup] == currInstr - 1) {
728 switch(fxd->types[currFixup]) {
729 case FIXUP_IMPORT:
730 if(debugmode)
731 fprintf(f, "IMP:%s", il.names[insn]);
732 else
733 fprintf(f, "%s", il.names[insn]);
734 break;
735 case FIXUP_FUNCTION: {
736 size_t x = 0;
737 for(; x < s->exportcount; x++) {
738 if(fl[x].type == EXPORT_FUNCTION && fl[x].instr == insn) {
739 fprintf(f, "%s", fl[x].fn);
740 break;
743 break;
745 case FIXUP_GLOBALDATA: {
746 char *vn = get_varname(fl, s->exportcount, insn);
747 if(vn) fprintf(f, "@%s", vn);
748 else fprintf(f, "@var%.6u", insn);
749 break; }
750 case FIXUP_STACK: /* it is unclear if and where those ever get generated */
751 fprintf(f, ".stack + %d", insn);
752 break;
753 case FIXUP_STRING:
754 escape(str->data + insn, escapebuf, sizeof(escapebuf));
755 fprintf(f, "\"%s\"", escapebuf);
756 default:
757 break;
759 currFixup++;
760 } else {
761 switch(op) {
762 case SCMD_JMP: case SCMD_JZ: case SCMD_JNZ:
763 fprintf(f, "label%.12zu", currInstr + (int) insn);
764 break;
765 default:
766 fprintf(f, "%d", insn);
771 fprintf(f, "\n");
773 free (fl);
774 return 1;
777 int ASI_disassemble(AF* a, ASI* s, char *fn, int flags) {
778 FILE *f;
779 int ret = 1;
780 if((f = fopen(fn, "wb")) == 0)
781 return 0;
782 AF_set_pos(a, s->start);
783 struct fixup_data fxd = {0};
784 if(!get_fixups(a, s->fixupstart, s->fixupcount, &fxd)) return 0;
785 struct strings str = get_strings(a, s->stringsstart, s->stringssize);
787 //if(!dump_globaldata(a, fd, s->globaldatastart, s->globaldatasize)) goto err_close;
788 if(!disassemble_code_and_data(a, s, f, flags, &fxd, &str)) goto err_close;
789 if(!dump_strings(a, f, &str)) goto err_close;
790 if((flags & DISAS_DEBUG_FIXUPS) && !dump_fixups(f, s->fixupcount, &fxd)) goto err_close;
791 if(!dump_import_export(a, f, s->importstart, s->importcount, 1)) goto err_close;
792 if(!dump_import_export(a, f, s->exportstart, s->exportcount, 0)) goto err_close;
793 if(!dump_sections(a, f, s->sectionstart, s->sectioncount)) goto err_close;
794 ret:
795 free_fixup_data(&fxd);
796 fclose(f);
797 return ret;
798 err_close:
799 ret = 0;
800 goto ret;
803 int ASI_read_script(AF *a, ASI* s) {
804 s->start = AF_get_pos(a);
805 char sig[4];
806 size_t l = 4;
807 if(l != (size_t) AF_read(a, sig, l)) return 0;
808 assert(memcmp("SCOM", sig, 4) == 0);
809 s->version = AF_read_int(a);
810 s->globaldatasize = AF_read_int(a);
811 s->codesize = AF_read_int(a);
812 s->stringssize = AF_read_int(a);
813 if(s->globaldatasize) {
814 s->globaldatastart = AF_get_pos(a);
815 l = s->globaldatasize;
816 if(!AF_read_junk(a, l)) return 0;
817 } else s->globaldatastart = 0;
818 if(s->codesize) {
819 s->codestart = AF_get_pos(a);
820 l = s->codesize * sizeof(int);
821 if(!AF_read_junk(a, l)) return 0;
822 } else s->codestart = 0;
823 if(s->stringssize) {
824 s->stringsstart = AF_get_pos(a);
825 l = s->stringssize;
826 if(!AF_read_junk(a, l)) return 0;
827 } else s->stringsstart = 0;
828 s->fixupcount = AF_read_int(a);
829 if(s->fixupcount) {
830 s->fixupstart = AF_get_pos(a);
831 l = s->fixupcount;
832 if(!AF_read_junk(a, l)) return 0; /* fixup types */
833 l *= sizeof(int);
834 if(!AF_read_junk(a, l)) return 0; /* fixups */
835 } else s->fixupstart = 0;
836 s->importcount = AF_read_int(a);
837 if(s->importcount) {
838 s->importstart = AF_get_pos(a);
839 char buf[300];
840 size_t i = 0;
841 for(; i < s->importcount; i++)
842 if(!AF_read_string(a, buf, sizeof(buf))) return 0;
843 } else s->importstart = 0;
844 s->exportcount = AF_read_int(a);
845 if(s->exportcount) {
846 s->exportstart = AF_get_pos(a);
847 char buf[300];
848 size_t i = 0;
849 for(; i < s->exportcount; i++) {
850 if(!AF_read_string(a, buf, sizeof(buf))) return 0;
851 AF_read_int(a); /* export_addr */
853 } else s->exportstart = 0;
854 s->sectionstart = 0;
855 s->sectioncount = 0;
856 if (s->version >= 83) {
857 s->sectioncount = AF_read_int(a);
858 if(s->sectioncount) {
859 s->sectionstart = AF_get_pos(a);
860 char buf[300];
861 size_t i = 0;
862 for(; i < s->sectioncount; i++) {
863 if(!AF_read_string(a, buf, sizeof(buf))) return 0;
864 AF_read_int(a); /* section offset */
868 if(0xbeefcafe != AF_read_uint(a)) return 0;
869 s->len = AF_get_pos(a) - s->start;
870 return 1;