1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
19 #include "libgame/libgame.h"
27 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
28 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
29 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
30 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
31 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
32 #define LEVEL_HEADER_UNUSED 13 /* unused level header bytes */
33 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
34 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
35 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
36 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
37 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
38 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
39 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
40 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
42 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
43 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
45 /* file identifier strings */
46 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
47 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
48 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
51 /* ========================================================================= */
52 /* level file functions */
53 /* ========================================================================= */
55 void setElementChangePages(struct ElementInfo *ei, int change_pages)
57 int change_page_size = sizeof(struct ElementChangeInfo);
59 ei->num_change_pages = MAX(1, change_pages);
62 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
64 if (ei->current_change_page >= ei->num_change_pages)
65 ei->current_change_page = ei->num_change_pages - 1;
67 ei->change = &ei->change_page[ei->current_change_page];
70 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
74 change->can_change = FALSE;
76 change->events = CE_BITMASK_DEFAULT;
77 change->sides = CH_SIDE_ANY;
79 change->target_element = EL_EMPTY_SPACE;
81 change->delay_fixed = 0;
82 change->delay_random = 0;
83 change->delay_frames = 1;
85 change->trigger_element = EL_EMPTY_SPACE;
87 change->explode = FALSE;
88 change->use_content = FALSE;
89 change->only_complete = FALSE;
90 change->use_random_change = FALSE;
92 change->power = CP_NON_DESTRUCTIVE;
94 for (x = 0; x < 3; x++)
95 for (y = 0; y < 3; y++)
96 change->content[x][y] = EL_EMPTY_SPACE;
98 change->direct_action = 0;
99 change->other_action = 0;
101 change->pre_change_function = NULL;
102 change->change_function = NULL;
103 change->post_change_function = NULL;
106 static void setLevelInfoToDefaults(struct LevelInfo *level)
110 level->file_version = FILE_VERSION_ACTUAL;
111 level->game_version = GAME_VERSION_ACTUAL;
113 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
114 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
115 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
117 level->fieldx = STD_LEV_FIELDX;
118 level->fieldy = STD_LEV_FIELDY;
120 for (x = 0; x < MAX_LEV_FIELDX; x++)
121 for (y = 0; y < MAX_LEV_FIELDY; y++)
122 level->field[x][y] = EL_SAND;
125 level->gems_needed = 0;
126 level->amoeba_speed = 10;
127 level->time_magic_wall = 10;
128 level->time_wheel = 10;
129 level->time_light = 10;
130 level->time_timegate = 10;
131 level->amoeba_content = EL_DIAMOND;
132 level->double_speed = FALSE;
133 level->initial_gravity = FALSE;
134 level->em_slippery_gems = FALSE;
136 level->use_custom_template = FALSE;
138 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
139 level->name[i] = '\0';
140 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
141 level->author[i] = '\0';
143 strcpy(level->name, NAMELESS_LEVEL_NAME);
144 strcpy(level->author, ANONYMOUS_NAME);
146 for (i = 0; i < 4; i++)
148 level->envelope_text[i][0] = '\0';
149 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
150 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
153 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
154 level->score[i] = 10;
156 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
157 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
158 for (x = 0; x < 3; x++)
159 for (y = 0; y < 3; y++)
160 level->yamyam_content[i][x][y] =
161 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
163 level->field[0][0] = EL_PLAYER_1;
164 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
166 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
168 setElementChangePages(&element_info[i], 1);
169 setElementChangeInfoToDefaults(element_info[i].change);
172 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
174 int element = EL_CUSTOM_START + i;
176 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
177 element_info[element].description[j] = '\0';
178 if (element_info[element].custom_description != NULL)
179 strncpy(element_info[element].description,
180 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
182 strcpy(element_info[element].description,
183 element_info[element].editor_description);
185 element_info[element].use_gfx_element = FALSE;
186 element_info[element].gfx_element = EL_EMPTY_SPACE;
188 element_info[element].collect_score = 10; /* special default */
189 element_info[element].collect_count = 1; /* special default */
191 element_info[element].push_delay_fixed = -1; /* initialize later */
192 element_info[element].push_delay_random = -1; /* initialize later */
193 element_info[element].move_delay_fixed = 0;
194 element_info[element].move_delay_random = 0;
196 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
197 element_info[element].move_direction_initial = MV_NO_MOVING;
198 element_info[element].move_stepsize = TILEX / 8;
200 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
202 for (x = 0; x < 3; x++)
203 for (y = 0; y < 3; y++)
204 element_info[element].content[x][y] = EL_EMPTY_SPACE;
206 element_info[element].access_type = 0;
207 element_info[element].access_layer = 0;
208 element_info[element].walk_to_action = 0;
209 element_info[element].smash_targets = 0;
210 element_info[element].deadliness = 0;
211 element_info[element].consistency = 0;
213 element_info[element].can_explode_by_fire = FALSE;
214 element_info[element].can_explode_smashed = FALSE;
215 element_info[element].can_explode_impact = FALSE;
217 element_info[element].current_change_page = 0;
219 /* start with no properties at all */
220 for (j = 0; j < NUM_EP_BITFIELDS; j++)
221 Properties[element][j] = EP_BITMASK_DEFAULT;
223 element_info[element].modified_settings = FALSE;
226 BorderElement = EL_STEELWALL;
228 level->no_level_file = FALSE;
230 if (leveldir_current == NULL) /* only when dumping level */
233 /* try to determine better author name than 'anonymous' */
234 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
236 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
237 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
241 switch (LEVELCLASS(leveldir_current))
243 case LEVELCLASS_TUTORIAL:
244 strcpy(level->author, PROGRAM_AUTHOR_STRING);
247 case LEVELCLASS_CONTRIB:
248 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
249 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
252 case LEVELCLASS_PRIVATE:
253 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
254 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
258 /* keep default value */
264 static void ActivateLevelTemplate()
266 /* Currently there is no special action needed to activate the template
267 data, because 'element_info' and 'Properties' overwrite the original
268 level data, while all other variables do not change. */
271 boolean LevelFileExists(int level_nr)
273 char *filename = getLevelFilename(level_nr);
275 return (access(filename, F_OK) == 0);
278 static int checkLevelElement(int element)
280 /* map some (historic, now obsolete) elements */
285 case EL_PLAYER_OBSOLETE:
286 element = EL_PLAYER_1;
289 case EL_KEY_OBSOLETE:
292 case EL_EM_KEY_1_FILE_OBSOLETE:
293 element = EL_EM_KEY_1;
296 case EL_EM_KEY_2_FILE_OBSOLETE:
297 element = EL_EM_KEY_2;
300 case EL_EM_KEY_3_FILE_OBSOLETE:
301 element = EL_EM_KEY_3;
304 case EL_EM_KEY_4_FILE_OBSOLETE:
305 element = EL_EM_KEY_4;
308 case EL_ENVELOPE_OBSOLETE:
309 element = EL_ENVELOPE_1;
317 if (element >= NUM_FILE_ELEMENTS)
319 Error(ERR_WARN, "invalid level element %d", element);
321 element = EL_CHAR_QUESTION;
326 if (element >= NUM_FILE_ELEMENTS)
328 Error(ERR_WARN, "invalid level element %d", element);
330 element = EL_CHAR_QUESTION;
332 else if (element == EL_PLAYER_OBSOLETE)
333 element = EL_PLAYER_1;
334 else if (element == EL_KEY_OBSOLETE)
341 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
343 level->file_version = getFileVersion(file);
344 level->game_version = getFileVersion(file);
349 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
353 level->fieldx = getFile8Bit(file);
354 level->fieldy = getFile8Bit(file);
356 level->time = getFile16BitBE(file);
357 level->gems_needed = getFile16BitBE(file);
359 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
360 level->name[i] = getFile8Bit(file);
361 level->name[MAX_LEVEL_NAME_LEN] = 0;
363 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
364 level->score[i] = getFile8Bit(file);
366 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
367 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
368 for (y = 0; y < 3; y++)
369 for (x = 0; x < 3; x++)
370 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
372 level->amoeba_speed = getFile8Bit(file);
373 level->time_magic_wall = getFile8Bit(file);
374 level->time_wheel = getFile8Bit(file);
375 level->amoeba_content = checkLevelElement(getFile8Bit(file));
376 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
377 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
378 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
379 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
381 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
383 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
388 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
392 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
393 level->author[i] = getFile8Bit(file);
394 level->author[MAX_LEVEL_NAME_LEN] = 0;
399 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
402 int chunk_size_expected = level->fieldx * level->fieldy;
404 /* Note: "chunk_size" was wrong before version 2.0 when elements are
405 stored with 16-bit encoding (and should be twice as big then).
406 Even worse, playfield data was stored 16-bit when only yamyam content
407 contained 16-bit elements and vice versa. */
409 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
410 chunk_size_expected *= 2;
412 if (chunk_size_expected != chunk_size)
414 ReadUnusedBytesFromFile(file, chunk_size);
415 return chunk_size_expected;
418 for (y = 0; y < level->fieldy; y++)
419 for (x = 0; x < level->fieldx; x++)
421 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
426 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
430 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
431 int chunk_size_expected = header_size + content_size;
433 /* Note: "chunk_size" was wrong before version 2.0 when elements are
434 stored with 16-bit encoding (and should be twice as big then).
435 Even worse, playfield data was stored 16-bit when only yamyam content
436 contained 16-bit elements and vice versa. */
438 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
439 chunk_size_expected += content_size;
441 if (chunk_size_expected != chunk_size)
443 ReadUnusedBytesFromFile(file, chunk_size);
444 return chunk_size_expected;
448 level->num_yamyam_contents = getFile8Bit(file);
452 /* correct invalid number of content fields -- should never happen */
453 if (level->num_yamyam_contents < 1 ||
454 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
455 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
457 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
458 for (y = 0; y < 3; y++)
459 for (x = 0; x < 3; x++)
460 level->yamyam_content[i][x][y] =
461 checkLevelElement(level->encoding_16bit_field ?
462 getFile16BitBE(file) : getFile8Bit(file));
466 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
470 int num_contents, content_xsize, content_ysize;
471 int content_array[MAX_ELEMENT_CONTENTS][3][3];
473 element = checkLevelElement(getFile16BitBE(file));
474 num_contents = getFile8Bit(file);
475 content_xsize = getFile8Bit(file);
476 content_ysize = getFile8Bit(file);
478 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
480 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
481 for (y = 0; y < 3; y++)
482 for (x = 0; x < 3; x++)
483 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
485 /* correct invalid number of content fields -- should never happen */
486 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
487 num_contents = STD_ELEMENT_CONTENTS;
489 if (element == EL_YAMYAM)
491 level->num_yamyam_contents = num_contents;
493 for (i = 0; i < num_contents; i++)
494 for (y = 0; y < 3; y++)
495 for (x = 0; x < 3; x++)
496 level->yamyam_content[i][x][y] = content_array[i][x][y];
498 else if (element == EL_BD_AMOEBA)
500 level->amoeba_content = content_array[0][0][0];
504 Error(ERR_WARN, "cannot load content for element '%d'", element);
510 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
516 int chunk_size_expected;
518 element = checkLevelElement(getFile16BitBE(file));
519 if (!IS_ENVELOPE(element))
520 element = EL_ENVELOPE_1;
522 envelope_nr = element - EL_ENVELOPE_1;
524 envelope_len = getFile16BitBE(file);
526 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
527 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
529 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
531 chunk_size_expected = LEVEL_CHUNK_CNT3_HEADER + envelope_len;
533 if (chunk_size_expected != chunk_size)
535 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
536 return chunk_size_expected;
539 for (i = 0; i < envelope_len; i++)
540 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
545 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
547 int num_changed_custom_elements = getFile16BitBE(file);
548 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
551 if (chunk_size_expected != chunk_size)
553 ReadUnusedBytesFromFile(file, chunk_size - 2);
554 return chunk_size_expected;
557 for (i = 0; i < num_changed_custom_elements; i++)
559 int element = getFile16BitBE(file);
560 int properties = getFile32BitBE(file);
562 if (IS_CUSTOM_ELEMENT(element))
563 Properties[element][EP_BITFIELD_BASE] = properties;
565 Error(ERR_WARN, "invalid custom element number %d", element);
571 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
573 int num_changed_custom_elements = getFile16BitBE(file);
574 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
577 if (chunk_size_expected != chunk_size)
579 ReadUnusedBytesFromFile(file, chunk_size - 2);
580 return chunk_size_expected;
583 for (i = 0; i < num_changed_custom_elements; i++)
585 int element = getFile16BitBE(file);
586 int custom_target_element = getFile16BitBE(file);
588 if (IS_CUSTOM_ELEMENT(element))
589 element_info[element].change->target_element = custom_target_element;
591 Error(ERR_WARN, "invalid custom element number %d", element);
597 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
599 int num_changed_custom_elements = getFile16BitBE(file);
600 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
603 if (chunk_size_expected != chunk_size)
605 ReadUnusedBytesFromFile(file, chunk_size - 2);
606 return chunk_size_expected;
609 for (i = 0; i < num_changed_custom_elements; i++)
611 int element = getFile16BitBE(file);
613 if (!IS_CUSTOM_ELEMENT(element))
615 Error(ERR_WARN, "invalid custom element number %d", element);
620 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
621 element_info[element].description[j] = getFile8Bit(file);
622 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
624 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
626 /* some free bytes for future properties and padding */
627 ReadUnusedBytesFromFile(file, 7);
629 element_info[element].use_gfx_element = getFile8Bit(file);
630 element_info[element].gfx_element =
631 checkLevelElement(getFile16BitBE(file));
633 element_info[element].collect_score = getFile8Bit(file);
634 element_info[element].collect_count = getFile8Bit(file);
636 element_info[element].push_delay_fixed = getFile16BitBE(file);
637 element_info[element].push_delay_random = getFile16BitBE(file);
638 element_info[element].move_delay_fixed = getFile16BitBE(file);
639 element_info[element].move_delay_random = getFile16BitBE(file);
641 element_info[element].move_pattern = getFile16BitBE(file);
642 element_info[element].move_direction_initial = getFile8Bit(file);
643 element_info[element].move_stepsize = getFile8Bit(file);
645 for (y = 0; y < 3; y++)
646 for (x = 0; x < 3; x++)
647 element_info[element].content[x][y] =
648 checkLevelElement(getFile16BitBE(file));
650 element_info[element].change->events = getFile32BitBE(file);
652 element_info[element].change->target_element =
653 checkLevelElement(getFile16BitBE(file));
655 element_info[element].change->delay_fixed = getFile16BitBE(file);
656 element_info[element].change->delay_random = getFile16BitBE(file);
657 element_info[element].change->delay_frames = getFile16BitBE(file);
659 element_info[element].change->trigger_element =
660 checkLevelElement(getFile16BitBE(file));
662 element_info[element].change->explode = getFile8Bit(file);
663 element_info[element].change->use_content = getFile8Bit(file);
664 element_info[element].change->only_complete = getFile8Bit(file);
665 element_info[element].change->use_random_change = getFile8Bit(file);
667 element_info[element].change->random = getFile8Bit(file);
668 element_info[element].change->power = getFile8Bit(file);
670 for (y = 0; y < 3; y++)
671 for (x = 0; x < 3; x++)
672 element_info[element].change->content[x][y] =
673 checkLevelElement(getFile16BitBE(file));
675 element_info[element].slippery_type = getFile8Bit(file);
677 /* some free bytes for future properties and padding */
678 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
680 /* mark that this custom element has been modified */
681 element_info[element].modified_settings = TRUE;
687 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
689 struct ElementInfo *ei;
690 int chunk_size_expected;
694 element = getFile16BitBE(file);
696 if (!IS_CUSTOM_ELEMENT(element))
698 Error(ERR_WARN, "invalid custom element number %d", element);
703 ei = &element_info[element];
705 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
706 ei->description[i] = getFile8Bit(file);
707 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
709 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
710 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
712 ei->num_change_pages = getFile8Bit(file);
714 /* some free bytes for future base property values and padding */
715 ReadUnusedBytesFromFile(file, 5);
717 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
718 if (chunk_size_expected != chunk_size)
720 ReadUnusedBytesFromFile(file, chunk_size - 48);
721 return chunk_size_expected;
724 /* read custom property values */
726 ei->use_gfx_element = getFile8Bit(file);
727 ei->gfx_element = checkLevelElement(getFile16BitBE(file));
729 ei->collect_score = getFile8Bit(file);
730 ei->collect_count = getFile8Bit(file);
732 ei->push_delay_fixed = getFile16BitBE(file);
733 ei->push_delay_random = getFile16BitBE(file);
734 ei->move_delay_fixed = getFile16BitBE(file);
735 ei->move_delay_random = getFile16BitBE(file);
737 ei->move_pattern = getFile16BitBE(file);
738 ei->move_direction_initial = getFile8Bit(file);
739 ei->move_stepsize = getFile8Bit(file);
741 ei->slippery_type = getFile8Bit(file);
743 for (y = 0; y < 3; y++)
744 for (x = 0; x < 3; x++)
745 ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
747 /* some free bytes for future custom property values and padding */
748 ReadUnusedBytesFromFile(file, 12);
750 /* read change property values */
752 setElementChangePages(ei, ei->num_change_pages);
754 for (i = 0; i < ei->num_change_pages; i++)
756 struct ElementChangeInfo *change = &ei->change_page[i];
758 /* always start with reliable default values */
759 setElementChangeInfoToDefaults(change);
761 change->events = getFile32BitBE(file);
763 change->target_element = checkLevelElement(getFile16BitBE(file));
765 change->delay_fixed = getFile16BitBE(file);
766 change->delay_random = getFile16BitBE(file);
767 change->delay_frames = getFile16BitBE(file);
769 change->trigger_element = checkLevelElement(getFile16BitBE(file));
771 change->explode = getFile8Bit(file);
772 change->use_content = getFile8Bit(file);
773 change->only_complete = getFile8Bit(file);
774 change->use_random_change = getFile8Bit(file);
776 change->random = getFile8Bit(file);
777 change->power = getFile8Bit(file);
779 for (y = 0; y < 3; y++)
780 for (x = 0; x < 3; x++)
781 change->content[x][y] = checkLevelElement(getFile16BitBE(file));
783 change->can_change = getFile8Bit(file);
785 change->sides = getFile8Bit(file);
787 if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
788 change->sides = CH_SIDE_ANY;
790 /* some free bytes for future change property values and padding */
791 ReadUnusedBytesFromFile(file, 8);
794 /* mark this custom element as modified */
795 ei->modified_settings = TRUE;
800 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
802 char cookie[MAX_LINE_LEN];
803 char chunk_name[CHUNK_ID_LEN + 1];
807 /* always start with reliable default values */
808 setLevelInfoToDefaults(level);
810 if (!(file = fopen(filename, MODE_READ)))
812 level->no_level_file = TRUE;
814 if (level != &level_template)
815 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
820 getFileChunkBE(file, chunk_name, NULL);
821 if (strcmp(chunk_name, "RND1") == 0)
823 getFile32BitBE(file); /* not used */
825 getFileChunkBE(file, chunk_name, NULL);
826 if (strcmp(chunk_name, "CAVE") != 0)
828 Error(ERR_WARN, "unknown format of level file '%s'", filename);
833 else /* check for pre-2.0 file format with cookie string */
835 strcpy(cookie, chunk_name);
836 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
837 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
838 cookie[strlen(cookie) - 1] = '\0';
840 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
842 Error(ERR_WARN, "unknown format of level file '%s'", filename);
847 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
849 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
854 /* pre-2.0 level files have no game version, so use file version here */
855 level->game_version = level->file_version;
858 if (level->file_version < FILE_VERSION_1_2)
860 /* level files from versions before 1.2.0 without chunk structure */
861 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
862 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
870 int (*loader)(FILE *, int, struct LevelInfo *);
874 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
875 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
876 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
877 { "BODY", -1, LoadLevel_BODY },
878 { "CONT", -1, LoadLevel_CONT },
879 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
880 { "CNT3", -1, LoadLevel_CNT3 },
881 { "CUS1", -1, LoadLevel_CUS1 },
882 { "CUS2", -1, LoadLevel_CUS2 },
883 { "CUS3", -1, LoadLevel_CUS3 },
884 { "CUS4", -1, LoadLevel_CUS4 },
888 while (getFileChunkBE(file, chunk_name, &chunk_size))
892 while (chunk_info[i].name != NULL &&
893 strcmp(chunk_name, chunk_info[i].name) != 0)
896 if (chunk_info[i].name == NULL)
898 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
899 chunk_name, filename);
900 ReadUnusedBytesFromFile(file, chunk_size);
902 else if (chunk_info[i].size != -1 &&
903 chunk_info[i].size != chunk_size)
905 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
906 chunk_size, chunk_name, filename);
907 ReadUnusedBytesFromFile(file, chunk_size);
911 /* call function to load this level chunk */
912 int chunk_size_expected =
913 (chunk_info[i].loader)(file, chunk_size, level);
915 /* the size of some chunks cannot be checked before reading other
916 chunks first (like "HEAD" and "BODY") that contain some header
917 information, so check them here */
918 if (chunk_size_expected != chunk_size)
920 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
921 chunk_size, chunk_name, filename);
930 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
932 if (leveldir_current == NULL) /* only when dumping level */
936 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
939 /* determine correct game engine version of current level */
941 if (!leveldir_current->latest_engine)
943 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
944 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
945 IS_LEVELCLASS_UNDEFINED(leveldir_current))
949 printf("\n::: This level is private or contributed: '%s'\n", filename);
953 printf("\n::: Use the stored game engine version for this level\n");
956 /* For all levels which are not forced to use the latest game engine
957 version (normally user contributed, private and undefined levels),
958 use the version of the game engine the levels were created for.
960 Since 2.0.1, the game engine version is now directly stored
961 in the level file (chunk "VERS"), so there is no need anymore
962 to set the game version from the file version (except for old,
963 pre-2.0 levels, where the game version is still taken from the
964 file format version used to store the level -- see above). */
966 /* do some special adjustments to support older level versions */
967 if (level->file_version == FILE_VERSION_1_0)
969 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
970 Error(ERR_WARN, "using high speed movement for player");
972 /* player was faster than monsters in (pre-)1.0 levels */
973 level->double_speed = TRUE;
976 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
977 if (level->game_version == VERSION_IDENT(2,0,1,0))
978 level->em_slippery_gems = TRUE;
983 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
984 leveldir_current->sort_priority, filename);
988 printf("\n::: Use latest game engine version for this level.\n");
991 /* For all levels which are forced to use the latest game engine version
992 (normally all but user contributed, private and undefined levels), set
993 the game engine version to the actual version; this allows for actual
994 corrections in the game engine to take effect for existing, converted
995 levels (from "classic" or other existing games) to make the emulation
996 of the corresponding game more accurate, while (hopefully) not breaking
997 existing levels created from other players. */
1000 printf("::: changing engine from %d to %d\n",
1001 level->game_version, GAME_VERSION_ACTUAL);
1004 level->game_version = GAME_VERSION_ACTUAL;
1006 /* Set special EM style gems behaviour: EM style gems slip down from
1007 normal, steel and growing wall. As this is a more fundamental change,
1008 it seems better to set the default behaviour to "off" (as it is more
1009 natural) and make it configurable in the level editor (as a property
1010 of gem style elements). Already existing converted levels (neither
1011 private nor contributed levels) are changed to the new behaviour. */
1013 if (level->file_version < FILE_VERSION_2_0)
1014 level->em_slippery_gems = TRUE;
1018 printf("::: => %d\n", level->game_version);
1022 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
1026 /* map custom element change events that have changed in newer versions
1027 (these following values were accidentally changed in version 3.0.1) */
1028 if (level->game_version <= VERSION_IDENT(3,0,0,0))
1030 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1032 int element = EL_CUSTOM_START + i;
1034 /* order of checking and copying events to be mapped is important */
1035 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
1037 if (HAS_CHANGE_EVENT(element, j - 2))
1039 SET_CHANGE_EVENT(element, j - 2, FALSE);
1040 SET_CHANGE_EVENT(element, j, TRUE);
1044 /* order of checking and copying events to be mapped is important */
1045 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION_ACTIVE; j--)
1047 if (HAS_CHANGE_EVENT(element, j - 1))
1049 SET_CHANGE_EVENT(element, j - 1, FALSE);
1050 SET_CHANGE_EVENT(element, j, TRUE);
1056 /* some custom element change events get mapped since version 3.0.3 */
1057 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1059 int element = EL_CUSTOM_START + i;
1061 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
1062 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
1064 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
1065 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
1067 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1071 /* initialize "can_change" field for old levels with only one change page */
1072 if (level->game_version <= VERSION_IDENT(3,0,2,0))
1074 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1076 int element = EL_CUSTOM_START + i;
1078 if (CAN_CHANGE(element))
1079 element_info[element].change->can_change = TRUE;
1084 /* set default push delay values (corrected since version 3.0.7-1) */
1085 if (level->game_version < VERSION_IDENT(3,0,7,1))
1087 game.default_push_delay_fixed = 2;
1088 game.default_push_delay_random = 8;
1092 game.default_push_delay_fixed = 8;
1093 game.default_push_delay_random = 8;
1096 /* set uninitialized push delay values of custom elements in older levels */
1097 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1099 int element = EL_CUSTOM_START + i;
1101 if (element_info[element].push_delay_fixed == -1)
1102 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
1103 if (element_info[element].push_delay_random == -1)
1104 element_info[element].push_delay_random = game.default_push_delay_random;
1108 /* initialize element properties for level editor etc. */
1109 InitElementPropertiesEngine(level->game_version);
1112 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1116 /* map elements that have changed in newer versions */
1117 for (y = 0; y < level->fieldy; y++)
1119 for (x = 0; x < level->fieldx; x++)
1121 int element = level->field[x][y];
1123 if (level->game_version <= VERSION_IDENT(2,2,0,0))
1125 /* map game font elements */
1126 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1127 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1128 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1129 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1132 if (level->game_version < VERSION_IDENT(3,0,0,0))
1134 /* map Supaplex gravity tube elements */
1135 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1136 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1137 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1138 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1142 level->field[x][y] = element;
1146 /* copy elements to runtime playfield array */
1147 for (x = 0; x < MAX_LEV_FIELDX; x++)
1148 for (y = 0; y < MAX_LEV_FIELDY; y++)
1149 Feld[x][y] = level->field[x][y];
1151 /* initialize level size variables for faster access */
1152 lev_fieldx = level->fieldx;
1153 lev_fieldy = level->fieldy;
1155 /* determine border element for this level */
1159 void LoadLevelTemplate(int level_nr)
1161 char *filename = getLevelFilename(level_nr);
1163 LoadLevelFromFilename(&level_template, filename);
1165 LoadLevel_InitVersion(&level, filename);
1166 LoadLevel_InitElements(&level, filename);
1168 ActivateLevelTemplate();
1171 void LoadLevel(int level_nr)
1173 char *filename = getLevelFilename(level_nr);
1175 LoadLevelFromFilename(&level, filename);
1177 if (level.use_custom_template)
1178 LoadLevelTemplate(-1);
1181 LoadLevel_InitVersion(&level, filename);
1182 LoadLevel_InitElements(&level, filename);
1183 LoadLevel_InitPlayfield(&level, filename);
1185 LoadLevel_InitLevel(&level, filename);
1189 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1191 putFileVersion(file, level->file_version);
1192 putFileVersion(file, level->game_version);
1195 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1199 putFile8Bit(file, level->fieldx);
1200 putFile8Bit(file, level->fieldy);
1202 putFile16BitBE(file, level->time);
1203 putFile16BitBE(file, level->gems_needed);
1205 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1206 putFile8Bit(file, level->name[i]);
1208 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1209 putFile8Bit(file, level->score[i]);
1211 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
1212 for (y = 0; y < 3; y++)
1213 for (x = 0; x < 3; x++)
1214 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1215 level->yamyam_content[i][x][y]));
1216 putFile8Bit(file, level->amoeba_speed);
1217 putFile8Bit(file, level->time_magic_wall);
1218 putFile8Bit(file, level->time_wheel);
1219 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1220 level->amoeba_content));
1221 putFile8Bit(file, (level->double_speed ? 1 : 0));
1222 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
1223 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1224 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1226 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1228 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1231 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1235 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1236 putFile8Bit(file, level->author[i]);
1239 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1243 for (y = 0; y < level->fieldy; y++)
1244 for (x = 0; x < level->fieldx; x++)
1245 if (level->encoding_16bit_field)
1246 putFile16BitBE(file, level->field[x][y]);
1248 putFile8Bit(file, level->field[x][y]);
1252 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1256 putFile8Bit(file, EL_YAMYAM);
1257 putFile8Bit(file, level->num_yamyam_contents);
1258 putFile8Bit(file, 0);
1259 putFile8Bit(file, 0);
1261 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1262 for (y = 0; y < 3; y++)
1263 for (x = 0; x < 3; x++)
1264 if (level->encoding_16bit_field)
1265 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1267 putFile8Bit(file, level->yamyam_content[i][x][y]);
1271 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1274 int num_contents, content_xsize, content_ysize;
1275 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1277 if (element == EL_YAMYAM)
1279 num_contents = level->num_yamyam_contents;
1283 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1284 for (y = 0; y < 3; y++)
1285 for (x = 0; x < 3; x++)
1286 content_array[i][x][y] = level->yamyam_content[i][x][y];
1288 else if (element == EL_BD_AMOEBA)
1294 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1295 for (y = 0; y < 3; y++)
1296 for (x = 0; x < 3; x++)
1297 content_array[i][x][y] = EL_EMPTY;
1298 content_array[0][0][0] = level->amoeba_content;
1302 /* chunk header already written -- write empty chunk data */
1303 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1305 Error(ERR_WARN, "cannot save content for element '%d'", element);
1309 putFile16BitBE(file, element);
1310 putFile8Bit(file, num_contents);
1311 putFile8Bit(file, content_xsize);
1312 putFile8Bit(file, content_ysize);
1314 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1316 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
1317 for (y = 0; y < 3; y++)
1318 for (x = 0; x < 3; x++)
1319 putFile16BitBE(file, content_array[i][x][y]);
1322 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1325 int envelope_nr = element - EL_ENVELOPE_1;
1326 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1328 putFile16BitBE(file, element);
1329 putFile16BitBE(file, envelope_len);
1330 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1331 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1333 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1335 for (i = 0; i < envelope_len; i++)
1336 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1340 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1341 int num_changed_custom_elements)
1345 putFile16BitBE(file, num_changed_custom_elements);
1347 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1349 int element = EL_CUSTOM_START + i;
1351 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1353 if (check < num_changed_custom_elements)
1355 putFile16BitBE(file, element);
1356 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1363 if (check != num_changed_custom_elements) /* should not happen */
1364 Error(ERR_WARN, "inconsistent number of custom element properties");
1369 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1370 int num_changed_custom_elements)
1374 putFile16BitBE(file, num_changed_custom_elements);
1376 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1378 int element = EL_CUSTOM_START + i;
1380 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1382 if (check < num_changed_custom_elements)
1384 putFile16BitBE(file, element);
1385 putFile16BitBE(file, element_info[element].change->target_element);
1392 if (check != num_changed_custom_elements) /* should not happen */
1393 Error(ERR_WARN, "inconsistent number of custom target elements");
1398 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1399 int num_changed_custom_elements)
1401 int i, j, x, y, check = 0;
1403 putFile16BitBE(file, num_changed_custom_elements);
1405 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1407 int element = EL_CUSTOM_START + i;
1409 if (element_info[element].modified_settings)
1411 if (check < num_changed_custom_elements)
1413 putFile16BitBE(file, element);
1415 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
1416 putFile8Bit(file, element_info[element].description[j]);
1418 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1420 /* some free bytes for future properties and padding */
1421 WriteUnusedBytesToFile(file, 7);
1423 putFile8Bit(file, element_info[element].use_gfx_element);
1424 putFile16BitBE(file, element_info[element].gfx_element);
1426 putFile8Bit(file, element_info[element].collect_score);
1427 putFile8Bit(file, element_info[element].collect_count);
1429 putFile16BitBE(file, element_info[element].push_delay_fixed);
1430 putFile16BitBE(file, element_info[element].push_delay_random);
1431 putFile16BitBE(file, element_info[element].move_delay_fixed);
1432 putFile16BitBE(file, element_info[element].move_delay_random);
1434 putFile16BitBE(file, element_info[element].move_pattern);
1435 putFile8Bit(file, element_info[element].move_direction_initial);
1436 putFile8Bit(file, element_info[element].move_stepsize);
1438 for (y = 0; y < 3; y++)
1439 for (x = 0; x < 3; x++)
1440 putFile16BitBE(file, element_info[element].content[x][y]);
1442 putFile32BitBE(file, element_info[element].change->events);
1444 putFile16BitBE(file, element_info[element].change->target_element);
1446 putFile16BitBE(file, element_info[element].change->delay_fixed);
1447 putFile16BitBE(file, element_info[element].change->delay_random);
1448 putFile16BitBE(file, element_info[element].change->delay_frames);
1450 putFile16BitBE(file, element_info[element].change->trigger_element);
1452 putFile8Bit(file, element_info[element].change->explode);
1453 putFile8Bit(file, element_info[element].change->use_content);
1454 putFile8Bit(file, element_info[element].change->only_complete);
1455 putFile8Bit(file, element_info[element].change->use_random_change);
1457 putFile8Bit(file, element_info[element].change->random);
1458 putFile8Bit(file, element_info[element].change->power);
1460 for (y = 0; y < 3; y++)
1461 for (x = 0; x < 3; x++)
1462 putFile16BitBE(file, element_info[element].change->content[x][y]);
1464 putFile8Bit(file, element_info[element].slippery_type);
1466 /* some free bytes for future properties and padding */
1467 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1474 if (check != num_changed_custom_elements) /* should not happen */
1475 Error(ERR_WARN, "inconsistent number of custom element properties");
1479 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1481 struct ElementInfo *ei = &element_info[element];
1484 putFile16BitBE(file, element);
1486 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1487 putFile8Bit(file, ei->description[i]);
1489 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1490 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
1492 putFile8Bit(file, ei->num_change_pages);
1494 /* some free bytes for future base property values and padding */
1495 WriteUnusedBytesToFile(file, 5);
1497 /* write custom property values */
1499 putFile8Bit(file, ei->use_gfx_element);
1500 putFile16BitBE(file, ei->gfx_element);
1502 putFile8Bit(file, ei->collect_score);
1503 putFile8Bit(file, ei->collect_count);
1505 putFile16BitBE(file, ei->push_delay_fixed);
1506 putFile16BitBE(file, ei->push_delay_random);
1507 putFile16BitBE(file, ei->move_delay_fixed);
1508 putFile16BitBE(file, ei->move_delay_random);
1510 putFile16BitBE(file, ei->move_pattern);
1511 putFile8Bit(file, ei->move_direction_initial);
1512 putFile8Bit(file, ei->move_stepsize);
1514 putFile8Bit(file, ei->slippery_type);
1516 for (y = 0; y < 3; y++)
1517 for (x = 0; x < 3; x++)
1518 putFile16BitBE(file, ei->content[x][y]);
1520 /* some free bytes for future custom property values and padding */
1521 WriteUnusedBytesToFile(file, 12);
1523 /* write change property values */
1525 for (i = 0; i < ei->num_change_pages; i++)
1527 struct ElementChangeInfo *change = &ei->change_page[i];
1529 putFile32BitBE(file, change->events);
1531 putFile16BitBE(file, change->target_element);
1533 putFile16BitBE(file, change->delay_fixed);
1534 putFile16BitBE(file, change->delay_random);
1535 putFile16BitBE(file, change->delay_frames);
1537 putFile16BitBE(file, change->trigger_element);
1539 putFile8Bit(file, change->explode);
1540 putFile8Bit(file, change->use_content);
1541 putFile8Bit(file, change->only_complete);
1542 putFile8Bit(file, change->use_random_change);
1544 putFile8Bit(file, change->random);
1545 putFile8Bit(file, change->power);
1547 for (y = 0; y < 3; y++)
1548 for (x = 0; x < 3; x++)
1549 putFile16BitBE(file, change->content[x][y]);
1551 putFile8Bit(file, change->can_change);
1553 putFile8Bit(file, change->sides);
1555 /* some free bytes for future change property values and padding */
1556 WriteUnusedBytesToFile(file, 8);
1560 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1562 int body_chunk_size;
1566 if (!(file = fopen(filename, MODE_WRITE)))
1568 Error(ERR_WARN, "cannot save level file '%s'", filename);
1572 level->file_version = FILE_VERSION_ACTUAL;
1573 level->game_version = GAME_VERSION_ACTUAL;
1575 /* check level field for 16-bit elements */
1576 level->encoding_16bit_field = FALSE;
1577 for (y = 0; y < level->fieldy; y++)
1578 for (x = 0; x < level->fieldx; x++)
1579 if (level->field[x][y] > 255)
1580 level->encoding_16bit_field = TRUE;
1582 /* check yamyam content for 16-bit elements */
1583 level->encoding_16bit_yamyam = FALSE;
1584 for (i = 0; i < level->num_yamyam_contents; i++)
1585 for (y = 0; y < 3; y++)
1586 for (x = 0; x < 3; x++)
1587 if (level->yamyam_content[i][x][y] > 255)
1588 level->encoding_16bit_yamyam = TRUE;
1590 /* check amoeba content for 16-bit elements */
1591 level->encoding_16bit_amoeba = FALSE;
1592 if (level->amoeba_content > 255)
1593 level->encoding_16bit_amoeba = TRUE;
1595 /* calculate size of "BODY" chunk */
1597 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1599 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1600 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1602 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1603 SaveLevel_VERS(file, level);
1605 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1606 SaveLevel_HEAD(file, level);
1608 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1609 SaveLevel_AUTH(file, level);
1611 putFileChunkBE(file, "BODY", body_chunk_size);
1612 SaveLevel_BODY(file, level);
1614 if (level->encoding_16bit_yamyam ||
1615 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1617 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1618 SaveLevel_CNT2(file, level, EL_YAMYAM);
1621 if (level->encoding_16bit_amoeba)
1623 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1624 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1627 /* check for envelope content */
1628 for (i = 0; i < 4; i++)
1630 if (strlen(level->envelope_text[i]) > 0)
1632 int envelope_len = strlen(level->envelope_text[i]) + 1;
1634 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
1635 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
1639 /* check for non-default custom elements (unless using template level) */
1640 if (!level->use_custom_template)
1642 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1644 int element = EL_CUSTOM_START + i;
1646 if (element_info[element].modified_settings)
1648 int num_change_pages = element_info[element].num_change_pages;
1650 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
1651 SaveLevel_CUS4(file, level, element);
1658 SetFilePermissions(filename, PERMS_PRIVATE);
1661 void SaveLevel(int level_nr)
1663 char *filename = getLevelFilename(level_nr);
1665 SaveLevelFromFilename(&level, filename);
1668 void SaveLevelTemplate()
1670 char *filename = getLevelFilename(-1);
1672 SaveLevelFromFilename(&level, filename);
1675 void DumpLevel(struct LevelInfo *level)
1677 printf_line("-", 79);
1678 printf("Level xxx (file version %08d, game version %08d)\n",
1679 level->file_version, level->game_version);
1680 printf_line("-", 79);
1682 printf("Level Author: '%s'\n", level->author);
1683 printf("Level Title: '%s'\n", level->name);
1685 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1687 printf("Level Time: %d seconds\n", level->time);
1688 printf("Gems needed: %d\n", level->gems_needed);
1690 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1691 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1692 printf("Time for Light: %d seconds\n", level->time_light);
1693 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1695 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1697 printf("Gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
1698 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1699 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1701 printf_line("-", 79);
1705 /* ========================================================================= */
1706 /* tape file functions */
1707 /* ========================================================================= */
1709 static void setTapeInfoToDefaults()
1713 /* always start with reliable default values (empty tape) */
1716 /* default values (also for pre-1.2 tapes) with only the first player */
1717 tape.player_participates[0] = TRUE;
1718 for (i = 1; i < MAX_PLAYERS; i++)
1719 tape.player_participates[i] = FALSE;
1721 /* at least one (default: the first) player participates in every tape */
1722 tape.num_participating_players = 1;
1724 tape.level_nr = level_nr;
1726 tape.changed = FALSE;
1728 tape.recording = FALSE;
1729 tape.playing = FALSE;
1730 tape.pausing = FALSE;
1733 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1735 tape->file_version = getFileVersion(file);
1736 tape->game_version = getFileVersion(file);
1741 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1745 tape->random_seed = getFile32BitBE(file);
1746 tape->date = getFile32BitBE(file);
1747 tape->length = getFile32BitBE(file);
1749 /* read header fields that are new since version 1.2 */
1750 if (tape->file_version >= FILE_VERSION_1_2)
1752 byte store_participating_players = getFile8Bit(file);
1755 /* since version 1.2, tapes store which players participate in the tape */
1756 tape->num_participating_players = 0;
1757 for (i = 0; i < MAX_PLAYERS; i++)
1759 tape->player_participates[i] = FALSE;
1761 if (store_participating_players & (1 << i))
1763 tape->player_participates[i] = TRUE;
1764 tape->num_participating_players++;
1768 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1770 engine_version = getFileVersion(file);
1771 if (engine_version > 0)
1772 tape->engine_version = engine_version;
1774 tape->engine_version = tape->game_version;
1780 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1782 int level_identifier_size;
1785 level_identifier_size = getFile16BitBE(file);
1787 tape->level_identifier =
1788 checked_realloc(tape->level_identifier, level_identifier_size);
1790 for (i = 0; i < level_identifier_size; i++)
1791 tape->level_identifier[i] = getFile8Bit(file);
1793 tape->level_nr = getFile16BitBE(file);
1795 chunk_size = 2 + level_identifier_size + 2;
1800 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1803 int chunk_size_expected =
1804 (tape->num_participating_players + 1) * tape->length;
1806 if (chunk_size_expected != chunk_size)
1808 ReadUnusedBytesFromFile(file, chunk_size);
1809 return chunk_size_expected;
1812 for (i = 0; i < tape->length; i++)
1814 if (i >= MAX_TAPELEN)
1817 for (j = 0; j < MAX_PLAYERS; j++)
1819 tape->pos[i].action[j] = MV_NO_MOVING;
1821 if (tape->player_participates[j])
1822 tape->pos[i].action[j] = getFile8Bit(file);
1825 tape->pos[i].delay = getFile8Bit(file);
1827 if (tape->file_version == FILE_VERSION_1_0)
1829 /* eliminate possible diagonal moves in old tapes */
1830 /* this is only for backward compatibility */
1832 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1833 byte action = tape->pos[i].action[0];
1834 int k, num_moves = 0;
1836 for (k = 0; k<4; k++)
1838 if (action & joy_dir[k])
1840 tape->pos[i + num_moves].action[0] = joy_dir[k];
1842 tape->pos[i + num_moves].delay = 0;
1851 tape->length += num_moves;
1854 else if (tape->file_version < FILE_VERSION_2_0)
1856 /* convert pre-2.0 tapes to new tape format */
1858 if (tape->pos[i].delay > 1)
1861 tape->pos[i + 1] = tape->pos[i];
1862 tape->pos[i + 1].delay = 1;
1865 for (j = 0; j < MAX_PLAYERS; j++)
1866 tape->pos[i].action[j] = MV_NO_MOVING;
1867 tape->pos[i].delay--;
1878 if (i != tape->length)
1879 chunk_size = (tape->num_participating_players + 1) * i;
1884 void LoadTapeFromFilename(char *filename)
1886 char cookie[MAX_LINE_LEN];
1887 char chunk_name[CHUNK_ID_LEN + 1];
1891 /* always start with reliable default values */
1892 setTapeInfoToDefaults();
1894 if (!(file = fopen(filename, MODE_READ)))
1897 getFileChunkBE(file, chunk_name, NULL);
1898 if (strcmp(chunk_name, "RND1") == 0)
1900 getFile32BitBE(file); /* not used */
1902 getFileChunkBE(file, chunk_name, NULL);
1903 if (strcmp(chunk_name, "TAPE") != 0)
1905 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1910 else /* check for pre-2.0 file format with cookie string */
1912 strcpy(cookie, chunk_name);
1913 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1914 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1915 cookie[strlen(cookie) - 1] = '\0';
1917 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1919 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1924 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1926 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1931 /* pre-2.0 tape files have no game version, so use file version here */
1932 tape.game_version = tape.file_version;
1935 if (tape.file_version < FILE_VERSION_1_2)
1937 /* tape files from versions before 1.2.0 without chunk structure */
1938 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1939 LoadTape_BODY(file, 2 * tape.length, &tape);
1947 int (*loader)(FILE *, int, struct TapeInfo *);
1951 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1952 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1953 { "INFO", -1, LoadTape_INFO },
1954 { "BODY", -1, LoadTape_BODY },
1958 while (getFileChunkBE(file, chunk_name, &chunk_size))
1962 while (chunk_info[i].name != NULL &&
1963 strcmp(chunk_name, chunk_info[i].name) != 0)
1966 if (chunk_info[i].name == NULL)
1968 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1969 chunk_name, filename);
1970 ReadUnusedBytesFromFile(file, chunk_size);
1972 else if (chunk_info[i].size != -1 &&
1973 chunk_info[i].size != chunk_size)
1975 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1976 chunk_size, chunk_name, filename);
1977 ReadUnusedBytesFromFile(file, chunk_size);
1981 /* call function to load this tape chunk */
1982 int chunk_size_expected =
1983 (chunk_info[i].loader)(file, chunk_size, &tape);
1985 /* the size of some chunks cannot be checked before reading other
1986 chunks first (like "HEAD" and "BODY") that contain some header
1987 information, so check them here */
1988 if (chunk_size_expected != chunk_size)
1990 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1991 chunk_size, chunk_name, filename);
1999 tape.length_seconds = GetTapeLength();
2002 printf("::: tape game version: %d\n", tape.game_version);
2003 printf("::: tape engine version: %d\n", tape.engine_version);
2007 void LoadTape(int level_nr)
2009 char *filename = getTapeFilename(level_nr);
2011 LoadTapeFromFilename(filename);
2014 void LoadSolutionTape(int level_nr)
2016 char *filename = getSolutionTapeFilename(level_nr);
2018 LoadTapeFromFilename(filename);
2021 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2023 putFileVersion(file, tape->file_version);
2024 putFileVersion(file, tape->game_version);
2027 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2030 byte store_participating_players = 0;
2032 /* set bits for participating players for compact storage */
2033 for (i = 0; i < MAX_PLAYERS; i++)
2034 if (tape->player_participates[i])
2035 store_participating_players |= (1 << i);
2037 putFile32BitBE(file, tape->random_seed);
2038 putFile32BitBE(file, tape->date);
2039 putFile32BitBE(file, tape->length);
2041 putFile8Bit(file, store_participating_players);
2043 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2044 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2046 putFileVersion(file, tape->engine_version);
2049 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2051 int level_identifier_size = strlen(tape->level_identifier) + 1;
2054 putFile16BitBE(file, level_identifier_size);
2056 for (i = 0; i < level_identifier_size; i++)
2057 putFile8Bit(file, tape->level_identifier[i]);
2059 putFile16BitBE(file, tape->level_nr);
2062 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2066 for (i = 0; i < tape->length; i++)
2068 for (j = 0; j < MAX_PLAYERS; j++)
2069 if (tape->player_participates[j])
2070 putFile8Bit(file, tape->pos[i].action[j]);
2072 putFile8Bit(file, tape->pos[i].delay);
2076 void SaveTape(int level_nr)
2078 char *filename = getTapeFilename(level_nr);
2080 boolean new_tape = TRUE;
2081 int num_participating_players = 0;
2082 int info_chunk_size;
2083 int body_chunk_size;
2086 InitTapeDirectory(leveldir_current->filename);
2088 /* if a tape still exists, ask to overwrite it */
2089 if (access(filename, F_OK) == 0)
2092 if (!Request("Replace old tape ?", REQ_ASK))
2096 if (!(file = fopen(filename, MODE_WRITE)))
2098 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2102 tape.file_version = FILE_VERSION_ACTUAL;
2103 tape.game_version = GAME_VERSION_ACTUAL;
2105 /* count number of participating players */
2106 for (i = 0; i < MAX_PLAYERS; i++)
2107 if (tape.player_participates[i])
2108 num_participating_players++;
2110 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2111 body_chunk_size = (num_participating_players + 1) * tape.length;
2113 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2114 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2116 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2117 SaveTape_VERS(file, &tape);
2119 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2120 SaveTape_HEAD(file, &tape);
2122 putFileChunkBE(file, "INFO", info_chunk_size);
2123 SaveTape_INFO(file, &tape);
2125 putFileChunkBE(file, "BODY", body_chunk_size);
2126 SaveTape_BODY(file, &tape);
2130 SetFilePermissions(filename, PERMS_PRIVATE);
2132 tape.changed = FALSE;
2135 Request("tape saved !", REQ_CONFIRM);
2138 void DumpTape(struct TapeInfo *tape)
2142 if (TAPE_IS_EMPTY(*tape))
2144 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2148 printf_line("-", 79);
2149 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2150 tape->level_nr, tape->file_version, tape->game_version);
2151 printf("Level series identifier: '%s'\n", tape->level_identifier);
2152 printf_line("-", 79);
2154 for (i = 0; i < tape->length; i++)
2156 if (i >= MAX_TAPELEN)
2159 printf("%03d: ", i);
2161 for (j = 0; j < MAX_PLAYERS; j++)
2163 if (tape->player_participates[j])
2165 int action = tape->pos[i].action[j];
2167 printf("%d:%02x ", j, action);
2168 printf("[%c%c%c%c|%c%c] - ",
2169 (action & JOY_LEFT ? '<' : ' '),
2170 (action & JOY_RIGHT ? '>' : ' '),
2171 (action & JOY_UP ? '^' : ' '),
2172 (action & JOY_DOWN ? 'v' : ' '),
2173 (action & JOY_BUTTON_1 ? '1' : ' '),
2174 (action & JOY_BUTTON_2 ? '2' : ' '));
2178 printf("(%03d)\n", tape->pos[i].delay);
2181 printf_line("-", 79);
2185 /* ========================================================================= */
2186 /* score file functions */
2187 /* ========================================================================= */
2189 void LoadScore(int level_nr)
2192 char *filename = getScoreFilename(level_nr);
2193 char cookie[MAX_LINE_LEN];
2194 char line[MAX_LINE_LEN];
2198 /* always start with reliable default values */
2199 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2201 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2202 highscore[i].Score = 0;
2205 if (!(file = fopen(filename, MODE_READ)))
2208 /* check file identifier */
2209 fgets(cookie, MAX_LINE_LEN, file);
2210 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2211 cookie[strlen(cookie) - 1] = '\0';
2213 if (!checkCookieString(cookie, SCORE_COOKIE))
2215 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2220 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2222 fscanf(file, "%d", &highscore[i].Score);
2223 fgets(line, MAX_LINE_LEN, file);
2225 if (line[strlen(line) - 1] == '\n')
2226 line[strlen(line) - 1] = '\0';
2228 for (line_ptr = line; *line_ptr; line_ptr++)
2230 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2232 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2233 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2242 void SaveScore(int level_nr)
2245 char *filename = getScoreFilename(level_nr);
2248 InitScoreDirectory(leveldir_current->filename);
2250 if (!(file = fopen(filename, MODE_WRITE)))
2252 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2256 fprintf(file, "%s\n\n", SCORE_COOKIE);
2258 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
2259 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2263 SetFilePermissions(filename, PERMS_PUBLIC);
2267 /* ========================================================================= */
2268 /* setup file functions */
2269 /* ========================================================================= */
2271 #define TOKEN_STR_PLAYER_PREFIX "player_"
2274 #define SETUP_TOKEN_PLAYER_NAME 0
2275 #define SETUP_TOKEN_SOUND 1
2276 #define SETUP_TOKEN_SOUND_LOOPS 2
2277 #define SETUP_TOKEN_SOUND_MUSIC 3
2278 #define SETUP_TOKEN_SOUND_SIMPLE 4
2279 #define SETUP_TOKEN_TOONS 5
2280 #define SETUP_TOKEN_SCROLL_DELAY 6
2281 #define SETUP_TOKEN_SOFT_SCROLLING 7
2282 #define SETUP_TOKEN_FADING 8
2283 #define SETUP_TOKEN_AUTORECORD 9
2284 #define SETUP_TOKEN_QUICK_DOORS 10
2285 #define SETUP_TOKEN_TEAM_MODE 11
2286 #define SETUP_TOKEN_HANDICAP 12
2287 #define SETUP_TOKEN_TIME_LIMIT 13
2288 #define SETUP_TOKEN_FULLSCREEN 14
2289 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2290 #define SETUP_TOKEN_GRAPHICS_SET 16
2291 #define SETUP_TOKEN_SOUNDS_SET 17
2292 #define SETUP_TOKEN_MUSIC_SET 18
2293 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2294 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2295 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2297 #define NUM_GLOBAL_SETUP_TOKENS 22
2300 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2301 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2302 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2303 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2304 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2305 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2306 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2307 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2308 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2309 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2310 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2311 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
2313 #define NUM_EDITOR_SETUP_TOKENS 12
2315 /* shortcut setup */
2316 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2317 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2318 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2320 #define NUM_SHORTCUT_SETUP_TOKENS 3
2323 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2324 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2325 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2326 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2327 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2328 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2329 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2330 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2331 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2332 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
2333 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2334 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2335 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2336 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2337 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2338 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
2340 #define NUM_PLAYER_SETUP_TOKENS 16
2343 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2344 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2346 #define NUM_SYSTEM_SETUP_TOKENS 2
2349 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2351 #define NUM_OPTIONS_SETUP_TOKENS 1
2354 static struct SetupInfo si;
2355 static struct SetupEditorInfo sei;
2356 static struct SetupShortcutInfo ssi;
2357 static struct SetupInputInfo sii;
2358 static struct SetupSystemInfo syi;
2359 static struct OptionInfo soi;
2361 static struct TokenInfo global_setup_tokens[] =
2363 { TYPE_STRING, &si.player_name, "player_name" },
2364 { TYPE_SWITCH, &si.sound, "sound" },
2365 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2366 { TYPE_SWITCH, &si.sound_music, "background_music" },
2367 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2368 { TYPE_SWITCH, &si.toons, "toons" },
2369 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2370 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2371 { TYPE_SWITCH, &si.fading, "screen_fading" },
2372 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2373 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2374 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2375 { TYPE_SWITCH, &si.handicap, "handicap" },
2376 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2377 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2378 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2379 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2380 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2381 { TYPE_STRING, &si.music_set, "music_set" },
2382 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2383 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2384 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2387 static struct TokenInfo editor_setup_tokens[] =
2389 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2390 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2391 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2392 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2393 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2394 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2395 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2396 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2397 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2398 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2399 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2400 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
2403 static struct TokenInfo shortcut_setup_tokens[] =
2405 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2406 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2407 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2410 static struct TokenInfo player_setup_tokens[] =
2412 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2413 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2414 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2415 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2416 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2417 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2418 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2419 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2420 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2421 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2422 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2423 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2424 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2425 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2426 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2427 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
2430 static struct TokenInfo system_setup_tokens[] =
2432 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2433 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2436 static struct TokenInfo options_setup_tokens[] =
2438 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2441 static char *get_corrected_login_name(char *login_name)
2443 /* needed because player name must be a fixed length string */
2444 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2446 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2447 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2449 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2450 if (strchr(login_name_new, ' '))
2451 *strchr(login_name_new, ' ') = '\0';
2453 return login_name_new;
2456 static void setSetupInfoToDefaults(struct SetupInfo *si)
2460 si->player_name = get_corrected_login_name(getLoginName());
2463 si->sound_loops = TRUE;
2464 si->sound_music = TRUE;
2465 si->sound_simple = TRUE;
2467 si->double_buffering = TRUE;
2468 si->direct_draw = !si->double_buffering;
2469 si->scroll_delay = TRUE;
2470 si->soft_scrolling = TRUE;
2472 si->autorecord = TRUE;
2473 si->quick_doors = FALSE;
2474 si->team_mode = FALSE;
2475 si->handicap = TRUE;
2476 si->time_limit = TRUE;
2477 si->fullscreen = FALSE;
2478 si->ask_on_escape = TRUE;
2480 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2481 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2482 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2483 si->override_level_graphics = FALSE;
2484 si->override_level_sounds = FALSE;
2485 si->override_level_music = FALSE;
2487 si->editor.el_boulderdash = TRUE;
2488 si->editor.el_emerald_mine = TRUE;
2489 si->editor.el_more = TRUE;
2490 si->editor.el_sokoban = TRUE;
2491 si->editor.el_supaplex = TRUE;
2492 si->editor.el_diamond_caves = TRUE;
2493 si->editor.el_dx_boulderdash = TRUE;
2494 si->editor.el_chars = TRUE;
2495 si->editor.el_custom = TRUE;
2496 si->editor.el_custom_more = FALSE;
2498 si->editor.el_headlines = TRUE;
2499 si->editor.el_user_defined = FALSE;
2501 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2502 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2503 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2505 for (i = 0; i < MAX_PLAYERS; i++)
2507 si->input[i].use_joystick = FALSE;
2508 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2509 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2510 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2511 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2512 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2513 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2514 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2515 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2516 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2517 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2518 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2519 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2520 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2521 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2522 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2525 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2526 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2528 si->options.verbose = FALSE;
2531 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2535 if (!setup_file_hash)
2540 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
2541 setSetupInfo(global_setup_tokens, i,
2542 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2547 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
2548 setSetupInfo(editor_setup_tokens, i,
2549 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2552 /* shortcut setup */
2553 ssi = setup.shortcut;
2554 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
2555 setSetupInfo(shortcut_setup_tokens, i,
2556 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2557 setup.shortcut = ssi;
2560 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
2564 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2566 sii = setup.input[pnr];
2567 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
2569 char full_token[100];
2571 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2572 setSetupInfo(player_setup_tokens, i,
2573 getHashEntry(setup_file_hash, full_token));
2575 setup.input[pnr] = sii;
2580 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
2581 setSetupInfo(system_setup_tokens, i,
2582 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2586 soi = setup.options;
2587 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
2588 setSetupInfo(options_setup_tokens, i,
2589 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2590 setup.options = soi;
2595 char *filename = getSetupFilename();
2596 SetupFileHash *setup_file_hash = NULL;
2598 /* always start with reliable default values */
2599 setSetupInfoToDefaults(&setup);
2601 setup_file_hash = loadSetupFileHash(filename);
2603 if (setup_file_hash)
2605 char *player_name_new;
2607 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2608 decodeSetupFileHash(setup_file_hash);
2610 setup.direct_draw = !setup.double_buffering;
2612 freeSetupFileHash(setup_file_hash);
2614 /* needed to work around problems with fixed length strings */
2615 player_name_new = get_corrected_login_name(setup.player_name);
2616 free(setup.player_name);
2617 setup.player_name = player_name_new;
2620 Error(ERR_WARN, "using default setup values");
2625 char *filename = getSetupFilename();
2629 InitUserDataDirectory();
2631 if (!(file = fopen(filename, MODE_WRITE)))
2633 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2637 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2638 getCookie("SETUP")));
2639 fprintf(file, "\n");
2643 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
2645 /* just to make things nicer :) */
2646 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2647 i == SETUP_TOKEN_GRAPHICS_SET)
2648 fprintf(file, "\n");
2650 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2655 fprintf(file, "\n");
2656 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
2657 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2659 /* shortcut setup */
2660 ssi = setup.shortcut;
2661 fprintf(file, "\n");
2662 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
2663 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2666 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
2670 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2671 fprintf(file, "\n");
2673 sii = setup.input[pnr];
2674 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
2675 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2680 fprintf(file, "\n");
2681 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
2682 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2685 soi = setup.options;
2686 fprintf(file, "\n");
2687 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
2688 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2692 SetFilePermissions(filename, PERMS_PRIVATE);
2695 void LoadCustomElementDescriptions()
2697 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2698 SetupFileHash *setup_file_hash;
2701 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
2703 if (element_info[i].custom_description != NULL)
2705 free(element_info[i].custom_description);
2706 element_info[i].custom_description = NULL;
2710 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2713 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
2715 char *token = getStringCat2(element_info[i].token_name, ".name");
2716 char *value = getHashEntry(setup_file_hash, token);
2719 element_info[i].custom_description = getStringCopy(value);
2724 freeSetupFileHash(setup_file_hash);
2727 void LoadSpecialMenuDesignSettings()
2729 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2730 SetupFileHash *setup_file_hash;
2733 /* always start with reliable default values from default config */
2734 for (i = 0; image_config_vars[i].token != NULL; i++)
2735 for (j = 0; image_config[j].token != NULL; j++)
2736 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2737 *image_config_vars[i].value =
2738 get_auto_parameter_value(image_config_vars[i].token,
2739 image_config[j].value);
2741 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2744 /* special case: initialize with default values that may be overwritten */
2745 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2747 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2748 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2749 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2751 if (value_x != NULL)
2752 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2753 if (value_y != NULL)
2754 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2755 if (list_size != NULL)
2756 menu.list_size[i] = get_integer_from_string(list_size);
2759 /* read (and overwrite with) values that may be specified in config file */
2760 for (i = 0; image_config_vars[i].token != NULL; i++)
2762 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2765 *image_config_vars[i].value =
2766 get_auto_parameter_value(image_config_vars[i].token, value);
2769 freeSetupFileHash(setup_file_hash);
2772 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
2774 char *filename = getEditorSetupFilename();
2775 SetupFileList *setup_file_list, *list;
2776 SetupFileHash *element_hash;
2777 int num_unknown_tokens = 0;
2780 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
2783 element_hash = newSetupFileHash();
2785 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
2786 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
2788 /* determined size may be larger than needed (due to unknown elements) */
2790 for (list = setup_file_list; list != NULL; list = list->next)
2793 /* add space for up to 3 more elements for padding that may be needed */
2796 *elements = checked_malloc(*num_elements * sizeof(int));
2799 for (list = setup_file_list; list != NULL; list = list->next)
2801 char *value = getHashEntry(element_hash, list->token);
2805 (*elements)[(*num_elements)++] = atoi(value);
2809 if (num_unknown_tokens == 0)
2811 Error(ERR_RETURN_LINE, "-");
2812 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
2813 Error(ERR_RETURN, "- config file: '%s'", filename);
2815 num_unknown_tokens++;
2818 Error(ERR_RETURN, "- token: '%s'", list->token);
2822 if (num_unknown_tokens > 0)
2823 Error(ERR_RETURN_LINE, "-");
2825 while (*num_elements % 4) /* pad with empty elements, if needed */
2826 (*elements)[(*num_elements)++] = EL_EMPTY;
2828 freeSetupFileList(setup_file_list);
2829 freeSetupFileHash(element_hash);
2833 for (i = 0; i < *num_elements; i++)
2834 printf("editor: element '%s' [%d]\n",
2835 element_info[(*elements)[i]].token_name, (*elements)[i]);
2839 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
2842 SetupFileHash *setup_file_hash = NULL;
2843 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
2844 char *filename_music, *filename_prefix, *filename_info;
2850 token_to_value_ptr[] =
2852 { "title_header", &tmp_music_file_info.title_header },
2853 { "artist_header", &tmp_music_file_info.artist_header },
2854 { "album_header", &tmp_music_file_info.album_header },
2855 { "year_header", &tmp_music_file_info.year_header },
2857 { "title", &tmp_music_file_info.title },
2858 { "artist", &tmp_music_file_info.artist },
2859 { "album", &tmp_music_file_info.album },
2860 { "year", &tmp_music_file_info.year },
2866 filename_music = (is_sound ? getCustomSoundFilename(basename) :
2867 getCustomMusicFilename(basename));
2869 if (filename_music == NULL)
2872 /* ---------- try to replace file extension ---------- */
2874 filename_prefix = getStringCopy(filename_music);
2875 if (strrchr(filename_prefix, '.') != NULL)
2876 *strrchr(filename_prefix, '.') = '\0';
2877 filename_info = getStringCat2(filename_prefix, ".txt");
2880 printf("trying to load file '%s'...\n", filename_info);
2883 if (fileExists(filename_info))
2884 setup_file_hash = loadSetupFileHash(filename_info);
2886 free(filename_prefix);
2887 free(filename_info);
2889 if (setup_file_hash == NULL)
2891 /* ---------- try to add file extension ---------- */
2893 filename_prefix = getStringCopy(filename_music);
2894 filename_info = getStringCat2(filename_prefix, ".txt");
2897 printf("trying to load file '%s'...\n", filename_info);
2900 if (fileExists(filename_info))
2901 setup_file_hash = loadSetupFileHash(filename_info);
2903 free(filename_prefix);
2904 free(filename_info);
2907 if (setup_file_hash == NULL)
2910 /* ---------- music file info found ---------- */
2912 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
2914 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
2916 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
2918 *token_to_value_ptr[i].value_ptr =
2919 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
2922 tmp_music_file_info.basename = getStringCopy(basename);
2923 tmp_music_file_info.music = music;
2924 tmp_music_file_info.is_sound = is_sound;
2926 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
2927 *new_music_file_info = tmp_music_file_info;
2929 return new_music_file_info;
2932 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
2934 return get_music_file_info_ext(basename, music, FALSE);
2937 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
2939 return get_music_file_info_ext(basename, sound, TRUE);
2942 static boolean music_info_listed_ext(struct MusicFileInfo *list,
2943 char *basename, boolean is_sound)
2945 for (; list != NULL; list = list->next)
2946 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
2952 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
2954 return music_info_listed_ext(list, basename, FALSE);
2957 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
2959 return music_info_listed_ext(list, basename, TRUE);
2962 void LoadMusicInfo()
2964 char *music_directory = getCustomMusicDirectory();
2965 int num_music = getMusicListSize();
2966 int num_music_noconf = 0;
2967 int num_sounds = getSoundListSize();
2969 struct dirent *dir_entry;
2970 struct FileInfo *music, *sound;
2971 struct MusicFileInfo *next, **new;
2974 while (music_file_info != NULL)
2976 next = music_file_info->next;
2978 checked_free(music_file_info->basename);
2980 checked_free(music_file_info->title_header);
2981 checked_free(music_file_info->artist_header);
2982 checked_free(music_file_info->album_header);
2983 checked_free(music_file_info->year_header);
2985 checked_free(music_file_info->title);
2986 checked_free(music_file_info->artist);
2987 checked_free(music_file_info->album);
2988 checked_free(music_file_info->year);
2990 free(music_file_info);
2992 music_file_info = next;
2995 new = &music_file_info;
2998 printf("::: num_music == %d\n", num_music);
3001 for (i = 0; i < num_music; i++)
3003 music = getMusicListEntry(i);
3006 printf("::: %d [%08x]\n", i, music->filename);
3009 if (music->filename == NULL)
3012 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
3015 /* a configured file may be not recognized as music */
3016 if (!FileIsMusic(music->filename))
3020 printf("::: -> '%s' (configured)\n", music->filename);
3023 if (!music_info_listed(music_file_info, music->filename))
3025 *new = get_music_file_info(music->filename, i);
3027 new = &(*new)->next;
3031 if ((dir = opendir(music_directory)) == NULL)
3033 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
3037 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
3039 char *basename = dir_entry->d_name;
3040 boolean music_already_used = FALSE;
3043 /* skip all music files that are configured in music config file */
3044 for (i = 0; i < num_music; i++)
3046 music = getMusicListEntry(i);
3048 if (music->filename == NULL)
3051 if (strcmp(basename, music->filename) == 0)
3053 music_already_used = TRUE;
3058 if (music_already_used)
3061 if (!FileIsMusic(basename))
3065 printf("::: -> '%s' (found in directory)\n", basename);
3068 if (!music_info_listed(music_file_info, basename))
3070 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
3072 new = &(*new)->next;
3080 for (i = 0; i < num_sounds; i++)
3082 sound = getSoundListEntry(i);
3084 if (sound->filename == NULL)
3087 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
3090 /* a configured file may be not recognized as sound */
3091 if (!FileIsSound(sound->filename))
3095 printf("::: -> '%s' (configured)\n", sound->filename);
3098 if (!sound_info_listed(music_file_info, sound->filename))
3100 *new = get_sound_file_info(sound->filename, i);
3102 new = &(*new)->next;
3108 for (next = music_file_info; next != NULL; next = next->next)
3109 printf("::: title == '%s'\n", next->title);
3113 void add_helpanim_entry(int element, int action, int direction, int delay,
3114 int *num_list_entries)
3116 struct HelpAnimInfo *new_list_entry;
3117 (*num_list_entries)++;
3120 checked_realloc(helpanim_info,
3121 *num_list_entries * sizeof(struct HelpAnimInfo));
3122 new_list_entry = &helpanim_info[*num_list_entries - 1];
3124 new_list_entry->element = element;
3125 new_list_entry->action = action;
3126 new_list_entry->direction = direction;
3127 new_list_entry->delay = delay;
3130 void print_unknown_token(char *filename, char *token, int token_nr)
3134 Error(ERR_RETURN_LINE, "-");
3135 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3136 Error(ERR_RETURN, "- config file: '%s'", filename);
3139 Error(ERR_RETURN, "- token: '%s'", token);
3142 void print_unknown_token_end(int token_nr)
3145 Error(ERR_RETURN_LINE, "-");
3148 void LoadHelpAnimInfo()
3150 char *filename = getHelpAnimFilename();
3151 SetupFileList *setup_file_list = NULL, *list;
3152 SetupFileHash *element_hash, *action_hash, *direction_hash;
3153 int num_list_entries = 0;
3154 int num_unknown_tokens = 0;
3157 if (fileExists(filename))
3158 setup_file_list = loadSetupFileList(filename);
3160 if (setup_file_list == NULL)
3162 /* use reliable default values from static configuration */
3163 SetupFileList *insert_ptr;
3165 insert_ptr = setup_file_list =
3166 newSetupFileList(helpanim_config[0].token,
3167 helpanim_config[0].value);
3169 for (i = 1; helpanim_config[i].token; i++)
3170 insert_ptr = addListEntry(insert_ptr,
3171 helpanim_config[i].token,
3172 helpanim_config[i].value);
3175 element_hash = newSetupFileHash();
3176 action_hash = newSetupFileHash();
3177 direction_hash = newSetupFileHash();
3179 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3180 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3182 for (i = 0; i < NUM_ACTIONS; i++)
3183 setHashEntry(action_hash, element_action_info[i].suffix,
3184 i_to_a(element_action_info[i].value));
3186 /* do not store direction index (bit) here, but direction value! */
3187 for (i = 0; i < NUM_DIRECTIONS; i++)
3188 setHashEntry(direction_hash, element_direction_info[i].suffix,
3189 i_to_a(1 << element_direction_info[i].value));
3191 for (list = setup_file_list; list != NULL; list = list->next)
3193 char *element_token, *action_token, *direction_token;
3194 char *element_value, *action_value, *direction_value;
3195 int delay = atoi(list->value);
3197 if (strcmp(list->token, "end") == 0)
3199 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3204 /* first try to break element into element/action/direction parts;
3205 if this does not work, also accept combined "element[.act][.dir]"
3206 elements (like "dynamite.active"), which are unique elements */
3208 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
3210 element_value = getHashEntry(element_hash, list->token);
3211 if (element_value != NULL) /* element found */
3212 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3216 /* no further suffixes found -- this is not an element */
3217 print_unknown_token(filename, list->token, num_unknown_tokens++);
3223 /* token has format "<prefix>.<something>" */
3225 action_token = strchr(list->token, '.'); /* suffix may be action ... */
3226 direction_token = action_token; /* ... or direction */
3228 element_token = getStringCopy(list->token);
3229 *strchr(element_token, '.') = '\0';
3231 element_value = getHashEntry(element_hash, element_token);
3233 if (element_value == NULL) /* this is no element */
3235 element_value = getHashEntry(element_hash, list->token);
3236 if (element_value != NULL) /* combined element found */
3237 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3240 print_unknown_token(filename, list->token, num_unknown_tokens++);
3242 free(element_token);
3247 action_value = getHashEntry(action_hash, action_token);
3249 if (action_value != NULL) /* action found */
3251 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
3254 free(element_token);
3259 direction_value = getHashEntry(direction_hash, direction_token);
3261 if (direction_value != NULL) /* direction found */
3263 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
3266 free(element_token);
3271 if (strchr(action_token + 1, '.') == NULL)
3273 /* no further suffixes found -- this is not an action nor direction */
3275 element_value = getHashEntry(element_hash, list->token);
3276 if (element_value != NULL) /* combined element found */
3277 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3280 print_unknown_token(filename, list->token, num_unknown_tokens++);
3282 free(element_token);
3287 /* token has format "<prefix>.<suffix>.<something>" */
3289 direction_token = strchr(action_token + 1, '.');
3291 action_token = getStringCopy(action_token);
3292 *strchr(action_token + 1, '.') = '\0';
3294 action_value = getHashEntry(action_hash, action_token);
3296 if (action_value == NULL) /* this is no action */
3298 element_value = getHashEntry(element_hash, list->token);
3299 if (element_value != NULL) /* combined element found */
3300 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3303 print_unknown_token(filename, list->token, num_unknown_tokens++);
3305 free(element_token);
3311 direction_value = getHashEntry(direction_hash, direction_token);
3313 if (direction_value != NULL) /* direction found */
3315 add_helpanim_entry(atoi(element_value), atoi(action_value),
3316 atoi(direction_value), delay, &num_list_entries);
3318 free(element_token);
3324 /* this is no direction */
3326 element_value = getHashEntry(element_hash, list->token);
3327 if (element_value != NULL) /* combined element found */
3328 add_helpanim_entry(atoi(element_value), -1, -1, delay,
3331 print_unknown_token(filename, list->token, num_unknown_tokens++);
3333 free(element_token);
3337 print_unknown_token_end(num_unknown_tokens);
3339 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
3340 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
3342 freeSetupFileList(setup_file_list);
3343 freeSetupFileHash(element_hash);
3344 freeSetupFileHash(action_hash);
3345 freeSetupFileHash(direction_hash);
3349 for (i = 0; i < num_list_entries; i++)
3350 printf("::: %d, %d, %d => %d\n",
3351 helpanim_info[i].element,
3352 helpanim_info[i].action,
3353 helpanim_info[i].direction,
3354 helpanim_info[i].delay);
3358 void LoadHelpTextInfo()
3360 char *filename = getHelpTextFilename();
3363 if (helptext_info != NULL)
3365 freeSetupFileHash(helptext_info);
3366 helptext_info = NULL;
3369 if (fileExists(filename))
3370 helptext_info = loadSetupFileHash(filename);
3372 if (helptext_info == NULL)
3374 /* use reliable default values from static configuration */
3375 helptext_info = newSetupFileHash();
3377 for (i = 0; helptext_config[i].token; i++)
3378 setHashEntry(helptext_info,
3379 helptext_config[i].token,
3380 helptext_config[i].value);
3385 BEGIN_HASH_ITERATION(helptext_info, itr)
3387 printf("::: '%s' => '%s'\n",
3388 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
3390 END_HASH_ITERATION(hash, itr)