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 ***********************************************************/
17 #include "libgame/libgame.h"
25 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
26 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
27 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
28 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
29 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
30 #define LEVEL_HEADER_UNUSED 13 /* unused level header bytes */
31 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
32 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
33 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
34 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
35 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
36 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
37 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
38 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
40 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
41 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
43 /* file identifier strings */
44 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
45 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
46 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
49 /* ========================================================================= */
50 /* level file functions */
51 /* ========================================================================= */
53 void setElementChangePages(struct ElementInfo *ei, int change_pages)
55 int change_page_size = sizeof(struct ElementChangeInfo);
57 ei->num_change_pages = MAX(1, change_pages);
60 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
62 if (ei->current_change_page >= ei->num_change_pages)
63 ei->current_change_page = ei->num_change_pages - 1;
65 ei->change = &ei->change_page[ei->current_change_page];
68 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
72 change->can_change = FALSE;
74 change->events = CE_BITMASK_DEFAULT;
75 change->sides = CH_SIDE_ANY;
77 change->target_element = EL_EMPTY_SPACE;
79 change->delay_fixed = 0;
80 change->delay_random = 0;
81 change->delay_frames = 1;
83 change->trigger_element = EL_EMPTY_SPACE;
85 change->explode = FALSE;
86 change->use_content = FALSE;
87 change->only_complete = FALSE;
88 change->use_random_change = FALSE;
90 change->power = CP_NON_DESTRUCTIVE;
94 change->content[x][y] = EL_EMPTY_SPACE;
96 change->direct_action = 0;
97 change->other_action = 0;
99 change->pre_change_function = NULL;
100 change->change_function = NULL;
101 change->post_change_function = NULL;
104 static void setLevelInfoToDefaults(struct LevelInfo *level)
108 level->file_version = FILE_VERSION_ACTUAL;
109 level->game_version = GAME_VERSION_ACTUAL;
111 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
112 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
113 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
115 level->fieldx = STD_LEV_FIELDX;
116 level->fieldy = STD_LEV_FIELDY;
118 for(x=0; x<MAX_LEV_FIELDX; x++)
119 for(y=0; y<MAX_LEV_FIELDY; y++)
120 level->field[x][y] = EL_SAND;
123 level->gems_needed = 0;
124 level->amoeba_speed = 10;
125 level->time_magic_wall = 10;
126 level->time_wheel = 10;
127 level->time_light = 10;
128 level->time_timegate = 10;
129 level->amoeba_content = EL_DIAMOND;
130 level->double_speed = FALSE;
131 level->gravity = FALSE;
132 level->em_slippery_gems = FALSE;
134 level->use_custom_template = FALSE;
136 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
137 level->name[i] = '\0';
138 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
139 level->author[i] = '\0';
141 strcpy(level->name, NAMELESS_LEVEL_NAME);
142 strcpy(level->author, ANONYMOUS_NAME);
146 level->envelope_text[i][0] = '\0';
147 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
148 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
151 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
152 level->score[i] = 10;
154 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
155 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
158 level->yamyam_content[i][x][y] =
159 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
161 level->field[0][0] = EL_PLAYER_1;
162 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
164 for (i=0; i < MAX_NUM_ELEMENTS; i++)
166 setElementChangePages(&element_info[i], 1);
167 setElementChangeInfoToDefaults(element_info[i].change);
170 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
172 int element = EL_CUSTOM_START + i;
174 for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
175 element_info[element].description[j] = '\0';
176 if (element_info[element].custom_description != NULL)
177 strncpy(element_info[element].description,
178 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
180 strcpy(element_info[element].description,
181 element_info[element].editor_description);
183 element_info[element].use_gfx_element = FALSE;
184 element_info[element].gfx_element = EL_EMPTY_SPACE;
186 element_info[element].collect_score = 10; /* special default */
187 element_info[element].collect_count = 1; /* special default */
189 element_info[element].push_delay_fixed = 2; /* special default */
190 element_info[element].push_delay_random = 8; /* special default */
191 element_info[element].move_delay_fixed = 0;
192 element_info[element].move_delay_random = 0;
194 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
195 element_info[element].move_direction_initial = MV_NO_MOVING;
196 element_info[element].move_stepsize = TILEX / 8;
198 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
202 element_info[element].content[x][y] = EL_EMPTY_SPACE;
204 element_info[element].access_type = 0;
205 element_info[element].access_layer = 0;
206 element_info[element].walk_to_action = 0;
207 element_info[element].smash_targets = 0;
208 element_info[element].deadliness = 0;
209 element_info[element].consistency = 0;
211 element_info[element].can_explode_by_fire = FALSE;
212 element_info[element].can_explode_smashed = FALSE;
213 element_info[element].can_explode_impact = FALSE;
215 element_info[element].current_change_page = 0;
217 /* start with no properties at all */
218 for (j=0; j < NUM_EP_BITFIELDS; j++)
219 Properties[element][j] = EP_BITMASK_DEFAULT;
221 element_info[element].modified_settings = FALSE;
224 BorderElement = EL_STEELWALL;
226 level->no_level_file = FALSE;
228 if (leveldir_current == NULL) /* only when dumping level */
231 /* try to determine better author name than 'anonymous' */
232 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
234 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
235 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
239 switch (LEVELCLASS(leveldir_current))
241 case LEVELCLASS_TUTORIAL:
242 strcpy(level->author, PROGRAM_AUTHOR_STRING);
245 case LEVELCLASS_CONTRIBUTION:
246 strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
247 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
250 case LEVELCLASS_USER:
251 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
252 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
256 /* keep default value */
262 static void ActivateLevelTemplate()
264 /* Currently there is no special action needed to activate the template
265 data, because 'element_info' and 'Properties' overwrite the original
266 level data, while all other variables do not change. */
269 boolean LevelFileExists(int level_nr)
271 char *filename = getLevelFilename(level_nr);
273 return (access(filename, F_OK) == 0);
276 static int checkLevelElement(int element)
278 /* map some (historic, now obsolete) elements */
283 case EL_PLAYER_OBSOLETE:
284 element = EL_PLAYER_1;
287 case EL_KEY_OBSOLETE:
290 case EL_EM_KEY_1_FILE_OBSOLETE:
291 element = EL_EM_KEY_1;
294 case EL_EM_KEY_2_FILE_OBSOLETE:
295 element = EL_EM_KEY_2;
298 case EL_EM_KEY_3_FILE_OBSOLETE:
299 element = EL_EM_KEY_3;
302 case EL_EM_KEY_4_FILE_OBSOLETE:
303 element = EL_EM_KEY_4;
306 case EL_ENVELOPE_OBSOLETE:
307 element = EL_ENVELOPE_1;
315 if (element >= NUM_FILE_ELEMENTS)
317 Error(ERR_WARN, "invalid level element %d", element);
319 element = EL_CHAR_QUESTION;
324 if (element >= NUM_FILE_ELEMENTS)
326 Error(ERR_WARN, "invalid level element %d", element);
328 element = EL_CHAR_QUESTION;
330 else if (element == EL_PLAYER_OBSOLETE)
331 element = EL_PLAYER_1;
332 else if (element == EL_KEY_OBSOLETE)
339 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
341 level->file_version = getFileVersion(file);
342 level->game_version = getFileVersion(file);
347 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
351 level->fieldx = getFile8Bit(file);
352 level->fieldy = getFile8Bit(file);
354 level->time = getFile16BitBE(file);
355 level->gems_needed = getFile16BitBE(file);
357 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
358 level->name[i] = getFile8Bit(file);
359 level->name[MAX_LEVEL_NAME_LEN] = 0;
361 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
362 level->score[i] = getFile8Bit(file);
364 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
365 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
368 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
370 level->amoeba_speed = getFile8Bit(file);
371 level->time_magic_wall = getFile8Bit(file);
372 level->time_wheel = getFile8Bit(file);
373 level->amoeba_content = checkLevelElement(getFile8Bit(file));
374 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
375 level->gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
376 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
377 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
379 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
381 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
386 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
390 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
391 level->author[i] = getFile8Bit(file);
392 level->author[MAX_LEVEL_NAME_LEN] = 0;
397 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
400 int chunk_size_expected = level->fieldx * level->fieldy;
402 /* Note: "chunk_size" was wrong before version 2.0 when elements are
403 stored with 16-bit encoding (and should be twice as big then).
404 Even worse, playfield data was stored 16-bit when only yamyam content
405 contained 16-bit elements and vice versa. */
407 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
408 chunk_size_expected *= 2;
410 if (chunk_size_expected != chunk_size)
412 ReadUnusedBytesFromFile(file, chunk_size);
413 return chunk_size_expected;
416 for(y=0; y<level->fieldy; y++)
417 for(x=0; x<level->fieldx; x++)
419 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
424 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
428 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
429 int chunk_size_expected = header_size + content_size;
431 /* Note: "chunk_size" was wrong before version 2.0 when elements are
432 stored with 16-bit encoding (and should be twice as big then).
433 Even worse, playfield data was stored 16-bit when only yamyam content
434 contained 16-bit elements and vice versa. */
436 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
437 chunk_size_expected += content_size;
439 if (chunk_size_expected != chunk_size)
441 ReadUnusedBytesFromFile(file, chunk_size);
442 return chunk_size_expected;
446 level->num_yamyam_contents = getFile8Bit(file);
450 /* correct invalid number of content fields -- should never happen */
451 if (level->num_yamyam_contents < 1 ||
452 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
453 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
455 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
458 level->yamyam_content[i][x][y] =
459 checkLevelElement(level->encoding_16bit_field ?
460 getFile16BitBE(file) : getFile8Bit(file));
464 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
468 int num_contents, content_xsize, content_ysize;
469 int content_array[MAX_ELEMENT_CONTENTS][3][3];
471 element = checkLevelElement(getFile16BitBE(file));
472 num_contents = getFile8Bit(file);
473 content_xsize = getFile8Bit(file);
474 content_ysize = getFile8Bit(file);
476 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
478 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
481 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
483 /* correct invalid number of content fields -- should never happen */
484 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
485 num_contents = STD_ELEMENT_CONTENTS;
487 if (element == EL_YAMYAM)
489 level->num_yamyam_contents = num_contents;
491 for(i=0; i<num_contents; i++)
494 level->yamyam_content[i][x][y] = content_array[i][x][y];
496 else if (element == EL_BD_AMOEBA)
498 level->amoeba_content = content_array[0][0][0];
502 Error(ERR_WARN, "cannot load content for element '%d'", element);
508 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
514 int chunk_size_expected;
516 element = checkLevelElement(getFile16BitBE(file));
517 if (!IS_ENVELOPE(element))
518 element = EL_ENVELOPE_1;
520 envelope_nr = element - EL_ENVELOPE_1;
522 envelope_len = getFile16BitBE(file);
524 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
525 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
527 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
529 chunk_size_expected = LEVEL_CHUNK_CNT3_HEADER + envelope_len;
531 if (chunk_size_expected != chunk_size)
533 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
534 return chunk_size_expected;
537 for(i=0; i < envelope_len; i++)
538 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
543 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
545 int num_changed_custom_elements = getFile16BitBE(file);
546 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
549 if (chunk_size_expected != chunk_size)
551 ReadUnusedBytesFromFile(file, chunk_size - 2);
552 return chunk_size_expected;
555 for (i=0; i < num_changed_custom_elements; i++)
557 int element = getFile16BitBE(file);
558 int properties = getFile32BitBE(file);
560 if (IS_CUSTOM_ELEMENT(element))
561 Properties[element][EP_BITFIELD_BASE] = properties;
563 Error(ERR_WARN, "invalid custom element number %d", element);
569 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
571 int num_changed_custom_elements = getFile16BitBE(file);
572 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
575 if (chunk_size_expected != chunk_size)
577 ReadUnusedBytesFromFile(file, chunk_size - 2);
578 return chunk_size_expected;
581 for (i=0; i < num_changed_custom_elements; i++)
583 int element = getFile16BitBE(file);
584 int custom_target_element = getFile16BitBE(file);
586 if (IS_CUSTOM_ELEMENT(element))
587 element_info[element].change->target_element = custom_target_element;
589 Error(ERR_WARN, "invalid custom element number %d", element);
595 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
597 int num_changed_custom_elements = getFile16BitBE(file);
598 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
601 if (chunk_size_expected != chunk_size)
603 ReadUnusedBytesFromFile(file, chunk_size - 2);
604 return chunk_size_expected;
607 for (i=0; i < num_changed_custom_elements; i++)
609 int element = getFile16BitBE(file);
611 if (!IS_CUSTOM_ELEMENT(element))
613 Error(ERR_WARN, "invalid custom element number %d", element);
615 element = EL_DEFAULT; /* dummy element used for artwork config */
618 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
619 element_info[element].description[j] = getFile8Bit(file);
620 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
622 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
624 /* some free bytes for future properties and padding */
625 ReadUnusedBytesFromFile(file, 7);
627 element_info[element].use_gfx_element = getFile8Bit(file);
628 element_info[element].gfx_element =
629 checkLevelElement(getFile16BitBE(file));
631 element_info[element].collect_score = getFile8Bit(file);
632 element_info[element].collect_count = getFile8Bit(file);
634 element_info[element].push_delay_fixed = getFile16BitBE(file);
635 element_info[element].push_delay_random = getFile16BitBE(file);
636 element_info[element].move_delay_fixed = getFile16BitBE(file);
637 element_info[element].move_delay_random = getFile16BitBE(file);
639 element_info[element].move_pattern = getFile16BitBE(file);
640 element_info[element].move_direction_initial = getFile8Bit(file);
641 element_info[element].move_stepsize = getFile8Bit(file);
645 element_info[element].content[x][y] =
646 checkLevelElement(getFile16BitBE(file));
648 element_info[element].change->events = getFile32BitBE(file);
650 element_info[element].change->target_element =
651 checkLevelElement(getFile16BitBE(file));
653 element_info[element].change->delay_fixed = getFile16BitBE(file);
654 element_info[element].change->delay_random = getFile16BitBE(file);
655 element_info[element].change->delay_frames = getFile16BitBE(file);
657 element_info[element].change->trigger_element =
658 checkLevelElement(getFile16BitBE(file));
660 element_info[element].change->explode = getFile8Bit(file);
661 element_info[element].change->use_content = getFile8Bit(file);
662 element_info[element].change->only_complete = getFile8Bit(file);
663 element_info[element].change->use_random_change = getFile8Bit(file);
665 element_info[element].change->random = getFile8Bit(file);
666 element_info[element].change->power = getFile8Bit(file);
670 element_info[element].change->content[x][y] =
671 checkLevelElement(getFile16BitBE(file));
673 element_info[element].slippery_type = getFile8Bit(file);
675 /* some free bytes for future properties and padding */
676 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
678 /* mark that this custom element has been modified */
679 element_info[element].modified_settings = TRUE;
685 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
687 struct ElementInfo *ei;
688 int chunk_size_expected;
692 element = getFile16BitBE(file);
694 if (!IS_CUSTOM_ELEMENT(element))
696 Error(ERR_WARN, "invalid custom element number %d", element);
698 element = EL_DEFAULT; /* dummy element used for artwork config */
701 ei = &element_info[element];
703 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
704 ei->description[i] = getFile8Bit(file);
705 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
707 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
708 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
710 ei->num_change_pages = getFile8Bit(file);
712 /* some free bytes for future base property values and padding */
713 ReadUnusedBytesFromFile(file, 5);
715 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
716 if (chunk_size_expected != chunk_size)
718 ReadUnusedBytesFromFile(file, chunk_size - 48);
719 return chunk_size_expected;
722 /* read custom property values */
724 ei->use_gfx_element = getFile8Bit(file);
725 ei->gfx_element = checkLevelElement(getFile16BitBE(file));
727 ei->collect_score = getFile8Bit(file);
728 ei->collect_count = getFile8Bit(file);
730 ei->push_delay_fixed = getFile16BitBE(file);
731 ei->push_delay_random = getFile16BitBE(file);
732 ei->move_delay_fixed = getFile16BitBE(file);
733 ei->move_delay_random = getFile16BitBE(file);
735 ei->move_pattern = getFile16BitBE(file);
736 ei->move_direction_initial = getFile8Bit(file);
737 ei->move_stepsize = getFile8Bit(file);
739 ei->slippery_type = getFile8Bit(file);
743 ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
745 /* some free bytes for future custom property values and padding */
746 ReadUnusedBytesFromFile(file, 12);
748 /* read change property values */
750 setElementChangePages(ei, ei->num_change_pages);
752 for (i=0; i < ei->num_change_pages; i++)
754 struct ElementChangeInfo *change = &ei->change_page[i];
756 /* always start with reliable default values */
757 setElementChangeInfoToDefaults(change);
759 change->events = getFile32BitBE(file);
761 change->target_element = checkLevelElement(getFile16BitBE(file));
763 change->delay_fixed = getFile16BitBE(file);
764 change->delay_random = getFile16BitBE(file);
765 change->delay_frames = getFile16BitBE(file);
767 change->trigger_element = checkLevelElement(getFile16BitBE(file));
769 change->explode = getFile8Bit(file);
770 change->use_content = getFile8Bit(file);
771 change->only_complete = getFile8Bit(file);
772 change->use_random_change = getFile8Bit(file);
774 change->random = getFile8Bit(file);
775 change->power = getFile8Bit(file);
779 change->content[x][y] = checkLevelElement(getFile16BitBE(file));
781 change->can_change = getFile8Bit(file);
783 change->sides = getFile8Bit(file);
785 if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
786 change->sides = CH_SIDE_ANY;
788 /* some free bytes for future change property values and padding */
789 ReadUnusedBytesFromFile(file, 8);
792 /* mark this custom element as modified */
793 ei->modified_settings = TRUE;
798 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
800 char cookie[MAX_LINE_LEN];
801 char chunk_name[CHUNK_ID_LEN + 1];
805 /* always start with reliable default values */
806 setLevelInfoToDefaults(level);
808 if (!(file = fopen(filename, MODE_READ)))
810 level->no_level_file = TRUE;
812 if (level != &level_template)
813 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
818 getFileChunkBE(file, chunk_name, NULL);
819 if (strcmp(chunk_name, "RND1") == 0)
821 getFile32BitBE(file); /* not used */
823 getFileChunkBE(file, chunk_name, NULL);
824 if (strcmp(chunk_name, "CAVE") != 0)
826 Error(ERR_WARN, "unknown format of level file '%s'", filename);
831 else /* check for pre-2.0 file format with cookie string */
833 strcpy(cookie, chunk_name);
834 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
835 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
836 cookie[strlen(cookie) - 1] = '\0';
838 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
840 Error(ERR_WARN, "unknown format of level file '%s'", filename);
845 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
847 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
852 /* pre-2.0 level files have no game version, so use file version here */
853 level->game_version = level->file_version;
856 if (level->file_version < FILE_VERSION_1_2)
858 /* level files from versions before 1.2.0 without chunk structure */
859 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
860 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
868 int (*loader)(FILE *, int, struct LevelInfo *);
872 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
873 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
874 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
875 { "BODY", -1, LoadLevel_BODY },
876 { "CONT", -1, LoadLevel_CONT },
877 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
878 { "CNT3", -1, LoadLevel_CNT3 },
879 { "CUS1", -1, LoadLevel_CUS1 },
880 { "CUS2", -1, LoadLevel_CUS2 },
881 { "CUS3", -1, LoadLevel_CUS3 },
882 { "CUS4", -1, LoadLevel_CUS4 },
886 while (getFileChunkBE(file, chunk_name, &chunk_size))
890 while (chunk_info[i].name != NULL &&
891 strcmp(chunk_name, chunk_info[i].name) != 0)
894 if (chunk_info[i].name == NULL)
896 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
897 chunk_name, filename);
898 ReadUnusedBytesFromFile(file, chunk_size);
900 else if (chunk_info[i].size != -1 &&
901 chunk_info[i].size != chunk_size)
903 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
904 chunk_size, chunk_name, filename);
905 ReadUnusedBytesFromFile(file, chunk_size);
909 /* call function to load this level chunk */
910 int chunk_size_expected =
911 (chunk_info[i].loader)(file, chunk_size, level);
913 /* the size of some chunks cannot be checked before reading other
914 chunks first (like "HEAD" and "BODY") that contain some header
915 information, so check them here */
916 if (chunk_size_expected != chunk_size)
918 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
919 chunk_size, chunk_name, filename);
930 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
932 if (leveldir_current == NULL) /* only when dumping level */
935 /* determine correct game engine version of current level */
936 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
937 IS_LEVELCLASS_USER(leveldir_current))
940 printf("\n::: This level is private or contributed: '%s'\n", filename);
943 /* For user contributed and private levels, use the version of
944 the game engine the levels were created for.
945 Since 2.0.1, the game engine version is now directly stored
946 in the level file (chunk "VERS"), so there is no need anymore
947 to set the game version from the file version (except for old,
948 pre-2.0 levels, where the game version is still taken from the
949 file format version used to store the level -- see above). */
951 /* do some special adjustments to support older level versions */
952 if (level->file_version == FILE_VERSION_1_0)
954 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
955 Error(ERR_WARN, "using high speed movement for player");
957 /* player was faster than monsters in (pre-)1.0 levels */
958 level->double_speed = TRUE;
961 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
962 if (level->game_version == VERSION_IDENT(2,0,1))
963 level->em_slippery_gems = TRUE;
968 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
969 leveldir_current->sort_priority, filename);
972 /* Always use the latest version of the game engine for all but
973 user contributed and private levels; this allows for actual
974 corrections in the game engine to take effect for existing,
975 converted levels (from "classic" or other existing games) to
976 make the game emulation more accurate, while (hopefully) not
977 breaking existing levels created from other players. */
979 level->game_version = GAME_VERSION_ACTUAL;
981 /* Set special EM style gems behaviour: EM style gems slip down from
982 normal, steel and growing wall. As this is a more fundamental change,
983 it seems better to set the default behaviour to "off" (as it is more
984 natural) and make it configurable in the level editor (as a property
985 of gem style elements). Already existing converted levels (neither
986 private nor contributed levels) are changed to the new behaviour. */
988 if (level->file_version < FILE_VERSION_2_0)
989 level->em_slippery_gems = TRUE;
993 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
997 /* map custom element change events that have changed in newer versions
998 (these following values were accidentally changed in version 3.0.1) */
999 if (level->game_version <= VERSION_IDENT(3,0,0))
1001 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1003 int element = EL_CUSTOM_START + i;
1005 /* order of checking and copying events to be mapped is important */
1006 for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
1008 if (HAS_CHANGE_EVENT(element, j - 2))
1010 SET_CHANGE_EVENT(element, j - 2, FALSE);
1011 SET_CHANGE_EVENT(element, j, TRUE);
1015 /* order of checking and copying events to be mapped is important */
1016 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1018 if (HAS_CHANGE_EVENT(element, j - 1))
1020 SET_CHANGE_EVENT(element, j - 1, FALSE);
1021 SET_CHANGE_EVENT(element, j, TRUE);
1027 /* some custom element change events get mapped since version 3.0.3 */
1028 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1030 int element = EL_CUSTOM_START + i;
1032 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
1033 HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
1035 SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
1036 SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
1038 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1042 /* initialize "can_change" field for old levels with only one change page */
1043 if (level->game_version <= VERSION_IDENT(3,0,2))
1045 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1047 int element = EL_CUSTOM_START + i;
1049 if (CAN_CHANGE(element))
1050 element_info[element].change->can_change = TRUE;
1054 /* initialize element properties for level editor etc. */
1055 InitElementPropertiesEngine(level->game_version);
1058 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1062 /* map elements that have changed in newer versions */
1063 for(y=0; y<level->fieldy; y++)
1065 for(x=0; x<level->fieldx; x++)
1067 int element = level->field[x][y];
1069 if (level->game_version <= VERSION_IDENT(2,2,0))
1071 /* map game font elements */
1072 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1073 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1074 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1075 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1078 if (level->game_version < VERSION_IDENT(3,0,0))
1080 /* map Supaplex gravity tube elements */
1081 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1082 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1083 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1084 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1088 level->field[x][y] = element;
1092 /* copy elements to runtime playfield array */
1093 for(x=0; x<MAX_LEV_FIELDX; x++)
1094 for(y=0; y<MAX_LEV_FIELDY; y++)
1095 Feld[x][y] = level->field[x][y];
1097 /* initialize level size variables for faster access */
1098 lev_fieldx = level->fieldx;
1099 lev_fieldy = level->fieldy;
1101 /* determine border element for this level */
1107 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
1111 if (leveldir_current == NULL) /* only when dumping level */
1114 /* determine correct game engine version of current level */
1115 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
1116 IS_LEVELCLASS_USER(leveldir_current))
1119 printf("\n::: This level is private or contributed: '%s'\n", filename);
1122 /* For user contributed and private levels, use the version of
1123 the game engine the levels were created for.
1124 Since 2.0.1, the game engine version is now directly stored
1125 in the level file (chunk "VERS"), so there is no need anymore
1126 to set the game version from the file version (except for old,
1127 pre-2.0 levels, where the game version is still taken from the
1128 file format version used to store the level -- see above). */
1130 /* do some special adjustments to support older level versions */
1131 if (level->file_version == FILE_VERSION_1_0)
1133 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
1134 Error(ERR_WARN, "using high speed movement for player");
1136 /* player was faster than monsters in (pre-)1.0 levels */
1137 level->double_speed = TRUE;
1140 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1141 if (level->game_version == VERSION_IDENT(2,0,1))
1142 level->em_slippery_gems = TRUE;
1147 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1148 leveldir_current->sort_priority, filename);
1151 /* Always use the latest version of the game engine for all but
1152 user contributed and private levels; this allows for actual
1153 corrections in the game engine to take effect for existing,
1154 converted levels (from "classic" or other existing games) to
1155 make the game emulation more accurate, while (hopefully) not
1156 breaking existing levels created from other players. */
1158 level->game_version = GAME_VERSION_ACTUAL;
1160 /* Set special EM style gems behaviour: EM style gems slip down from
1161 normal, steel and growing wall. As this is a more fundamental change,
1162 it seems better to set the default behaviour to "off" (as it is more
1163 natural) and make it configurable in the level editor (as a property
1164 of gem style elements). Already existing converted levels (neither
1165 private nor contributed levels) are changed to the new behaviour. */
1167 if (level->file_version < FILE_VERSION_2_0)
1168 level->em_slippery_gems = TRUE;
1171 /* map elements that have changed in newer versions */
1172 for(y=0; y<level->fieldy; y++)
1174 for(x=0; x<level->fieldx; x++)
1176 int element = level->field[x][y];
1178 if (level->game_version <= VERSION_IDENT(2,2,0))
1180 /* map game font elements */
1181 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1182 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1183 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1184 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1187 if (level->game_version < VERSION_IDENT(3,0,0))
1189 /* map Supaplex gravity tube elements */
1190 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1191 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1192 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1193 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1197 level->field[x][y] = element;
1201 /* map custom element change events that have changed in newer versions
1202 (these following values have accidentally changed in version 3.0.1) */
1203 if (level->game_version <= VERSION_IDENT(3,0,0))
1205 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1207 int element = EL_CUSTOM_START + i;
1209 /* order of checking events to be mapped is important */
1210 for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
1212 if (HAS_CHANGE_EVENT(element, j - 2))
1214 SET_CHANGE_EVENT(element, j - 2, FALSE);
1215 SET_CHANGE_EVENT(element, j, TRUE);
1219 /* order of checking events to be mapped is important */
1220 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1222 if (HAS_CHANGE_EVENT(element, j - 1))
1224 SET_CHANGE_EVENT(element, j - 1, FALSE);
1225 SET_CHANGE_EVENT(element, j, TRUE);
1231 /* initialize "can_change" field for old levels with only one change page */
1232 if (level->game_version <= VERSION_IDENT(3,0,2))
1234 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1236 int element = EL_CUSTOM_START + i;
1238 if (CAN_CHANGE(element))
1239 element_info[element].change->can_change = TRUE;
1243 /* copy elements to runtime playfield array */
1244 for(x=0; x<MAX_LEV_FIELDX; x++)
1245 for(y=0; y<MAX_LEV_FIELDY; y++)
1246 Feld[x][y] = level->field[x][y];
1248 /* initialize level size variables for faster access */
1249 lev_fieldx = level->fieldx;
1250 lev_fieldy = level->fieldy;
1252 /* determine border element for this level */
1255 /* initialize element properties for level editor etc. */
1256 InitElementPropertiesEngine(level->game_version);
1261 void LoadLevelTemplate(int level_nr)
1263 char *filename = getLevelFilename(level_nr);
1265 LoadLevelFromFilename(&level_template, filename);
1267 LoadLevel_InitVersion(&level, filename);
1268 LoadLevel_InitElements(&level, filename);
1270 ActivateLevelTemplate();
1273 void LoadLevel(int level_nr)
1275 char *filename = getLevelFilename(level_nr);
1277 LoadLevelFromFilename(&level, filename);
1279 if (level.use_custom_template)
1280 LoadLevelTemplate(-1);
1283 LoadLevel_InitVersion(&level, filename);
1284 LoadLevel_InitElements(&level, filename);
1285 LoadLevel_InitPlayfield(&level, filename);
1287 LoadLevel_InitLevel(&level, filename);
1291 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1293 putFileVersion(file, level->file_version);
1294 putFileVersion(file, level->game_version);
1297 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1301 putFile8Bit(file, level->fieldx);
1302 putFile8Bit(file, level->fieldy);
1304 putFile16BitBE(file, level->time);
1305 putFile16BitBE(file, level->gems_needed);
1307 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1308 putFile8Bit(file, level->name[i]);
1310 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1311 putFile8Bit(file, level->score[i]);
1313 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1316 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1317 level->yamyam_content[i][x][y]));
1318 putFile8Bit(file, level->amoeba_speed);
1319 putFile8Bit(file, level->time_magic_wall);
1320 putFile8Bit(file, level->time_wheel);
1321 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1322 level->amoeba_content));
1323 putFile8Bit(file, (level->double_speed ? 1 : 0));
1324 putFile8Bit(file, (level->gravity ? 1 : 0));
1325 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1326 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1328 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1330 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1333 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1337 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1338 putFile8Bit(file, level->author[i]);
1341 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1345 for(y=0; y<level->fieldy; y++)
1346 for(x=0; x<level->fieldx; x++)
1347 if (level->encoding_16bit_field)
1348 putFile16BitBE(file, level->field[x][y]);
1350 putFile8Bit(file, level->field[x][y]);
1354 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1358 putFile8Bit(file, EL_YAMYAM);
1359 putFile8Bit(file, level->num_yamyam_contents);
1360 putFile8Bit(file, 0);
1361 putFile8Bit(file, 0);
1363 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1366 if (level->encoding_16bit_field)
1367 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1369 putFile8Bit(file, level->yamyam_content[i][x][y]);
1373 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1376 int num_contents, content_xsize, content_ysize;
1377 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1379 if (element == EL_YAMYAM)
1381 num_contents = level->num_yamyam_contents;
1385 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1388 content_array[i][x][y] = level->yamyam_content[i][x][y];
1390 else if (element == EL_BD_AMOEBA)
1396 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1399 content_array[i][x][y] = EL_EMPTY;
1400 content_array[0][0][0] = level->amoeba_content;
1404 /* chunk header already written -- write empty chunk data */
1405 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1407 Error(ERR_WARN, "cannot save content for element '%d'", element);
1411 putFile16BitBE(file, element);
1412 putFile8Bit(file, num_contents);
1413 putFile8Bit(file, content_xsize);
1414 putFile8Bit(file, content_ysize);
1416 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1418 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1421 putFile16BitBE(file, content_array[i][x][y]);
1424 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1427 int envelope_nr = element - EL_ENVELOPE_1;
1428 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1430 putFile16BitBE(file, element);
1431 putFile16BitBE(file, envelope_len);
1432 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1433 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1435 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1437 for(i=0; i < envelope_len; i++)
1438 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1442 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1443 int num_changed_custom_elements)
1447 putFile16BitBE(file, num_changed_custom_elements);
1449 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1451 int element = EL_CUSTOM_START + i;
1453 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1455 if (check < num_changed_custom_elements)
1457 putFile16BitBE(file, element);
1458 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1465 if (check != num_changed_custom_elements) /* should not happen */
1466 Error(ERR_WARN, "inconsistent number of custom element properties");
1471 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1472 int num_changed_custom_elements)
1476 putFile16BitBE(file, num_changed_custom_elements);
1478 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1480 int element = EL_CUSTOM_START + i;
1482 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1484 if (check < num_changed_custom_elements)
1486 putFile16BitBE(file, element);
1487 putFile16BitBE(file, element_info[element].change->target_element);
1494 if (check != num_changed_custom_elements) /* should not happen */
1495 Error(ERR_WARN, "inconsistent number of custom target elements");
1500 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1501 int num_changed_custom_elements)
1503 int i, j, x, y, check = 0;
1505 putFile16BitBE(file, num_changed_custom_elements);
1507 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1509 int element = EL_CUSTOM_START + i;
1511 if (element_info[element].modified_settings)
1513 if (check < num_changed_custom_elements)
1515 putFile16BitBE(file, element);
1517 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1518 putFile8Bit(file, element_info[element].description[j]);
1520 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1522 /* some free bytes for future properties and padding */
1523 WriteUnusedBytesToFile(file, 7);
1525 putFile8Bit(file, element_info[element].use_gfx_element);
1526 putFile16BitBE(file, element_info[element].gfx_element);
1528 putFile8Bit(file, element_info[element].collect_score);
1529 putFile8Bit(file, element_info[element].collect_count);
1531 putFile16BitBE(file, element_info[element].push_delay_fixed);
1532 putFile16BitBE(file, element_info[element].push_delay_random);
1533 putFile16BitBE(file, element_info[element].move_delay_fixed);
1534 putFile16BitBE(file, element_info[element].move_delay_random);
1536 putFile16BitBE(file, element_info[element].move_pattern);
1537 putFile8Bit(file, element_info[element].move_direction_initial);
1538 putFile8Bit(file, element_info[element].move_stepsize);
1542 putFile16BitBE(file, element_info[element].content[x][y]);
1544 putFile32BitBE(file, element_info[element].change->events);
1546 putFile16BitBE(file, element_info[element].change->target_element);
1548 putFile16BitBE(file, element_info[element].change->delay_fixed);
1549 putFile16BitBE(file, element_info[element].change->delay_random);
1550 putFile16BitBE(file, element_info[element].change->delay_frames);
1552 putFile16BitBE(file, element_info[element].change->trigger_element);
1554 putFile8Bit(file, element_info[element].change->explode);
1555 putFile8Bit(file, element_info[element].change->use_content);
1556 putFile8Bit(file, element_info[element].change->only_complete);
1557 putFile8Bit(file, element_info[element].change->use_random_change);
1559 putFile8Bit(file, element_info[element].change->random);
1560 putFile8Bit(file, element_info[element].change->power);
1564 putFile16BitBE(file, element_info[element].change->content[x][y]);
1566 putFile8Bit(file, element_info[element].slippery_type);
1568 /* some free bytes for future properties and padding */
1569 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1576 if (check != num_changed_custom_elements) /* should not happen */
1577 Error(ERR_WARN, "inconsistent number of custom element properties");
1581 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1583 struct ElementInfo *ei = &element_info[element];
1586 putFile16BitBE(file, element);
1588 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
1589 putFile8Bit(file, ei->description[i]);
1591 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1592 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
1594 putFile8Bit(file, ei->num_change_pages);
1596 /* some free bytes for future base property values and padding */
1597 WriteUnusedBytesToFile(file, 5);
1599 /* write custom property values */
1601 putFile8Bit(file, ei->use_gfx_element);
1602 putFile16BitBE(file, ei->gfx_element);
1604 putFile8Bit(file, ei->collect_score);
1605 putFile8Bit(file, ei->collect_count);
1607 putFile16BitBE(file, ei->push_delay_fixed);
1608 putFile16BitBE(file, ei->push_delay_random);
1609 putFile16BitBE(file, ei->move_delay_fixed);
1610 putFile16BitBE(file, ei->move_delay_random);
1612 putFile16BitBE(file, ei->move_pattern);
1613 putFile8Bit(file, ei->move_direction_initial);
1614 putFile8Bit(file, ei->move_stepsize);
1616 putFile8Bit(file, ei->slippery_type);
1620 putFile16BitBE(file, ei->content[x][y]);
1622 /* some free bytes for future custom property values and padding */
1623 WriteUnusedBytesToFile(file, 12);
1625 /* write change property values */
1627 for (i=0; i < ei->num_change_pages; i++)
1629 struct ElementChangeInfo *change = &ei->change_page[i];
1631 putFile32BitBE(file, change->events);
1633 putFile16BitBE(file, change->target_element);
1635 putFile16BitBE(file, change->delay_fixed);
1636 putFile16BitBE(file, change->delay_random);
1637 putFile16BitBE(file, change->delay_frames);
1639 putFile16BitBE(file, change->trigger_element);
1641 putFile8Bit(file, change->explode);
1642 putFile8Bit(file, change->use_content);
1643 putFile8Bit(file, change->only_complete);
1644 putFile8Bit(file, change->use_random_change);
1646 putFile8Bit(file, change->random);
1647 putFile8Bit(file, change->power);
1651 putFile16BitBE(file, change->content[x][y]);
1653 putFile8Bit(file, change->can_change);
1655 putFile8Bit(file, change->sides);
1657 /* some free bytes for future change property values and padding */
1658 WriteUnusedBytesToFile(file, 8);
1662 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1664 int body_chunk_size;
1668 if (!(file = fopen(filename, MODE_WRITE)))
1670 Error(ERR_WARN, "cannot save level file '%s'", filename);
1674 level->file_version = FILE_VERSION_ACTUAL;
1675 level->game_version = GAME_VERSION_ACTUAL;
1677 /* check level field for 16-bit elements */
1678 level->encoding_16bit_field = FALSE;
1679 for(y=0; y<level->fieldy; y++)
1680 for(x=0; x<level->fieldx; x++)
1681 if (level->field[x][y] > 255)
1682 level->encoding_16bit_field = TRUE;
1684 /* check yamyam content for 16-bit elements */
1685 level->encoding_16bit_yamyam = FALSE;
1686 for(i=0; i<level->num_yamyam_contents; i++)
1689 if (level->yamyam_content[i][x][y] > 255)
1690 level->encoding_16bit_yamyam = TRUE;
1692 /* check amoeba content for 16-bit elements */
1693 level->encoding_16bit_amoeba = FALSE;
1694 if (level->amoeba_content > 255)
1695 level->encoding_16bit_amoeba = TRUE;
1697 /* calculate size of "BODY" chunk */
1699 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1701 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1702 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1704 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1705 SaveLevel_VERS(file, level);
1707 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1708 SaveLevel_HEAD(file, level);
1710 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1711 SaveLevel_AUTH(file, level);
1713 putFileChunkBE(file, "BODY", body_chunk_size);
1714 SaveLevel_BODY(file, level);
1716 if (level->encoding_16bit_yamyam ||
1717 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1719 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1720 SaveLevel_CNT2(file, level, EL_YAMYAM);
1723 if (level->encoding_16bit_amoeba)
1725 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1726 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1729 /* check for envelope content */
1732 if (strlen(level->envelope_text[i]) > 0)
1734 int envelope_len = strlen(level->envelope_text[i]) + 1;
1736 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
1737 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
1741 /* check for non-default custom elements (unless using template level) */
1742 if (!level->use_custom_template)
1744 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1746 int element = EL_CUSTOM_START + i;
1748 if (element_info[element].modified_settings)
1750 int num_change_pages = element_info[element].num_change_pages;
1752 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
1753 SaveLevel_CUS4(file, level, element);
1760 SetFilePermissions(filename, PERMS_PRIVATE);
1763 void SaveLevel(int level_nr)
1765 char *filename = getLevelFilename(level_nr);
1767 SaveLevelFromFilename(&level, filename);
1770 void SaveLevelTemplate()
1772 char *filename = getLevelFilename(-1);
1774 SaveLevelFromFilename(&level, filename);
1777 void DumpLevel(struct LevelInfo *level)
1779 printf_line("-", 79);
1780 printf("Level xxx (file version %08d, game version %08d)\n",
1781 level->file_version, level->game_version);
1782 printf_line("-", 79);
1784 printf("Level Author: '%s'\n", level->author);
1785 printf("Level Title: '%s'\n", level->name);
1787 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1789 printf("Level Time: %d seconds\n", level->time);
1790 printf("Gems needed: %d\n", level->gems_needed);
1792 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1793 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1794 printf("Time for Light: %d seconds\n", level->time_light);
1795 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1797 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1799 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1800 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1801 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1803 printf_line("-", 79);
1807 /* ========================================================================= */
1808 /* tape file functions */
1809 /* ========================================================================= */
1811 static void setTapeInfoToDefaults()
1815 /* always start with reliable default values (empty tape) */
1818 /* default values (also for pre-1.2 tapes) with only the first player */
1819 tape.player_participates[0] = TRUE;
1820 for(i=1; i<MAX_PLAYERS; i++)
1821 tape.player_participates[i] = FALSE;
1823 /* at least one (default: the first) player participates in every tape */
1824 tape.num_participating_players = 1;
1826 tape.level_nr = level_nr;
1828 tape.changed = FALSE;
1830 tape.recording = FALSE;
1831 tape.playing = FALSE;
1832 tape.pausing = FALSE;
1835 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1837 tape->file_version = getFileVersion(file);
1838 tape->game_version = getFileVersion(file);
1843 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1847 tape->random_seed = getFile32BitBE(file);
1848 tape->date = getFile32BitBE(file);
1849 tape->length = getFile32BitBE(file);
1851 /* read header fields that are new since version 1.2 */
1852 if (tape->file_version >= FILE_VERSION_1_2)
1854 byte store_participating_players = getFile8Bit(file);
1857 /* since version 1.2, tapes store which players participate in the tape */
1858 tape->num_participating_players = 0;
1859 for(i=0; i<MAX_PLAYERS; i++)
1861 tape->player_participates[i] = FALSE;
1863 if (store_participating_players & (1 << i))
1865 tape->player_participates[i] = TRUE;
1866 tape->num_participating_players++;
1870 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1872 engine_version = getFileVersion(file);
1873 if (engine_version > 0)
1874 tape->engine_version = engine_version;
1876 tape->engine_version = tape->game_version;
1882 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1884 int level_identifier_size;
1887 level_identifier_size = getFile16BitBE(file);
1889 tape->level_identifier =
1890 checked_realloc(tape->level_identifier, level_identifier_size);
1892 for(i=0; i < level_identifier_size; i++)
1893 tape->level_identifier[i] = getFile8Bit(file);
1895 tape->level_nr = getFile16BitBE(file);
1897 chunk_size = 2 + level_identifier_size + 2;
1902 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1905 int chunk_size_expected =
1906 (tape->num_participating_players + 1) * tape->length;
1908 if (chunk_size_expected != chunk_size)
1910 ReadUnusedBytesFromFile(file, chunk_size);
1911 return chunk_size_expected;
1914 for(i=0; i<tape->length; i++)
1916 if (i >= MAX_TAPELEN)
1919 for(j=0; j<MAX_PLAYERS; j++)
1921 tape->pos[i].action[j] = MV_NO_MOVING;
1923 if (tape->player_participates[j])
1924 tape->pos[i].action[j] = getFile8Bit(file);
1927 tape->pos[i].delay = getFile8Bit(file);
1929 if (tape->file_version == FILE_VERSION_1_0)
1931 /* eliminate possible diagonal moves in old tapes */
1932 /* this is only for backward compatibility */
1934 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1935 byte action = tape->pos[i].action[0];
1936 int k, num_moves = 0;
1940 if (action & joy_dir[k])
1942 tape->pos[i + num_moves].action[0] = joy_dir[k];
1944 tape->pos[i + num_moves].delay = 0;
1953 tape->length += num_moves;
1956 else if (tape->file_version < FILE_VERSION_2_0)
1958 /* convert pre-2.0 tapes to new tape format */
1960 if (tape->pos[i].delay > 1)
1963 tape->pos[i + 1] = tape->pos[i];
1964 tape->pos[i + 1].delay = 1;
1967 for(j=0; j<MAX_PLAYERS; j++)
1968 tape->pos[i].action[j] = MV_NO_MOVING;
1969 tape->pos[i].delay--;
1980 if (i != tape->length)
1981 chunk_size = (tape->num_participating_players + 1) * i;
1986 void LoadTapeFromFilename(char *filename)
1988 char cookie[MAX_LINE_LEN];
1989 char chunk_name[CHUNK_ID_LEN + 1];
1993 /* always start with reliable default values */
1994 setTapeInfoToDefaults();
1996 if (!(file = fopen(filename, MODE_READ)))
1999 getFileChunkBE(file, chunk_name, NULL);
2000 if (strcmp(chunk_name, "RND1") == 0)
2002 getFile32BitBE(file); /* not used */
2004 getFileChunkBE(file, chunk_name, NULL);
2005 if (strcmp(chunk_name, "TAPE") != 0)
2007 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2012 else /* check for pre-2.0 file format with cookie string */
2014 strcpy(cookie, chunk_name);
2015 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
2016 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2017 cookie[strlen(cookie) - 1] = '\0';
2019 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
2021 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2026 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
2028 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
2033 /* pre-2.0 tape files have no game version, so use file version here */
2034 tape.game_version = tape.file_version;
2037 if (tape.file_version < FILE_VERSION_1_2)
2039 /* tape files from versions before 1.2.0 without chunk structure */
2040 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
2041 LoadTape_BODY(file, 2 * tape.length, &tape);
2049 int (*loader)(FILE *, int, struct TapeInfo *);
2053 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
2054 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
2055 { "INFO", -1, LoadTape_INFO },
2056 { "BODY", -1, LoadTape_BODY },
2060 while (getFileChunkBE(file, chunk_name, &chunk_size))
2064 while (chunk_info[i].name != NULL &&
2065 strcmp(chunk_name, chunk_info[i].name) != 0)
2068 if (chunk_info[i].name == NULL)
2070 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
2071 chunk_name, filename);
2072 ReadUnusedBytesFromFile(file, chunk_size);
2074 else if (chunk_info[i].size != -1 &&
2075 chunk_info[i].size != chunk_size)
2077 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2078 chunk_size, chunk_name, filename);
2079 ReadUnusedBytesFromFile(file, chunk_size);
2083 /* call function to load this tape chunk */
2084 int chunk_size_expected =
2085 (chunk_info[i].loader)(file, chunk_size, &tape);
2087 /* the size of some chunks cannot be checked before reading other
2088 chunks first (like "HEAD" and "BODY") that contain some header
2089 information, so check them here */
2090 if (chunk_size_expected != chunk_size)
2092 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2093 chunk_size, chunk_name, filename);
2101 tape.length_seconds = GetTapeLength();
2104 printf("tape game version: %d\n", tape.game_version);
2105 printf("tape engine version: %d\n", tape.engine_version);
2109 void LoadTape(int level_nr)
2111 char *filename = getTapeFilename(level_nr);
2113 LoadTapeFromFilename(filename);
2116 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2118 putFileVersion(file, tape->file_version);
2119 putFileVersion(file, tape->game_version);
2122 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2125 byte store_participating_players = 0;
2127 /* set bits for participating players for compact storage */
2128 for(i=0; i<MAX_PLAYERS; i++)
2129 if (tape->player_participates[i])
2130 store_participating_players |= (1 << i);
2132 putFile32BitBE(file, tape->random_seed);
2133 putFile32BitBE(file, tape->date);
2134 putFile32BitBE(file, tape->length);
2136 putFile8Bit(file, store_participating_players);
2138 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2139 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2141 putFileVersion(file, tape->engine_version);
2144 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2146 int level_identifier_size = strlen(tape->level_identifier) + 1;
2149 putFile16BitBE(file, level_identifier_size);
2151 for(i=0; i < level_identifier_size; i++)
2152 putFile8Bit(file, tape->level_identifier[i]);
2154 putFile16BitBE(file, tape->level_nr);
2157 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2161 for(i=0; i<tape->length; i++)
2163 for(j=0; j<MAX_PLAYERS; j++)
2164 if (tape->player_participates[j])
2165 putFile8Bit(file, tape->pos[i].action[j]);
2167 putFile8Bit(file, tape->pos[i].delay);
2171 void SaveTape(int level_nr)
2173 char *filename = getTapeFilename(level_nr);
2175 boolean new_tape = TRUE;
2176 int num_participating_players = 0;
2177 int info_chunk_size;
2178 int body_chunk_size;
2181 InitTapeDirectory(leveldir_current->filename);
2183 /* if a tape still exists, ask to overwrite it */
2184 if (access(filename, F_OK) == 0)
2187 if (!Request("Replace old tape ?", REQ_ASK))
2191 if (!(file = fopen(filename, MODE_WRITE)))
2193 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2197 tape.file_version = FILE_VERSION_ACTUAL;
2198 tape.game_version = GAME_VERSION_ACTUAL;
2200 /* count number of participating players */
2201 for(i=0; i<MAX_PLAYERS; i++)
2202 if (tape.player_participates[i])
2203 num_participating_players++;
2205 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2206 body_chunk_size = (num_participating_players + 1) * tape.length;
2208 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2209 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2211 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2212 SaveTape_VERS(file, &tape);
2214 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2215 SaveTape_HEAD(file, &tape);
2217 putFileChunkBE(file, "INFO", info_chunk_size);
2218 SaveTape_INFO(file, &tape);
2220 putFileChunkBE(file, "BODY", body_chunk_size);
2221 SaveTape_BODY(file, &tape);
2225 SetFilePermissions(filename, PERMS_PRIVATE);
2227 tape.changed = FALSE;
2230 Request("tape saved !", REQ_CONFIRM);
2233 void DumpTape(struct TapeInfo *tape)
2237 if (TAPE_IS_EMPTY(*tape))
2239 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2243 printf_line("-", 79);
2244 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2245 tape->level_nr, tape->file_version, tape->game_version);
2246 printf("Level series identifier: '%s'\n", tape->level_identifier);
2247 printf_line("-", 79);
2249 for(i=0; i<tape->length; i++)
2251 if (i >= MAX_TAPELEN)
2254 printf("%03d: ", i);
2256 for(j=0; j<MAX_PLAYERS; j++)
2258 if (tape->player_participates[j])
2260 int action = tape->pos[i].action[j];
2262 printf("%d:%02x ", j, action);
2263 printf("[%c%c%c%c|%c%c] - ",
2264 (action & JOY_LEFT ? '<' : ' '),
2265 (action & JOY_RIGHT ? '>' : ' '),
2266 (action & JOY_UP ? '^' : ' '),
2267 (action & JOY_DOWN ? 'v' : ' '),
2268 (action & JOY_BUTTON_1 ? '1' : ' '),
2269 (action & JOY_BUTTON_2 ? '2' : ' '));
2273 printf("(%03d)\n", tape->pos[i].delay);
2276 printf_line("-", 79);
2280 /* ========================================================================= */
2281 /* score file functions */
2282 /* ========================================================================= */
2284 void LoadScore(int level_nr)
2287 char *filename = getScoreFilename(level_nr);
2288 char cookie[MAX_LINE_LEN];
2289 char line[MAX_LINE_LEN];
2293 /* always start with reliable default values */
2294 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2296 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2297 highscore[i].Score = 0;
2300 if (!(file = fopen(filename, MODE_READ)))
2303 /* check file identifier */
2304 fgets(cookie, MAX_LINE_LEN, file);
2305 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2306 cookie[strlen(cookie) - 1] = '\0';
2308 if (!checkCookieString(cookie, SCORE_COOKIE))
2310 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2315 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2317 fscanf(file, "%d", &highscore[i].Score);
2318 fgets(line, MAX_LINE_LEN, file);
2320 if (line[strlen(line) - 1] == '\n')
2321 line[strlen(line) - 1] = '\0';
2323 for (line_ptr = line; *line_ptr; line_ptr++)
2325 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2327 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2328 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2337 void SaveScore(int level_nr)
2340 char *filename = getScoreFilename(level_nr);
2343 InitScoreDirectory(leveldir_current->filename);
2345 if (!(file = fopen(filename, MODE_WRITE)))
2347 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2351 fprintf(file, "%s\n\n", SCORE_COOKIE);
2353 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2354 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2358 SetFilePermissions(filename, PERMS_PUBLIC);
2362 /* ========================================================================= */
2363 /* setup file functions */
2364 /* ========================================================================= */
2366 #define TOKEN_STR_PLAYER_PREFIX "player_"
2369 #define SETUP_TOKEN_PLAYER_NAME 0
2370 #define SETUP_TOKEN_SOUND 1
2371 #define SETUP_TOKEN_SOUND_LOOPS 2
2372 #define SETUP_TOKEN_SOUND_MUSIC 3
2373 #define SETUP_TOKEN_SOUND_SIMPLE 4
2374 #define SETUP_TOKEN_TOONS 5
2375 #define SETUP_TOKEN_SCROLL_DELAY 6
2376 #define SETUP_TOKEN_SOFT_SCROLLING 7
2377 #define SETUP_TOKEN_FADING 8
2378 #define SETUP_TOKEN_AUTORECORD 9
2379 #define SETUP_TOKEN_QUICK_DOORS 10
2380 #define SETUP_TOKEN_TEAM_MODE 11
2381 #define SETUP_TOKEN_HANDICAP 12
2382 #define SETUP_TOKEN_TIME_LIMIT 13
2383 #define SETUP_TOKEN_FULLSCREEN 14
2384 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2385 #define SETUP_TOKEN_GRAPHICS_SET 16
2386 #define SETUP_TOKEN_SOUNDS_SET 17
2387 #define SETUP_TOKEN_MUSIC_SET 18
2388 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2389 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2390 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2392 #define NUM_GLOBAL_SETUP_TOKENS 22
2395 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2396 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2397 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2398 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2399 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2400 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2401 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2402 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2403 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2404 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2405 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2407 #define NUM_EDITOR_SETUP_TOKENS 11
2409 /* shortcut setup */
2410 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2411 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2412 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2414 #define NUM_SHORTCUT_SETUP_TOKENS 3
2417 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2418 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2419 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2420 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2421 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2422 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2423 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2424 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2425 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2426 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
2427 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2428 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2429 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2430 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2431 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2432 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
2434 #define NUM_PLAYER_SETUP_TOKENS 16
2437 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2438 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2440 #define NUM_SYSTEM_SETUP_TOKENS 2
2443 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2445 #define NUM_OPTIONS_SETUP_TOKENS 1
2448 static struct SetupInfo si;
2449 static struct SetupEditorInfo sei;
2450 static struct SetupShortcutInfo ssi;
2451 static struct SetupInputInfo sii;
2452 static struct SetupSystemInfo syi;
2453 static struct OptionInfo soi;
2455 static struct TokenInfo global_setup_tokens[] =
2457 { TYPE_STRING, &si.player_name, "player_name" },
2458 { TYPE_SWITCH, &si.sound, "sound" },
2459 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2460 { TYPE_SWITCH, &si.sound_music, "background_music" },
2461 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2462 { TYPE_SWITCH, &si.toons, "toons" },
2463 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2464 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2465 { TYPE_SWITCH, &si.fading, "screen_fading" },
2466 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2467 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2468 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2469 { TYPE_SWITCH, &si.handicap, "handicap" },
2470 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2471 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2472 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2473 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2474 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2475 { TYPE_STRING, &si.music_set, "music_set" },
2476 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2477 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2478 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2481 static struct TokenInfo editor_setup_tokens[] =
2483 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2484 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2485 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2486 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2487 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2488 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2489 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2490 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2491 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2492 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2493 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2496 static struct TokenInfo shortcut_setup_tokens[] =
2498 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2499 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2500 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2503 static struct TokenInfo player_setup_tokens[] =
2505 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2506 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2507 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2508 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2509 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2510 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2511 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2512 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2513 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2514 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2515 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2516 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2517 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2518 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2519 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2520 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
2523 static struct TokenInfo system_setup_tokens[] =
2525 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2526 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2529 static struct TokenInfo options_setup_tokens[] =
2531 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2534 static char *get_corrected_login_name(char *login_name)
2536 /* needed because player name must be a fixed length string */
2537 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2539 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2540 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2542 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2543 if (strchr(login_name_new, ' '))
2544 *strchr(login_name_new, ' ') = '\0';
2546 return login_name_new;
2549 static void setSetupInfoToDefaults(struct SetupInfo *si)
2553 si->player_name = get_corrected_login_name(getLoginName());
2556 si->sound_loops = TRUE;
2557 si->sound_music = TRUE;
2558 si->sound_simple = TRUE;
2560 si->double_buffering = TRUE;
2561 si->direct_draw = !si->double_buffering;
2562 si->scroll_delay = TRUE;
2563 si->soft_scrolling = TRUE;
2565 si->autorecord = TRUE;
2566 si->quick_doors = FALSE;
2567 si->team_mode = FALSE;
2568 si->handicap = TRUE;
2569 si->time_limit = TRUE;
2570 si->fullscreen = FALSE;
2571 si->ask_on_escape = TRUE;
2573 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2574 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2575 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2576 si->override_level_graphics = FALSE;
2577 si->override_level_sounds = FALSE;
2578 si->override_level_music = FALSE;
2580 si->editor.el_boulderdash = TRUE;
2581 si->editor.el_emerald_mine = TRUE;
2582 si->editor.el_more = TRUE;
2583 si->editor.el_sokoban = TRUE;
2584 si->editor.el_supaplex = TRUE;
2585 si->editor.el_diamond_caves = TRUE;
2586 si->editor.el_dx_boulderdash = TRUE;
2587 si->editor.el_chars = TRUE;
2588 si->editor.el_custom = TRUE;
2589 si->editor.el_custom_more = FALSE;
2591 si->editor.el_headlines = TRUE;
2593 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2594 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2595 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2597 for (i=0; i<MAX_PLAYERS; i++)
2599 si->input[i].use_joystick = FALSE;
2600 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2601 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2602 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2603 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2604 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2605 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2606 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2607 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2608 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2609 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2610 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2611 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2612 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2613 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2614 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2617 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2618 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2620 si->options.verbose = FALSE;
2623 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2627 if (!setup_file_hash)
2632 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2633 setSetupInfo(global_setup_tokens, i,
2634 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2639 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2640 setSetupInfo(editor_setup_tokens, i,
2641 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2644 /* shortcut setup */
2645 ssi = setup.shortcut;
2646 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2647 setSetupInfo(shortcut_setup_tokens, i,
2648 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2649 setup.shortcut = ssi;
2652 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2656 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2658 sii = setup.input[pnr];
2659 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2661 char full_token[100];
2663 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2664 setSetupInfo(player_setup_tokens, i,
2665 getHashEntry(setup_file_hash, full_token));
2667 setup.input[pnr] = sii;
2672 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2673 setSetupInfo(system_setup_tokens, i,
2674 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2678 soi = setup.options;
2679 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2680 setSetupInfo(options_setup_tokens, i,
2681 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2682 setup.options = soi;
2687 char *filename = getSetupFilename();
2688 SetupFileHash *setup_file_hash = NULL;
2690 /* always start with reliable default values */
2691 setSetupInfoToDefaults(&setup);
2693 setup_file_hash = loadSetupFileHash(filename);
2695 if (setup_file_hash)
2697 char *player_name_new;
2699 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2700 decodeSetupFileHash(setup_file_hash);
2702 setup.direct_draw = !setup.double_buffering;
2704 freeSetupFileHash(setup_file_hash);
2706 /* needed to work around problems with fixed length strings */
2707 player_name_new = get_corrected_login_name(setup.player_name);
2708 free(setup.player_name);
2709 setup.player_name = player_name_new;
2712 Error(ERR_WARN, "using default setup values");
2717 char *filename = getSetupFilename();
2721 InitUserDataDirectory();
2723 if (!(file = fopen(filename, MODE_WRITE)))
2725 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2729 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2730 getCookie("SETUP")));
2731 fprintf(file, "\n");
2735 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2737 /* just to make things nicer :) */
2738 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2739 i == SETUP_TOKEN_GRAPHICS_SET)
2740 fprintf(file, "\n");
2742 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2747 fprintf(file, "\n");
2748 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2749 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2751 /* shortcut setup */
2752 ssi = setup.shortcut;
2753 fprintf(file, "\n");
2754 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2755 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2758 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2762 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2763 fprintf(file, "\n");
2765 sii = setup.input[pnr];
2766 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2767 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2772 fprintf(file, "\n");
2773 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2774 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2777 soi = setup.options;
2778 fprintf(file, "\n");
2779 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2780 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2784 SetFilePermissions(filename, PERMS_PRIVATE);
2787 void LoadCustomElementDescriptions()
2789 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2790 SetupFileHash *setup_file_hash;
2793 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2795 if (element_info[i].custom_description != NULL)
2797 free(element_info[i].custom_description);
2798 element_info[i].custom_description = NULL;
2802 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2805 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2807 char *token = getStringCat2(element_info[i].token_name, ".name");
2808 char *value = getHashEntry(setup_file_hash, token);
2811 element_info[i].custom_description = getStringCopy(value);
2816 freeSetupFileHash(setup_file_hash);
2819 void LoadSpecialMenuDesignSettings()
2821 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2822 SetupFileHash *setup_file_hash;
2825 /* always start with reliable default values from default config */
2826 for (i=0; image_config_vars[i].token != NULL; i++)
2827 for (j=0; image_config[j].token != NULL; j++)
2828 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2829 *image_config_vars[i].value =
2830 get_auto_parameter_value(image_config_vars[i].token,
2831 image_config[j].value);
2833 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2836 /* special case: initialize with default values that may be overwritten */
2837 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2839 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2840 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2841 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2843 if (value_x != NULL)
2844 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2845 if (value_y != NULL)
2846 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2847 if (list_size != NULL)
2848 menu.list_size[i] = get_integer_from_string(list_size);
2851 /* read (and overwrite with) values that may be specified in config file */
2852 for (i=0; image_config_vars[i].token != NULL; i++)
2854 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2857 *image_config_vars[i].value =
2858 get_auto_parameter_value(image_config_vars[i].token, value);
2861 freeSetupFileHash(setup_file_hash);