11 #define MKDIR(D) mkdir(D)
14 #define MKDIR(D) mkdir(D, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
18 #define ADS ":::AGStract " VERSION " by rofl0r:::"
20 static int usage(char *argv0
) {
21 fprintf(stderr
, ADS
"\nusage:\n%s [-oblf] dir [outdir]\n"
22 "extract all scripts from game files in dir\n"
23 "pass a directory with extracted game files.\n"
25 "-o : dump offset comments in disassembly\n"
26 "-b : dump hexadecimal bytecode comments in disassembly\n"
27 "-f : dump informative original fixups section\n"
28 "-l : remove linenumber debug assembly directives [produces smaller files]\n"
33 static void disas(const char*inp
, char *o
, int flags
) {
42 ASI
*i
= ASI_read_script(f
, &sc
) ? &sc
: 0;
43 fprintf(stdout
, "disassembling [%s] %s -> %s", inp
, o
, s
);
44 if(!i
|| !ASI_disassemble(f
, i
, s
, flags
)) fprintf(stdout
, " FAIL");
45 fprintf(stdout
, "\n");
50 static char *filename(const char *dir
, const char *fn
, char *buf
, size_t bsize
) {
51 snprintf(buf
, bsize
, "%s%c%s", dir
, PSEP
, fn
);
58 static int is_roomfile(const char *fn
) {
59 const char *p
= strrchr(fn
, '.');
62 #define LOWER(X) tolower((unsigned) (X))
63 return LOWER(p
[0]) == 'c' && LOWER(p
[1]) == 'r' && LOWER(p
[2]) == 'm' && p
[3] == 0;
67 static int dumprooms(const char* dir
, const char* out
, int flags
) {
68 DIR* d
= opendir(dir
);
71 struct dirent
* di
= 0;
73 while((di
= readdir(d
))) {
74 size_t l
= strlen(di
->d_name
);
75 if(l
> 4 && is_roomfile(di
->d_name
)) {
77 snprintf(fnbuf
, sizeof(fnbuf
), "%s%c%s", dir
, PSEP
, di
->d_name
);
78 AF f
; ssize_t off
; ASI s
;
79 if(!AF_open(&f
, fnbuf
)) goto extract_error
;
80 struct RoomFile rinfo
= {0};
81 if(!RoomFile_read(&f
, &rinfo
)) goto extract_error
;
82 if((off
= ARF_find_code_start(&f
, 0)) == -1) goto extract_error
;
83 assert(off
== rinfo
.blockpos
[BLOCKTYPE_COMPSCRIPT3
]);
85 if(!ASI_read_script(&f
, &s
)) {
86 fprintf(stderr
, "trouble finding script in %s\n", di
->d_name
);
90 assert(l
< sizeof(buf
));
91 memcpy(buf
, di
->d_name
, l
- 4);
96 AF_dump_chunk(&f
, s
.start
, s
.len
, filename(out
, buf
, outbuf
, sizeof outbuf
));
97 disas(di
->d_name
, outbuf
, flags
);
99 char *source
= RoomFile_extract_source(&f
, &rinfo
, &sourcelen
);
105 FILE *f
= fopen(filename(out
, buf
, outbuf
, sizeof outbuf
), "wb");
107 fprintf(stdout
, "extracting room source %s -> %s\n", di
->d_name
, outbuf
);
108 fwrite(source
, 1, sourcelen
, f
);
115 fprintf(stderr
, "warning: extraction of file %s failed\n", di
->d_name
);
123 void dump_script(AF
* f
, ASI
* s
, char* fn
, int flags
) {
125 AF_dump_chunk(f
, s
->start
, s
->len
, fn
);
126 disas("game28.dta", fn
, flags
);
129 static char *pfx_capitalize(char *in
, char prefix
, char *out
) {
130 char *p
= out
, *n
= in
;
132 *(p
++) = toupper(*(n
++));
133 while(*n
) *(p
++) = tolower(*(n
++));
138 void dump_header(ADF
*a
, char *fn
) {
140 fprintf(stdout
, "regenerating script header %s\n", fn
);
141 FILE *f
= fopen(fn
, "w");
142 fprintf(f
, "#if SCRIPT_API < 300000 && SCRIPT_API > 262000\n");
143 if(ADF_get_cursorcount(a
)) fprintf(f
, "enum CursorMode {\n");
144 for(i
=0; i
<ADF_get_cursorcount(a
); ++i
) {
145 if(i
> 0) fprintf(f
, ",\n");
146 char buf
[16] = {0}, *p
= ADF_get_cursorname(a
, i
), *q
= buf
;
148 if(!isspace(*p
)) *(q
++) = *p
;
151 fprintf(f
, " eMode%s = %u", buf
, i
);
153 if(i
) fprintf(f
, "};\n");
154 fprintf(f
, "import Character character[%zu];\n", ADF_get_charactercount(a
));
155 for(i
=0; i
<ADF_get_charactercount(a
); ++i
) {
156 char buf
[64], *p
= buf
, *n
= ADF_get_characterscriptname(a
, i
);
157 pfx_capitalize(ADF_get_characterscriptname(a
, i
), 'c', buf
);
158 fprintf(f
, "import Character %s;\n", buf
);
160 fprintf(f
, "import InventoryItem inventory[%zu];\n", ADF_get_inventorycount(a
));
161 if(a
->inventorynames
) for(i
=1; i
<ADF_get_inventorycount(a
); ++i
) {
162 fprintf(f
, "import InventoryItem %s;\n", ADF_get_inventoryname(a
, i
));
164 for(i
=0; i
<ADF_get_guicount(a
); ++i
) {
166 pfx_capitalize(ADF_get_guiname(a
, i
), 'g', buf
);
167 fprintf(f
, "import GUI %s;\n", buf
);
169 fprintf(f
, "#endif\n");
170 for(i
=0; i
<ADF_get_charactercount(a
); ++i
)
171 fprintf(f
, "#define %s %zu\n", ADF_get_characterscriptname(a
, i
), (size_t)i
);
172 for(i
=0; i
<ADF_get_guicount(a
); ++i
) {
173 char buf
[64], *p
= ADF_get_guiname(a
, i
), *q
= buf
;
174 while(*p
) *(q
++) = toupper(*(p
++));
176 fprintf(f
, "#define %s FindGUIID(\"%s\")\n", buf
, ADF_get_guiname(a
, i
));
178 for(i
=0; i
<ADF_get_viewcount(a
); ++i
) if(ADF_get_viewname(a
, i
)[0])
179 fprintf(f
, "#define %s %d\n", ADF_get_viewname(a
, i
), (int) i
+1);
184 static void dump_old_dialogscripts(ADF
*a
, char *dir
) {
185 if(!a
->old_dialogscripts
) return;
186 size_t i
, n
=a
->game
.dialogcount
;
189 snprintf(fnbuf
, sizeof(fnbuf
), "%s%cdialogscript%03d.ads", dir
, PSEP
, (int)i
);
190 fprintf(stdout
, "extracting dialogscript source %s\n", fnbuf
);
191 FILE *f
= fopen(fnbuf
, "w");
196 fprintf(f
, "%s", a
->old_dialogscripts
[i
]);
201 int main(int argc
, char**argv
) {
203 while ((c
= getopt(argc
, argv
, "oblf")) != EOF
) switch(c
) {
204 case 'o': flags
|= DISAS_DEBUG_OFFSETS
; break;
205 case 'b': flags
|= DISAS_DEBUG_BYTECODE
; break;
206 case 'l': flags
|= DISAS_SKIP_LINENO
; break;
207 case 'f': flags
|= DISAS_DEBUG_FIXUPS
; break;
208 default: return usage(argv
[0]);
210 if(!argv
[optind
]) return usage(argv
[0]);
211 char *dir
= argv
[optind
];
212 char *out
= argv
[optind
+1];
219 enum ADF_open_error aoe
;
220 if(!ADF_find_datafile(dir
, fnbuf
, sizeof(fnbuf
))) {
221 fprintf(stderr
, "failed to find datafile\n");
224 aoe
= ADF_open(a
, fnbuf
);
225 if(aoe
!= AOE_success
&& aoe
<= AOE_script
) {
226 fprintf(stderr
, "failed to open/process data file: %s\n", AOE2str(aoe
));
228 } else if (aoe
!= AOE_success
) {
229 fprintf(stderr
, "warning: failed to process some non-essential parts (%s) of gamefile, probably from a newer game format\n", AOE2str(aoe
));
232 s
= ADF_get_global_script(a
);
234 dump_script(a
->f
, s
, filename(out
, "globalscript.o", buf
, sizeof buf
), flags
);
235 s
= ADF_get_dialog_script(a
);
236 dump_script(a
->f
, s
, filename(out
, "dialogscript.o", buf
, sizeof buf
), flags
);
237 size_t i
, l
= ADF_get_scriptcount(a
);
238 for(i
= 0; i
< l
; i
++) {
240 s
= ADF_get_script(a
, i
);
241 snprintf(fnbuf
, sizeof(fnbuf
), "gamescript%zu.o", i
);
242 dump_script(a
->f
, s
, filename(out
, fnbuf
, buf
, sizeof buf
), flags
);
245 if(aoe
== AOE_success
) {
246 dump_header(a
, filename(out
, "builtinscriptheader.ash", buf
, sizeof buf
));
247 dump_old_dialogscripts(a
, out
);
249 fprintf(stderr
, "skipping scriptheader and dialogscripts due to non-fatal errors\n");
252 errors
+= dumprooms(dir
, out
, flags
);
253 if(errors
) fprintf(stderr
, "agscriptxtract: got %d errors\n", errors
);