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_CONTRIBUTION:
246 strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
247 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
250 case LEVELCLASS_USER:
251 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
252 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
256 /* keep default value */
262 static void ActivateLevelTemplate()
264 /* Currently there is no special action needed to activate the template
265 data, because 'element_info' and 'Properties' overwrite the original
266 level data, while all other variables do not change. */
269 boolean LevelFileExists(int level_nr)
271 char *filename = getLevelFilename(level_nr);
273 return (access(filename, F_OK) == 0);
276 static int checkLevelElement(int element)
278 /* map some (historic, now obsolete) elements */
283 case EL_PLAYER_OBSOLETE:
284 element = EL_PLAYER_1;
287 case EL_KEY_OBSOLETE:
290 case EL_EM_KEY_1_FILE_OBSOLETE:
291 element = EL_EM_KEY_1;
294 case EL_EM_KEY_2_FILE_OBSOLETE:
295 element = EL_EM_KEY_2;
298 case EL_EM_KEY_3_FILE_OBSOLETE:
299 element = EL_EM_KEY_3;
302 case EL_EM_KEY_4_FILE_OBSOLETE:
303 element = EL_EM_KEY_4;
306 case EL_ENVELOPE_OBSOLETE:
307 element = EL_ENVELOPE_1;
315 if (element >= NUM_FILE_ELEMENTS)
317 Error(ERR_WARN, "invalid level element %d", element);
319 element = EL_CHAR_QUESTION;
324 if (element >= NUM_FILE_ELEMENTS)
326 Error(ERR_WARN, "invalid level element %d", element);
328 element = EL_CHAR_QUESTION;
330 else if (element == EL_PLAYER_OBSOLETE)
331 element = EL_PLAYER_1;
332 else if (element == EL_KEY_OBSOLETE)
339 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
341 level->file_version = getFileVersion(file);
342 level->game_version = getFileVersion(file);
347 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
351 level->fieldx = getFile8Bit(file);
352 level->fieldy = getFile8Bit(file);
354 level->time = getFile16BitBE(file);
355 level->gems_needed = getFile16BitBE(file);
357 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
358 level->name[i] = getFile8Bit(file);
359 level->name[MAX_LEVEL_NAME_LEN] = 0;
361 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
362 level->score[i] = getFile8Bit(file);
364 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
365 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
368 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
370 level->amoeba_speed = getFile8Bit(file);
371 level->time_magic_wall = getFile8Bit(file);
372 level->time_wheel = getFile8Bit(file);
373 level->amoeba_content = checkLevelElement(getFile8Bit(file));
374 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
375 level->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 */
933 /* determine correct game engine version of current level */
934 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
935 IS_LEVELCLASS_USER(leveldir_current))
938 printf("\n::: This level is private or contributed: '%s'\n", filename);
941 /* For user contributed and private levels, use the version of
942 the game engine the levels were created for.
943 Since 2.0.1, the game engine version is now directly stored
944 in the level file (chunk "VERS"), so there is no need anymore
945 to set the game version from the file version (except for old,
946 pre-2.0 levels, where the game version is still taken from the
947 file format version used to store the level -- see above). */
949 /* do some special adjustments to support older level versions */
950 if (level->file_version == FILE_VERSION_1_0)
952 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
953 Error(ERR_WARN, "using high speed movement for player");
955 /* player was faster than monsters in (pre-)1.0 levels */
956 level->double_speed = TRUE;
959 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
960 if (level->game_version == VERSION_IDENT(2,0,1))
961 level->em_slippery_gems = TRUE;
966 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
967 leveldir_current->sort_priority, filename);
970 /* Always use the latest version of the game engine for all but
971 user contributed and private levels; this allows for actual
972 corrections in the game engine to take effect for existing,
973 converted levels (from "classic" or other existing games) to
974 make the game emulation more accurate, while (hopefully) not
975 breaking existing levels created from other players. */
977 level->game_version = GAME_VERSION_ACTUAL;
979 /* Set special EM style gems behaviour: EM style gems slip down from
980 normal, steel and growing wall. As this is a more fundamental change,
981 it seems better to set the default behaviour to "off" (as it is more
982 natural) and make it configurable in the level editor (as a property
983 of gem style elements). Already existing converted levels (neither
984 private nor contributed levels) are changed to the new behaviour. */
986 if (level->file_version < FILE_VERSION_2_0)
987 level->em_slippery_gems = TRUE;
991 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
995 /* map custom element change events that have changed in newer versions
996 (these following values were accidentally changed in version 3.0.1) */
997 if (level->game_version <= VERSION_IDENT(3,0,0))
999 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1001 int element = EL_CUSTOM_START + i;
1003 /* order of checking and copying events to be mapped is important */
1004 for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
1006 if (HAS_CHANGE_EVENT(element, j - 2))
1008 SET_CHANGE_EVENT(element, j - 2, FALSE);
1009 SET_CHANGE_EVENT(element, j, TRUE);
1013 /* order of checking and copying events to be mapped is important */
1014 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1016 if (HAS_CHANGE_EVENT(element, j - 1))
1018 SET_CHANGE_EVENT(element, j - 1, FALSE);
1019 SET_CHANGE_EVENT(element, j, TRUE);
1025 /* some custom element change events get mapped since version 3.0.3 */
1026 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1028 int element = EL_CUSTOM_START + i;
1030 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
1031 HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
1033 SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
1034 SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
1036 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1040 /* initialize "can_change" field for old levels with only one change page */
1041 if (level->game_version <= VERSION_IDENT(3,0,2))
1043 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1045 int element = EL_CUSTOM_START + i;
1047 if (CAN_CHANGE(element))
1048 element_info[element].change->can_change = TRUE;
1052 /* set default push delay values (corrected since version 3.0.7) */
1053 if (level->game_version < VERSION_IDENT(3,0,7))
1055 game.default_push_delay_fixed = 2;
1056 game.default_push_delay_random = 8;
1060 game.default_push_delay_fixed = 8;
1061 game.default_push_delay_random = 8;
1064 /* set uninitialized push delay values of custom elements in older levels */
1065 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1067 int element = EL_CUSTOM_START + i;
1069 if (element_info[element].push_delay_fixed == -1)
1070 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
1071 if (element_info[element].push_delay_random == -1)
1072 element_info[element].push_delay_random = game.default_push_delay_random;
1075 /* initialize element properties for level editor etc. */
1076 InitElementPropertiesEngine(level->game_version);
1079 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1083 /* map elements that have changed in newer versions */
1084 for(y=0; y<level->fieldy; y++)
1086 for(x=0; x<level->fieldx; x++)
1088 int element = level->field[x][y];
1090 if (level->game_version <= VERSION_IDENT(2,2,0))
1092 /* map game font elements */
1093 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1094 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1095 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1096 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1099 if (level->game_version < VERSION_IDENT(3,0,0))
1101 /* map Supaplex gravity tube elements */
1102 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1103 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1104 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1105 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1109 level->field[x][y] = element;
1113 /* copy elements to runtime playfield array */
1114 for(x=0; x<MAX_LEV_FIELDX; x++)
1115 for(y=0; y<MAX_LEV_FIELDY; y++)
1116 Feld[x][y] = level->field[x][y];
1118 /* initialize level size variables for faster access */
1119 lev_fieldx = level->fieldx;
1120 lev_fieldy = level->fieldy;
1122 /* determine border element for this level */
1126 void LoadLevelTemplate(int level_nr)
1128 char *filename = getLevelFilename(level_nr);
1130 LoadLevelFromFilename(&level_template, filename);
1132 LoadLevel_InitVersion(&level, filename);
1133 LoadLevel_InitElements(&level, filename);
1135 ActivateLevelTemplate();
1138 void LoadLevel(int level_nr)
1140 char *filename = getLevelFilename(level_nr);
1142 LoadLevelFromFilename(&level, filename);
1144 if (level.use_custom_template)
1145 LoadLevelTemplate(-1);
1148 LoadLevel_InitVersion(&level, filename);
1149 LoadLevel_InitElements(&level, filename);
1150 LoadLevel_InitPlayfield(&level, filename);
1152 LoadLevel_InitLevel(&level, filename);
1156 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1158 putFileVersion(file, level->file_version);
1159 putFileVersion(file, level->game_version);
1162 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1166 putFile8Bit(file, level->fieldx);
1167 putFile8Bit(file, level->fieldy);
1169 putFile16BitBE(file, level->time);
1170 putFile16BitBE(file, level->gems_needed);
1172 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1173 putFile8Bit(file, level->name[i]);
1175 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1176 putFile8Bit(file, level->score[i]);
1178 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1181 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1182 level->yamyam_content[i][x][y]));
1183 putFile8Bit(file, level->amoeba_speed);
1184 putFile8Bit(file, level->time_magic_wall);
1185 putFile8Bit(file, level->time_wheel);
1186 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1187 level->amoeba_content));
1188 putFile8Bit(file, (level->double_speed ? 1 : 0));
1189 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
1190 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1191 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1193 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1195 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1198 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1202 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1203 putFile8Bit(file, level->author[i]);
1206 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1210 for(y=0; y<level->fieldy; y++)
1211 for(x=0; x<level->fieldx; x++)
1212 if (level->encoding_16bit_field)
1213 putFile16BitBE(file, level->field[x][y]);
1215 putFile8Bit(file, level->field[x][y]);
1219 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1223 putFile8Bit(file, EL_YAMYAM);
1224 putFile8Bit(file, level->num_yamyam_contents);
1225 putFile8Bit(file, 0);
1226 putFile8Bit(file, 0);
1228 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1231 if (level->encoding_16bit_field)
1232 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1234 putFile8Bit(file, level->yamyam_content[i][x][y]);
1238 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1241 int num_contents, content_xsize, content_ysize;
1242 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1244 if (element == EL_YAMYAM)
1246 num_contents = level->num_yamyam_contents;
1250 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1253 content_array[i][x][y] = level->yamyam_content[i][x][y];
1255 else if (element == EL_BD_AMOEBA)
1261 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1264 content_array[i][x][y] = EL_EMPTY;
1265 content_array[0][0][0] = level->amoeba_content;
1269 /* chunk header already written -- write empty chunk data */
1270 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1272 Error(ERR_WARN, "cannot save content for element '%d'", element);
1276 putFile16BitBE(file, element);
1277 putFile8Bit(file, num_contents);
1278 putFile8Bit(file, content_xsize);
1279 putFile8Bit(file, content_ysize);
1281 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1283 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1286 putFile16BitBE(file, content_array[i][x][y]);
1289 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1292 int envelope_nr = element - EL_ENVELOPE_1;
1293 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1295 putFile16BitBE(file, element);
1296 putFile16BitBE(file, envelope_len);
1297 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1298 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1300 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1302 for(i=0; i < envelope_len; i++)
1303 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1307 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1308 int num_changed_custom_elements)
1312 putFile16BitBE(file, num_changed_custom_elements);
1314 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1316 int element = EL_CUSTOM_START + i;
1318 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1320 if (check < num_changed_custom_elements)
1322 putFile16BitBE(file, element);
1323 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1330 if (check != num_changed_custom_elements) /* should not happen */
1331 Error(ERR_WARN, "inconsistent number of custom element properties");
1336 static void SaveLevel_CUS2(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 (element_info[element].change->target_element != EL_EMPTY_SPACE)
1349 if (check < num_changed_custom_elements)
1351 putFile16BitBE(file, element);
1352 putFile16BitBE(file, element_info[element].change->target_element);
1359 if (check != num_changed_custom_elements) /* should not happen */
1360 Error(ERR_WARN, "inconsistent number of custom target elements");
1365 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1366 int num_changed_custom_elements)
1368 int i, j, x, y, check = 0;
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].modified_settings)
1378 if (check < num_changed_custom_elements)
1380 putFile16BitBE(file, element);
1382 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1383 putFile8Bit(file, element_info[element].description[j]);
1385 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1387 /* some free bytes for future properties and padding */
1388 WriteUnusedBytesToFile(file, 7);
1390 putFile8Bit(file, element_info[element].use_gfx_element);
1391 putFile16BitBE(file, element_info[element].gfx_element);
1393 putFile8Bit(file, element_info[element].collect_score);
1394 putFile8Bit(file, element_info[element].collect_count);
1396 putFile16BitBE(file, element_info[element].push_delay_fixed);
1397 putFile16BitBE(file, element_info[element].push_delay_random);
1398 putFile16BitBE(file, element_info[element].move_delay_fixed);
1399 putFile16BitBE(file, element_info[element].move_delay_random);
1401 putFile16BitBE(file, element_info[element].move_pattern);
1402 putFile8Bit(file, element_info[element].move_direction_initial);
1403 putFile8Bit(file, element_info[element].move_stepsize);
1407 putFile16BitBE(file, element_info[element].content[x][y]);
1409 putFile32BitBE(file, element_info[element].change->events);
1411 putFile16BitBE(file, element_info[element].change->target_element);
1413 putFile16BitBE(file, element_info[element].change->delay_fixed);
1414 putFile16BitBE(file, element_info[element].change->delay_random);
1415 putFile16BitBE(file, element_info[element].change->delay_frames);
1417 putFile16BitBE(file, element_info[element].change->trigger_element);
1419 putFile8Bit(file, element_info[element].change->explode);
1420 putFile8Bit(file, element_info[element].change->use_content);
1421 putFile8Bit(file, element_info[element].change->only_complete);
1422 putFile8Bit(file, element_info[element].change->use_random_change);
1424 putFile8Bit(file, element_info[element].change->random);
1425 putFile8Bit(file, element_info[element].change->power);
1429 putFile16BitBE(file, element_info[element].change->content[x][y]);
1431 putFile8Bit(file, element_info[element].slippery_type);
1433 /* some free bytes for future properties and padding */
1434 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1441 if (check != num_changed_custom_elements) /* should not happen */
1442 Error(ERR_WARN, "inconsistent number of custom element properties");
1446 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1448 struct ElementInfo *ei = &element_info[element];
1451 putFile16BitBE(file, element);
1453 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
1454 putFile8Bit(file, ei->description[i]);
1456 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1457 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
1459 putFile8Bit(file, ei->num_change_pages);
1461 /* some free bytes for future base property values and padding */
1462 WriteUnusedBytesToFile(file, 5);
1464 /* write custom property values */
1466 putFile8Bit(file, ei->use_gfx_element);
1467 putFile16BitBE(file, ei->gfx_element);
1469 putFile8Bit(file, ei->collect_score);
1470 putFile8Bit(file, ei->collect_count);
1472 putFile16BitBE(file, ei->push_delay_fixed);
1473 putFile16BitBE(file, ei->push_delay_random);
1474 putFile16BitBE(file, ei->move_delay_fixed);
1475 putFile16BitBE(file, ei->move_delay_random);
1477 putFile16BitBE(file, ei->move_pattern);
1478 putFile8Bit(file, ei->move_direction_initial);
1479 putFile8Bit(file, ei->move_stepsize);
1481 putFile8Bit(file, ei->slippery_type);
1485 putFile16BitBE(file, ei->content[x][y]);
1487 /* some free bytes for future custom property values and padding */
1488 WriteUnusedBytesToFile(file, 12);
1490 /* write change property values */
1492 for (i=0; i < ei->num_change_pages; i++)
1494 struct ElementChangeInfo *change = &ei->change_page[i];
1496 putFile32BitBE(file, change->events);
1498 putFile16BitBE(file, change->target_element);
1500 putFile16BitBE(file, change->delay_fixed);
1501 putFile16BitBE(file, change->delay_random);
1502 putFile16BitBE(file, change->delay_frames);
1504 putFile16BitBE(file, change->trigger_element);
1506 putFile8Bit(file, change->explode);
1507 putFile8Bit(file, change->use_content);
1508 putFile8Bit(file, change->only_complete);
1509 putFile8Bit(file, change->use_random_change);
1511 putFile8Bit(file, change->random);
1512 putFile8Bit(file, change->power);
1516 putFile16BitBE(file, change->content[x][y]);
1518 putFile8Bit(file, change->can_change);
1520 putFile8Bit(file, change->sides);
1522 /* some free bytes for future change property values and padding */
1523 WriteUnusedBytesToFile(file, 8);
1527 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1529 int body_chunk_size;
1533 if (!(file = fopen(filename, MODE_WRITE)))
1535 Error(ERR_WARN, "cannot save level file '%s'", filename);
1539 level->file_version = FILE_VERSION_ACTUAL;
1540 level->game_version = GAME_VERSION_ACTUAL;
1542 /* check level field for 16-bit elements */
1543 level->encoding_16bit_field = FALSE;
1544 for(y=0; y<level->fieldy; y++)
1545 for(x=0; x<level->fieldx; x++)
1546 if (level->field[x][y] > 255)
1547 level->encoding_16bit_field = TRUE;
1549 /* check yamyam content for 16-bit elements */
1550 level->encoding_16bit_yamyam = FALSE;
1551 for(i=0; i<level->num_yamyam_contents; i++)
1554 if (level->yamyam_content[i][x][y] > 255)
1555 level->encoding_16bit_yamyam = TRUE;
1557 /* check amoeba content for 16-bit elements */
1558 level->encoding_16bit_amoeba = FALSE;
1559 if (level->amoeba_content > 255)
1560 level->encoding_16bit_amoeba = TRUE;
1562 /* calculate size of "BODY" chunk */
1564 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1566 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1567 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1569 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1570 SaveLevel_VERS(file, level);
1572 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1573 SaveLevel_HEAD(file, level);
1575 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1576 SaveLevel_AUTH(file, level);
1578 putFileChunkBE(file, "BODY", body_chunk_size);
1579 SaveLevel_BODY(file, level);
1581 if (level->encoding_16bit_yamyam ||
1582 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1584 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1585 SaveLevel_CNT2(file, level, EL_YAMYAM);
1588 if (level->encoding_16bit_amoeba)
1590 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1591 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1594 /* check for envelope content */
1597 if (strlen(level->envelope_text[i]) > 0)
1599 int envelope_len = strlen(level->envelope_text[i]) + 1;
1601 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
1602 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
1606 /* check for non-default custom elements (unless using template level) */
1607 if (!level->use_custom_template)
1609 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1611 int element = EL_CUSTOM_START + i;
1613 if (element_info[element].modified_settings)
1615 int num_change_pages = element_info[element].num_change_pages;
1617 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
1618 SaveLevel_CUS4(file, level, element);
1625 SetFilePermissions(filename, PERMS_PRIVATE);
1628 void SaveLevel(int level_nr)
1630 char *filename = getLevelFilename(level_nr);
1632 SaveLevelFromFilename(&level, filename);
1635 void SaveLevelTemplate()
1637 char *filename = getLevelFilename(-1);
1639 SaveLevelFromFilename(&level, filename);
1642 void DumpLevel(struct LevelInfo *level)
1644 printf_line("-", 79);
1645 printf("Level xxx (file version %08d, game version %08d)\n",
1646 level->file_version, level->game_version);
1647 printf_line("-", 79);
1649 printf("Level Author: '%s'\n", level->author);
1650 printf("Level Title: '%s'\n", level->name);
1652 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1654 printf("Level Time: %d seconds\n", level->time);
1655 printf("Gems needed: %d\n", level->gems_needed);
1657 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1658 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1659 printf("Time for Light: %d seconds\n", level->time_light);
1660 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1662 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1664 printf("Gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
1665 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1666 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1668 printf_line("-", 79);
1672 /* ========================================================================= */
1673 /* tape file functions */
1674 /* ========================================================================= */
1676 static void setTapeInfoToDefaults()
1680 /* always start with reliable default values (empty tape) */
1683 /* default values (also for pre-1.2 tapes) with only the first player */
1684 tape.player_participates[0] = TRUE;
1685 for(i=1; i<MAX_PLAYERS; i++)
1686 tape.player_participates[i] = FALSE;
1688 /* at least one (default: the first) player participates in every tape */
1689 tape.num_participating_players = 1;
1691 tape.level_nr = level_nr;
1693 tape.changed = FALSE;
1695 tape.recording = FALSE;
1696 tape.playing = FALSE;
1697 tape.pausing = FALSE;
1700 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1702 tape->file_version = getFileVersion(file);
1703 tape->game_version = getFileVersion(file);
1708 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1712 tape->random_seed = getFile32BitBE(file);
1713 tape->date = getFile32BitBE(file);
1714 tape->length = getFile32BitBE(file);
1716 /* read header fields that are new since version 1.2 */
1717 if (tape->file_version >= FILE_VERSION_1_2)
1719 byte store_participating_players = getFile8Bit(file);
1722 /* since version 1.2, tapes store which players participate in the tape */
1723 tape->num_participating_players = 0;
1724 for(i=0; i<MAX_PLAYERS; i++)
1726 tape->player_participates[i] = FALSE;
1728 if (store_participating_players & (1 << i))
1730 tape->player_participates[i] = TRUE;
1731 tape->num_participating_players++;
1735 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1737 engine_version = getFileVersion(file);
1738 if (engine_version > 0)
1739 tape->engine_version = engine_version;
1741 tape->engine_version = tape->game_version;
1747 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1749 int level_identifier_size;
1752 level_identifier_size = getFile16BitBE(file);
1754 tape->level_identifier =
1755 checked_realloc(tape->level_identifier, level_identifier_size);
1757 for(i=0; i < level_identifier_size; i++)
1758 tape->level_identifier[i] = getFile8Bit(file);
1760 tape->level_nr = getFile16BitBE(file);
1762 chunk_size = 2 + level_identifier_size + 2;
1767 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1770 int chunk_size_expected =
1771 (tape->num_participating_players + 1) * tape->length;
1773 if (chunk_size_expected != chunk_size)
1775 ReadUnusedBytesFromFile(file, chunk_size);
1776 return chunk_size_expected;
1779 for(i=0; i<tape->length; i++)
1781 if (i >= MAX_TAPELEN)
1784 for(j=0; j<MAX_PLAYERS; j++)
1786 tape->pos[i].action[j] = MV_NO_MOVING;
1788 if (tape->player_participates[j])
1789 tape->pos[i].action[j] = getFile8Bit(file);
1792 tape->pos[i].delay = getFile8Bit(file);
1794 if (tape->file_version == FILE_VERSION_1_0)
1796 /* eliminate possible diagonal moves in old tapes */
1797 /* this is only for backward compatibility */
1799 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1800 byte action = tape->pos[i].action[0];
1801 int k, num_moves = 0;
1805 if (action & joy_dir[k])
1807 tape->pos[i + num_moves].action[0] = joy_dir[k];
1809 tape->pos[i + num_moves].delay = 0;
1818 tape->length += num_moves;
1821 else if (tape->file_version < FILE_VERSION_2_0)
1823 /* convert pre-2.0 tapes to new tape format */
1825 if (tape->pos[i].delay > 1)
1828 tape->pos[i + 1] = tape->pos[i];
1829 tape->pos[i + 1].delay = 1;
1832 for(j=0; j<MAX_PLAYERS; j++)
1833 tape->pos[i].action[j] = MV_NO_MOVING;
1834 tape->pos[i].delay--;
1845 if (i != tape->length)
1846 chunk_size = (tape->num_participating_players + 1) * i;
1851 void LoadTapeFromFilename(char *filename)
1853 char cookie[MAX_LINE_LEN];
1854 char chunk_name[CHUNK_ID_LEN + 1];
1858 /* always start with reliable default values */
1859 setTapeInfoToDefaults();
1861 if (!(file = fopen(filename, MODE_READ)))
1864 getFileChunkBE(file, chunk_name, NULL);
1865 if (strcmp(chunk_name, "RND1") == 0)
1867 getFile32BitBE(file); /* not used */
1869 getFileChunkBE(file, chunk_name, NULL);
1870 if (strcmp(chunk_name, "TAPE") != 0)
1872 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1877 else /* check for pre-2.0 file format with cookie string */
1879 strcpy(cookie, chunk_name);
1880 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1881 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1882 cookie[strlen(cookie) - 1] = '\0';
1884 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1886 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1891 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1893 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1898 /* pre-2.0 tape files have no game version, so use file version here */
1899 tape.game_version = tape.file_version;
1902 if (tape.file_version < FILE_VERSION_1_2)
1904 /* tape files from versions before 1.2.0 without chunk structure */
1905 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1906 LoadTape_BODY(file, 2 * tape.length, &tape);
1914 int (*loader)(FILE *, int, struct TapeInfo *);
1918 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1919 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1920 { "INFO", -1, LoadTape_INFO },
1921 { "BODY", -1, LoadTape_BODY },
1925 while (getFileChunkBE(file, chunk_name, &chunk_size))
1929 while (chunk_info[i].name != NULL &&
1930 strcmp(chunk_name, chunk_info[i].name) != 0)
1933 if (chunk_info[i].name == NULL)
1935 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1936 chunk_name, filename);
1937 ReadUnusedBytesFromFile(file, chunk_size);
1939 else if (chunk_info[i].size != -1 &&
1940 chunk_info[i].size != chunk_size)
1942 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1943 chunk_size, chunk_name, filename);
1944 ReadUnusedBytesFromFile(file, chunk_size);
1948 /* call function to load this tape chunk */
1949 int chunk_size_expected =
1950 (chunk_info[i].loader)(file, chunk_size, &tape);
1952 /* the size of some chunks cannot be checked before reading other
1953 chunks first (like "HEAD" and "BODY") that contain some header
1954 information, so check them here */
1955 if (chunk_size_expected != chunk_size)
1957 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1958 chunk_size, chunk_name, filename);
1966 tape.length_seconds = GetTapeLength();
1969 printf("tape game version: %d\n", tape.game_version);
1970 printf("tape engine version: %d\n", tape.engine_version);
1974 void LoadTape(int level_nr)
1976 char *filename = getTapeFilename(level_nr);
1978 LoadTapeFromFilename(filename);
1981 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1983 putFileVersion(file, tape->file_version);
1984 putFileVersion(file, tape->game_version);
1987 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1990 byte store_participating_players = 0;
1992 /* set bits for participating players for compact storage */
1993 for(i=0; i<MAX_PLAYERS; i++)
1994 if (tape->player_participates[i])
1995 store_participating_players |= (1 << i);
1997 putFile32BitBE(file, tape->random_seed);
1998 putFile32BitBE(file, tape->date);
1999 putFile32BitBE(file, tape->length);
2001 putFile8Bit(file, store_participating_players);
2003 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2004 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2006 putFileVersion(file, tape->engine_version);
2009 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2011 int level_identifier_size = strlen(tape->level_identifier) + 1;
2014 putFile16BitBE(file, level_identifier_size);
2016 for(i=0; i < level_identifier_size; i++)
2017 putFile8Bit(file, tape->level_identifier[i]);
2019 putFile16BitBE(file, tape->level_nr);
2022 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2026 for(i=0; i<tape->length; i++)
2028 for(j=0; j<MAX_PLAYERS; j++)
2029 if (tape->player_participates[j])
2030 putFile8Bit(file, tape->pos[i].action[j]);
2032 putFile8Bit(file, tape->pos[i].delay);
2036 void SaveTape(int level_nr)
2038 char *filename = getTapeFilename(level_nr);
2040 boolean new_tape = TRUE;
2041 int num_participating_players = 0;
2042 int info_chunk_size;
2043 int body_chunk_size;
2046 InitTapeDirectory(leveldir_current->filename);
2048 /* if a tape still exists, ask to overwrite it */
2049 if (access(filename, F_OK) == 0)
2052 if (!Request("Replace old tape ?", REQ_ASK))
2056 if (!(file = fopen(filename, MODE_WRITE)))
2058 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2062 tape.file_version = FILE_VERSION_ACTUAL;
2063 tape.game_version = GAME_VERSION_ACTUAL;
2065 /* count number of participating players */
2066 for(i=0; i<MAX_PLAYERS; i++)
2067 if (tape.player_participates[i])
2068 num_participating_players++;
2070 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2071 body_chunk_size = (num_participating_players + 1) * tape.length;
2073 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2074 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2076 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2077 SaveTape_VERS(file, &tape);
2079 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2080 SaveTape_HEAD(file, &tape);
2082 putFileChunkBE(file, "INFO", info_chunk_size);
2083 SaveTape_INFO(file, &tape);
2085 putFileChunkBE(file, "BODY", body_chunk_size);
2086 SaveTape_BODY(file, &tape);
2090 SetFilePermissions(filename, PERMS_PRIVATE);
2092 tape.changed = FALSE;
2095 Request("tape saved !", REQ_CONFIRM);
2098 void DumpTape(struct TapeInfo *tape)
2102 if (TAPE_IS_EMPTY(*tape))
2104 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2108 printf_line("-", 79);
2109 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2110 tape->level_nr, tape->file_version, tape->game_version);
2111 printf("Level series identifier: '%s'\n", tape->level_identifier);
2112 printf_line("-", 79);
2114 for(i=0; i<tape->length; i++)
2116 if (i >= MAX_TAPELEN)
2119 printf("%03d: ", i);
2121 for(j=0; j<MAX_PLAYERS; j++)
2123 if (tape->player_participates[j])
2125 int action = tape->pos[i].action[j];
2127 printf("%d:%02x ", j, action);
2128 printf("[%c%c%c%c|%c%c] - ",
2129 (action & JOY_LEFT ? '<' : ' '),
2130 (action & JOY_RIGHT ? '>' : ' '),
2131 (action & JOY_UP ? '^' : ' '),
2132 (action & JOY_DOWN ? 'v' : ' '),
2133 (action & JOY_BUTTON_1 ? '1' : ' '),
2134 (action & JOY_BUTTON_2 ? '2' : ' '));
2138 printf("(%03d)\n", tape->pos[i].delay);
2141 printf_line("-", 79);
2145 /* ========================================================================= */
2146 /* score file functions */
2147 /* ========================================================================= */
2149 void LoadScore(int level_nr)
2152 char *filename = getScoreFilename(level_nr);
2153 char cookie[MAX_LINE_LEN];
2154 char line[MAX_LINE_LEN];
2158 /* always start with reliable default values */
2159 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2161 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2162 highscore[i].Score = 0;
2165 if (!(file = fopen(filename, MODE_READ)))
2168 /* check file identifier */
2169 fgets(cookie, MAX_LINE_LEN, file);
2170 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2171 cookie[strlen(cookie) - 1] = '\0';
2173 if (!checkCookieString(cookie, SCORE_COOKIE))
2175 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2180 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2182 fscanf(file, "%d", &highscore[i].Score);
2183 fgets(line, MAX_LINE_LEN, file);
2185 if (line[strlen(line) - 1] == '\n')
2186 line[strlen(line) - 1] = '\0';
2188 for (line_ptr = line; *line_ptr; line_ptr++)
2190 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2192 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2193 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2202 void SaveScore(int level_nr)
2205 char *filename = getScoreFilename(level_nr);
2208 InitScoreDirectory(leveldir_current->filename);
2210 if (!(file = fopen(filename, MODE_WRITE)))
2212 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2216 fprintf(file, "%s\n\n", SCORE_COOKIE);
2218 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2219 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2223 SetFilePermissions(filename, PERMS_PUBLIC);
2227 /* ========================================================================= */
2228 /* setup file functions */
2229 /* ========================================================================= */
2231 #define TOKEN_STR_PLAYER_PREFIX "player_"
2234 #define SETUP_TOKEN_PLAYER_NAME 0
2235 #define SETUP_TOKEN_SOUND 1
2236 #define SETUP_TOKEN_SOUND_LOOPS 2
2237 #define SETUP_TOKEN_SOUND_MUSIC 3
2238 #define SETUP_TOKEN_SOUND_SIMPLE 4
2239 #define SETUP_TOKEN_TOONS 5
2240 #define SETUP_TOKEN_SCROLL_DELAY 6
2241 #define SETUP_TOKEN_SOFT_SCROLLING 7
2242 #define SETUP_TOKEN_FADING 8
2243 #define SETUP_TOKEN_AUTORECORD 9
2244 #define SETUP_TOKEN_QUICK_DOORS 10
2245 #define SETUP_TOKEN_TEAM_MODE 11
2246 #define SETUP_TOKEN_HANDICAP 12
2247 #define SETUP_TOKEN_TIME_LIMIT 13
2248 #define SETUP_TOKEN_FULLSCREEN 14
2249 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2250 #define SETUP_TOKEN_GRAPHICS_SET 16
2251 #define SETUP_TOKEN_SOUNDS_SET 17
2252 #define SETUP_TOKEN_MUSIC_SET 18
2253 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2254 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2255 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2257 #define NUM_GLOBAL_SETUP_TOKENS 22
2260 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2261 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2262 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2263 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2264 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2265 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2266 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2267 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2268 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2269 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2270 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2272 #define NUM_EDITOR_SETUP_TOKENS 11
2274 /* shortcut setup */
2275 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2276 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2277 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2279 #define NUM_SHORTCUT_SETUP_TOKENS 3
2282 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2283 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2284 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2285 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2286 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2287 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2288 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2289 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2290 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2291 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
2292 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2293 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2294 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2295 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2296 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2297 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
2299 #define NUM_PLAYER_SETUP_TOKENS 16
2302 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2303 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2305 #define NUM_SYSTEM_SETUP_TOKENS 2
2308 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2310 #define NUM_OPTIONS_SETUP_TOKENS 1
2313 static struct SetupInfo si;
2314 static struct SetupEditorInfo sei;
2315 static struct SetupShortcutInfo ssi;
2316 static struct SetupInputInfo sii;
2317 static struct SetupSystemInfo syi;
2318 static struct OptionInfo soi;
2320 static struct TokenInfo global_setup_tokens[] =
2322 { TYPE_STRING, &si.player_name, "player_name" },
2323 { TYPE_SWITCH, &si.sound, "sound" },
2324 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2325 { TYPE_SWITCH, &si.sound_music, "background_music" },
2326 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2327 { TYPE_SWITCH, &si.toons, "toons" },
2328 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2329 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2330 { TYPE_SWITCH, &si.fading, "screen_fading" },
2331 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2332 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2333 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2334 { TYPE_SWITCH, &si.handicap, "handicap" },
2335 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2336 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2337 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2338 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2339 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2340 { TYPE_STRING, &si.music_set, "music_set" },
2341 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2342 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2343 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2346 static struct TokenInfo editor_setup_tokens[] =
2348 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2349 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2350 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2351 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2352 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2353 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2354 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2355 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2356 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2357 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2358 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2361 static struct TokenInfo shortcut_setup_tokens[] =
2363 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2364 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2365 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2368 static struct TokenInfo player_setup_tokens[] =
2370 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2371 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2372 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2373 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2374 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2375 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2376 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2377 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2378 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2379 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2380 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2381 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2382 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2383 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2384 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2385 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
2388 static struct TokenInfo system_setup_tokens[] =
2390 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2391 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2394 static struct TokenInfo options_setup_tokens[] =
2396 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2399 static char *get_corrected_login_name(char *login_name)
2401 /* needed because player name must be a fixed length string */
2402 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2404 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2405 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2407 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2408 if (strchr(login_name_new, ' '))
2409 *strchr(login_name_new, ' ') = '\0';
2411 return login_name_new;
2414 static void setSetupInfoToDefaults(struct SetupInfo *si)
2418 si->player_name = get_corrected_login_name(getLoginName());
2421 si->sound_loops = TRUE;
2422 si->sound_music = TRUE;
2423 si->sound_simple = TRUE;
2425 si->double_buffering = TRUE;
2426 si->direct_draw = !si->double_buffering;
2427 si->scroll_delay = TRUE;
2428 si->soft_scrolling = TRUE;
2430 si->autorecord = TRUE;
2431 si->quick_doors = FALSE;
2432 si->team_mode = FALSE;
2433 si->handicap = TRUE;
2434 si->time_limit = TRUE;
2435 si->fullscreen = FALSE;
2436 si->ask_on_escape = TRUE;
2438 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2439 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2440 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2441 si->override_level_graphics = FALSE;
2442 si->override_level_sounds = FALSE;
2443 si->override_level_music = FALSE;
2445 si->editor.el_boulderdash = TRUE;
2446 si->editor.el_emerald_mine = TRUE;
2447 si->editor.el_more = TRUE;
2448 si->editor.el_sokoban = TRUE;
2449 si->editor.el_supaplex = TRUE;
2450 si->editor.el_diamond_caves = TRUE;
2451 si->editor.el_dx_boulderdash = TRUE;
2452 si->editor.el_chars = TRUE;
2453 si->editor.el_custom = TRUE;
2454 si->editor.el_custom_more = FALSE;
2456 si->editor.el_headlines = TRUE;
2458 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2459 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2460 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2462 for (i=0; i<MAX_PLAYERS; i++)
2464 si->input[i].use_joystick = FALSE;
2465 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2466 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2467 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2468 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2469 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2470 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2471 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2472 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2473 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2474 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2475 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2476 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2477 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2478 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2479 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2482 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2483 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2485 si->options.verbose = FALSE;
2488 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2492 if (!setup_file_hash)
2497 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2498 setSetupInfo(global_setup_tokens, i,
2499 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2504 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2505 setSetupInfo(editor_setup_tokens, i,
2506 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2509 /* shortcut setup */
2510 ssi = setup.shortcut;
2511 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2512 setSetupInfo(shortcut_setup_tokens, i,
2513 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2514 setup.shortcut = ssi;
2517 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2521 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2523 sii = setup.input[pnr];
2524 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2526 char full_token[100];
2528 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2529 setSetupInfo(player_setup_tokens, i,
2530 getHashEntry(setup_file_hash, full_token));
2532 setup.input[pnr] = sii;
2537 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2538 setSetupInfo(system_setup_tokens, i,
2539 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2543 soi = setup.options;
2544 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2545 setSetupInfo(options_setup_tokens, i,
2546 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2547 setup.options = soi;
2552 char *filename = getSetupFilename();
2553 SetupFileHash *setup_file_hash = NULL;
2555 /* always start with reliable default values */
2556 setSetupInfoToDefaults(&setup);
2558 setup_file_hash = loadSetupFileHash(filename);
2560 if (setup_file_hash)
2562 char *player_name_new;
2564 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2565 decodeSetupFileHash(setup_file_hash);
2567 setup.direct_draw = !setup.double_buffering;
2569 freeSetupFileHash(setup_file_hash);
2571 /* needed to work around problems with fixed length strings */
2572 player_name_new = get_corrected_login_name(setup.player_name);
2573 free(setup.player_name);
2574 setup.player_name = player_name_new;
2577 Error(ERR_WARN, "using default setup values");
2582 char *filename = getSetupFilename();
2586 InitUserDataDirectory();
2588 if (!(file = fopen(filename, MODE_WRITE)))
2590 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2594 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2595 getCookie("SETUP")));
2596 fprintf(file, "\n");
2600 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2602 /* just to make things nicer :) */
2603 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2604 i == SETUP_TOKEN_GRAPHICS_SET)
2605 fprintf(file, "\n");
2607 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2612 fprintf(file, "\n");
2613 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2614 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2616 /* shortcut setup */
2617 ssi = setup.shortcut;
2618 fprintf(file, "\n");
2619 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2620 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2623 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2627 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2628 fprintf(file, "\n");
2630 sii = setup.input[pnr];
2631 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2632 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2637 fprintf(file, "\n");
2638 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2639 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2642 soi = setup.options;
2643 fprintf(file, "\n");
2644 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2645 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2649 SetFilePermissions(filename, PERMS_PRIVATE);
2652 void LoadCustomElementDescriptions()
2654 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2655 SetupFileHash *setup_file_hash;
2658 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2660 if (element_info[i].custom_description != NULL)
2662 free(element_info[i].custom_description);
2663 element_info[i].custom_description = NULL;
2667 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2670 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2672 char *token = getStringCat2(element_info[i].token_name, ".name");
2673 char *value = getHashEntry(setup_file_hash, token);
2676 element_info[i].custom_description = getStringCopy(value);
2681 freeSetupFileHash(setup_file_hash);
2684 void LoadSpecialMenuDesignSettings()
2686 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2687 SetupFileHash *setup_file_hash;
2690 /* always start with reliable default values from default config */
2691 for (i=0; image_config_vars[i].token != NULL; i++)
2692 for (j=0; image_config[j].token != NULL; j++)
2693 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2694 *image_config_vars[i].value =
2695 get_auto_parameter_value(image_config_vars[i].token,
2696 image_config[j].value);
2698 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2701 /* special case: initialize with default values that may be overwritten */
2702 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2704 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2705 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2706 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2708 if (value_x != NULL)
2709 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2710 if (value_y != NULL)
2711 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2712 if (list_size != NULL)
2713 menu.list_size[i] = get_integer_from_string(list_size);
2716 /* read (and overwrite with) values that may be specified in config file */
2717 for (i=0; image_config_vars[i].token != NULL; i++)
2719 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2722 *image_config_vars[i].value =
2723 get_auto_parameter_value(image_config_vars[i].token, value);
2726 freeSetupFileHash(setup_file_hash);