1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      Datafile editing functions, for use by the datafile tools.
12  *
13  *      By Shawn Hargreaves.
14  *
15  *      See readme.txt for copyright information.
16  */
17 
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <time.h>
23 
24 #include "allegro.h"
25 #include "allegro/internal/aintern.h"
26 #include "datedit.h"
27 
28 
29 PALETTE datedit_current_palette;
30 PALETTE datedit_last_read_pal;
31 
32 static char info_msg[] = "For internal use by the grabber";
33 
34 DATAFILE datedit_info = { info_msg, DAT_INFO, sizeof(info_msg), NULL };
35 
36 static int file_datasize;
37 
38 static DATAFILE_PROPERTY *builtin_prop = NULL;
39 
40 void (*grabber_sel_palette)(PALETTE pal) = NULL;
41 void (*grabber_select_property)(int type) = NULL;
42 void (*grabber_get_grid_size)(int *x, int *y) = NULL;
43 void (*grabber_rebuild_list)(void *old, int clear) = NULL;
44 void (*grabber_get_selection_info)(DATAFILE **dat, DATAFILE ***parent) = NULL;
45 int (*grabber_foreach_selection)(int (*proc)(DATAFILE *, int *, int), int *count, int *param, int param2) = NULL;
46 DATAFILE *(*grabber_single_selection)(void) = NULL;
47 void (*grabber_set_selection)(void *object) = NULL;
48 void (*grabber_busy_mouse)(int busy) = NULL;
49 void (*grabber_modified)(int modified) = NULL;
50 
51 char grabber_data_file[FILENAME_LENGTH] = "";
52 BITMAP *grabber_graphic = NULL;
53 PALETTE grabber_palette;
54 
55 char grabber_import_file[256] = "";
56 char grabber_graphic_origin[GRABBER_GRAPHIC_ORIGIN_SIZE] = "";
57 char grabber_graphic_date[256] = "";
58 
59 
60 
61 /* comparison function for sorting the menu list */
menu_cmp(MENU * m1,MENU * m2)62 static int menu_cmp(MENU *m1, MENU *m2)
63 {
64    if ((m1->child) && (!m2->child))
65       return 1;
66    else if ((m2->child) && (!m1->child))
67       return -1;
68    else
69       return stricmp(m1->text, m2->text);
70 }
71 
72 
73 
74 /* helper function for inserting a list of builtin properties */
insert_builtin_prop(AL_CONST char * prop_types)75 static void insert_builtin_prop(AL_CONST char *prop_types)
76 {
77    int type;
78 
79    while (TRUE) {
80       ASSERT(strlen(prop_types)>=4 && (!prop_types[4] || prop_types[4]==';'));
81       type = DAT_ID(prop_types[0], prop_types[1], prop_types[2], prop_types[3]);
82       datedit_insert_property(&builtin_prop, type, "*");
83       if (!prop_types[4])
84          break;
85       prop_types += 5;
86    }
87 }
88 
89 
90 
91 /* main cleanup routine */
datedit_exit(void)92 static void datedit_exit(void)
93 {
94    DATAFILE_PROPERTY *iter;
95    if (builtin_prop) {
96       for (iter=builtin_prop; iter->type != DAT_END; iter++)
97          _AL_FREE(iter->dat);
98       _AL_FREE(builtin_prop);
99    }
100 }
101 
102 
103 
104 /* main initialisation routine */
datedit_init(void)105 void datedit_init(void)
106 {
107    int done, i;
108    AL_CONST char *prop_types;
109 
110    #include "plugins.h"
111 
112    do {
113       done = TRUE;
114 
115       for (i=0; datedit_object_info[i+1]->type != DAT_END; i++) {
116 	 if (stricmp(datedit_object_info[i]->desc, datedit_object_info[i+1]->desc) > 0) {
117 	    DATEDIT_OBJECT_INFO *tmp = datedit_object_info[i];
118 	    datedit_object_info[i] = datedit_object_info[i+1];
119 	    datedit_object_info[i+1] = tmp;
120 	    done = FALSE;
121 	 }
122       }
123    } while (!done);
124 
125    do {
126       done = TRUE;
127 
128       for (i=0; (datedit_menu_info[i]) && (datedit_menu_info[i+1]); i++) {
129 	 if (menu_cmp(datedit_menu_info[i]->menu, datedit_menu_info[i+1]->menu) > 0) {
130 	    DATEDIT_MENU_INFO *tmp = datedit_menu_info[i];
131 	    datedit_menu_info[i] = datedit_menu_info[i+1];
132 	    datedit_menu_info[i+1] = tmp;
133 	    done = FALSE;
134 	 }
135       }
136    } while (!done);
137 
138    /* Build the list of builtin properties. */
139    datedit_insert_property(&builtin_prop, DAT_ORIG, "*");
140    datedit_insert_property(&builtin_prop, DAT_DATE, "*");
141 
142    for (i=0; datedit_grabber_info[i]->type != DAT_END; i++) {
143       prop_types = datedit_grabber_info[i]->prop_types;
144       if (prop_types)
145 	 insert_builtin_prop(prop_types);
146    }
147 
148    for (i=0; (datedit_menu_info[i]) && (datedit_menu_info[i+1]); i++) {
149       prop_types = datedit_menu_info[i]->prop_types;
150       if (prop_types)
151 	 insert_builtin_prop(prop_types);
152    }
153 
154    atexit(datedit_exit);
155 }
156 
157 
158 
159 /* export raw binary data */
export_binary(AL_CONST DATAFILE * dat,AL_CONST char * filename)160 static int export_binary(AL_CONST DATAFILE *dat, AL_CONST char *filename)
161 {
162    PACKFILE *f = pack_fopen(filename, F_WRITE);
163    int ret = TRUE;
164 
165    if (f) {
166       if (pack_fwrite(dat->dat, dat->size, f) < dat->size)
167 	 ret = FALSE;
168 
169       pack_fclose(f);
170    }
171    else
172       ret = FALSE;
173 
174    return ret;
175 }
176 
177 
178 
179 /* grab raw binary data */
grab_binary(int type,AL_CONST char * filename,DATAFILE_PROPERTY ** prop,int depth)180 static DATAFILE *grab_binary(int type, AL_CONST char *filename, DATAFILE_PROPERTY **prop, int depth)
181 {
182    void *mem;
183    int64_t sz = file_size_ex(filename);
184    PACKFILE *f;
185 
186    if (sz <= 0)
187       return NULL;
188 
189    mem = malloc(sz);
190 
191    f = pack_fopen(filename, F_READ);
192    if (!f) {
193       free(mem);
194       return NULL;
195    }
196 
197    if (pack_fread(mem, sz, f) < sz) {
198       pack_fclose(f);
199       free(mem);
200       return NULL;
201    }
202 
203    pack_fclose(f);
204 
205    return datedit_construct(type, mem, sz, prop);
206 }
207 
208 
209 
210 /* save raw binary data */
save_binary(DATAFILE * dat,AL_CONST int * fixed_prop,int pack,int pack_kids,int strip,int sort,int verbose,int extra,PACKFILE * f)211 static int save_binary(DATAFILE *dat, AL_CONST int *fixed_prop, int pack, int pack_kids, int strip, int sort, int verbose, int extra, PACKFILE *f)
212 {
213    if (pack_fwrite(dat->dat, dat->size, f) < dat->size)
214       return FALSE;
215 
216    return TRUE;
217 }
218 
219 
220 
221 /* export a child datafile */
export_datafile(AL_CONST DATAFILE * dat,AL_CONST char * filename)222 static int export_datafile(AL_CONST DATAFILE *dat, AL_CONST char *filename)
223 {
224    DATEDIT_SAVE_DATAFILE_OPTIONS options = {-1,    /* pack      */
225 					    -1,    /* strip     */
226 					    -1,    /* sort      */
227 					    FALSE, /* verbose   */
228 					    FALSE, /* write_msg */
229 					    FALSE, /* backup    */
230 					    FALSE  /* rel. path */ };
231 
232    return datedit_save_datafile((DATAFILE *)dat->dat, filename, NULL, &options, NULL);
233 }
234 
235 
236 
237 /* loads object names from a header file, if they are missing */
load_header(DATAFILE * dat,AL_CONST char * filename)238 static void load_header(DATAFILE *dat, AL_CONST char *filename)
239 {
240    char buf[160], buf2[160];
241    int datsize, i, c, c2;
242    PACKFILE *f;
243 
244    datsize = 0;
245    while (dat[datsize].type != DAT_END)
246       datsize++;
247 
248    f = pack_fopen(filename, F_READ);
249 
250    if (f) {
251       while (pack_fgets(buf, 160, f) != 0) {
252 	 if (strncmp(buf, "#define ", 8) == 0) {
253 	    c2 = 0;
254 	    c = 8;
255 
256 	    while ((buf[c]) && (buf[c] != ' '))
257 	       buf2[c2++] = buf[c++];
258 
259 	    buf2[c2] = 0;
260 	    while (buf[c]==' ')
261 	       c++;
262 
263 	    i = 0;
264 	    while ((buf[c] >= '0') && (buf[c] <= '9')) {
265 	       i *= 10;
266 	       i += buf[c] - '0';
267 	       c++;
268 	    }
269 
270 	    if ((i < datsize) && (!*get_datafile_property(dat+i, DAT_NAME)))
271 	       datedit_set_property(dat+i, DAT_NAME, buf2);
272 	 }
273       }
274 
275       pack_fclose(f);
276    }
277    else {
278       /* don't let the error propagate */
279       errno = 0;
280    }
281 }
282 
283 
284 
285 /* generates object names, if they are missing */
generate_names(DATAFILE * dat,int n)286 static int generate_names(DATAFILE *dat, int n)
287 {
288    int i;
289 
290    while (dat->type != DAT_END) {
291       if (!*get_datafile_property(dat, DAT_NAME)) {
292 	 char tmp[32];
293 
294 	 sprintf(tmp, "%03d_%c%c%c%c", n++,
295 			(dat->type>>24)&0xFF, (dat->type>>16)&0xFF,
296 			(dat->type>>8)&0xFF, dat->type&0xFF);
297 
298 	 for (i=4; tmp[i]; i++) {
299 	    if (((tmp[i] < '0') || (tmp[i] > '9')) &&
300 		((tmp[i] < 'A') || (tmp[i] > 'Z')) &&
301 		((tmp[i] < 'a') || (tmp[i] > 'z')))
302 	       tmp[i] = 0;
303 	 }
304 
305 	 datedit_set_property(dat, DAT_NAME, tmp);
306       }
307 
308       if (dat->type == DAT_FILE)
309 	 n = generate_names((DATAFILE *)dat->dat, n);
310 
311       dat++;
312    }
313 
314    return n;
315 }
316 
317 
318 
319 /* retrieves whatever grabber information is stored in a datafile */
extract_info(DATAFILE * dat,int save)320 static DATAFILE *extract_info(DATAFILE *dat, int save)
321 {
322    DATAFILE_PROPERTY *prop;
323    int i;
324 
325    if (save) {
326       if (datedit_info.prop) {
327 	 prop = datedit_info.prop;
328 	 while (prop->type != DAT_END) {
329 	    if (prop->dat)
330 	       _AL_FREE(prop->dat);
331 	    prop++;
332 	 }
333 	 _AL_FREE(datedit_info.prop);
334 	 datedit_info.prop = NULL;
335       }
336    }
337 
338    for (i=0; dat[i].type != DAT_END; i++) {
339       if (dat[i].type == DAT_INFO) {
340 	 if (save) {
341 	    prop = dat[i].prop;
342 	    while ((prop) && (prop->type != DAT_END)) {
343 	       datedit_set_property(&datedit_info, prop->type, prop->dat);
344 	       prop++;
345 	    }
346 	 }
347 
348 	 dat = datedit_delete(dat, i);
349 	 i--;
350       }
351    }
352 
353    return dat;
354 }
355 
356 
357 
358 /* grabs a child datafile */
grab_datafile(int type,AL_CONST char * filename,DATAFILE_PROPERTY ** prop,int depth)359 static DATAFILE *grab_datafile(int type, AL_CONST char *filename, DATAFILE_PROPERTY **prop, int depth)
360 {
361    DATAFILE *dat = load_datafile(filename);
362 
363    if (dat) {
364       load_header(dat, datedit_pretty_name(filename, "h", TRUE));
365       generate_names(dat, 0);
366       dat = extract_info(dat, FALSE);
367       return datedit_construct(type, dat, 0, prop);
368    }
369 
370    return NULL;
371 }
372 
373 
374 
375 /* queries whether this property belongs in the current strip mode */
should_save_prop(int type,AL_CONST int * fixed_prop,int strip)376 static int should_save_prop(int type, AL_CONST int *fixed_prop, int strip)
377 {
378    if (strip == 0)
379       return TRUE;
380 
381    if (fixed_prop) {
382       while (*fixed_prop) {
383 	 if (type == *fixed_prop++)
384 	    return TRUE;
385       }
386    }
387 
388    if (strip >= 2)
389       return FALSE;
390    else
391       return (datedit_get_property(&builtin_prop, type) == empty_string);  /* not builtin */
392 }
393 
394 
395 
396 /* wrapper to avoid division by zero when files are empty */
percent(int a,int b)397 static int percent(int a, int b)
398 {
399    if (a)
400       return (b * 100) / a;
401    else
402       return 0;
403 }
404 
405 
406 
407 /* saves an object */
save_object(DATAFILE * dat,AL_CONST int * fixed_prop,int pack,int pack_kids,int strip,int sort,int verbose,PACKFILE * AL_CONST f)408 static int save_object(DATAFILE *dat, AL_CONST int *fixed_prop, int pack, int pack_kids,
409                        int strip, int sort, int verbose, PACKFILE * AL_CONST f)
410 {
411    int i, ret;
412    DATAFILE_PROPERTY *prop;
413    int (*save)(DATAFILE *, AL_CONST int *, int, int, int, int, int, int, PACKFILE *);
414    PACKFILE *fchunk;
415 
416    ASSERT(f);
417 
418    prop = dat->prop;
419    datedit_sort_properties(prop);
420 
421    while ((prop) && (prop->type != DAT_END)) {
422       if (should_save_prop(prop->type, fixed_prop, strip)) {
423 	 pack_mputl(DAT_PROPERTY, f);
424 	 pack_mputl(prop->type, f);
425 	 pack_mputl(strlen(prop->dat), f);
426 	 if (pack_fwrite(prop->dat, strlen(prop->dat), f) < (signed)strlen(prop->dat))
427 	    return FALSE;
428 	 file_datasize += 12 + strlen(prop->dat);
429       }
430 
431       prop++;
432    }
433 
434    if (verbose)
435       datedit_startmsg("%-28s", get_datafile_property(dat, DAT_NAME));
436 
437    pack_mputl(dat->type, f);
438    fchunk = pack_fopen_chunk(f, ((!pack) && (pack_kids) && (dat->type != DAT_FILE)));
439    if (!fchunk) {
440       return FALSE;
441    }
442    file_datasize += 12;
443 
444    save = NULL;
445 
446    for (i=0; datedit_object_info[i]->type != DAT_END; i++) {
447       if (datedit_object_info[i]->type == dat->type) {
448 	 save = datedit_object_info[i]->save;
449 	 break;
450       }
451    }
452 
453    if (!save)
454       save = save_binary;
455 
456    if (dat->type == DAT_FILE) {
457       if (verbose)
458 	 datedit_endmsg("");
459 
460       ret = save((DATAFILE *)dat->dat, fixed_prop, pack, pack_kids, strip, sort, verbose, FALSE, fchunk);
461 
462       if (verbose)
463 	 datedit_startmsg("End of %-21s", get_datafile_property(dat, DAT_NAME));
464    }
465    else
466       ret = save(dat, fixed_prop, (pack || pack_kids), FALSE, strip, sort, verbose, FALSE, fchunk);
467 
468    pack_fclose_chunk(fchunk);
469    fchunk = NULL;
470 
471    if (verbose) {
472       if ((!pack) && (pack_kids) && (dat->type != DAT_FILE)) {
473 	 datedit_endmsg("%7d bytes into %-7d (%d%%)",
474 			_packfile_datasize, _packfile_filesize,
475 			percent(_packfile_datasize, _packfile_filesize));
476       }
477       else
478 	 datedit_endmsg("");
479    }
480 
481    if (dat->type == DAT_FILE)
482       file_datasize += 4;
483    else
484       file_datasize += _packfile_datasize;
485 
486    return ret;
487 }
488 
489 
490 
491 /* saves a datafile */
save_datafile(DATAFILE * dat,AL_CONST int * fixed_prop,int pack,int pack_kids,int strip,int sort,int verbose,int extra,PACKFILE * f)492 static int save_datafile(DATAFILE *dat, AL_CONST int *fixed_prop, int pack, int pack_kids, int strip, int sort, int verbose, int extra, PACKFILE *f)
493 {
494    int c, size;
495 
496    ASSERT(f);
497 
498    if (sort)
499       datedit_sort_datafile(dat);
500 
501    size = 0;
502    while (dat[size].type != DAT_END)
503       size++;
504 
505    pack_mputl(extra ? size+1 : size, f);
506 
507    for (c=0; c<size; c++) {
508       if (!save_object(dat+c, fixed_prop, pack, pack_kids, strip, sort, verbose, f))
509 	 return FALSE;
510    }
511 
512    return TRUE;
513 }
514 
515 
516 
517 /* creates a new datafile */
makenew_file(long * size)518 static void *makenew_file(long *size)
519 {
520    DATAFILE *dat = _AL_MALLOC(sizeof(DATAFILE));
521 
522    dat->dat = NULL;
523    dat->type = DAT_END;
524    dat->size = 0;
525    dat->prop = NULL;
526 
527    return dat;
528 }
529 
530 
531 
532 /* header block for datafile objects */
533 DATEDIT_OBJECT_INFO datfile_info =
534 {
535    DAT_FILE,
536    "Datafile",
537    NULL,
538    makenew_file,
539    save_datafile,
540    NULL,
541    NULL,
542    NULL
543 };
544 
545 
546 
547 DATEDIT_GRABBER_INFO datfile_grabber =
548 {
549    DAT_FILE,
550    "dat",
551    "dat",
552    grab_datafile,
553    export_datafile,
554    NULL
555 };
556 
557 
558 
559 /* dummy header block to use as a terminator */
560 DATEDIT_OBJECT_INFO datend_info =
561 {
562    DAT_END,
563    NULL,
564    NULL,
565    NULL,
566    NULL,
567    NULL,
568    NULL,
569    NULL
570 };
571 
572 
573 
574 DATEDIT_GRABBER_INFO datend_grabber =
575 {
576    DAT_END,
577    NULL,
578    NULL,
579    NULL,
580    NULL,
581    NULL
582 };
583 
584 
585 
586 /* list of available object types */
587 DATEDIT_OBJECT_INFO *datedit_object_info[32] =
588 {
589    &datfile_info, &datend_info,  &datend_info,  &datend_info,
590    &datend_info,  &datend_info,  &datend_info,  &datend_info,
591    &datend_info,  &datend_info,  &datend_info,  &datend_info,
592    &datend_info,  &datend_info,  &datend_info,  &datend_info,
593    &datend_info,  &datend_info,  &datend_info,  &datend_info,
594    &datend_info,  &datend_info,  &datend_info,  &datend_info,
595    &datend_info,  &datend_info,  &datend_info,  &datend_info,
596    &datend_info,  &datend_info,  &datend_info,  &datend_info
597 };
598 
599 
600 
601 /* list of available object grabber routines */
602 DATEDIT_GRABBER_INFO *datedit_grabber_info[32] =
603 {
604    &datfile_grabber, &datend_grabber,  &datend_grabber,  &datend_grabber,
605    &datend_grabber,  &datend_grabber,  &datend_grabber,  &datend_grabber,
606    &datend_grabber,  &datend_grabber,  &datend_grabber,  &datend_grabber,
607    &datend_grabber,  &datend_grabber,  &datend_grabber,  &datend_grabber,
608    &datend_grabber,  &datend_grabber,  &datend_grabber,  &datend_grabber,
609    &datend_grabber,  &datend_grabber,  &datend_grabber,  &datend_grabber,
610    &datend_grabber,  &datend_grabber,  &datend_grabber,  &datend_grabber,
611    &datend_grabber,  &datend_grabber,  &datend_grabber,  &datend_grabber
612 };
613 
614 
615 
616 /* list of active menu hooks */
617 DATEDIT_MENU_INFO *datedit_menu_info[32] =
618 {
619    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
620    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
621    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
622    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
623 };
624 
625 
626 
627 /* adds a new object to the list of available methods */
datedit_register_object(DATEDIT_OBJECT_INFO * info)628 void datedit_register_object(DATEDIT_OBJECT_INFO *info)
629 {
630    int i = 0;
631 
632    while (datedit_object_info[i]->type != DAT_END)
633       i++;
634 
635    datedit_object_info[i] = info;
636 }
637 
638 
639 
640 /* adds a new grabber to the list of available methods */
datedit_register_grabber(DATEDIT_GRABBER_INFO * info)641 void datedit_register_grabber(DATEDIT_GRABBER_INFO *info)
642 {
643    int i = 0;
644 
645    while (datedit_grabber_info[i]->type != DAT_END)
646       i++;
647 
648    datedit_grabber_info[i] = info;
649 }
650 
651 
652 
653 /* adds a new entry to the list of active menus */
datedit_register_menu(DATEDIT_MENU_INFO * info)654 void datedit_register_menu(DATEDIT_MENU_INFO *info)
655 {
656    int i = 0;
657 
658    while (datedit_menu_info[i])
659       i++;
660 
661    datedit_menu_info[i] = info;
662 }
663 
664 
665 
666 /* adds extensions to filenames if they are missing, or changes them */
datedit_pretty_name(AL_CONST char * name,AL_CONST char * ext,int force_ext)667 char *datedit_pretty_name(AL_CONST char *name, AL_CONST char *ext, int force_ext)
668 {
669    static char buf[256];
670    char *s;
671 
672    strcpy(buf, name);
673 
674    s = get_extension(buf);
675    if ((s > buf) && (*(s-1)=='.')) {
676       if (force_ext)
677 	 strcpy(s, ext);
678    }
679    else {
680       *s = '.';
681       strcpy(s+1, ext);
682    }
683 
684    fix_filename_case(buf);
685    return buf;
686 }
687 
688 
689 
690 /* returns a description string for an object */
datedit_desc(AL_CONST DATAFILE * dat)691 AL_CONST char *datedit_desc(AL_CONST DATAFILE *dat)
692 {
693    static char buf[256];
694    int i;
695 
696    for (i=0; datedit_object_info[i]->type != DAT_END; i++) {
697       if (datedit_object_info[i]->type == dat->type) {
698 	 if (datedit_object_info[i]->get_desc)
699 	    datedit_object_info[i]->get_desc(dat, buf);
700 	 else
701 	    strcpy(buf, datedit_object_info[i]->desc);
702 
703 	 return buf;
704       }
705    }
706 
707    sprintf(buf, "binary data (%ld bytes)", dat->size);
708    return buf;
709 }
710 
711 
712 
713 /* qsort callback for comparing datafile objects */
dat_cmp(AL_CONST void * e1,AL_CONST void * e2)714 static int dat_cmp(AL_CONST void *e1, AL_CONST void *e2)
715 {
716    DATAFILE *d1 = (DATAFILE *)e1;
717    DATAFILE *d2 = (DATAFILE *)e2;
718 
719    return stricmp(get_datafile_property((AL_CONST DATAFILE*)d1, DAT_NAME), get_datafile_property((AL_CONST DATAFILE*)d2, DAT_NAME));
720 }
721 
722 
723 
724 /* sorts a datafile */
datedit_sort_datafile(DATAFILE * dat)725 void datedit_sort_datafile(DATAFILE *dat)
726 {
727    int len;
728 
729    if (dat) {
730       len = 0;
731       while (dat[len].type != DAT_END)
732 	 len++;
733 
734       if (len > 1)
735 	 qsort(dat, len, sizeof(DATAFILE), dat_cmp);
736    }
737 }
738 
739 
740 
741 /* qsort callback for comparing datafile properties */
prop_cmp(AL_CONST void * e1,AL_CONST void * e2)742 static int prop_cmp(AL_CONST void *e1, AL_CONST void *e2)
743 {
744    DATAFILE_PROPERTY *p1 = (DATAFILE_PROPERTY *)e1;
745    DATAFILE_PROPERTY *p2 = (DATAFILE_PROPERTY *)e2;
746 
747    return p1->type - p2->type;
748 }
749 
750 
751 
752 /* sorts a list of object properties */
datedit_sort_properties(DATAFILE_PROPERTY * prop)753 void datedit_sort_properties(DATAFILE_PROPERTY *prop)
754 {
755    int len;
756 
757    if (prop) {
758       len = 0;
759       while (prop[len].type != DAT_END)
760 	 len++;
761 
762       if (len > 1)
763 	 qsort(prop, len, sizeof(DATAFILE_PROPERTY), prop_cmp);
764    }
765 }
766 
767 
768 
769 /* splits bitmaps into sub-sprites, using regions bounded by col #255 */
datedit_find_character(BITMAP * bmp,int * x,int * y,int * w,int * h)770 void datedit_find_character(BITMAP *bmp, int *x, int *y, int *w, int *h)
771 {
772    int c1;
773    int c2;
774 
775    if (bitmap_color_depth(bmp) == 8) {
776       c1 = 255;
777       c2 = 255;
778    }
779    else {
780       c1 = makecol_depth(bitmap_color_depth(bmp), 255, 255, 0);
781       c2 = makecol_depth(bitmap_color_depth(bmp), 0, 255, 255);
782    }
783 
784    /* look for top left corner of character */
785    while ((getpixel(bmp, *x, *y) != c1) ||
786 	  (getpixel(bmp, *x+1, *y) != c2) ||
787 	  (getpixel(bmp, *x, *y+1) != c2) ||
788 	  (getpixel(bmp, *x+1, *y+1) == c1) ||
789 	  (getpixel(bmp, *x+1, *y+1) == c2)) {
790       (*x)++;
791       if (*x >= bmp->w) {
792 	 *x = 0;
793 	 (*y)++;
794 	 if (*y >= bmp->h) {
795 	    *w = 0;
796 	    *h = 0;
797 	    return;
798 	 }
799       }
800    }
801 
802    /* look for right edge of character */
803    *w = 0;
804    while ((getpixel(bmp, *x+*w+1, *y) == c2) &&
805 	  (getpixel(bmp, *x+*w+1, *y+1) != c2) &&
806 	  (*x+*w+1 <= bmp->w))
807       (*w)++;
808 
809    /* look for bottom edge of character */
810    *h = 0;
811    while ((getpixel(bmp, *x, *y+*h+1) == c2) &&
812 	  (getpixel(bmp, *x+1, *y+*h+1) != c2) &&
813 	  (*y+*h+1 <= bmp->h))
814       (*h)++;
815 }
816 
817 
818 
819 /* cleans up an object type string, and packs it */
datedit_clean_typename(AL_CONST char * type)820 int datedit_clean_typename(AL_CONST char *type)
821 {
822    int c1, c2, c3, c4;
823 
824    if (!type)
825       return 0;
826 
827    c1 = (*type) ? utoupper(*(type++)) : ' ';
828    c2 = (*type) ? utoupper(*(type++)) : ' ';
829    c3 = (*type) ? utoupper(*(type++)) : ' ';
830    c4 = (*type) ? utoupper(*(type++)) : ' ';
831 
832    return DAT_ID(c1, c2, c3, c4);
833 }
834 
835 
836 
837 /* returns the property string for the specified property (if any) */
datedit_get_property(DATAFILE_PROPERTY ** prop,int type)838 AL_CONST char *datedit_get_property(DATAFILE_PROPERTY **prop, int type)
839 {
840    DATAFILE_PROPERTY *iter = *prop;
841 
842    if (iter) {
843       while (iter->type != DAT_END) {
844 	 if (iter->type == type)
845 	    return (iter->dat ? iter->dat : empty_string);
846 
847 	 iter++;
848       }
849    }
850 
851    return empty_string;
852 }
853 
854 
855 
856 /* insert a new property into a list without duplication */
datedit_insert_property(DATAFILE_PROPERTY ** prop,int type,AL_CONST char * value)857 void datedit_insert_property(DATAFILE_PROPERTY **prop, int type, AL_CONST char *value)
858 {
859    DATAFILE_PROPERTY *iter = *prop, *the_prop;
860    int size;
861 
862    if (iter) {
863       size = 0;
864       the_prop = NULL;
865 
866       for (; iter->type != DAT_END; iter++) {
867          size++;
868 	 if (iter->type == type)
869 	    the_prop = iter;
870 	    /* no break, we need the full size of the array */
871       }
872 
873       if ((value) && (strlen(value) > 0)) {
874 	 if (the_prop) {
875 	    the_prop->dat = _AL_REALLOC(the_prop->dat, strlen(value)+1);
876 	    strcpy(the_prop->dat, value);
877 	 }
878 	 else {
879 	    *prop = _AL_REALLOC(*prop, sizeof(DATAFILE_PROPERTY)*(size+2));
880 	    the_prop = *prop + size;
881 	    the_prop->type = type;
882 	    the_prop->dat = _AL_MALLOC(strlen(value)+1);
883 	    strcpy(the_prop->dat, value);
884 	    the_prop++;
885 	    the_prop->type = DAT_END;
886 	    the_prop->dat = NULL;
887 	 }
888       }
889       else {
890 	 if (the_prop) {
891 	    _AL_FREE(the_prop->dat);
892 	    for (iter = the_prop; iter->type != DAT_END; iter++)
893 	       *iter = *(iter+1);
894 	    *prop = _AL_REALLOC(*prop, sizeof(DATAFILE_PROPERTY)*size);
895 	 }
896       }
897    }
898    else {
899       if ((value) && (strlen(value) > 0)) {
900 	 *prop = _AL_MALLOC(sizeof(DATAFILE_PROPERTY)*2);
901 	 the_prop = *prop;
902 	 the_prop->type = type;
903 	 the_prop->dat = _AL_MALLOC(strlen(value)+1);
904 	 strcpy(the_prop->dat, value);
905 	 the_prop++;
906 	 the_prop->type = DAT_END;
907 	 the_prop->dat = NULL;
908       }
909    }
910 }
911 
912 
913 
914 /* sets an object property string */
datedit_set_property(DATAFILE * dat,int type,AL_CONST char * value)915 void datedit_set_property(DATAFILE *dat, int type, AL_CONST char *value)
916 {
917    datedit_insert_property(&dat->prop, type, value);
918 }
919 
920 
921 
922 /* loads a datafile */
datedit_load_datafile(AL_CONST char * name,int compile_sprites,AL_CONST char * password)923 DATAFILE *datedit_load_datafile(AL_CONST char *name, int compile_sprites, AL_CONST char *password)
924 {
925    char *pretty_name;
926    DATAFILE *datafile;
927 
928    _compile_sprites = compile_sprites;
929 
930    if (!compile_sprites) {
931       typedef void (*DF)(void *);
932       register_datafile_object(DAT_C_SPRITE, NULL, (void (*)(void *))destroy_bitmap);
933       register_datafile_object(DAT_XC_SPRITE, NULL, (void (*)(void *))destroy_bitmap);
934    }
935 
936    if (name)
937       pretty_name = datedit_pretty_name(name, "dat", FALSE);
938    else
939       pretty_name = NULL;
940 
941    if ((pretty_name) && (exists(pretty_name))) {
942       datedit_msg("Reading %s", pretty_name);
943 
944       packfile_password(password);
945 
946       datafile = load_datafile(pretty_name);
947 
948       packfile_password(NULL);
949 
950       if (!datafile) {
951 	 datedit_error("Error reading %s", pretty_name);
952 	 return NULL;
953       }
954       else {
955 	 load_header(datafile, datedit_pretty_name(name, "h", TRUE));
956 	 generate_names(datafile, 0);
957       }
958    }
959    else {
960       if (pretty_name)
961 	 datedit_msg("%s not found: creating new datafile", pretty_name);
962 
963       datafile = _AL_MALLOC(sizeof(DATAFILE));
964       datafile->dat = NULL;
965       datafile->type = DAT_END;
966       datafile->size = 0;
967       datafile->prop = NULL;
968    }
969 
970    datafile = extract_info(datafile, TRUE);
971 
972    return datafile;
973 }
974 
975 
976 
977 /* works out what name to give an exported object */
datedit_export_name(AL_CONST DATAFILE * dat,AL_CONST char * name,AL_CONST char * ext,char * buf)978 void datedit_export_name(AL_CONST DATAFILE *dat, AL_CONST char *name, AL_CONST char *ext, char *buf)
979 {
980    AL_CONST char *obname = get_datafile_property(dat, DAT_NAME);
981    AL_CONST char *oborig = get_datafile_property(dat, DAT_ORIG);
982    char tmp[32];
983    int i;
984 
985    if (name)
986       strcpy(buf, name);
987    else
988       strcpy(buf, oborig);
989 
990    if (*get_filename(buf) == 0) {
991       if (*oborig) {
992 	 strcat(buf, get_filename(oborig));
993       }
994       else {
995 	 strcat(buf, obname);
996 	 if (!ALLEGRO_LFN)
997 	    get_filename(buf)[8] = 0;
998       }
999    }
1000 
1001    if (ext) {
1002       strcpy(tmp, ext);
1003       for (i=0; tmp[i]; i++) {
1004 	 if (tmp[i] == ';') {
1005 	    tmp[i] = 0;
1006 	    break;
1007 	 }
1008       }
1009       strcpy(buf, datedit_pretty_name(buf, tmp, ((name == NULL) || (!*get_extension(name)))));
1010    }
1011    else
1012       fix_filename_case(buf);
1013 }
1014 
1015 
1016 
1017 /* exports a datafile object */
datedit_export(AL_CONST DATAFILE * dat,AL_CONST char * name)1018 int datedit_export(AL_CONST DATAFILE *dat, AL_CONST char *name)
1019 {
1020    AL_CONST char *obname = get_datafile_property(dat, DAT_NAME);
1021    int (*export)(AL_CONST DATAFILE *dat, AL_CONST char *filename) = NULL;
1022    char buf[256], tmp[256];
1023    char *ext = NULL;
1024    char *tok;
1025    int i;
1026 
1027    for (i=0; datedit_grabber_info[i]->type != DAT_END; i++) {
1028       if ((datedit_grabber_info[i]->type == dat->type) && (datedit_grabber_info[i]->export_ext)) {
1029 	 ext = datedit_grabber_info[i]->export_ext;
1030 	 break;
1031       }
1032    }
1033 
1034    datedit_export_name(dat, name, ext, buf);
1035 
1036    if (exists(buf)) {
1037       i = datedit_ask("%s already exists, overwrite", buf);
1038       if (i == 27)
1039 	 return FALSE;
1040       else if ((i == 'n') || (i == 'N'))
1041 	 return TRUE;
1042    }
1043 
1044    datedit_msg("Exporting %s -> %s", obname, buf);
1045 
1046    ext = get_extension(buf);
1047 
1048    for (i=0; datedit_grabber_info[i]->type != DAT_END; i++) {
1049       if ((datedit_grabber_info[i]->type == dat->type) && (datedit_grabber_info[i]->export_ext) && (datedit_grabber_info[i]->exporter)) {
1050 	 strcpy(tmp, datedit_grabber_info[i]->export_ext);
1051 	 tok = strtok(tmp, ";");
1052 	 while (tok) {
1053 	    if (stricmp(tok, ext) == 0) {
1054 	       export = datedit_grabber_info[i]->exporter;
1055 	       break;
1056 	    }
1057 	    tok = strtok(NULL, ";");
1058 	 }
1059       }
1060    }
1061 
1062    if (!export)
1063       export = export_binary;
1064 
1065    if (!export(dat, buf)) {
1066       delete_file(buf);
1067       datedit_error("Error writing %s", buf);
1068       return FALSE;
1069    }
1070 
1071    return TRUE;
1072 }
1073 
1074 
1075 
1076 /* deletes a datafile object */
datedit_delete(DATAFILE * dat,int i)1077 DATAFILE *datedit_delete(DATAFILE *dat, int i)
1078 {
1079    _unload_datafile_object(dat+i);
1080 
1081    do {
1082       dat[i] = dat[i+1];
1083       i++;
1084    } while (dat[i].type != DAT_END);
1085 
1086    return _AL_REALLOC(dat, sizeof(DATAFILE)*i);
1087 }
1088 
1089 
1090 
1091 /* swaps two datafile objects */
datedit_swap(DATAFILE * dat,int i1,int i2)1092 void datedit_swap(DATAFILE *dat, int i1, int i2)
1093 {
1094    DATAFILE temp = dat[i1];
1095 
1096    dat[i1] = dat[i2];
1097    dat[i2] = temp;
1098 }
1099 
1100 
1101 
1102 /* fixup function for the strip options */
datedit_striptype(int strip)1103 int datedit_striptype(int strip)
1104 {
1105    if (strip >= 0)
1106       return strip;
1107    else
1108       return 0;
1109 }
1110 
1111 
1112 
1113 /* fixup function for the pack options */
datedit_packtype(int pack)1114 int datedit_packtype(int pack)
1115 {
1116    if (pack >= 0) {
1117       char buf[80];
1118 
1119       sprintf(buf, "%d", pack);
1120       datedit_set_property(&datedit_info, DAT_PACK, buf);
1121 
1122       return pack;
1123    }
1124    else {
1125       AL_CONST char *p = get_datafile_property(&datedit_info, DAT_PACK);
1126 
1127       if (*p)
1128 	 return atoi(p);
1129       else
1130 	 return 0;
1131    }
1132 }
1133 
1134 
1135 
1136 /* fixup function for the sort options */
datedit_sorttype(int sort)1137 int datedit_sorttype(int sort)
1138 {
1139    if (sort >= 0) {
1140       sort = (sort == 1 ? TRUE : FALSE);
1141       datedit_set_property(&datedit_info, DAT_SORT, sort ? "y" : "n");
1142 
1143       return sort;
1144    }
1145    else {
1146       AL_CONST char *p = get_datafile_property(&datedit_info, DAT_SORT);
1147 
1148       if (*p)
1149 	 return (utolower(*p)=='y');
1150       else
1151 	 return TRUE;  /* sort if SORT property not present */
1152    }
1153 }
1154 
1155 
1156 
1157 /* saves a datafile */
datedit_save_datafile(DATAFILE * dat,AL_CONST char * name,AL_CONST int * fixed_prop,AL_CONST DATEDIT_SAVE_DATAFILE_OPTIONS * options,AL_CONST char * password)1158 int datedit_save_datafile(DATAFILE *dat, AL_CONST char *name, AL_CONST int *fixed_prop, AL_CONST DATEDIT_SAVE_DATAFILE_OPTIONS *options, AL_CONST char *password)
1159 {
1160    char *pretty_name;
1161    char backup_name[256];
1162    int pack, strip, sort;
1163    PACKFILE *f;
1164    int ret;
1165 
1166    packfile_password(password);
1167 
1168    pack = datedit_packtype(options->pack);
1169    strip = datedit_striptype(options->strip);
1170    sort = datedit_sorttype(options->sort);
1171 
1172    strcpy(backup_name, datedit_pretty_name(name, "bak", TRUE));
1173    pretty_name = datedit_pretty_name(name, "dat", FALSE);
1174 
1175    if (options->write_msg)
1176       datedit_msg("Writing %s", pretty_name);
1177 
1178    delete_file(backup_name);
1179    rename(pretty_name, backup_name);
1180 
1181    f = pack_fopen(pretty_name, (pack >= 2) ? F_WRITE_PACKED : F_WRITE_NOPACK);
1182 
1183    if (f) {
1184       pack_mputl(DAT_MAGIC, f);
1185       file_datasize = 12;
1186 
1187       ret = save_datafile(dat, fixed_prop, (pack >= 2), (pack >= 1), strip, sort, options->verbose, (strip <= 0), f);
1188 
1189       if ((ret == TRUE) && (strip <= 0)) {
1190 	 datedit_set_property(&datedit_info, DAT_NAME, "GrabberInfo");
1191 	 ret = save_object(&datedit_info, NULL, FALSE, FALSE, FALSE, FALSE, FALSE, f);
1192       }
1193 
1194       pack_fclose(f);
1195    }
1196    else
1197       ret = FALSE;
1198 
1199    if (ret == FALSE) {
1200       delete_file(pretty_name);
1201       datedit_error("Error writing %s", pretty_name);
1202       packfile_password(NULL);
1203       return FALSE;
1204    }
1205 
1206    if (!options->backup)
1207       delete_file(backup_name);
1208 
1209    if (options->verbose) {
1210       uint64_t file_filesize = file_size_ex(pretty_name);
1211       datedit_msg("%-28s%7d bytes into %-7d (%d%%)", "- GLOBAL COMPRESSION -",
1212 		  file_datasize, file_filesize, percent(file_datasize, file_filesize));
1213    }
1214 
1215    packfile_password(NULL);
1216    return TRUE;
1217 }
1218 
1219 
1220 
1221 /* writes object definitions into a header file */
save_header(AL_CONST DATAFILE * dat,FILE * f,AL_CONST char * prefix)1222 static void save_header(AL_CONST DATAFILE *dat, FILE *f, AL_CONST char *prefix)
1223 {
1224    int c;
1225 
1226    fprintf(f, "\n");
1227 
1228    for (c=0; dat[c].type != DAT_END; c++) {
1229       fprintf(f, "#define %s%-*s %-8d /* %c%c%c%c */\n",
1230 	      prefix, 32-(int)strlen(prefix),
1231 	      get_datafile_property(dat+c, DAT_NAME), c,
1232 	      (dat[c].type>>24), (dat[c].type>>16)&0xFF,
1233 	      (dat[c].type>>8)&0xFF, (dat[c].type&0xFF));
1234 
1235       if (dat[c].type == DAT_FILE) {
1236 	 char p[256];
1237 
1238 	 strcpy(p, prefix);
1239 	 strcat(p, get_datafile_property(dat+c, DAT_NAME));
1240 	 strcat(p, "_");
1241 
1242 	 save_header((DATAFILE *)dat[c].dat, f, p);
1243       }
1244    }
1245 
1246    if (*prefix)
1247       fprintf(f, "#define %s%-*s %d\n", prefix, 32-(int)strlen(prefix), "COUNT", c);
1248 
1249    fprintf(f, "\n");
1250 }
1251 
1252 
1253 
1254 /* helper for renaming files (works across drives) */
rename_file(AL_CONST char * oldname,AL_CONST char * newname)1255 static int rename_file(AL_CONST char *oldname, AL_CONST char *newname)
1256 {
1257    PACKFILE *oldfile, *newfile;
1258    int c;
1259 
1260    oldfile = pack_fopen(oldname, F_READ);
1261    if (!oldfile)
1262       return -1;
1263 
1264    newfile = pack_fopen(newname, F_WRITE);
1265    if (!newfile) {
1266       pack_fclose(oldfile);
1267       return -1;
1268    }
1269 
1270    c = pack_getc(oldfile);
1271 
1272    while (c != EOF) {
1273       pack_putc(c, newfile);
1274       c = pack_getc(oldfile);
1275    }
1276 
1277    pack_fclose(oldfile);
1278    pack_fclose(newfile);
1279 
1280    delete_file(oldname);
1281 
1282    return 0;
1283 }
1284 
1285 
1286 
1287 /* checks whether the header needs updating */
cond_update_header(AL_CONST char * tn,AL_CONST char * n,int verbose)1288 static int cond_update_header(AL_CONST char *tn, AL_CONST char *n, int verbose)
1289 {
1290    PACKFILE *f1, *f2;
1291    char b1[256], b2[256];
1292    int i;
1293    int differ = FALSE;
1294 
1295    if (!exists(n)) {
1296       if (rename_file(tn, n) != 0)
1297 	 return FALSE;
1298    }
1299    else {
1300       f1 = pack_fopen(tn, F_READ);
1301       if (!f1)
1302 	 return FALSE;
1303 
1304       f2 = pack_fopen(n, F_READ);
1305       if (!f2) {
1306 	 pack_fclose(f1);
1307 	 return FALSE;
1308       }
1309 
1310       for (i=0; i<4; i++) {
1311 	 /* skip date, which may differ */
1312 	 pack_fgets(b1, 255, f1);
1313 	 pack_fgets(b2, 255, f2);
1314       }
1315 
1316       while ((!pack_feof(f1)) && (!pack_feof(f2)) && (!differ)) {
1317 	 pack_fgets(b1, 255, f1);
1318 	 pack_fgets(b2, 255, f2);
1319 	 if (strcmp(b1, b2) != 0)
1320 	    differ = TRUE;
1321       }
1322 
1323       if ((!pack_feof(f1)) || (!pack_feof(f2)))
1324 	 differ = TRUE;
1325 
1326       pack_fclose(f1);
1327       pack_fclose(f2);
1328 
1329       if (differ) {
1330 	 if (verbose)
1331 	    datedit_msg("%s has changed: updating", n);
1332 
1333 	 delete_file(n);
1334 	 rename_file(tn, n);
1335       }
1336       else {
1337 	 if (verbose)
1338 	    datedit_msg("%s has not changed: no update", n);
1339 
1340 	 delete_file(tn);
1341       }
1342    }
1343 
1344    return TRUE;
1345 }
1346 
1347 
1348 
1349 /* exports a datafile header */
datedit_save_header(AL_CONST DATAFILE * dat,AL_CONST char * name,AL_CONST char * headername,AL_CONST char * progname,AL_CONST char * prefix,int verbose)1350 int datedit_save_header(AL_CONST DATAFILE *dat, AL_CONST char *name, AL_CONST char *headername, AL_CONST char *progname, AL_CONST char *prefix, int verbose)
1351 {
1352    char *pretty_name, *tmp_name;
1353    char tm[80];
1354    char p[80];
1355    time_t now;
1356    FILE *f;
1357    int c;
1358 
1359    #ifdef ALLEGRO_HAVE_MKSTEMP
1360 
1361       char tmp_buf[] = "XXXXXX";
1362       char tmp[512];
1363       int tmp_fd;
1364 
1365       tmp_fd = mkstemp(tmp_buf);
1366       if (tmp_fd < 0) {
1367 	 datedit_error("Error creating temporary file");
1368 	 return FALSE;
1369       }
1370       close(tmp_fd);
1371       tmp_name = uconvert_ascii(tmp_buf, tmp);
1372 
1373    #else
1374 
1375       tmp_name = tmpnam(NULL);
1376 
1377    #endif
1378 
1379    if (prefix)
1380       strcpy(p, prefix);
1381    else
1382       strcpy(p, get_datafile_property(&datedit_info, DAT_HPRE));
1383 
1384    if ((p[0]) && (p[(strlen(p)-1)] != '_'))
1385       strcat(p, "_");
1386 
1387    pretty_name = datedit_pretty_name(headername, "h", FALSE);
1388    datedit_msg("Writing ID's into %s", pretty_name);
1389 
1390    f = fopen(tmp_name, "w");
1391    if (f) {
1392       time(&now);
1393       strcpy(tm, asctime(localtime(&now)));
1394       for (c=0; tm[c]; c++)
1395 	 if ((tm[c] == '\r') || (tm[c] == '\n'))
1396 	    tm[c] = 0;
1397 
1398       fprintf(f, "/* Allegro datafile object indexes, produced by %s v" ALLEGRO_VERSION_STR ", " ALLEGRO_PLATFORM_STR " */\n", progname);
1399       fprintf(f, "/* Datafile: %s */\n", name);
1400       fprintf(f, "/* Date: %s */\n", tm);
1401       fprintf(f, "/* Do not hand edit! */\n");
1402 
1403       save_header(dat, f, p);
1404 
1405       fclose(f);
1406 
1407       if (!cond_update_header(tmp_name, pretty_name, verbose)) {
1408 	 datedit_error("Error writing %s", pretty_name);
1409 	 return FALSE;
1410       }
1411    }
1412    else {
1413       datedit_error("Error writing %s", pretty_name);
1414       return FALSE;
1415    }
1416 
1417    return TRUE;
1418 }
1419 
1420 
1421 
1422 /* converts a file timestamp from ASCII to integer representation */
datedit_asc2ftime(AL_CONST char * time)1423 long datedit_asc2ftime(AL_CONST char *time)
1424 {
1425    static char *sep = "-,: ";
1426    char tmp[256], *tok;
1427    struct tm t;
1428 
1429    memset(&t, 0, sizeof(struct tm));
1430 
1431    strcpy(tmp, time);
1432    tok = strtok(tmp, sep);
1433 
1434    if (tok) {
1435       t.tm_mon = atoi(tok)-1;
1436       tok = strtok(NULL, sep);
1437       if (tok) {
1438 	 t.tm_mday = atoi(tok);
1439 	 tok = strtok(NULL, sep);
1440 	 if (tok) {
1441 	    t.tm_year = atoi(tok)-1900;
1442 	    tok = strtok(NULL, sep);
1443 	    if (tok) {
1444 	       t.tm_hour = atoi(tok);
1445 	       tok = strtok(NULL, sep);
1446 	       if (tok) {
1447 		  t.tm_min = atoi(tok);
1448 	       }
1449 	    }
1450 	 }
1451       }
1452    }
1453 
1454    {
1455       /* make timezone adjustments by converting to time_t with adjustment
1456        * from local time, then back again as GMT (=UTC) */
1457       time_t tm = mktime (&t);
1458       if (tm != (time_t)-1) {               /* cast needed in djgpp */
1459 	 struct tm *temp = gmtime (&tm);
1460 	 if (temp) memcpy (&t, temp, sizeof t);
1461       }
1462    }
1463 
1464    return mktime(&t);
1465 }
1466 
1467 
1468 
1469 /* converts a file timestamp from integer to ASCII representation */
datedit_ftime2asc(long time)1470 AL_CONST char *datedit_ftime2asc(long time)
1471 {
1472    static char buf[80];
1473 
1474    time_t tim = time;
1475    struct tm *t = gmtime(&tim);
1476 
1477    sprintf(buf, "%d-%02d-%d, %d:%02d",
1478 		t->tm_mon+1, t->tm_mday, t->tm_year+1900,
1479 		t->tm_hour, t->tm_min);
1480 
1481    return buf;
1482 }
1483 
1484 
1485 
1486 /* converts a file timestamp to international ASCII representation */
datedit_ftime2asc_int(long time)1487 AL_CONST char *datedit_ftime2asc_int(long time)
1488 {
1489    static char month[12][4] =
1490    {
1491        "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
1492        "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
1493    };
1494 
1495    static char buf[80];
1496 
1497    time_t tim = time;
1498    struct tm *t = gmtime(&tim);
1499 
1500    sprintf(buf, "%.3s-%02d-%d, %d:%02d",
1501 		month[t->tm_mon%12], t->tm_mday, t->tm_year+1900,
1502 		t->tm_hour, t->tm_min);
1503 
1504    return buf;
1505 }
1506 
1507 
1508 
1509 /* constructs a datafile object
1510  * WARNING: the object is private so it must be duplicated.
1511  */
datedit_construct(int type,void * dat,long size,DATAFILE_PROPERTY ** prop)1512 DATAFILE *datedit_construct(int type, void *dat, long size, DATAFILE_PROPERTY **prop)
1513 {
1514    static DATAFILE datafile;
1515 
1516    if (!dat)
1517       return NULL;
1518 
1519    ASSERT(size >= 0);
1520 
1521    datafile.type = type;
1522    datafile.dat = dat;
1523    datafile.size = size;
1524    if (prop != NULL) {
1525       datafile.prop = *prop;
1526 
1527       /* Make sure we own the property list. */
1528       *prop = NULL;
1529    }
1530    else {
1531       datafile.prop = NULL;
1532    }
1533 
1534    return &datafile;
1535 }
1536 
1537 
1538 
1539 /* clone properties */
clone_properties(DATAFILE_PROPERTY * prop)1540 static DATAFILE_PROPERTY *clone_properties(DATAFILE_PROPERTY *prop)
1541 {
1542    DATAFILE_PROPERTY *clone, *iter;
1543    int size = 0, i;
1544 
1545    if (!prop)
1546       return NULL;
1547 
1548    for (iter = prop; iter->type != DAT_END; iter++) {
1549       size++;
1550    }
1551 
1552    clone = _AL_MALLOC(sizeof(DATAFILE_PROPERTY)*(size+1));
1553 
1554    for (i = 0; i <= size; i++) {
1555        clone[i].type = prop[i].type;
1556        if (prop[i].dat)
1557           clone[i].dat = ustrdup(prop[i].dat);
1558        else
1559           clone[i].dat = NULL;
1560    }
1561 
1562    return clone;
1563 }
1564 
1565 
1566 
1567 /* grabs an object from a disk file
1568  * WARNING: the object is private so it must be duplicated.
1569  */
datedit_grab(DATAFILE_PROPERTY * prop,AL_CONST DATEDIT_GRAB_PARAMETERS * params)1570 DATAFILE *datedit_grab(DATAFILE_PROPERTY *prop, AL_CONST DATEDIT_GRAB_PARAMETERS *params)
1571 {
1572    DATAFILE *dat;
1573    DATAFILE *(*grab)(int type, AL_CONST char *filename, DATAFILE_PROPERTY **prop, int depth);
1574    char *ext = get_extension(params->filename);
1575    char *tok;
1576    char tmp[1024];
1577    int type, type_index, c;
1578 
1579    type = params->type;
1580 
1581    if (!type) {
1582       type = DAT_DATA;
1583 
1584       if ((ext) && (*ext)) {
1585 	 for (c=0; datedit_grabber_info[c]->type != DAT_END; c++) {
1586 	    if (datedit_grabber_info[c]->grab_ext) {
1587 	       strcpy(tmp, datedit_grabber_info[c]->grab_ext);
1588 	       tok = strtok(tmp, ";");
1589 	       while (tok) {
1590 		  if (stricmp(tok, ext) == 0) {
1591 		     type = datedit_grabber_info[c]->type;
1592 		     goto found_type;
1593 		  }
1594 		  tok = strtok(NULL, ";");
1595 	       }
1596 	    }
1597 	 }
1598       }
1599    }
1600 
1601   found_type:
1602    type_index = -1;
1603 
1604    for (c=0; datedit_grabber_info[c]->type != DAT_END; c++) {
1605       if ((datedit_grabber_info[c]->type == type) && (datedit_grabber_info[c]->grab_ext) && (datedit_grabber_info[c]->grab)) {
1606 	 if ((ext) && (*ext)) {
1607 	    strcpy(tmp, datedit_grabber_info[c]->grab_ext);
1608 	    tok = strtok(tmp, ";");
1609 	    while (tok) {
1610 	       if (stricmp(tok, ext) == 0) {
1611 		  type_index = c;
1612 		  goto found_grabber;
1613 	       }
1614 	       tok = strtok(NULL, ";");
1615 	    }
1616 	 }
1617 	 if (!type_index < 0)
1618 	    type_index = c;
1619       }
1620    }
1621 
1622   found_grabber:
1623    if (type_index >= 0)
1624       grab = datedit_grabber_info[type_index]->grab;
1625    else
1626       grab = grab_binary;
1627 
1628    if (prop)
1629       prop = clone_properties(prop);
1630    else {
1631       /* Set standard properties. */
1632       datedit_insert_property(&prop, DAT_NAME, params->name);
1633       if (params->relative) {
1634 	 make_relative_filename(tmp, params->datafile, params->filename, sizeof(tmp));
1635 	 datedit_insert_property(&prop, DAT_ORIG, tmp);
1636       }
1637       else
1638 	 datedit_insert_property(&prop, DAT_ORIG, params->filename);
1639 
1640       /* Set specific properties. */
1641       if ((type_index >= 0) && (datedit_grabber_info[type_index]->prop_types)) {
1642 	 strcpy(tmp, datedit_grabber_info[type_index]->prop_types);
1643 	 tok = strtok(tmp, ";");
1644 	 while (tok) {
1645 	    if (stricmp(tok, "XPOS") == 0) {
1646 	       sprintf(tmp, "%d", params->x);
1647 	       datedit_insert_property(&prop, DAT_XPOS, tmp);
1648 	    }
1649 	    else if (stricmp(tok, "YPOS") == 0) {
1650 	       sprintf(tmp, "%d", params->y);
1651 	       datedit_insert_property(&prop, DAT_YPOS, tmp);
1652 	    }
1653 	    else if (stricmp(tok, "XSIZ") == 0) {
1654 	       sprintf(tmp, "%d", params->w);
1655 	       datedit_insert_property(&prop, DAT_XSIZ, tmp);
1656 	    }
1657 	    else if (stricmp(tok, "YSIZ") == 0) {
1658 	       sprintf(tmp, "%d", params->h);
1659 	       datedit_insert_property(&prop, DAT_YSIZ, tmp);
1660 	    }
1661 
1662 	    tok = strtok(NULL, ";");
1663 	 }
1664       }
1665    }
1666 
1667    /* Always modify the date. */
1668    datedit_insert_property(&prop, DAT_DATE, datedit_ftime2asc(file_time(params->filename)));
1669 
1670    dat = grab(type, params->filename, &prop, params->colordepth);
1671 
1672    if (!dat) {
1673       datedit_error("Error reading %s as type %c%c%c%c", params->filename,
1674 		    type>>24, (type>>16)&0xFF, (type>>8)&0xFF, type&0xFF);
1675       if (prop)
1676 	 free(prop);
1677    }
1678 
1679    return dat;
1680 }
1681 
1682 
1683 
1684 /* grabs an object over the top of an existing one */
datedit_grabreplace(DATAFILE * dat,AL_CONST DATEDIT_GRAB_PARAMETERS * params)1685 int datedit_grabreplace(DATAFILE *dat, AL_CONST DATEDIT_GRAB_PARAMETERS *params)
1686 {
1687    DATAFILE *tmp = datedit_grab(NULL, params);
1688 
1689    if (tmp) {
1690       _unload_datafile_object(dat);
1691       *dat = *tmp;
1692       return TRUE;
1693    }
1694    else
1695       return FALSE;
1696 }
1697 
1698 
1699 
1700 /* updates an object in-place */
datedit_grabupdate(DATAFILE * dat,DATEDIT_GRAB_PARAMETERS * params)1701 int datedit_grabupdate(DATAFILE *dat, DATEDIT_GRAB_PARAMETERS *params)
1702 {
1703    DATAFILE *tmp;
1704 
1705    params->name = "dummyname";
1706    params->type = dat->type;
1707    params->colordepth = -1;
1708 
1709    tmp = datedit_grab(dat->prop, params);
1710 
1711    if (tmp) {
1712       /* adjust color depth? */
1713       if ((dat->type == DAT_BITMAP) || (dat->type == DAT_RLE_SPRITE) ||
1714 	  (dat->type == DAT_C_SPRITE) || (dat->type == DAT_XC_SPRITE)) {
1715 
1716 	 int src_depth, dest_depth;
1717 
1718 	 if (dat->type == DAT_RLE_SPRITE) {
1719 	    dest_depth = ((RLE_SPRITE *)dat->dat)->color_depth;
1720 	    src_depth = ((RLE_SPRITE *)tmp->dat)->color_depth;
1721 	 }
1722 	 else {
1723 	    dest_depth = bitmap_color_depth(dat->dat);
1724 	    src_depth = bitmap_color_depth(tmp->dat);
1725 	 }
1726 
1727 	 if (src_depth != dest_depth) {
1728 	    BITMAP *b1, *b2;
1729 
1730 	    if (dat->type == DAT_RLE_SPRITE) {
1731 	       RLE_SPRITE *spr = (RLE_SPRITE *)tmp->dat;
1732 	       b1 = create_bitmap_ex(src_depth, spr->w, spr->h);
1733 	       clear_to_color(b1, b1->vtable->mask_color);
1734 	       draw_rle_sprite(b1, spr, 0, 0);
1735 	       destroy_rle_sprite(spr);
1736 	    }
1737 	    else
1738 	       b1 = (BITMAP *)tmp->dat;
1739 
1740 	    if (dest_depth == 8)
1741 	       datedit_msg("Warning: lossy conversion from truecolor to 256 colors!");
1742 
1743 	    if ((dat->type == DAT_RLE_SPRITE) ||
1744 		(dat->type == DAT_C_SPRITE) || (dat->type == DAT_XC_SPRITE)) {
1745 	       datedit_last_read_pal[0].r = 63;
1746 	       datedit_last_read_pal[0].g = 0;
1747 	       datedit_last_read_pal[0].b = 63;
1748 	    }
1749 
1750 	    select_palette(datedit_last_read_pal);
1751 	    b2 = create_bitmap_ex(dest_depth, b1->w, b1->h);
1752 	    blit(b1, b2, 0, 0, 0, 0, b1->w, b1->h);
1753 	    unselect_palette();
1754 
1755 	    if (dat->type == DAT_RLE_SPRITE) {
1756 	       tmp->dat = get_rle_sprite(b2);
1757 	       destroy_bitmap(b1);
1758 	       destroy_bitmap(b2);
1759 	    }
1760 	    else {
1761 	       tmp->dat = b2;
1762 	       destroy_bitmap(b1);
1763 	    }
1764 	 }
1765       }
1766 
1767       _unload_datafile_object(dat);
1768       *dat = *tmp;
1769       return TRUE;
1770    }
1771    else
1772       return FALSE;
1773 }
1774 
1775 
1776 
1777 /* grabs a new object, inserting it into the datafile */
datedit_grabnew(DATAFILE * dat,AL_CONST DATEDIT_GRAB_PARAMETERS * params)1778 DATAFILE *datedit_grabnew(DATAFILE *dat, AL_CONST DATEDIT_GRAB_PARAMETERS *params)
1779 {
1780    DATAFILE *tmp = datedit_grab(NULL, params);
1781    int len;
1782 
1783    if (tmp) {
1784       len = 0;
1785       while (dat[len].type != DAT_END)
1786 	 len++;
1787 
1788       dat = _AL_REALLOC(dat, sizeof(DATAFILE)*(len+2));
1789       dat[len+1] = dat[len];
1790       dat[len] = *tmp;
1791       return dat;
1792    }
1793    else
1794       return NULL;
1795 }
1796 
1797 
1798 
1799 /* inserts a new object into the datafile */
datedit_insert(DATAFILE * dat,DATAFILE ** ret,AL_CONST char * name,int type,void * v,long size)1800 DATAFILE *datedit_insert(DATAFILE *dat, DATAFILE **ret, AL_CONST char *name, int type, void *v, long size)
1801 {
1802    int len;
1803 
1804    len = 0;
1805    while (dat[len].type != DAT_END)
1806       len++;
1807 
1808    dat = _AL_REALLOC(dat, sizeof(DATAFILE)*(len+2));
1809    dat[len+1] = dat[len];
1810 
1811    dat[len].dat = v;
1812    dat[len].type = type;
1813    dat[len].size = size;
1814    dat[len].prop = NULL;
1815    datedit_set_property(dat+len, DAT_NAME, name);
1816 
1817    if (ret)
1818       *ret = dat+len;
1819 
1820    return dat;
1821 }
1822 
1823 
1824 
1825 /* wrapper for examining numeric property values */
datedit_numprop(DATAFILE_PROPERTY ** prop,int type)1826 int datedit_numprop(DATAFILE_PROPERTY **prop, int type)
1827 {
1828    AL_CONST char *p = datedit_get_property(prop, type);
1829 
1830    if (*p)
1831       return atoi(p);
1832    else
1833       return -1;
1834 }
1835 
1836 
1837 
1838 /* scans plugins to find available import/export file extensions */
make_ext_list(int type,int grab)1839 static AL_CONST char *make_ext_list(int type, int grab)
1840 {
1841    static char buf[256];
1842    char extlist[256][16];
1843    int num_ext = 0;
1844    char *s;
1845    int i, j;
1846    int done;
1847 
1848    for (i=0; datedit_grabber_info[i]->type != DAT_END; i++) {
1849       if (datedit_grabber_info[i]->type == type) {
1850 	 if (grab)
1851 	    s = datedit_grabber_info[i]->grab_ext;
1852 	 else if (!grab)
1853 	    s = datedit_grabber_info[i]->export_ext;
1854 	 else
1855 	    s = NULL;
1856 
1857 	 if (s) {
1858 	    strcpy(buf, s);
1859 	    s = strtok(buf, ";");
1860 	    while (s) {
1861 	       for (j=0; j<num_ext; j++) {
1862 		  if (stricmp(s, extlist[j]) == 0)
1863 		     break;
1864 	       }
1865 	       if (j >= num_ext) {
1866 		  strcpy(extlist[num_ext], s);
1867 		  num_ext++;
1868 	       }
1869 	       s = strtok(NULL, ";");
1870 	    }
1871 	 }
1872       }
1873    }
1874 
1875    if (num_ext <= 0)
1876       return NULL;
1877 
1878    do {
1879       done = TRUE;
1880 
1881       for (i=0; i<num_ext-1; i++) {
1882 	 if (stricmp(extlist[i], extlist[i+1]) > 0) {
1883 	    strcpy(buf, extlist[i]);
1884 	    strcpy(extlist[i], extlist[i+1]);
1885 	    strcpy(extlist[i+1], buf);
1886 	    done = FALSE;
1887 	 }
1888       }
1889    } while (!done);
1890 
1891    buf[0] = 0;
1892 
1893    for (i=0; i<num_ext; i++) {
1894       strcat(buf, extlist[i]);
1895       if (i<num_ext-1)
1896 	 strcat(buf, ";");
1897    }
1898 
1899    return buf;
1900 }
1901 
1902 
1903 
1904 /* returns a list of suitable file extensions for this object type */
datedit_grab_ext(int type)1905 AL_CONST char *datedit_grab_ext(int type)
1906 {
1907    return make_ext_list(type, TRUE);
1908 }
1909 
1910 
1911 
1912 /* returns a list of suitable file extensions for this object type */
datedit_export_ext(int type)1913 AL_CONST char *datedit_export_ext(int type)
1914 {
1915    return make_ext_list(type, FALSE);
1916 }
1917 
1918 
1919 /* (un)conditionally update an object */
datedit_update(DATAFILE * dat,AL_CONST char * datafile,int force,int verbose,int * changed)1920 int datedit_update(DATAFILE *dat, AL_CONST char *datafile, int force, int verbose, int *changed)
1921 {
1922    AL_CONST char *name = get_datafile_property(dat, DAT_NAME);
1923    AL_CONST char *origin = get_datafile_property(dat, DAT_ORIG);
1924    DATEDIT_GRAB_PARAMETERS params;
1925    char filename[1024];
1926    int relf;
1927 
1928    if (!*origin) {
1929       datedit_msg("%s has no origin data - skipping", name);
1930       return TRUE;
1931    }
1932 
1933    if (is_relative_filename(origin)) {
1934       make_absolute_filename(filename, datafile, origin, sizeof(filename));
1935       relf = TRUE;
1936    }
1937    else {
1938       strcpy(filename, origin);
1939       relf = FALSE;
1940    }
1941 
1942    if (!exists(filename)) {
1943       datedit_msg("%s: %s not found - skipping", name, origin);
1944       return TRUE;
1945    }
1946 
1947    if (!force) {
1948       AL_CONST char *date = get_datafile_property(dat, DAT_DATE);
1949 
1950       if (date[0]) {
1951 	 time_t origt = file_time(filename);
1952 	 time_t datat = datedit_asc2ftime(date);
1953 
1954 	 if ((origt/60) <= (datat/60)) {
1955 	    if (verbose)
1956 	       datedit_msg("%s: %s has not changed - skipping", name, origin);
1957 	    return TRUE;
1958 	 }
1959       }
1960    }
1961 
1962    datedit_msg("Updating %s -> %s", origin, name);
1963 
1964    if (changed)
1965       *changed = TRUE;
1966 
1967    params.datafile = datafile;
1968    params.filename = filename;
1969    /* params.name = */
1970    /* params.type = */
1971    /* params.x = */
1972    /* params.y = */
1973    /* params.w = */
1974    /* params.h = */
1975    /* params.colordepth = */
1976    params.relative = relf;
1977 
1978    return datedit_grabupdate(dat, &params);
1979 }
1980 
1981