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