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->initial_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 = -1; /* initialize later */
190 element_info[element].push_delay_random = -1; /* initialize later */
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_CONTRIB:
246 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
247 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
250 case LEVELCLASS_PRIVATE:
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->initial_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);
928 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
930 if (leveldir_current == NULL) /* only when dumping level */
934 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
937 /* determine correct game engine version of current level */
939 if (!leveldir_current->latest_engine)
941 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
942 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
943 IS_LEVELCLASS_UNDEFINED(leveldir_current))
947 printf("\n::: This level is private or contributed: '%s'\n", filename);
951 printf("\n::: Use the stored game engine version for this level\n");
954 /* For all levels which are not forced to use the latest game engine
955 version (normally user contributed, private and undefined levels),
956 use the version of the game engine the levels were created for.
958 Since 2.0.1, the game engine version is now directly stored
959 in the level file (chunk "VERS"), so there is no need anymore
960 to set the game version from the file version (except for old,
961 pre-2.0 levels, where the game version is still taken from the
962 file format version used to store the level -- see above). */
964 /* do some special adjustments to support older level versions */
965 if (level->file_version == FILE_VERSION_1_0)
967 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
968 Error(ERR_WARN, "using high speed movement for player");
970 /* player was faster than monsters in (pre-)1.0 levels */
971 level->double_speed = TRUE;
974 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
975 if (level->game_version == VERSION_IDENT(2,0,1,0))
976 level->em_slippery_gems = TRUE;
981 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
982 leveldir_current->sort_priority, filename);
986 printf("\n::: Use latest game engine version for this level.\n");
989 /* For all levels which are forced to use the latest game engine version
990 (normally all but user contributed, private and undefined levels), set
991 the game engine version to the actual version; this allows for actual
992 corrections in the game engine to take effect for existing, converted
993 levels (from "classic" or other existing games) to make the emulation
994 of the corresponding game more accurate, while (hopefully) not breaking
995 existing levels created from other players. */
998 printf("::: changing engine from %d to %d\n",
999 level->game_version, GAME_VERSION_ACTUAL);
1002 level->game_version = GAME_VERSION_ACTUAL;
1004 /* Set special EM style gems behaviour: EM style gems slip down from
1005 normal, steel and growing wall. As this is a more fundamental change,
1006 it seems better to set the default behaviour to "off" (as it is more
1007 natural) and make it configurable in the level editor (as a property
1008 of gem style elements). Already existing converted levels (neither
1009 private nor contributed levels) are changed to the new behaviour. */
1011 if (level->file_version < FILE_VERSION_2_0)
1012 level->em_slippery_gems = TRUE;
1016 printf("::: => %d\n", level->game_version);
1020 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
1024 /* map custom element change events that have changed in newer versions
1025 (these following values were accidentally changed in version 3.0.1) */
1026 if (level->game_version <= VERSION_IDENT(3,0,0,0))
1028 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1030 int element = EL_CUSTOM_START + i;
1032 /* order of checking and copying events to be mapped is important */
1033 for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
1035 if (HAS_CHANGE_EVENT(element, j - 2))
1037 SET_CHANGE_EVENT(element, j - 2, FALSE);
1038 SET_CHANGE_EVENT(element, j, TRUE);
1042 /* order of checking and copying events to be mapped is important */
1043 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1045 if (HAS_CHANGE_EVENT(element, j - 1))
1047 SET_CHANGE_EVENT(element, j - 1, FALSE);
1048 SET_CHANGE_EVENT(element, j, TRUE);
1054 /* some custom element change events get mapped since version 3.0.3 */
1055 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1057 int element = EL_CUSTOM_START + i;
1059 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
1060 HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
1062 SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
1063 SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
1065 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1069 /* initialize "can_change" field for old levels with only one change page */
1070 if (level->game_version <= VERSION_IDENT(3,0,2,0))
1072 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1074 int element = EL_CUSTOM_START + i;
1076 if (CAN_CHANGE(element))
1077 element_info[element].change->can_change = TRUE;
1082 /* set default push delay values (corrected since version 3.0.7-1) */
1083 if (level->game_version < VERSION_IDENT(3,0,7,1))
1085 game.default_push_delay_fixed = 2;
1086 game.default_push_delay_random = 8;
1090 game.default_push_delay_fixed = 8;
1091 game.default_push_delay_random = 8;
1094 /* set uninitialized push delay values of custom elements in older levels */
1095 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1097 int element = EL_CUSTOM_START + i;
1099 if (element_info[element].push_delay_fixed == -1)
1100 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
1101 if (element_info[element].push_delay_random == -1)
1102 element_info[element].push_delay_random = game.default_push_delay_random;
1106 /* initialize element properties for level editor etc. */
1107 InitElementPropertiesEngine(level->game_version);
1110 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1114 /* map elements that have changed in newer versions */
1115 for(y=0; y<level->fieldy; y++)
1117 for(x=0; x<level->fieldx; x++)
1119 int element = level->field[x][y];
1121 if (level->game_version <= VERSION_IDENT(2,2,0,0))
1123 /* map game font elements */
1124 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1125 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1126 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1127 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1130 if (level->game_version < VERSION_IDENT(3,0,0,0))
1132 /* map Supaplex gravity tube elements */
1133 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1134 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1135 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1136 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1140 level->field[x][y] = element;
1144 /* copy elements to runtime playfield array */
1145 for(x=0; x<MAX_LEV_FIELDX; x++)
1146 for(y=0; y<MAX_LEV_FIELDY; y++)
1147 Feld[x][y] = level->field[x][y];
1149 /* initialize level size variables for faster access */
1150 lev_fieldx = level->fieldx;
1151 lev_fieldy = level->fieldy;
1153 /* determine border element for this level */
1157 void LoadLevelTemplate(int level_nr)
1159 char *filename = getLevelFilename(level_nr);
1161 LoadLevelFromFilename(&level_template, filename);
1163 LoadLevel_InitVersion(&level, filename);
1164 LoadLevel_InitElements(&level, filename);
1166 ActivateLevelTemplate();
1169 void LoadLevel(int level_nr)
1171 char *filename = getLevelFilename(level_nr);
1173 LoadLevelFromFilename(&level, filename);
1175 if (level.use_custom_template)
1176 LoadLevelTemplate(-1);
1179 LoadLevel_InitVersion(&level, filename);
1180 LoadLevel_InitElements(&level, filename);
1181 LoadLevel_InitPlayfield(&level, filename);
1183 LoadLevel_InitLevel(&level, filename);
1187 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1189 putFileVersion(file, level->file_version);
1190 putFileVersion(file, level->game_version);
1193 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1197 putFile8Bit(file, level->fieldx);
1198 putFile8Bit(file, level->fieldy);
1200 putFile16BitBE(file, level->time);
1201 putFile16BitBE(file, level->gems_needed);
1203 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1204 putFile8Bit(file, level->name[i]);
1206 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1207 putFile8Bit(file, level->score[i]);
1209 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1212 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1213 level->yamyam_content[i][x][y]));
1214 putFile8Bit(file, level->amoeba_speed);
1215 putFile8Bit(file, level->time_magic_wall);
1216 putFile8Bit(file, level->time_wheel);
1217 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1218 level->amoeba_content));
1219 putFile8Bit(file, (level->double_speed ? 1 : 0));
1220 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
1221 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1222 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1224 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1226 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1229 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1233 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1234 putFile8Bit(file, level->author[i]);
1237 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1241 for(y=0; y<level->fieldy; y++)
1242 for(x=0; x<level->fieldx; x++)
1243 if (level->encoding_16bit_field)
1244 putFile16BitBE(file, level->field[x][y]);
1246 putFile8Bit(file, level->field[x][y]);
1250 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1254 putFile8Bit(file, EL_YAMYAM);
1255 putFile8Bit(file, level->num_yamyam_contents);
1256 putFile8Bit(file, 0);
1257 putFile8Bit(file, 0);
1259 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1262 if (level->encoding_16bit_field)
1263 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1265 putFile8Bit(file, level->yamyam_content[i][x][y]);
1269 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1272 int num_contents, content_xsize, content_ysize;
1273 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1275 if (element == EL_YAMYAM)
1277 num_contents = level->num_yamyam_contents;
1281 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1284 content_array[i][x][y] = level->yamyam_content[i][x][y];
1286 else if (element == EL_BD_AMOEBA)
1292 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1295 content_array[i][x][y] = EL_EMPTY;
1296 content_array[0][0][0] = level->amoeba_content;
1300 /* chunk header already written -- write empty chunk data */
1301 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1303 Error(ERR_WARN, "cannot save content for element '%d'", element);
1307 putFile16BitBE(file, element);
1308 putFile8Bit(file, num_contents);
1309 putFile8Bit(file, content_xsize);
1310 putFile8Bit(file, content_ysize);
1312 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1314 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1317 putFile16BitBE(file, content_array[i][x][y]);
1320 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1323 int envelope_nr = element - EL_ENVELOPE_1;
1324 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1326 putFile16BitBE(file, element);
1327 putFile16BitBE(file, envelope_len);
1328 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1329 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1331 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1333 for(i=0; i < envelope_len; i++)
1334 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1338 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1339 int num_changed_custom_elements)
1343 putFile16BitBE(file, num_changed_custom_elements);
1345 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1347 int element = EL_CUSTOM_START + i;
1349 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1351 if (check < num_changed_custom_elements)
1353 putFile16BitBE(file, element);
1354 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1361 if (check != num_changed_custom_elements) /* should not happen */
1362 Error(ERR_WARN, "inconsistent number of custom element properties");
1367 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1368 int num_changed_custom_elements)
1372 putFile16BitBE(file, num_changed_custom_elements);
1374 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1376 int element = EL_CUSTOM_START + i;
1378 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1380 if (check < num_changed_custom_elements)
1382 putFile16BitBE(file, element);
1383 putFile16BitBE(file, element_info[element].change->target_element);
1390 if (check != num_changed_custom_elements) /* should not happen */
1391 Error(ERR_WARN, "inconsistent number of custom target elements");
1396 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1397 int num_changed_custom_elements)
1399 int i, j, x, y, check = 0;
1401 putFile16BitBE(file, num_changed_custom_elements);
1403 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1405 int element = EL_CUSTOM_START + i;
1407 if (element_info[element].modified_settings)
1409 if (check < num_changed_custom_elements)
1411 putFile16BitBE(file, element);
1413 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1414 putFile8Bit(file, element_info[element].description[j]);
1416 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1418 /* some free bytes for future properties and padding */
1419 WriteUnusedBytesToFile(file, 7);
1421 putFile8Bit(file, element_info[element].use_gfx_element);
1422 putFile16BitBE(file, element_info[element].gfx_element);
1424 putFile8Bit(file, element_info[element].collect_score);
1425 putFile8Bit(file, element_info[element].collect_count);
1427 putFile16BitBE(file, element_info[element].push_delay_fixed);
1428 putFile16BitBE(file, element_info[element].push_delay_random);
1429 putFile16BitBE(file, element_info[element].move_delay_fixed);
1430 putFile16BitBE(file, element_info[element].move_delay_random);
1432 putFile16BitBE(file, element_info[element].move_pattern);
1433 putFile8Bit(file, element_info[element].move_direction_initial);
1434 putFile8Bit(file, element_info[element].move_stepsize);
1438 putFile16BitBE(file, element_info[element].content[x][y]);
1440 putFile32BitBE(file, element_info[element].change->events);
1442 putFile16BitBE(file, element_info[element].change->target_element);
1444 putFile16BitBE(file, element_info[element].change->delay_fixed);
1445 putFile16BitBE(file, element_info[element].change->delay_random);
1446 putFile16BitBE(file, element_info[element].change->delay_frames);
1448 putFile16BitBE(file, element_info[element].change->trigger_element);
1450 putFile8Bit(file, element_info[element].change->explode);
1451 putFile8Bit(file, element_info[element].change->use_content);
1452 putFile8Bit(file, element_info[element].change->only_complete);
1453 putFile8Bit(file, element_info[element].change->use_random_change);
1455 putFile8Bit(file, element_info[element].change->random);
1456 putFile8Bit(file, element_info[element].change->power);
1460 putFile16BitBE(file, element_info[element].change->content[x][y]);
1462 putFile8Bit(file, element_info[element].slippery_type);
1464 /* some free bytes for future properties and padding */
1465 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1472 if (check != num_changed_custom_elements) /* should not happen */
1473 Error(ERR_WARN, "inconsistent number of custom element properties");
1477 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1479 struct ElementInfo *ei = &element_info[element];
1482 putFile16BitBE(file, element);
1484 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
1485 putFile8Bit(file, ei->description[i]);
1487 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1488 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
1490 putFile8Bit(file, ei->num_change_pages);
1492 /* some free bytes for future base property values and padding */
1493 WriteUnusedBytesToFile(file, 5);
1495 /* write custom property values */
1497 putFile8Bit(file, ei->use_gfx_element);
1498 putFile16BitBE(file, ei->gfx_element);
1500 putFile8Bit(file, ei->collect_score);
1501 putFile8Bit(file, ei->collect_count);
1503 putFile16BitBE(file, ei->push_delay_fixed);
1504 putFile16BitBE(file, ei->push_delay_random);
1505 putFile16BitBE(file, ei->move_delay_fixed);
1506 putFile16BitBE(file, ei->move_delay_random);
1508 putFile16BitBE(file, ei->move_pattern);
1509 putFile8Bit(file, ei->move_direction_initial);
1510 putFile8Bit(file, ei->move_stepsize);
1512 putFile8Bit(file, ei->slippery_type);
1516 putFile16BitBE(file, ei->content[x][y]);
1518 /* some free bytes for future custom property values and padding */
1519 WriteUnusedBytesToFile(file, 12);
1521 /* write change property values */
1523 for (i=0; i < ei->num_change_pages; i++)
1525 struct ElementChangeInfo *change = &ei->change_page[i];
1527 putFile32BitBE(file, change->events);
1529 putFile16BitBE(file, change->target_element);
1531 putFile16BitBE(file, change->delay_fixed);
1532 putFile16BitBE(file, change->delay_random);
1533 putFile16BitBE(file, change->delay_frames);
1535 putFile16BitBE(file, change->trigger_element);
1537 putFile8Bit(file, change->explode);
1538 putFile8Bit(file, change->use_content);
1539 putFile8Bit(file, change->only_complete);
1540 putFile8Bit(file, change->use_random_change);
1542 putFile8Bit(file, change->random);
1543 putFile8Bit(file, change->power);
1547 putFile16BitBE(file, change->content[x][y]);
1549 putFile8Bit(file, change->can_change);
1551 putFile8Bit(file, change->sides);
1553 /* some free bytes for future change property values and padding */
1554 WriteUnusedBytesToFile(file, 8);
1558 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1560 int body_chunk_size;
1564 if (!(file = fopen(filename, MODE_WRITE)))
1566 Error(ERR_WARN, "cannot save level file '%s'", filename);
1570 level->file_version = FILE_VERSION_ACTUAL;
1571 level->game_version = GAME_VERSION_ACTUAL;
1573 /* check level field for 16-bit elements */
1574 level->encoding_16bit_field = FALSE;
1575 for(y=0; y<level->fieldy; y++)
1576 for(x=0; x<level->fieldx; x++)
1577 if (level->field[x][y] > 255)
1578 level->encoding_16bit_field = TRUE;
1580 /* check yamyam content for 16-bit elements */
1581 level->encoding_16bit_yamyam = FALSE;
1582 for(i=0; i<level->num_yamyam_contents; i++)
1585 if (level->yamyam_content[i][x][y] > 255)
1586 level->encoding_16bit_yamyam = TRUE;
1588 /* check amoeba content for 16-bit elements */
1589 level->encoding_16bit_amoeba = FALSE;
1590 if (level->amoeba_content > 255)
1591 level->encoding_16bit_amoeba = TRUE;
1593 /* calculate size of "BODY" chunk */
1595 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1597 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1598 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1600 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1601 SaveLevel_VERS(file, level);
1603 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1604 SaveLevel_HEAD(file, level);
1606 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1607 SaveLevel_AUTH(file, level);
1609 putFileChunkBE(file, "BODY", body_chunk_size);
1610 SaveLevel_BODY(file, level);
1612 if (level->encoding_16bit_yamyam ||
1613 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1615 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1616 SaveLevel_CNT2(file, level, EL_YAMYAM);
1619 if (level->encoding_16bit_amoeba)
1621 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1622 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1625 /* check for envelope content */
1628 if (strlen(level->envelope_text[i]) > 0)
1630 int envelope_len = strlen(level->envelope_text[i]) + 1;
1632 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
1633 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
1637 /* check for non-default custom elements (unless using template level) */
1638 if (!level->use_custom_template)
1640 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1642 int element = EL_CUSTOM_START + i;
1644 if (element_info[element].modified_settings)
1646 int num_change_pages = element_info[element].num_change_pages;
1648 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
1649 SaveLevel_CUS4(file, level, element);
1656 SetFilePermissions(filename, PERMS_PRIVATE);
1659 void SaveLevel(int level_nr)
1661 char *filename = getLevelFilename(level_nr);
1663 SaveLevelFromFilename(&level, filename);
1666 void SaveLevelTemplate()
1668 char *filename = getLevelFilename(-1);
1670 SaveLevelFromFilename(&level, filename);
1673 void DumpLevel(struct LevelInfo *level)
1675 printf_line("-", 79);
1676 printf("Level xxx (file version %08d, game version %08d)\n",
1677 level->file_version, level->game_version);
1678 printf_line("-", 79);
1680 printf("Level Author: '%s'\n", level->author);
1681 printf("Level Title: '%s'\n", level->name);
1683 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1685 printf("Level Time: %d seconds\n", level->time);
1686 printf("Gems needed: %d\n", level->gems_needed);
1688 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1689 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1690 printf("Time for Light: %d seconds\n", level->time_light);
1691 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1693 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1695 printf("Gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
1696 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1697 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1699 printf_line("-", 79);
1703 /* ========================================================================= */
1704 /* tape file functions */
1705 /* ========================================================================= */
1707 static void setTapeInfoToDefaults()
1711 /* always start with reliable default values (empty tape) */
1714 /* default values (also for pre-1.2 tapes) with only the first player */
1715 tape.player_participates[0] = TRUE;
1716 for(i=1; i<MAX_PLAYERS; i++)
1717 tape.player_participates[i] = FALSE;
1719 /* at least one (default: the first) player participates in every tape */
1720 tape.num_participating_players = 1;
1722 tape.level_nr = level_nr;
1724 tape.changed = FALSE;
1726 tape.recording = FALSE;
1727 tape.playing = FALSE;
1728 tape.pausing = FALSE;
1731 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1733 tape->file_version = getFileVersion(file);
1734 tape->game_version = getFileVersion(file);
1739 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1743 tape->random_seed = getFile32BitBE(file);
1744 tape->date = getFile32BitBE(file);
1745 tape->length = getFile32BitBE(file);
1747 /* read header fields that are new since version 1.2 */
1748 if (tape->file_version >= FILE_VERSION_1_2)
1750 byte store_participating_players = getFile8Bit(file);
1753 /* since version 1.2, tapes store which players participate in the tape */
1754 tape->num_participating_players = 0;
1755 for(i=0; i<MAX_PLAYERS; i++)
1757 tape->player_participates[i] = FALSE;
1759 if (store_participating_players & (1 << i))
1761 tape->player_participates[i] = TRUE;
1762 tape->num_participating_players++;
1766 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1768 engine_version = getFileVersion(file);
1769 if (engine_version > 0)
1770 tape->engine_version = engine_version;
1772 tape->engine_version = tape->game_version;
1778 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1780 int level_identifier_size;
1783 level_identifier_size = getFile16BitBE(file);
1785 tape->level_identifier =
1786 checked_realloc(tape->level_identifier, level_identifier_size);
1788 for(i=0; i < level_identifier_size; i++)
1789 tape->level_identifier[i] = getFile8Bit(file);
1791 tape->level_nr = getFile16BitBE(file);
1793 chunk_size = 2 + level_identifier_size + 2;
1798 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1801 int chunk_size_expected =
1802 (tape->num_participating_players + 1) * tape->length;
1804 if (chunk_size_expected != chunk_size)
1806 ReadUnusedBytesFromFile(file, chunk_size);
1807 return chunk_size_expected;
1810 for(i=0; i<tape->length; i++)
1812 if (i >= MAX_TAPELEN)
1815 for(j=0; j<MAX_PLAYERS; j++)
1817 tape->pos[i].action[j] = MV_NO_MOVING;
1819 if (tape->player_participates[j])
1820 tape->pos[i].action[j] = getFile8Bit(file);
1823 tape->pos[i].delay = getFile8Bit(file);
1825 if (tape->file_version == FILE_VERSION_1_0)
1827 /* eliminate possible diagonal moves in old tapes */
1828 /* this is only for backward compatibility */
1830 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1831 byte action = tape->pos[i].action[0];
1832 int k, num_moves = 0;
1836 if (action & joy_dir[k])
1838 tape->pos[i + num_moves].action[0] = joy_dir[k];
1840 tape->pos[i + num_moves].delay = 0;
1849 tape->length += num_moves;
1852 else if (tape->file_version < FILE_VERSION_2_0)
1854 /* convert pre-2.0 tapes to new tape format */
1856 if (tape->pos[i].delay > 1)
1859 tape->pos[i + 1] = tape->pos[i];
1860 tape->pos[i + 1].delay = 1;
1863 for(j=0; j<MAX_PLAYERS; j++)
1864 tape->pos[i].action[j] = MV_NO_MOVING;
1865 tape->pos[i].delay--;
1876 if (i != tape->length)
1877 chunk_size = (tape->num_participating_players + 1) * i;
1882 void LoadTapeFromFilename(char *filename)
1884 char cookie[MAX_LINE_LEN];
1885 char chunk_name[CHUNK_ID_LEN + 1];
1889 /* always start with reliable default values */
1890 setTapeInfoToDefaults();
1892 if (!(file = fopen(filename, MODE_READ)))
1895 getFileChunkBE(file, chunk_name, NULL);
1896 if (strcmp(chunk_name, "RND1") == 0)
1898 getFile32BitBE(file); /* not used */
1900 getFileChunkBE(file, chunk_name, NULL);
1901 if (strcmp(chunk_name, "TAPE") != 0)
1903 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1908 else /* check for pre-2.0 file format with cookie string */
1910 strcpy(cookie, chunk_name);
1911 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1912 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1913 cookie[strlen(cookie) - 1] = '\0';
1915 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1917 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1922 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1924 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1929 /* pre-2.0 tape files have no game version, so use file version here */
1930 tape.game_version = tape.file_version;
1933 if (tape.file_version < FILE_VERSION_1_2)
1935 /* tape files from versions before 1.2.0 without chunk structure */
1936 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1937 LoadTape_BODY(file, 2 * tape.length, &tape);
1945 int (*loader)(FILE *, int, struct TapeInfo *);
1949 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1950 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1951 { "INFO", -1, LoadTape_INFO },
1952 { "BODY", -1, LoadTape_BODY },
1956 while (getFileChunkBE(file, chunk_name, &chunk_size))
1960 while (chunk_info[i].name != NULL &&
1961 strcmp(chunk_name, chunk_info[i].name) != 0)
1964 if (chunk_info[i].name == NULL)
1966 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1967 chunk_name, filename);
1968 ReadUnusedBytesFromFile(file, chunk_size);
1970 else if (chunk_info[i].size != -1 &&
1971 chunk_info[i].size != chunk_size)
1973 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1974 chunk_size, chunk_name, filename);
1975 ReadUnusedBytesFromFile(file, chunk_size);
1979 /* call function to load this tape chunk */
1980 int chunk_size_expected =
1981 (chunk_info[i].loader)(file, chunk_size, &tape);
1983 /* the size of some chunks cannot be checked before reading other
1984 chunks first (like "HEAD" and "BODY") that contain some header
1985 information, so check them here */
1986 if (chunk_size_expected != chunk_size)
1988 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1989 chunk_size, chunk_name, filename);
1997 tape.length_seconds = GetTapeLength();
2000 printf("::: tape game version: %d\n", tape.game_version);
2001 printf("::: tape engine version: %d\n", tape.engine_version);
2005 void LoadTape(int level_nr)
2007 char *filename = getTapeFilename(level_nr);
2009 LoadTapeFromFilename(filename);
2012 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2014 putFileVersion(file, tape->file_version);
2015 putFileVersion(file, tape->game_version);
2018 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2021 byte store_participating_players = 0;
2023 /* set bits for participating players for compact storage */
2024 for(i=0; i<MAX_PLAYERS; i++)
2025 if (tape->player_participates[i])
2026 store_participating_players |= (1 << i);
2028 putFile32BitBE(file, tape->random_seed);
2029 putFile32BitBE(file, tape->date);
2030 putFile32BitBE(file, tape->length);
2032 putFile8Bit(file, store_participating_players);
2034 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2035 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2037 putFileVersion(file, tape->engine_version);
2040 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2042 int level_identifier_size = strlen(tape->level_identifier) + 1;
2045 putFile16BitBE(file, level_identifier_size);
2047 for(i=0; i < level_identifier_size; i++)
2048 putFile8Bit(file, tape->level_identifier[i]);
2050 putFile16BitBE(file, tape->level_nr);
2053 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2057 for(i=0; i<tape->length; i++)
2059 for(j=0; j<MAX_PLAYERS; j++)
2060 if (tape->player_participates[j])
2061 putFile8Bit(file, tape->pos[i].action[j]);
2063 putFile8Bit(file, tape->pos[i].delay);
2067 void SaveTape(int level_nr)
2069 char *filename = getTapeFilename(level_nr);
2071 boolean new_tape = TRUE;
2072 int num_participating_players = 0;
2073 int info_chunk_size;
2074 int body_chunk_size;
2077 InitTapeDirectory(leveldir_current->filename);
2079 /* if a tape still exists, ask to overwrite it */
2080 if (access(filename, F_OK) == 0)
2083 if (!Request("Replace old tape ?", REQ_ASK))
2087 if (!(file = fopen(filename, MODE_WRITE)))
2089 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2093 tape.file_version = FILE_VERSION_ACTUAL;
2094 tape.game_version = GAME_VERSION_ACTUAL;
2096 /* count number of participating players */
2097 for(i=0; i<MAX_PLAYERS; i++)
2098 if (tape.player_participates[i])
2099 num_participating_players++;
2101 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2102 body_chunk_size = (num_participating_players + 1) * tape.length;
2104 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2105 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2107 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2108 SaveTape_VERS(file, &tape);
2110 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2111 SaveTape_HEAD(file, &tape);
2113 putFileChunkBE(file, "INFO", info_chunk_size);
2114 SaveTape_INFO(file, &tape);
2116 putFileChunkBE(file, "BODY", body_chunk_size);
2117 SaveTape_BODY(file, &tape);
2121 SetFilePermissions(filename, PERMS_PRIVATE);
2123 tape.changed = FALSE;
2126 Request("tape saved !", REQ_CONFIRM);
2129 void DumpTape(struct TapeInfo *tape)
2133 if (TAPE_IS_EMPTY(*tape))
2135 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2139 printf_line("-", 79);
2140 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2141 tape->level_nr, tape->file_version, tape->game_version);
2142 printf("Level series identifier: '%s'\n", tape->level_identifier);
2143 printf_line("-", 79);
2145 for(i=0; i<tape->length; i++)
2147 if (i >= MAX_TAPELEN)
2150 printf("%03d: ", i);
2152 for(j=0; j<MAX_PLAYERS; j++)
2154 if (tape->player_participates[j])
2156 int action = tape->pos[i].action[j];
2158 printf("%d:%02x ", j, action);
2159 printf("[%c%c%c%c|%c%c] - ",
2160 (action & JOY_LEFT ? '<' : ' '),
2161 (action & JOY_RIGHT ? '>' : ' '),
2162 (action & JOY_UP ? '^' : ' '),
2163 (action & JOY_DOWN ? 'v' : ' '),
2164 (action & JOY_BUTTON_1 ? '1' : ' '),
2165 (action & JOY_BUTTON_2 ? '2' : ' '));
2169 printf("(%03d)\n", tape->pos[i].delay);
2172 printf_line("-", 79);
2176 /* ========================================================================= */
2177 /* score file functions */
2178 /* ========================================================================= */
2180 void LoadScore(int level_nr)
2183 char *filename = getScoreFilename(level_nr);
2184 char cookie[MAX_LINE_LEN];
2185 char line[MAX_LINE_LEN];
2189 /* always start with reliable default values */
2190 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2192 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2193 highscore[i].Score = 0;
2196 if (!(file = fopen(filename, MODE_READ)))
2199 /* check file identifier */
2200 fgets(cookie, MAX_LINE_LEN, file);
2201 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2202 cookie[strlen(cookie) - 1] = '\0';
2204 if (!checkCookieString(cookie, SCORE_COOKIE))
2206 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2211 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2213 fscanf(file, "%d", &highscore[i].Score);
2214 fgets(line, MAX_LINE_LEN, file);
2216 if (line[strlen(line) - 1] == '\n')
2217 line[strlen(line) - 1] = '\0';
2219 for (line_ptr = line; *line_ptr; line_ptr++)
2221 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2223 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2224 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2233 void SaveScore(int level_nr)
2236 char *filename = getScoreFilename(level_nr);
2239 InitScoreDirectory(leveldir_current->filename);
2241 if (!(file = fopen(filename, MODE_WRITE)))
2243 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2247 fprintf(file, "%s\n\n", SCORE_COOKIE);
2249 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2250 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2254 SetFilePermissions(filename, PERMS_PUBLIC);
2258 /* ========================================================================= */
2259 /* setup file functions */
2260 /* ========================================================================= */
2262 #define TOKEN_STR_PLAYER_PREFIX "player_"
2265 #define SETUP_TOKEN_PLAYER_NAME 0
2266 #define SETUP_TOKEN_SOUND 1
2267 #define SETUP_TOKEN_SOUND_LOOPS 2
2268 #define SETUP_TOKEN_SOUND_MUSIC 3
2269 #define SETUP_TOKEN_SOUND_SIMPLE 4
2270 #define SETUP_TOKEN_TOONS 5
2271 #define SETUP_TOKEN_SCROLL_DELAY 6
2272 #define SETUP_TOKEN_SOFT_SCROLLING 7
2273 #define SETUP_TOKEN_FADING 8
2274 #define SETUP_TOKEN_AUTORECORD 9
2275 #define SETUP_TOKEN_QUICK_DOORS 10
2276 #define SETUP_TOKEN_TEAM_MODE 11
2277 #define SETUP_TOKEN_HANDICAP 12
2278 #define SETUP_TOKEN_TIME_LIMIT 13
2279 #define SETUP_TOKEN_FULLSCREEN 14
2280 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2281 #define SETUP_TOKEN_GRAPHICS_SET 16
2282 #define SETUP_TOKEN_SOUNDS_SET 17
2283 #define SETUP_TOKEN_MUSIC_SET 18
2284 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2285 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2286 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2288 #define NUM_GLOBAL_SETUP_TOKENS 22
2291 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2292 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2293 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2294 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2295 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2296 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2297 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2298 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2299 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2300 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2301 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2303 #define NUM_EDITOR_SETUP_TOKENS 11
2305 /* shortcut setup */
2306 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2307 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2308 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2310 #define NUM_SHORTCUT_SETUP_TOKENS 3
2313 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2314 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2315 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2316 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2317 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2318 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2319 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2320 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2321 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2322 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
2323 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2324 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2325 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2326 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2327 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2328 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
2330 #define NUM_PLAYER_SETUP_TOKENS 16
2333 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2334 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2336 #define NUM_SYSTEM_SETUP_TOKENS 2
2339 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2341 #define NUM_OPTIONS_SETUP_TOKENS 1
2344 static struct SetupInfo si;
2345 static struct SetupEditorInfo sei;
2346 static struct SetupShortcutInfo ssi;
2347 static struct SetupInputInfo sii;
2348 static struct SetupSystemInfo syi;
2349 static struct OptionInfo soi;
2351 static struct TokenInfo global_setup_tokens[] =
2353 { TYPE_STRING, &si.player_name, "player_name" },
2354 { TYPE_SWITCH, &si.sound, "sound" },
2355 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2356 { TYPE_SWITCH, &si.sound_music, "background_music" },
2357 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2358 { TYPE_SWITCH, &si.toons, "toons" },
2359 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2360 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2361 { TYPE_SWITCH, &si.fading, "screen_fading" },
2362 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2363 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2364 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2365 { TYPE_SWITCH, &si.handicap, "handicap" },
2366 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2367 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2368 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2369 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2370 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2371 { TYPE_STRING, &si.music_set, "music_set" },
2372 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2373 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2374 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2377 static struct TokenInfo editor_setup_tokens[] =
2379 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2380 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2381 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2382 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2383 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2384 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2385 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2386 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2387 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2388 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2389 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2392 static struct TokenInfo shortcut_setup_tokens[] =
2394 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2395 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2396 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2399 static struct TokenInfo player_setup_tokens[] =
2401 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2402 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2403 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2404 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2405 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2406 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2407 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2408 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2409 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2410 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2411 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2412 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2413 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2414 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2415 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2416 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
2419 static struct TokenInfo system_setup_tokens[] =
2421 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2422 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2425 static struct TokenInfo options_setup_tokens[] =
2427 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2430 static char *get_corrected_login_name(char *login_name)
2432 /* needed because player name must be a fixed length string */
2433 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2435 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2436 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2438 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2439 if (strchr(login_name_new, ' '))
2440 *strchr(login_name_new, ' ') = '\0';
2442 return login_name_new;
2445 static void setSetupInfoToDefaults(struct SetupInfo *si)
2449 si->player_name = get_corrected_login_name(getLoginName());
2452 si->sound_loops = TRUE;
2453 si->sound_music = TRUE;
2454 si->sound_simple = TRUE;
2456 si->double_buffering = TRUE;
2457 si->direct_draw = !si->double_buffering;
2458 si->scroll_delay = TRUE;
2459 si->soft_scrolling = TRUE;
2461 si->autorecord = TRUE;
2462 si->quick_doors = FALSE;
2463 si->team_mode = FALSE;
2464 si->handicap = TRUE;
2465 si->time_limit = TRUE;
2466 si->fullscreen = FALSE;
2467 si->ask_on_escape = TRUE;
2469 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2470 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2471 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2472 si->override_level_graphics = FALSE;
2473 si->override_level_sounds = FALSE;
2474 si->override_level_music = FALSE;
2476 si->editor.el_boulderdash = TRUE;
2477 si->editor.el_emerald_mine = TRUE;
2478 si->editor.el_more = TRUE;
2479 si->editor.el_sokoban = TRUE;
2480 si->editor.el_supaplex = TRUE;
2481 si->editor.el_diamond_caves = TRUE;
2482 si->editor.el_dx_boulderdash = TRUE;
2483 si->editor.el_chars = TRUE;
2484 si->editor.el_custom = TRUE;
2485 si->editor.el_custom_more = FALSE;
2487 si->editor.el_headlines = TRUE;
2489 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2490 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2491 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2493 for (i=0; i<MAX_PLAYERS; i++)
2495 si->input[i].use_joystick = FALSE;
2496 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2497 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2498 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2499 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2500 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2501 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2502 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2503 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2504 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2505 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2506 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2507 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2508 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2509 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2510 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2513 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2514 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2516 si->options.verbose = FALSE;
2519 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2523 if (!setup_file_hash)
2528 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2529 setSetupInfo(global_setup_tokens, i,
2530 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2535 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2536 setSetupInfo(editor_setup_tokens, i,
2537 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2540 /* shortcut setup */
2541 ssi = setup.shortcut;
2542 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2543 setSetupInfo(shortcut_setup_tokens, i,
2544 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2545 setup.shortcut = ssi;
2548 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2552 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2554 sii = setup.input[pnr];
2555 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2557 char full_token[100];
2559 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2560 setSetupInfo(player_setup_tokens, i,
2561 getHashEntry(setup_file_hash, full_token));
2563 setup.input[pnr] = sii;
2568 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2569 setSetupInfo(system_setup_tokens, i,
2570 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2574 soi = setup.options;
2575 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2576 setSetupInfo(options_setup_tokens, i,
2577 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2578 setup.options = soi;
2583 char *filename = getSetupFilename();
2584 SetupFileHash *setup_file_hash = NULL;
2586 /* always start with reliable default values */
2587 setSetupInfoToDefaults(&setup);
2589 setup_file_hash = loadSetupFileHash(filename);
2591 if (setup_file_hash)
2593 char *player_name_new;
2595 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2596 decodeSetupFileHash(setup_file_hash);
2598 setup.direct_draw = !setup.double_buffering;
2600 freeSetupFileHash(setup_file_hash);
2602 /* needed to work around problems with fixed length strings */
2603 player_name_new = get_corrected_login_name(setup.player_name);
2604 free(setup.player_name);
2605 setup.player_name = player_name_new;
2608 Error(ERR_WARN, "using default setup values");
2613 char *filename = getSetupFilename();
2617 InitUserDataDirectory();
2619 if (!(file = fopen(filename, MODE_WRITE)))
2621 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2625 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2626 getCookie("SETUP")));
2627 fprintf(file, "\n");
2631 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2633 /* just to make things nicer :) */
2634 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2635 i == SETUP_TOKEN_GRAPHICS_SET)
2636 fprintf(file, "\n");
2638 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2643 fprintf(file, "\n");
2644 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2645 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2647 /* shortcut setup */
2648 ssi = setup.shortcut;
2649 fprintf(file, "\n");
2650 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2651 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2654 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2658 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2659 fprintf(file, "\n");
2661 sii = setup.input[pnr];
2662 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2663 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2668 fprintf(file, "\n");
2669 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2670 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2673 soi = setup.options;
2674 fprintf(file, "\n");
2675 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2676 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2680 SetFilePermissions(filename, PERMS_PRIVATE);
2683 void LoadCustomElementDescriptions()
2685 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2686 SetupFileHash *setup_file_hash;
2689 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2691 if (element_info[i].custom_description != NULL)
2693 free(element_info[i].custom_description);
2694 element_info[i].custom_description = NULL;
2698 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2701 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2703 char *token = getStringCat2(element_info[i].token_name, ".name");
2704 char *value = getHashEntry(setup_file_hash, token);
2707 element_info[i].custom_description = getStringCopy(value);
2712 freeSetupFileHash(setup_file_hash);
2715 void LoadSpecialMenuDesignSettings()
2717 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2718 SetupFileHash *setup_file_hash;
2721 /* always start with reliable default values from default config */
2722 for (i=0; image_config_vars[i].token != NULL; i++)
2723 for (j=0; image_config[j].token != NULL; j++)
2724 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2725 *image_config_vars[i].value =
2726 get_auto_parameter_value(image_config_vars[i].token,
2727 image_config[j].value);
2729 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2732 /* special case: initialize with default values that may be overwritten */
2733 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2735 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2736 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2737 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2739 if (value_x != NULL)
2740 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2741 if (value_y != NULL)
2742 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2743 if (list_size != NULL)
2744 menu.list_size[i] = get_integer_from_string(list_size);
2747 /* read (and overwrite with) values that may be specified in config file */
2748 for (i=0; image_config_vars[i].token != NULL; i++)
2750 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2753 *image_config_vars[i].value =
2754 get_auto_parameter_value(image_config_vars[i].token, value);
2757 freeSetupFileHash(setup_file_hash);