1 /* ______ ___ ___
2 * /\ _ \ /\_ \ /\_ \
3 * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
5 * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
6 * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7 * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8 * /\____/
9 * \_/__/
10 *
11 * Utility for appending data onto your program executable.
12 * This information can be read by passing the special "#" filename
13 * to the packfile functions.
14 *
15 * By Shawn Hargreaves.
16 *
17 * See readme.txt for copyright information.
18 */
19
20
21 #define ALLEGRO_USE_CONSOLE
22
23 #include <stdio.h>
24
25 #include "allegro.h"
26 #include "allegro/internal/aintern.h"
27
28 #ifdef ALLEGRO_UNIX
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #endif
32
33
34
usage(void)35 void usage(void)
36 {
37 printf("\nExecutable data appendation utility for Allegro " ALLEGRO_VERSION_STR ", " ALLEGRO_PLATFORM_STR);
38 printf("\nBy Shawn Hargreaves, " ALLEGRO_DATE_STR "\n\n");
39 #if (defined ALLEGRO_DOS) || (defined ALLEGRO_WINDOWS)
40 printf("Usage: exedat [options] program.exe [filename]\n\n");
41 #else
42 printf("Usage: exedat [options] program [filename]\n\n");
43 #endif
44 printf("Options:\n");
45 printf("\t'-a' use the Allegro packfile format\n");
46 printf("\t'-b' use raw binary format\n");
47 printf("\t'-c' compress the appended data\n");
48 printf("\t'-d' remove any currently appended data\n");
49 printf("\t'-x' extract appended data into the output filename\n");
50 printf("\t'-007 password' sets the datafile password\n");
51 }
52
53
54
55 static int err = 0;
56
57 char *opt_filename = NULL;
58 char *opt_dataname = NULL;
59 char *opt_password = NULL;
60
61 int opt_allegro = FALSE;
62 int opt_binary = FALSE;
63 int opt_compress = FALSE;
64 int opt_delete = FALSE;
65 int opt_extract = FALSE;
66
67 int stat_hasdata = FALSE;
68 int stat_exe_size = 0;
69 int stat_compressed_size = 0;
70 int stat_uncompressed_size = 0;
71
72 #ifdef ALLEGRO_UNIX
73 struct stat stat_stat;
74 #endif
75
76
77
get_stats(void)78 void get_stats(void)
79 {
80 PACKFILE *f;
81
82 #ifdef ALLEGRO_UNIX
83 if (stat(opt_filename, &stat_stat)) {
84 fprintf(stderr, "Error statting %s\n", opt_filename);
85 err = 1;
86 return;
87 }
88 #endif
89
90 f = pack_fopen(opt_filename, F_READ);
91 if (!f) {
92 fprintf(stderr, "Error opening %s\n", opt_filename);
93 err = 1;
94 return;
95 }
96
97 stat_exe_size = f->normal.todo;
98
99 pack_fseek(f, f->normal.todo-8);
100
101 if (pack_mgetl(f) != F_EXE_MAGIC) {
102 stat_hasdata = FALSE;
103 stat_compressed_size = 0;
104 stat_uncompressed_size = 0;
105 pack_fclose(f);
106 return;
107 }
108
109 stat_hasdata = TRUE;
110 stat_compressed_size = pack_mgetl(f) - 16;
111
112 pack_fclose(f);
113 f = pack_fopen(opt_filename, F_READ);
114 if (!f) {
115 fprintf(stderr, "Error reading %s\n", opt_filename);
116 err = 1;
117 return;
118 }
119
120 stat_exe_size = f->normal.todo - stat_compressed_size - 16;
121
122 pack_fseek(f, stat_exe_size);
123
124 f = pack_fopen_chunk(f, FALSE);
125 if (!f) {
126 fprintf(stderr, "Error reading %s\n", opt_filename);
127 err = 1;
128 return;
129 }
130
131 stat_uncompressed_size = f->normal.todo;
132
133 pack_fclose(f);
134 }
135
136
137
rename_file(char * oldname,char * newname)138 int rename_file(char *oldname, char *newname)
139 {
140 PACKFILE *oldfile, *newfile;
141 int c;
142
143 oldfile = pack_fopen(oldname, F_READ);
144 if (!oldfile)
145 return -1;
146
147 newfile = pack_fopen(newname, F_WRITE);
148 if (!newfile) {
149 pack_fclose(oldfile);
150 return -1;
151 }
152
153 c = pack_getc(oldfile);
154
155 while (c != EOF) {
156 pack_putc(c, newfile);
157 c = pack_getc(oldfile);
158 }
159
160 pack_fclose(oldfile);
161 pack_fclose(newfile);
162
163 delete_file(oldname);
164
165 return 0;
166 }
167
168
169
update_file(char * filename,char * dataname)170 void update_file(char *filename, char *dataname)
171 {
172 PACKFILE *f, *ef, *df = NULL;
173 char *tmpname;
174 int c;
175
176 #ifdef ALLEGRO_HAVE_MKSTEMP
177
178 char tmp_buf[] = "XXXXXX";
179 char tmp[512];
180 int tmp_fd;
181
182 tmp_fd = mkstemp(tmp_buf);
183 close(tmp_fd);
184 tmpname = uconvert_ascii(tmp_buf, tmp);
185
186 #else
187
188 tmpname = tmpnam(NULL);
189
190 #endif
191
192 get_stats();
193
194 if (!err) {
195 if ((!stat_hasdata) && (!dataname)) {
196 fprintf(stderr, "%s has no appended data: cannot remove it\n", filename);
197 err = 1;
198 return;
199 }
200
201 rename_file(filename, tmpname);
202
203 ef = pack_fopen(tmpname, F_READ);
204 if (!ef) {
205 delete_file(tmpname);
206 fprintf(stderr, "Error reading temporary file\n");
207 err = 1;
208 return;
209 }
210
211 f = pack_fopen(filename, F_WRITE);
212 if (!f) {
213 pack_fclose(ef);
214 rename_file(tmpname, filename);
215 fprintf(stderr, "Error writing to %s\n", filename);
216 err = 1;
217 return;
218 }
219
220 #ifdef ALLEGRO_UNIX
221 chmod(filename, stat_stat.st_mode);
222 #endif
223
224 for (c=0; c<stat_exe_size; c++)
225 pack_putc(pack_getc(ef), f);
226
227 pack_fclose(ef);
228 delete_file(tmpname);
229
230 if (dataname) {
231 if (stat_hasdata)
232 printf("replacing %d bytes of currently appended data\n", stat_uncompressed_size);
233
234 if (opt_allegro) {
235 printf("reading %s as Allegro format compressed data\n", dataname);
236 df = pack_fopen(dataname, F_READ_PACKED);
237 }
238 else if (opt_binary) {
239 printf("reading %s as raw binary data\n", dataname);
240 df = pack_fopen(dataname, F_READ);
241 }
242 else {
243 printf("autodetecting format of %s: ", dataname);
244 df = pack_fopen(dataname, F_READ_PACKED);
245 if (df) {
246 printf("Allegro compressed data\n");
247 }
248 else {
249 df = pack_fopen(dataname, F_READ);
250 if (df)
251 printf("raw binary data\n");
252 else
253 printf("\n");
254 }
255 }
256
257 if (!df) {
258 pack_fclose(f);
259 fprintf(stderr, "Error opening %s\n", dataname);
260 err = 1;
261 return;
262 }
263
264 f = pack_fopen_chunk(f, opt_compress);
265
266 while ((c = pack_getc(df)) != EOF)
267 pack_putc(c, f);
268
269 f = pack_fclose_chunk(f);
270
271 pack_mputl(F_EXE_MAGIC, f);
272 pack_mputl(_packfile_filesize+16, f);
273
274 printf("%s has been appended onto %s\n"
275 "original executable size: %d bytes (%dk)\n"
276 "appended data size: %d bytes (%dk)\n"
277 "compressed into: %d bytes (%dk)\n"
278 "ratio: %d%%\n",
279 dataname, filename,
280 stat_exe_size, stat_exe_size/1024,
281 _packfile_datasize, _packfile_datasize/1024,
282 _packfile_filesize, _packfile_filesize/1024,
283 (_packfile_datasize) ? _packfile_filesize*100/_packfile_datasize : 0);
284 }
285 else {
286 printf("%d bytes of appended data removed from %s\n", stat_uncompressed_size, filename);
287 }
288
289 pack_fclose(f);
290 pack_fclose(df);
291 }
292 }
293
294
295
extract_data(void)296 void extract_data(void)
297 {
298 PACKFILE *f, *df;
299 int c;
300
301 get_stats();
302
303 if (!err) {
304 if (!stat_hasdata) {
305 fprintf(stderr, "%s has no appended data: cannot extract it\n", opt_filename);
306 err = 1;
307 return;
308 }
309
310 f = pack_fopen(opt_filename, F_READ);
311 if (!f) {
312 fprintf(stderr, "Error reading %s\n", opt_filename);
313 err = 1;
314 return;
315 }
316
317 pack_fseek(f, stat_exe_size);
318 f = pack_fopen_chunk(f, FALSE);
319 if (!f) {
320 fprintf(stderr, "Error reading %s\n", opt_filename);
321 err = 1;
322 return;
323 }
324
325 if (opt_allegro)
326 df = pack_fopen(opt_dataname, (opt_compress ? F_WRITE_PACKED : F_WRITE_NOPACK));
327 else
328 df = pack_fopen(opt_dataname, F_WRITE);
329
330 if (!df) {
331 pack_fclose(f);
332 fprintf(stderr, "Error writing %s\n", opt_dataname);
333 err = 1;
334 return;
335 }
336
337 while ((c = pack_getc(f)) != EOF)
338 pack_putc(c, df);
339
340 printf("%d bytes extracted from %s into %s\n", stat_uncompressed_size, opt_filename, opt_dataname);
341
342 pack_fclose(f);
343 pack_fclose(df);
344 }
345 }
346
347
348
show_stats(void)349 void show_stats(void)
350 {
351 get_stats();
352
353 if (!err) {
354 if (stat_hasdata) {
355 printf("%s has appended data\n"
356 "original executable size: %d bytes (%dk)\n"
357 "appended data size: %d bytes (%dk)\n"
358 "compressed into: %d bytes (%dk)\n"
359 "ratio: %d%%\n",
360 opt_filename,
361 stat_exe_size, stat_exe_size/1024,
362 stat_uncompressed_size, stat_uncompressed_size/1024,
363 stat_compressed_size, stat_compressed_size/1024,
364 (stat_uncompressed_size) ? stat_compressed_size*100/stat_uncompressed_size : 0);
365 }
366 else {
367 printf("%s has no appended data\n"
368 "executable size: %d bytes (%dk)\n",
369 opt_filename, stat_exe_size, stat_exe_size/1024);
370 }
371 }
372 }
373
374
375
main(int argc,char * argv[])376 int main(int argc, char *argv[])
377 {
378 int c;
379
380 if (install_allegro(SYSTEM_NONE, &errno, atexit) != 0)
381 return 1;
382
383 for (c=1; c<argc; c++) {
384 if (argv[c][0] == '-') {
385 switch (utolower(argv[c][1])) {
386
387 case 'a':
388 opt_allegro = TRUE;
389 break;
390
391 case 'b':
392 opt_binary = TRUE;
393 break;
394
395 case 'c':
396 opt_compress = TRUE;
397 break;
398
399 case 'd':
400 opt_delete = TRUE;
401 break;
402
403 case 'x':
404 opt_extract = TRUE;
405 break;
406
407 case '0':
408 if ((opt_password) || (c >= argc-1)) {
409 usage();
410 return 1;
411 }
412 opt_password = argv[++c];
413 break;
414
415 default:
416 printf("Unknown option '%s'\n", argv[c]);
417 return 1;
418 }
419 }
420 else {
421 if (!opt_filename)
422 opt_filename = argv[c];
423 else if (!opt_dataname)
424 opt_dataname = argv[c];
425 else {
426 usage();
427 return 1;
428 }
429 }
430 }
431
432 if ((!opt_filename) ||
433 ((opt_allegro) && (opt_binary)) ||
434 ((opt_delete) && (opt_extract))) {
435 usage();
436 return 1;
437 }
438
439 if (opt_password)
440 packfile_password(opt_password);
441
442 if (opt_delete) {
443 if (opt_dataname) {
444 usage();
445 return 1;
446 }
447 update_file(opt_filename, NULL);
448 }
449 else if (opt_extract) {
450 if (!opt_dataname) {
451 usage();
452 return 1;
453 }
454 if ((!opt_allegro) && (!opt_binary)) {
455 printf("The -x option must be used together with -a or -b\n");
456 return 1;
457 }
458 extract_data();
459 }
460 else {
461 if (opt_dataname)
462 update_file(opt_filename, opt_dataname);
463 else
464 show_stats();
465 }
466
467 return err;
468 }
469
470 END_OF_MAIN()
471