agsinject: simplify code
[rofl0r-agsutils.git] / agsinject.c
blob706306046d621e78be4dc2d0e83be2187c5ca794
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 can 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 = 0;
27 size_t found = 0;
28 int isroom = (which == 0 && !strcmp(".crm", inj + strlen(inj) - 4));
29 if(AF_open(f, inj)) {
30 ssize_t start;
31 while(1) {
32 if((start = ARF_find_code_start(f, index)) == -1) {
33 dprintf(2, "error, only %zu scripts found\n", found);
34 return 0;
36 if(found == which) {
37 char *tmp = tempnam(".", "agsinject.tmp");
38 FILE *out = fopen(tmp, "w");
39 if(!out) return 0;
41 /* 1) dump header */
42 AF_dump_chunk_stream(f, 0, isroom ? start -4 : start, out);
43 AF_set_pos(f, start);
45 /* open replacement object file */
46 struct ByteArray b;
47 ByteArray_ctor(&b);
48 ByteArray_open_file(&b, o);
50 if(isroom) {
51 /* 2a) if room, write length */
52 /* room files, unlike game files, have a length field of size 4 before
53 * the compiled script starts. */
54 unsigned l = ByteArray_get_length(&b);
55 struct ByteArray c;
56 ByteArray_ctor(&c);
57 ByteArray_open_mem(&c, 0, 0);
58 ByteArray_set_flags(&c, BAF_CANGROW);
59 ByteArray_set_endian(&c, BAE_LITTLE);
60 ByteArray_writeInt(&c, l);
61 ByteArray_dump_to_stream(&c, out);
62 ByteArray_close(&c);
64 /* 2b) dump object file */
65 ByteArray_dump_to_stream(&b, out);
66 ByteArray_close_file(&b);
68 ASI s;
69 if(!ASI_read_script(f, &s)) {
70 dprintf(2, "trouble finding script in %s\n", inj);
71 return 0;
73 /* 3) dump rest of file */
74 AF_dump_chunk_stream(f, start + s.len, ByteArray_get_length(f->b) - (start + s.len), out);
75 AF_close(f);
76 fclose(out);
77 return !rename(tmp, inj);
80 found++;
81 index = start + 4;
84 } else {
85 return 0;
89 int main(int argc, char**argv) {
90 if(argc != 4) usage(argv[0]);
91 char *o = argv[2], *inj = argv[3], *p;
92 if(!(p = strrchr(o, '.')) || strcmp(p, ".o")) {
93 dprintf(2, "error: object file has no .o extension\n");
94 return 1;
96 int which = atoi(argv[1]);
97 dprintf(1, "injecting %s into %s as %d'th script ...", o, inj, which);
98 int ret = inject(o, inj, which);
99 if(ret) dprintf(1, "OK\n");
100 else {
101 dprintf(1, "FAIL\n");
102 perror("error:");
104 return !ret;