11 #define ADS ":::AGSinject " VERSION " by rofl0r:::"
13 int usage(char *argv0
) {
18 "%s index input.o inject_to.crm\n"
19 "index is the number of script to replace, i.e. 0 for first script\n"
20 "only relevant if the output file is a gamefile which contains multiple scripts\n"
21 "for example gamescript is 0, dialogscript is 1 (if existing), etc\n"
22 "a room file (.crm) only has one script so you must pass 0.\n\n"
26 "%s -e [OPTIONS] target index1:input1.o [index2:input2.o...indexN:inputN.o]\n"
27 "in extended mode, indicated by -e switch, target denotes destination file\n"
28 "(e.g. game28.dta, *.crm...), and file(s) to inject are passed as\n"
29 "index:filename tuples.\n"
30 "this allows to inject several compiled scripts at once.\n"
32 "-t : only inject obj files whose timestamps are newer than the one of target.\n"
33 "example: %s -e game28.dta 0:globalscript.o 1:dialogscript.o\n"
34 , argv0
, argv0
, argv0
);
38 /* inj = filename of file to inject in */
39 static int inject(const char *o
, const char *inj
, unsigned which
) {
43 int isroom
= !strcmp(".crm", inj
+ strlen(inj
) - 4);
44 if(isroom
&& which
!= 0) return -1;
45 if(!AF_open(f
, inj
)) return 0;
47 for(index
= found
= 0; 1 ; found
++, index
= start
+ 4) {
48 if(!isroom
&& (start
= ARF_find_code_start(f
, index
)) == -1) {
49 dprintf(2, "error, only %zu scripts found\n", found
);
52 /* use roomfile specific script lookup, as it's faster */
53 struct RoomFile rinfo
= {0};
54 if(!RoomFile_read(f
, &rinfo
)) return 0;
55 start
= rinfo
.blockpos
[BLOCKTYPE_COMPSCRIPT3
];
57 if(found
!= which
) continue;
58 char *tmp
= tempnam(".", "agsinject.tmp");
59 FILE *out
= fopen(tmp
, "w");
63 AF_dump_chunk_stream(f
, 0, isroom
? start
-4 : start
, out
);
66 /* open replacement object file */
69 ByteArray_open_file(&b
, o
);
72 /* 2a) if room, write length */
73 /* room files, unlike game files, have a length field of size 4 before
74 * the compiled script starts. */
75 unsigned l
= ByteArray_get_length(&b
);
78 ByteArray_open_mem(&c
, 0, 0);
79 ByteArray_set_flags(&c
, BAF_CANGROW
);
80 ByteArray_set_endian(&c
, BAE_LITTLE
);
81 ByteArray_writeInt(&c
, l
);
82 ByteArray_dump_to_stream(&c
, out
);
85 /* 2b) dump object file */
86 ByteArray_dump_to_stream(&b
, out
);
87 ByteArray_close_file(&b
);
90 if(!ASI_read_script(f
, &s
)) {
91 dprintf(2, "trouble finding script in %s\n", inj
);
94 /* 3) dump rest of file */
95 AF_dump_chunk_stream(f
, start
+ s
.len
, ByteArray_get_length(f
->b
) - (start
+ s
.len
), out
);
98 return !rename(tmp
, inj
);
103 static int check_objname(const char* o
) {
105 if(!(p
= strrchr(o
, '.')) || strcmp(p
, ".o")) {
106 dprintf(2, "error: object file has no .o extension\n");
112 static int injectpr(const char *obj
, const char *out
, unsigned which
) {
113 printf("injecting %s into %s as %d'th script ...", obj
, out
, which
);
114 int ret
= inject(obj
, out
, which
);
115 if(ret
>= 0) printf("OK\n");
119 dprintf(2, "invalid index %d for roomfile, only 0 possible\n", which
);
121 } else perror("error:");
126 static int getstamp(const char* fn
, struct timespec
*stamp
) {
128 if(stat(fn
, &st
) == -1) {
136 static int ts_is_newer(const struct timespec
*t1
, const struct timespec
*t2
)
138 return t2
->tv_sec
> t1
->tv_sec
||
139 (t2
->tv_sec
== t1
->tv_sec
&& t2
->tv_nsec
> t1
->tv_nsec
);
142 int main(int argc
, char**argv
) {
145 if(argc
== 4 && isdigit(*argv
[1])) {
148 which
= atoi(argv
[1]);
149 if(!check_objname(obj
)) return 1;
150 if(!injectpr(obj
, out
, which
)) return 1;
153 int c
, extended
= 0, usestamps
= 0;
154 while ((c
= getopt(argc
, argv
, "et")) != EOF
) switch(c
) {
155 case 'e': extended
= 1; break;
156 case 't': usestamps
= 1; break;
157 default: return usage(argv
[0]);
159 if(!extended
|| !argv
[optind
] || !argv
[optind
+1])
160 return usage(argv
[0]);
163 struct timespec stamp
;
165 if(usestamps
&& !getstamp(out
, &stamp
)) return 1;
167 while(argv
[++optind
]) {
169 char *p
= strchr(obj
, ':');
170 if(!isdigit(*obj
) || !p
) return usage(argv
[0]);
174 if(!check_objname(obj
)) return 1;
176 struct timespec ostamp
;
177 if(!getstamp(obj
, &ostamp
)) return 1;
178 if(!ts_is_newer(&stamp
, &ostamp
)) continue;
180 if(!injectpr(obj
, out
, which
)) return 1;