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))
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))
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))
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;
1081 /* set default push delay values (corrected since version 3.0.7) */
1082 if (level->game_version < VERSION_IDENT(3,0,7))
1084 game.default_push_delay_fixed = 2;
1085 game.default_push_delay_random = 8;
1089 game.default_push_delay_fixed = 8;
1090 game.default_push_delay_random = 8;
1093 /* set uninitialized push delay values of custom elements in older levels */
1094 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1096 int element = EL_CUSTOM_START + i;
1098 if (element_info[element].push_delay_fixed == -1)
1099 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
1100 if (element_info[element].push_delay_random == -1)
1101 element_info[element].push_delay_random = game.default_push_delay_random;
1104 /* initialize element properties for level editor etc. */
1105 InitElementPropertiesEngine(level->game_version);
1108 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1112 /* map elements that have changed in newer versions */
1113 for(y=0; y<level->fieldy; y++)
1115 for(x=0; x<level->fieldx; x++)
1117 int element = level->field[x][y];
1119 if (level->game_version <= VERSION_IDENT(2,2,0))
1121 /* map game font elements */
1122 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1123 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1124 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1125 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1128 if (level->game_version < VERSION_IDENT(3,0,0))
1130 /* map Supaplex gravity tube elements */
1131 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1132 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1133 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1134 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1138 level->field[x][y] = element;
1142 /* copy elements to runtime playfield array */
1143 for(x=0; x<MAX_LEV_FIELDX; x++)
1144 for(y=0; y<MAX_LEV_FIELDY; y++)
1145 Feld[x][y] = level->field[x][y];
1147 /* initialize level size variables for faster access */
1148 lev_fieldx = level->fieldx;
1149 lev_fieldy = level->fieldy;
1151 /* determine border element for this level */
1155 void LoadLevelTemplate(int level_nr)
1157 char *filename = getLevelFilename(level_nr);
1159 LoadLevelFromFilename(&level_template, filename);
1161 LoadLevel_InitVersion(&level, filename);
1162 LoadLevel_InitElements(&level, filename);
1164 ActivateLevelTemplate();
1167 void LoadLevel(int level_nr)
1169 char *filename = getLevelFilename(level_nr);
1171 LoadLevelFromFilename(&level, filename);
1173 if (level.use_custom_template)
1174 LoadLevelTemplate(-1);
1177 LoadLevel_InitVersion(&level, filename);
1178 LoadLevel_InitElements(&level, filename);
1179 LoadLevel_InitPlayfield(&level, filename);
1181 LoadLevel_InitLevel(&level, filename);
1185 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1187 putFileVersion(file, level->file_version);
1188 putFileVersion(file, level->game_version);
1191 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1195 putFile8Bit(file, level->fieldx);
1196 putFile8Bit(file, level->fieldy);
1198 putFile16BitBE(file, level->time);
1199 putFile16BitBE(file, level->gems_needed);
1201 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1202 putFile8Bit(file, level->name[i]);
1204 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1205 putFile8Bit(file, level->score[i]);
1207 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1210 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1211 level->yamyam_content[i][x][y]));
1212 putFile8Bit(file, level->amoeba_speed);
1213 putFile8Bit(file, level->time_magic_wall);
1214 putFile8Bit(file, level->time_wheel);
1215 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1216 level->amoeba_content));
1217 putFile8Bit(file, (level->double_speed ? 1 : 0));
1218 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
1219 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1220 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1222 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1224 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1227 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1231 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1232 putFile8Bit(file, level->author[i]);
1235 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1239 for(y=0; y<level->fieldy; y++)
1240 for(x=0; x<level->fieldx; x++)
1241 if (level->encoding_16bit_field)
1242 putFile16BitBE(file, level->field[x][y]);
1244 putFile8Bit(file, level->field[x][y]);
1248 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1252 putFile8Bit(file, EL_YAMYAM);
1253 putFile8Bit(file, level->num_yamyam_contents);
1254 putFile8Bit(file, 0);
1255 putFile8Bit(file, 0);
1257 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1260 if (level->encoding_16bit_field)
1261 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1263 putFile8Bit(file, level->yamyam_content[i][x][y]);
1267 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1270 int num_contents, content_xsize, content_ysize;
1271 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1273 if (element == EL_YAMYAM)
1275 num_contents = level->num_yamyam_contents;
1279 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1282 content_array[i][x][y] = level->yamyam_content[i][x][y];
1284 else if (element == EL_BD_AMOEBA)
1290 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1293 content_array[i][x][y] = EL_EMPTY;
1294 content_array[0][0][0] = level->amoeba_content;
1298 /* chunk header already written -- write empty chunk data */
1299 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1301 Error(ERR_WARN, "cannot save content for element '%d'", element);
1305 putFile16BitBE(file, element);
1306 putFile8Bit(file, num_contents);
1307 putFile8Bit(file, content_xsize);
1308 putFile8Bit(file, content_ysize);
1310 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1312 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1315 putFile16BitBE(file, content_array[i][x][y]);
1318 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1321 int envelope_nr = element - EL_ENVELOPE_1;
1322 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1324 putFile16BitBE(file, element);
1325 putFile16BitBE(file, envelope_len);
1326 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1327 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1329 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1331 for(i=0; i < envelope_len; i++)
1332 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1336 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1337 int num_changed_custom_elements)
1341 putFile16BitBE(file, num_changed_custom_elements);
1343 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1345 int element = EL_CUSTOM_START + i;
1347 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1349 if (check < num_changed_custom_elements)
1351 putFile16BitBE(file, element);
1352 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1359 if (check != num_changed_custom_elements) /* should not happen */
1360 Error(ERR_WARN, "inconsistent number of custom element properties");
1365 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1366 int num_changed_custom_elements)
1370 putFile16BitBE(file, num_changed_custom_elements);
1372 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1374 int element = EL_CUSTOM_START + i;
1376 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1378 if (check < num_changed_custom_elements)
1380 putFile16BitBE(file, element);
1381 putFile16BitBE(file, element_info[element].change->target_element);
1388 if (check != num_changed_custom_elements) /* should not happen */
1389 Error(ERR_WARN, "inconsistent number of custom target elements");
1394 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1395 int num_changed_custom_elements)
1397 int i, j, x, y, check = 0;
1399 putFile16BitBE(file, num_changed_custom_elements);
1401 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1403 int element = EL_CUSTOM_START + i;
1405 if (element_info[element].modified_settings)
1407 if (check < num_changed_custom_elements)
1409 putFile16BitBE(file, element);
1411 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1412 putFile8Bit(file, element_info[element].description[j]);
1414 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1416 /* some free bytes for future properties and padding */
1417 WriteUnusedBytesToFile(file, 7);
1419 putFile8Bit(file, element_info[element].use_gfx_element);
1420 putFile16BitBE(file, element_info[element].gfx_element);
1422 putFile8Bit(file, element_info[element].collect_score);
1423 putFile8Bit(file, element_info[element].collect_count);
1425 putFile16BitBE(file, element_info[element].push_delay_fixed);
1426 putFile16BitBE(file, element_info[element].push_delay_random);
1427 putFile16BitBE(file, element_info[element].move_delay_fixed);
1428 putFile16BitBE(file, element_info[element].move_delay_random);
1430 putFile16BitBE(file, element_info[element].move_pattern);
1431 putFile8Bit(file, element_info[element].move_direction_initial);
1432 putFile8Bit(file, element_info[element].move_stepsize);
1436 putFile16BitBE(file, element_info[element].content[x][y]);
1438 putFile32BitBE(file, element_info[element].change->events);
1440 putFile16BitBE(file, element_info[element].change->target_element);
1442 putFile16BitBE(file, element_info[element].change->delay_fixed);
1443 putFile16BitBE(file, element_info[element].change->delay_random);
1444 putFile16BitBE(file, element_info[element].change->delay_frames);
1446 putFile16BitBE(file, element_info[element].change->trigger_element);
1448 putFile8Bit(file, element_info[element].change->explode);
1449 putFile8Bit(file, element_info[element].change->use_content);
1450 putFile8Bit(file, element_info[element].change->only_complete);
1451 putFile8Bit(file, element_info[element].change->use_random_change);
1453 putFile8Bit(file, element_info[element].change->random);
1454 putFile8Bit(file, element_info[element].change->power);
1458 putFile16BitBE(file, element_info[element].change->content[x][y]);
1460 putFile8Bit(file, element_info[element].slippery_type);
1462 /* some free bytes for future properties and padding */
1463 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1470 if (check != num_changed_custom_elements) /* should not happen */
1471 Error(ERR_WARN, "inconsistent number of custom element properties");
1475 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1477 struct ElementInfo *ei = &element_info[element];
1480 putFile16BitBE(file, element);
1482 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
1483 putFile8Bit(file, ei->description[i]);
1485 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1486 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
1488 putFile8Bit(file, ei->num_change_pages);
1490 /* some free bytes for future base property values and padding */
1491 WriteUnusedBytesToFile(file, 5);
1493 /* write custom property values */
1495 putFile8Bit(file, ei->use_gfx_element);
1496 putFile16BitBE(file, ei->gfx_element);
1498 putFile8Bit(file, ei->collect_score);
1499 putFile8Bit(file, ei->collect_count);
1501 putFile16BitBE(file, ei->push_delay_fixed);
1502 putFile16BitBE(file, ei->push_delay_random);
1503 putFile16BitBE(file, ei->move_delay_fixed);
1504 putFile16BitBE(file, ei->move_delay_random);
1506 putFile16BitBE(file, ei->move_pattern);
1507 putFile8Bit(file, ei->move_direction_initial);
1508 putFile8Bit(file, ei->move_stepsize);
1510 putFile8Bit(file, ei->slippery_type);
1514 putFile16BitBE(file, ei->content[x][y]);
1516 /* some free bytes for future custom property values and padding */
1517 WriteUnusedBytesToFile(file, 12);
1519 /* write change property values */
1521 for (i=0; i < ei->num_change_pages; i++)
1523 struct ElementChangeInfo *change = &ei->change_page[i];
1525 putFile32BitBE(file, change->events);
1527 putFile16BitBE(file, change->target_element);
1529 putFile16BitBE(file, change->delay_fixed);
1530 putFile16BitBE(file, change->delay_random);
1531 putFile16BitBE(file, change->delay_frames);
1533 putFile16BitBE(file, change->trigger_element);
1535 putFile8Bit(file, change->explode);
1536 putFile8Bit(file, change->use_content);
1537 putFile8Bit(file, change->only_complete);
1538 putFile8Bit(file, change->use_random_change);
1540 putFile8Bit(file, change->random);
1541 putFile8Bit(file, change->power);
1545 putFile16BitBE(file, change->content[x][y]);
1547 putFile8Bit(file, change->can_change);
1549 putFile8Bit(file, change->sides);
1551 /* some free bytes for future change property values and padding */
1552 WriteUnusedBytesToFile(file, 8);
1556 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1558 int body_chunk_size;
1562 if (!(file = fopen(filename, MODE_WRITE)))
1564 Error(ERR_WARN, "cannot save level file '%s'", filename);
1568 level->file_version = FILE_VERSION_ACTUAL;
1569 level->game_version = GAME_VERSION_ACTUAL;
1571 /* check level field for 16-bit elements */
1572 level->encoding_16bit_field = FALSE;
1573 for(y=0; y<level->fieldy; y++)
1574 for(x=0; x<level->fieldx; x++)
1575 if (level->field[x][y] > 255)
1576 level->encoding_16bit_field = TRUE;
1578 /* check yamyam content for 16-bit elements */
1579 level->encoding_16bit_yamyam = FALSE;
1580 for(i=0; i<level->num_yamyam_contents; i++)
1583 if (level->yamyam_content[i][x][y] > 255)
1584 level->encoding_16bit_yamyam = TRUE;
1586 /* check amoeba content for 16-bit elements */
1587 level->encoding_16bit_amoeba = FALSE;
1588 if (level->amoeba_content > 255)
1589 level->encoding_16bit_amoeba = TRUE;
1591 /* calculate size of "BODY" chunk */
1593 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1595 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1596 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1598 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1599 SaveLevel_VERS(file, level);
1601 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1602 SaveLevel_HEAD(file, level);
1604 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1605 SaveLevel_AUTH(file, level);
1607 putFileChunkBE(file, "BODY", body_chunk_size);
1608 SaveLevel_BODY(file, level);
1610 if (level->encoding_16bit_yamyam ||
1611 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1613 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1614 SaveLevel_CNT2(file, level, EL_YAMYAM);
1617 if (level->encoding_16bit_amoeba)
1619 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1620 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1623 /* check for envelope content */
1626 if (strlen(level->envelope_text[i]) > 0)
1628 int envelope_len = strlen(level->envelope_text[i]) + 1;
1630 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
1631 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
1635 /* check for non-default custom elements (unless using template level) */
1636 if (!level->use_custom_template)
1638 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1640 int element = EL_CUSTOM_START + i;
1642 if (element_info[element].modified_settings)
1644 int num_change_pages = element_info[element].num_change_pages;
1646 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
1647 SaveLevel_CUS4(file, level, element);
1654 SetFilePermissions(filename, PERMS_PRIVATE);
1657 void SaveLevel(int level_nr)
1659 char *filename = getLevelFilename(level_nr);
1661 SaveLevelFromFilename(&level, filename);
1664 void SaveLevelTemplate()
1666 char *filename = getLevelFilename(-1);
1668 SaveLevelFromFilename(&level, filename);
1671 void DumpLevel(struct LevelInfo *level)
1673 printf_line("-", 79);
1674 printf("Level xxx (file version %08d, game version %08d)\n",
1675 level->file_version, level->game_version);
1676 printf_line("-", 79);
1678 printf("Level Author: '%s'\n", level->author);
1679 printf("Level Title: '%s'\n", level->name);
1681 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1683 printf("Level Time: %d seconds\n", level->time);
1684 printf("Gems needed: %d\n", level->gems_needed);
1686 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1687 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1688 printf("Time for Light: %d seconds\n", level->time_light);
1689 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1691 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1693 printf("Gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
1694 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1695 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1697 printf_line("-", 79);
1701 /* ========================================================================= */
1702 /* tape file functions */
1703 /* ========================================================================= */
1705 static void setTapeInfoToDefaults()
1709 /* always start with reliable default values (empty tape) */
1712 /* default values (also for pre-1.2 tapes) with only the first player */
1713 tape.player_participates[0] = TRUE;
1714 for(i=1; i<MAX_PLAYERS; i++)
1715 tape.player_participates[i] = FALSE;
1717 /* at least one (default: the first) player participates in every tape */
1718 tape.num_participating_players = 1;
1720 tape.level_nr = level_nr;
1722 tape.changed = FALSE;
1724 tape.recording = FALSE;
1725 tape.playing = FALSE;
1726 tape.pausing = FALSE;
1729 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1731 tape->file_version = getFileVersion(file);
1732 tape->game_version = getFileVersion(file);
1737 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1741 tape->random_seed = getFile32BitBE(file);
1742 tape->date = getFile32BitBE(file);
1743 tape->length = getFile32BitBE(file);
1745 /* read header fields that are new since version 1.2 */
1746 if (tape->file_version >= FILE_VERSION_1_2)
1748 byte store_participating_players = getFile8Bit(file);
1751 /* since version 1.2, tapes store which players participate in the tape */
1752 tape->num_participating_players = 0;
1753 for(i=0; i<MAX_PLAYERS; i++)
1755 tape->player_participates[i] = FALSE;
1757 if (store_participating_players & (1 << i))
1759 tape->player_participates[i] = TRUE;
1760 tape->num_participating_players++;
1764 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1766 engine_version = getFileVersion(file);
1767 if (engine_version > 0)
1768 tape->engine_version = engine_version;
1770 tape->engine_version = tape->game_version;
1776 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1778 int level_identifier_size;
1781 level_identifier_size = getFile16BitBE(file);
1783 tape->level_identifier =
1784 checked_realloc(tape->level_identifier, level_identifier_size);
1786 for(i=0; i < level_identifier_size; i++)
1787 tape->level_identifier[i] = getFile8Bit(file);
1789 tape->level_nr = getFile16BitBE(file);
1791 chunk_size = 2 + level_identifier_size + 2;
1796 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1799 int chunk_size_expected =
1800 (tape->num_participating_players + 1) * tape->length;
1802 if (chunk_size_expected != chunk_size)
1804 ReadUnusedBytesFromFile(file, chunk_size);
1805 return chunk_size_expected;
1808 for(i=0; i<tape->length; i++)
1810 if (i >= MAX_TAPELEN)
1813 for(j=0; j<MAX_PLAYERS; j++)
1815 tape->pos[i].action[j] = MV_NO_MOVING;
1817 if (tape->player_participates[j])
1818 tape->pos[i].action[j] = getFile8Bit(file);
1821 tape->pos[i].delay = getFile8Bit(file);
1823 if (tape->file_version == FILE_VERSION_1_0)
1825 /* eliminate possible diagonal moves in old tapes */
1826 /* this is only for backward compatibility */
1828 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1829 byte action = tape->pos[i].action[0];
1830 int k, num_moves = 0;
1834 if (action & joy_dir[k])
1836 tape->pos[i + num_moves].action[0] = joy_dir[k];
1838 tape->pos[i + num_moves].delay = 0;
1847 tape->length += num_moves;
1850 else if (tape->file_version < FILE_VERSION_2_0)
1852 /* convert pre-2.0 tapes to new tape format */
1854 if (tape->pos[i].delay > 1)
1857 tape->pos[i + 1] = tape->pos[i];
1858 tape->pos[i + 1].delay = 1;
1861 for(j=0; j<MAX_PLAYERS; j++)
1862 tape->pos[i].action[j] = MV_NO_MOVING;
1863 tape->pos[i].delay--;
1874 if (i != tape->length)
1875 chunk_size = (tape->num_participating_players + 1) * i;
1880 void LoadTapeFromFilename(char *filename)
1882 char cookie[MAX_LINE_LEN];
1883 char chunk_name[CHUNK_ID_LEN + 1];
1887 /* always start with reliable default values */
1888 setTapeInfoToDefaults();
1890 if (!(file = fopen(filename, MODE_READ)))
1893 getFileChunkBE(file, chunk_name, NULL);
1894 if (strcmp(chunk_name, "RND1") == 0)
1896 getFile32BitBE(file); /* not used */
1898 getFileChunkBE(file, chunk_name, NULL);
1899 if (strcmp(chunk_name, "TAPE") != 0)
1901 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1906 else /* check for pre-2.0 file format with cookie string */
1908 strcpy(cookie, chunk_name);
1909 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1910 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1911 cookie[strlen(cookie) - 1] = '\0';
1913 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1915 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1920 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1922 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1927 /* pre-2.0 tape files have no game version, so use file version here */
1928 tape.game_version = tape.file_version;
1931 if (tape.file_version < FILE_VERSION_1_2)
1933 /* tape files from versions before 1.2.0 without chunk structure */
1934 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1935 LoadTape_BODY(file, 2 * tape.length, &tape);
1943 int (*loader)(FILE *, int, struct TapeInfo *);
1947 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1948 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1949 { "INFO", -1, LoadTape_INFO },
1950 { "BODY", -1, LoadTape_BODY },
1954 while (getFileChunkBE(file, chunk_name, &chunk_size))
1958 while (chunk_info[i].name != NULL &&
1959 strcmp(chunk_name, chunk_info[i].name) != 0)
1962 if (chunk_info[i].name == NULL)
1964 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1965 chunk_name, filename);
1966 ReadUnusedBytesFromFile(file, chunk_size);
1968 else if (chunk_info[i].size != -1 &&
1969 chunk_info[i].size != chunk_size)
1971 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1972 chunk_size, chunk_name, filename);
1973 ReadUnusedBytesFromFile(file, chunk_size);
1977 /* call function to load this tape chunk */
1978 int chunk_size_expected =
1979 (chunk_info[i].loader)(file, chunk_size, &tape);
1981 /* the size of some chunks cannot be checked before reading other
1982 chunks first (like "HEAD" and "BODY") that contain some header
1983 information, so check them here */
1984 if (chunk_size_expected != chunk_size)
1986 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1987 chunk_size, chunk_name, filename);
1995 tape.length_seconds = GetTapeLength();
1998 printf("tape game version: %d\n", tape.game_version);
1999 printf("tape engine version: %d\n", tape.engine_version);
2003 void LoadTape(int level_nr)
2005 char *filename = getTapeFilename(level_nr);
2007 LoadTapeFromFilename(filename);
2010 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2012 putFileVersion(file, tape->file_version);
2013 putFileVersion(file, tape->game_version);
2016 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2019 byte store_participating_players = 0;
2021 /* set bits for participating players for compact storage */
2022 for(i=0; i<MAX_PLAYERS; i++)
2023 if (tape->player_participates[i])
2024 store_participating_players |= (1 << i);
2026 putFile32BitBE(file, tape->random_seed);
2027 putFile32BitBE(file, tape->date);
2028 putFile32BitBE(file, tape->length);
2030 putFile8Bit(file, store_participating_players);
2032 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2033 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2035 putFileVersion(file, tape->engine_version);
2038 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2040 int level_identifier_size = strlen(tape->level_identifier) + 1;
2043 putFile16BitBE(file, level_identifier_size);
2045 for(i=0; i < level_identifier_size; i++)
2046 putFile8Bit(file, tape->level_identifier[i]);
2048 putFile16BitBE(file, tape->level_nr);
2051 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2055 for(i=0; i<tape->length; i++)
2057 for(j=0; j<MAX_PLAYERS; j++)
2058 if (tape->player_participates[j])
2059 putFile8Bit(file, tape->pos[i].action[j]);
2061 putFile8Bit(file, tape->pos[i].delay);
2065 void SaveTape(int level_nr)
2067 char *filename = getTapeFilename(level_nr);
2069 boolean new_tape = TRUE;
2070 int num_participating_players = 0;
2071 int info_chunk_size;
2072 int body_chunk_size;
2075 InitTapeDirectory(leveldir_current->filename);
2077 /* if a tape still exists, ask to overwrite it */
2078 if (access(filename, F_OK) == 0)
2081 if (!Request("Replace old tape ?", REQ_ASK))
2085 if (!(file = fopen(filename, MODE_WRITE)))
2087 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2091 tape.file_version = FILE_VERSION_ACTUAL;
2092 tape.game_version = GAME_VERSION_ACTUAL;
2094 /* count number of participating players */
2095 for(i=0; i<MAX_PLAYERS; i++)
2096 if (tape.player_participates[i])
2097 num_participating_players++;
2099 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2100 body_chunk_size = (num_participating_players + 1) * tape.length;
2102 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2103 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2105 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2106 SaveTape_VERS(file, &tape);
2108 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2109 SaveTape_HEAD(file, &tape);
2111 putFileChunkBE(file, "INFO", info_chunk_size);
2112 SaveTape_INFO(file, &tape);
2114 putFileChunkBE(file, "BODY", body_chunk_size);
2115 SaveTape_BODY(file, &tape);
2119 SetFilePermissions(filename, PERMS_PRIVATE);
2121 tape.changed = FALSE;
2124 Request("tape saved !", REQ_CONFIRM);
2127 void DumpTape(struct TapeInfo *tape)
2131 if (TAPE_IS_EMPTY(*tape))
2133 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2137 printf_line("-", 79);
2138 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2139 tape->level_nr, tape->file_version, tape->game_version);
2140 printf("Level series identifier: '%s'\n", tape->level_identifier);
2141 printf_line("-", 79);
2143 for(i=0; i<tape->length; i++)
2145 if (i >= MAX_TAPELEN)
2148 printf("%03d: ", i);
2150 for(j=0; j<MAX_PLAYERS; j++)
2152 if (tape->player_participates[j])
2154 int action = tape->pos[i].action[j];
2156 printf("%d:%02x ", j, action);
2157 printf("[%c%c%c%c|%c%c] - ",
2158 (action & JOY_LEFT ? '<' : ' '),
2159 (action & JOY_RIGHT ? '>' : ' '),
2160 (action & JOY_UP ? '^' : ' '),
2161 (action & JOY_DOWN ? 'v' : ' '),
2162 (action & JOY_BUTTON_1 ? '1' : ' '),
2163 (action & JOY_BUTTON_2 ? '2' : ' '));
2167 printf("(%03d)\n", tape->pos[i].delay);
2170 printf_line("-", 79);
2174 /* ========================================================================= */
2175 /* score file functions */
2176 /* ========================================================================= */
2178 void LoadScore(int level_nr)
2181 char *filename = getScoreFilename(level_nr);
2182 char cookie[MAX_LINE_LEN];
2183 char line[MAX_LINE_LEN];
2187 /* always start with reliable default values */
2188 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2190 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2191 highscore[i].Score = 0;
2194 if (!(file = fopen(filename, MODE_READ)))
2197 /* check file identifier */
2198 fgets(cookie, MAX_LINE_LEN, file);
2199 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2200 cookie[strlen(cookie) - 1] = '\0';
2202 if (!checkCookieString(cookie, SCORE_COOKIE))
2204 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2209 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2211 fscanf(file, "%d", &highscore[i].Score);
2212 fgets(line, MAX_LINE_LEN, file);
2214 if (line[strlen(line) - 1] == '\n')
2215 line[strlen(line) - 1] = '\0';
2217 for (line_ptr = line; *line_ptr; line_ptr++)
2219 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2221 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2222 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2231 void SaveScore(int level_nr)
2234 char *filename = getScoreFilename(level_nr);
2237 InitScoreDirectory(leveldir_current->filename);
2239 if (!(file = fopen(filename, MODE_WRITE)))
2241 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2245 fprintf(file, "%s\n\n", SCORE_COOKIE);
2247 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2248 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2252 SetFilePermissions(filename, PERMS_PUBLIC);
2256 /* ========================================================================= */
2257 /* setup file functions */
2258 /* ========================================================================= */
2260 #define TOKEN_STR_PLAYER_PREFIX "player_"
2263 #define SETUP_TOKEN_PLAYER_NAME 0
2264 #define SETUP_TOKEN_SOUND 1
2265 #define SETUP_TOKEN_SOUND_LOOPS 2
2266 #define SETUP_TOKEN_SOUND_MUSIC 3
2267 #define SETUP_TOKEN_SOUND_SIMPLE 4
2268 #define SETUP_TOKEN_TOONS 5
2269 #define SETUP_TOKEN_SCROLL_DELAY 6
2270 #define SETUP_TOKEN_SOFT_SCROLLING 7
2271 #define SETUP_TOKEN_FADING 8
2272 #define SETUP_TOKEN_AUTORECORD 9
2273 #define SETUP_TOKEN_QUICK_DOORS 10
2274 #define SETUP_TOKEN_TEAM_MODE 11
2275 #define SETUP_TOKEN_HANDICAP 12
2276 #define SETUP_TOKEN_TIME_LIMIT 13
2277 #define SETUP_TOKEN_FULLSCREEN 14
2278 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2279 #define SETUP_TOKEN_GRAPHICS_SET 16
2280 #define SETUP_TOKEN_SOUNDS_SET 17
2281 #define SETUP_TOKEN_MUSIC_SET 18
2282 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2283 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2284 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2286 #define NUM_GLOBAL_SETUP_TOKENS 22
2289 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2290 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2291 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2292 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2293 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2294 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2295 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2296 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2297 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2298 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2299 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2301 #define NUM_EDITOR_SETUP_TOKENS 11
2303 /* shortcut setup */
2304 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2305 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2306 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2308 #define NUM_SHORTCUT_SETUP_TOKENS 3
2311 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2312 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2313 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2314 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2315 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2316 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2317 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2318 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2319 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2320 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
2321 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2322 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2323 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2324 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2325 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2326 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
2328 #define NUM_PLAYER_SETUP_TOKENS 16
2331 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2332 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2334 #define NUM_SYSTEM_SETUP_TOKENS 2
2337 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2339 #define NUM_OPTIONS_SETUP_TOKENS 1
2342 static struct SetupInfo si;
2343 static struct SetupEditorInfo sei;
2344 static struct SetupShortcutInfo ssi;
2345 static struct SetupInputInfo sii;
2346 static struct SetupSystemInfo syi;
2347 static struct OptionInfo soi;
2349 static struct TokenInfo global_setup_tokens[] =
2351 { TYPE_STRING, &si.player_name, "player_name" },
2352 { TYPE_SWITCH, &si.sound, "sound" },
2353 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2354 { TYPE_SWITCH, &si.sound_music, "background_music" },
2355 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2356 { TYPE_SWITCH, &si.toons, "toons" },
2357 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2358 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2359 { TYPE_SWITCH, &si.fading, "screen_fading" },
2360 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2361 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2362 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2363 { TYPE_SWITCH, &si.handicap, "handicap" },
2364 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2365 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2366 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2367 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2368 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2369 { TYPE_STRING, &si.music_set, "music_set" },
2370 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2371 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2372 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2375 static struct TokenInfo editor_setup_tokens[] =
2377 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2378 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2379 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2380 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2381 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2382 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2383 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2384 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2385 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2386 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2387 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2390 static struct TokenInfo shortcut_setup_tokens[] =
2392 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2393 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2394 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2397 static struct TokenInfo player_setup_tokens[] =
2399 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2400 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2401 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2402 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2403 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2404 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2405 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2406 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2407 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2408 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2409 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2410 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2411 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2412 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2413 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2414 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
2417 static struct TokenInfo system_setup_tokens[] =
2419 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2420 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2423 static struct TokenInfo options_setup_tokens[] =
2425 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2428 static char *get_corrected_login_name(char *login_name)
2430 /* needed because player name must be a fixed length string */
2431 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2433 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2434 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2436 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2437 if (strchr(login_name_new, ' '))
2438 *strchr(login_name_new, ' ') = '\0';
2440 return login_name_new;
2443 static void setSetupInfoToDefaults(struct SetupInfo *si)
2447 si->player_name = get_corrected_login_name(getLoginName());
2450 si->sound_loops = TRUE;
2451 si->sound_music = TRUE;
2452 si->sound_simple = TRUE;
2454 si->double_buffering = TRUE;
2455 si->direct_draw = !si->double_buffering;
2456 si->scroll_delay = TRUE;
2457 si->soft_scrolling = TRUE;
2459 si->autorecord = TRUE;
2460 si->quick_doors = FALSE;
2461 si->team_mode = FALSE;
2462 si->handicap = TRUE;
2463 si->time_limit = TRUE;
2464 si->fullscreen = FALSE;
2465 si->ask_on_escape = TRUE;
2467 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2468 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2469 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2470 si->override_level_graphics = FALSE;
2471 si->override_level_sounds = FALSE;
2472 si->override_level_music = FALSE;
2474 si->editor.el_boulderdash = TRUE;
2475 si->editor.el_emerald_mine = TRUE;
2476 si->editor.el_more = TRUE;
2477 si->editor.el_sokoban = TRUE;
2478 si->editor.el_supaplex = TRUE;
2479 si->editor.el_diamond_caves = TRUE;
2480 si->editor.el_dx_boulderdash = TRUE;
2481 si->editor.el_chars = TRUE;
2482 si->editor.el_custom = TRUE;
2483 si->editor.el_custom_more = FALSE;
2485 si->editor.el_headlines = TRUE;
2487 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2488 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2489 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2491 for (i=0; i<MAX_PLAYERS; i++)
2493 si->input[i].use_joystick = FALSE;
2494 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2495 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2496 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2497 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2498 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2499 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2500 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2501 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2502 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2503 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2504 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2505 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2506 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2507 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2508 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2511 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2512 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2514 si->options.verbose = FALSE;
2517 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2521 if (!setup_file_hash)
2526 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2527 setSetupInfo(global_setup_tokens, i,
2528 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2533 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2534 setSetupInfo(editor_setup_tokens, i,
2535 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2538 /* shortcut setup */
2539 ssi = setup.shortcut;
2540 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2541 setSetupInfo(shortcut_setup_tokens, i,
2542 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2543 setup.shortcut = ssi;
2546 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2550 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2552 sii = setup.input[pnr];
2553 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2555 char full_token[100];
2557 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2558 setSetupInfo(player_setup_tokens, i,
2559 getHashEntry(setup_file_hash, full_token));
2561 setup.input[pnr] = sii;
2566 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2567 setSetupInfo(system_setup_tokens, i,
2568 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2572 soi = setup.options;
2573 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2574 setSetupInfo(options_setup_tokens, i,
2575 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2576 setup.options = soi;
2581 char *filename = getSetupFilename();
2582 SetupFileHash *setup_file_hash = NULL;
2584 /* always start with reliable default values */
2585 setSetupInfoToDefaults(&setup);
2587 setup_file_hash = loadSetupFileHash(filename);
2589 if (setup_file_hash)
2591 char *player_name_new;
2593 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2594 decodeSetupFileHash(setup_file_hash);
2596 setup.direct_draw = !setup.double_buffering;
2598 freeSetupFileHash(setup_file_hash);
2600 /* needed to work around problems with fixed length strings */
2601 player_name_new = get_corrected_login_name(setup.player_name);
2602 free(setup.player_name);
2603 setup.player_name = player_name_new;
2606 Error(ERR_WARN, "using default setup values");
2611 char *filename = getSetupFilename();
2615 InitUserDataDirectory();
2617 if (!(file = fopen(filename, MODE_WRITE)))
2619 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2623 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2624 getCookie("SETUP")));
2625 fprintf(file, "\n");
2629 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2631 /* just to make things nicer :) */
2632 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2633 i == SETUP_TOKEN_GRAPHICS_SET)
2634 fprintf(file, "\n");
2636 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2641 fprintf(file, "\n");
2642 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2643 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2645 /* shortcut setup */
2646 ssi = setup.shortcut;
2647 fprintf(file, "\n");
2648 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2649 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2652 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2656 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2657 fprintf(file, "\n");
2659 sii = setup.input[pnr];
2660 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2661 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2666 fprintf(file, "\n");
2667 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2668 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2671 soi = setup.options;
2672 fprintf(file, "\n");
2673 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2674 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2678 SetFilePermissions(filename, PERMS_PRIVATE);
2681 void LoadCustomElementDescriptions()
2683 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2684 SetupFileHash *setup_file_hash;
2687 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2689 if (element_info[i].custom_description != NULL)
2691 free(element_info[i].custom_description);
2692 element_info[i].custom_description = NULL;
2696 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2699 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2701 char *token = getStringCat2(element_info[i].token_name, ".name");
2702 char *value = getHashEntry(setup_file_hash, token);
2705 element_info[i].custom_description = getStringCopy(value);
2710 freeSetupFileHash(setup_file_hash);
2713 void LoadSpecialMenuDesignSettings()
2715 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2716 SetupFileHash *setup_file_hash;
2719 /* always start with reliable default values from default config */
2720 for (i=0; image_config_vars[i].token != NULL; i++)
2721 for (j=0; image_config[j].token != NULL; j++)
2722 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2723 *image_config_vars[i].value =
2724 get_auto_parameter_value(image_config_vars[i].token,
2725 image_config[j].value);
2727 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2730 /* special case: initialize with default values that may be overwritten */
2731 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2733 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2734 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2735 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2737 if (value_x != NULL)
2738 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2739 if (value_y != NULL)
2740 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2741 if (list_size != NULL)
2742 menu.list_size[i] = get_integer_from_string(list_size);
2745 /* read (and overwrite with) values that may be specified in config file */
2746 for (i=0; image_config_vars[i].token != NULL; i++)
2748 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2751 *image_config_vars[i].value =
2752 get_auto_parameter_value(image_config_vars[i].token, value);
2755 freeSetupFileHash(setup_file_hash);