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, ¶ms);
1979 }
1980
1981