disassembler: fix bug omitting exported vars not used in module
[rofl0r-agsutils.git] / agsinject.c
blob697983beef5c6ba35f56cf339db4168d86d15c22
1 #define _GNU_SOURCE
2 #include "DataFile.h"
3 #include "RoomFile.h"
4 #include "ByteArray.h"
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include "version.h"
9 #define ADS ":::AGSinject " VERSION " by rofl0r:::"
11 __attribute__((noreturn))
12 void usage(char *argv0) {
13 dprintf(2,
14 ADS "\nusage:\n%s index input.o inject_to.crm\n"
15 "index is the number of script to replace, i.e. 0 for first script\n"
16 "only relevant if the output file is a gamefile which contains multiple scripts\n"
17 "for example gamescript is 0, dialogscript is 1 (if existing), etc\n"
18 "a room file (.crm) only has one script so you must pass 0.\n", argv0);
19 exit(1);
22 /* inj = filename of file to inject in */
23 static int inject(char *o, char *inj, unsigned which) {
24 //ARF_find_code_start
25 AF f_b, *f = &f_b;
26 size_t index, found;
27 int isroom = !strcmp(".crm", inj + strlen(inj) - 4);
28 if(isroom && which != 0) return -1;
29 if(!AF_open(f, inj)) return 0;
30 ssize_t start;
31 for(index = found = 0; 1 ; found++, index = start + 4) {
32 if(!isroom && (start = ARF_find_code_start(f, index)) == -1) {
33 dprintf(2, "error, only %zu scripts found\n", found);
34 return 0;
35 } else if(isroom) {
36 /* use roomfile specific script lookup, as it's faster */
37 struct RoomFile rinfo = {0};
38 if(!RoomFile_read(f, &rinfo)) return 0;
39 start = rinfo.blockpos[BLOCKTYPE_COMPSCRIPT3];
41 if(found != which) continue;
42 char *tmp = tempnam(".", "agsinject.tmp");
43 FILE *out = fopen(tmp, "w");
44 if(!out) return 0;
46 /* 1) dump header */
47 AF_dump_chunk_stream(f, 0, isroom ? start -4 : start, out);
48 AF_set_pos(f, start);
50 /* open replacement object file */
51 struct ByteArray b;
52 ByteArray_ctor(&b);
53 ByteArray_open_file(&b, o);
55 if(isroom) {
56 /* 2a) if room, write length */
57 /* room files, unlike game files, have a length field of size 4 before
58 * the compiled script starts. */
59 unsigned l = ByteArray_get_length(&b);
60 struct ByteArray c;
61 ByteArray_ctor(&c);
62 ByteArray_open_mem(&c, 0, 0);
63 ByteArray_set_flags(&c, BAF_CANGROW);
64 ByteArray_set_endian(&c, BAE_LITTLE);
65 ByteArray_writeInt(&c, l);
66 ByteArray_dump_to_stream(&c, out);
67 ByteArray_close(&c);
69 /* 2b) dump object file */
70 ByteArray_dump_to_stream(&b, out);
71 ByteArray_close_file(&b);
73 ASI s;
74 if(!ASI_read_script(f, &s)) {
75 dprintf(2, "trouble finding script in %s\n", inj);
76 return 0;
78 /* 3) dump rest of file */
79 AF_dump_chunk_stream(f, start + s.len, ByteArray_get_length(f->b) - (start + s.len), out);
80 AF_close(f);
81 fclose(out);
82 return !rename(tmp, inj);
84 return 0;
87 int main(int argc, char**argv) {
88 if(argc != 4) usage(argv[0]);
89 char *o = argv[2], *inj = argv[3], *p;
90 if(!(p = strrchr(o, '.')) || strcmp(p, ".o")) {
91 dprintf(2, "error: object file has no .o extension\n");
92 return 1;
94 int which = atoi(argv[1]);
95 dprintf(1, "injecting %s into %s as %d'th script ...", o, inj, which);
96 int ret = inject(o, inj, which);
97 if(ret >= 0) dprintf(1, "OK\n");
98 else {
99 dprintf(1, "FAIL\n");
100 if(ret == -1) {
101 dprintf(2, "invalid index %d for roomfile, only 0 possible\n", which);
102 ret = 0;
103 } else perror("error:");
105 return !ret;