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
);
57 static int dumprooms(const char* dir
, const char* out
, int flags
) {
58 DIR* d
= opendir(dir
);
61 struct dirent
* di
= 0;
63 while((di
= readdir(d
))) {
64 size_t l
= strlen(di
->d_name
);
65 if(l
> 4 + 4 && !memcmp(di
->d_name
, "room", 4) && !memcmp(di
->d_name
+ l
- 4, ".crm", 4)) {
67 snprintf(fnbuf
, sizeof(fnbuf
), "%s%c%s", dir
, PSEP
, di
->d_name
);
68 AF f
; ssize_t off
; ASI s
;
69 if(!AF_open(&f
, fnbuf
)) goto extract_error
;
70 struct RoomFile rinfo
= {0};
71 if(!RoomFile_read(&f
, &rinfo
)) goto extract_error
;
72 if((off
= ARF_find_code_start(&f
, 0)) == -1) goto extract_error
;
73 assert(off
== rinfo
.blockpos
[BLOCKTYPE_COMPSCRIPT3
]);
75 if(!ASI_read_script(&f
, &s
)) {
76 fprintf(stderr
, "trouble finding script in %s\n", di
->d_name
);
80 assert(l
< sizeof(buf
));
81 memcpy(buf
, di
->d_name
, l
- 4);
86 AF_dump_chunk(&f
, s
.start
, s
.len
, filename(out
, buf
, outbuf
, sizeof outbuf
));
87 disas(di
->d_name
, outbuf
, flags
);
89 char *source
= RoomFile_extract_source(&f
, &rinfo
, &sourcelen
);
95 FILE *f
= fopen(filename(out
, buf
, outbuf
, sizeof outbuf
), "wb");
97 fprintf(stdout
, "extracting room source %s -> %s\n", di
->d_name
, outbuf
);
98 fwrite(source
, 1, sourcelen
, f
);
105 fprintf(stderr
, "warning: extraction of file %s failed\n", di
->d_name
);
113 void dump_script(AF
* f
, ASI
* s
, char* fn
, int flags
) {
115 AF_dump_chunk(f
, s
->start
, s
->len
, fn
);
116 disas("game28.dta", fn
, flags
);
119 static char *pfx_capitalize(char *in
, char prefix
, char *out
) {
120 char *p
= out
, *n
= in
;
122 *(p
++) = toupper(*(n
++));
123 while(*n
) *(p
++) = tolower(*(n
++));
128 void dump_header(ADF
*a
, char *fn
) {
130 fprintf(stdout
, "regenerating script header %s\n", fn
);
131 FILE *f
= fopen(fn
, "w");
132 fprintf(f
, "#if SCRIPT_API < 300000 && SCRIPT_API > 262000\n");
133 if(ADF_get_cursorcount(a
)) fprintf(f
, "enum CursorMode {\n");
134 for(i
=0; i
<ADF_get_cursorcount(a
); ++i
) {
135 if(i
> 0) fprintf(f
, ",\n");
136 char buf
[16] = {0}, *p
= ADF_get_cursorname(a
, i
), *q
= buf
;
138 if(!isspace(*p
)) *(q
++) = *p
;
141 fprintf(f
, " eMode%s = %u", buf
, i
);
143 if(i
) fprintf(f
, "};\n");
144 fprintf(f
, "import Character character[%zu];\n", ADF_get_charactercount(a
));
145 for(i
=0; i
<ADF_get_charactercount(a
); ++i
) {
146 char buf
[64], *p
= buf
, *n
= ADF_get_characterscriptname(a
, i
);
147 pfx_capitalize(ADF_get_characterscriptname(a
, i
), 'c', buf
);
148 fprintf(f
, "import Character %s;\n", buf
);
150 fprintf(f
, "import InventoryItem inventory[%zu];\n", ADF_get_inventorycount(a
));
151 if(a
->inventorynames
) for(i
=1; i
<ADF_get_inventorycount(a
); ++i
) {
152 fprintf(f
, "import InventoryItem %s;\n", ADF_get_inventoryname(a
, i
));
154 for(i
=0; i
<ADF_get_guicount(a
); ++i
) {
156 pfx_capitalize(ADF_get_guiname(a
, i
), 'g', buf
);
157 fprintf(f
, "import GUI %s;\n", buf
);
159 fprintf(f
, "#endif\n");
160 for(i
=0; i
<ADF_get_charactercount(a
); ++i
)
161 fprintf(f
, "#define %s %zu\n", ADF_get_characterscriptname(a
, i
), (size_t)i
);
162 for(i
=0; i
<ADF_get_guicount(a
); ++i
) {
163 char buf
[64], *p
= ADF_get_guiname(a
, i
), *q
= buf
;
164 while(*p
) *(q
++) = toupper(*(p
++));
166 fprintf(f
, "#define %s FindGUIID(\"%s\")\n", buf
, ADF_get_guiname(a
, i
));
168 for(i
=0; i
<ADF_get_viewcount(a
); ++i
) if(ADF_get_viewname(a
, i
)[0])
169 fprintf(f
, "#define %s %d\n", ADF_get_viewname(a
, i
), (int) i
+1);
174 static void dump_old_dialogscripts(ADF
*a
, char *dir
) {
175 if(!a
->old_dialogscripts
) return;
176 size_t i
, n
=a
->game
.dialogcount
;
179 snprintf(fnbuf
, sizeof(fnbuf
), "%s%cdialogscript%03d.ads", dir
, PSEP
, (int)i
);
180 fprintf(stdout
, "extracting dialogscript source %s\n", fnbuf
);
181 FILE *f
= fopen(fnbuf
, "w");
186 fprintf(f
, "%s", a
->old_dialogscripts
[i
]);
191 int main(int argc
, char**argv
) {
193 while ((c
= getopt(argc
, argv
, "oblf")) != EOF
) switch(c
) {
194 case 'o': flags
|= DISAS_DEBUG_OFFSETS
; break;
195 case 'b': flags
|= DISAS_DEBUG_BYTECODE
; break;
196 case 'l': flags
|= DISAS_SKIP_LINENO
; break;
197 case 'f': flags
|= DISAS_DEBUG_FIXUPS
; break;
198 default: return usage(argv
[0]);
200 if(!argv
[optind
]) return usage(argv
[0]);
201 char *dir
= argv
[optind
];
202 char *out
= argv
[optind
+1];
209 enum ADF_open_error aoe
;
210 if(!ADF_find_datafile(dir
, fnbuf
, sizeof(fnbuf
))) {
211 fprintf(stderr
, "failed to find datafile\n");
214 aoe
= ADF_open(a
, fnbuf
);
215 if(aoe
!= AOE_success
&& aoe
<= AOE_script
) {
216 fprintf(stderr
, "failed to open/process data file: %s\n", AOE2str(aoe
));
218 } else if (aoe
!= AOE_success
) {
219 fprintf(stderr
, "warning: failed to process some non-essential parts (%s) of gamefile, probably from a newer game format\n", AOE2str(aoe
));
222 s
= ADF_get_global_script(a
);
224 dump_script(a
->f
, s
, filename(out
, "globalscript.o", buf
, sizeof buf
), flags
);
225 s
= ADF_get_dialog_script(a
);
226 dump_script(a
->f
, s
, filename(out
, "dialogscript.o", buf
, sizeof buf
), flags
);
227 size_t i
, l
= ADF_get_scriptcount(a
);
228 for(i
= 0; i
< l
; i
++) {
230 s
= ADF_get_script(a
, i
);
231 snprintf(fnbuf
, sizeof(fnbuf
), "gamescript%zu.o", i
);
232 dump_script(a
->f
, s
, filename(out
, fnbuf
, buf
, sizeof buf
), flags
);
235 if(aoe
== AOE_success
) {
236 dump_header(a
, filename(out
, "builtinscriptheader.ash", buf
, sizeof buf
));
237 dump_old_dialogscripts(a
, out
);
239 fprintf(stderr
, "skipping scriptheader and dialogscripts due to non-fatal errors\n");
242 errors
+= dumprooms(dir
, out
, flags
);
243 if(errors
) fprintf(stderr
, "agscriptxtract: got %d errors\n", errors
);