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 LEVEL_CPART_CUS4_SIZE ??? /* size of CUS4 chunk part */
38 #define LEVEL_CPART_CUS4_UNUSED ??? /* unused CUS4 bytes / part */
39 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
40 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
42 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
43 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
45 /* file identifier strings */
46 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
47 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
48 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
51 /* ========================================================================= */
52 /* level file functions */
53 /* ========================================================================= */
55 void setElementChangePages(struct ElementInfo *ei, int change_pages)
57 int change_page_size = sizeof(struct ElementChangeInfo);
59 ei->num_change_pages = MAX(1, change_pages);
62 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
64 if (ei->current_change_page >= ei->num_change_pages)
65 ei->current_change_page = ei->num_change_pages - 1;
67 ei->change = &ei->change_page[ei->current_change_page];
70 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
74 change->can_change = FALSE;
76 change->events = CE_BITMASK_DEFAULT;
77 change->sides = CH_SIDE_ANY;
79 change->target_element = EL_EMPTY_SPACE;
81 change->delay_fixed = 0;
82 change->delay_random = 0;
83 change->delay_frames = -1; /* later set to reliable default value */
85 change->trigger_element = EL_EMPTY_SPACE;
87 change->explode = FALSE;
88 change->use_content = FALSE;
89 change->only_complete = FALSE;
90 change->use_random_change = FALSE;
92 change->power = CP_NON_DESTRUCTIVE;
96 change->content[x][y] = EL_EMPTY_SPACE;
98 change->direct_action = 0;
99 change->other_action = 0;
101 change->pre_change_function = NULL;
102 change->change_function = NULL;
103 change->post_change_function = NULL;
106 static void setLevelInfoToDefaults(struct LevelInfo *level)
110 level->file_version = FILE_VERSION_ACTUAL;
111 level->game_version = GAME_VERSION_ACTUAL;
113 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
114 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
115 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
117 level->fieldx = STD_LEV_FIELDX;
118 level->fieldy = STD_LEV_FIELDY;
120 for(x=0; x<MAX_LEV_FIELDX; x++)
121 for(y=0; y<MAX_LEV_FIELDY; y++)
122 level->field[x][y] = EL_SAND;
125 level->gems_needed = 0;
126 level->amoeba_speed = 10;
127 level->time_magic_wall = 10;
128 level->time_wheel = 10;
129 level->time_light = 10;
130 level->time_timegate = 10;
131 level->amoeba_content = EL_DIAMOND;
132 level->double_speed = FALSE;
133 level->gravity = FALSE;
134 level->em_slippery_gems = FALSE;
136 level->use_custom_template = FALSE;
138 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
139 level->name[i] = '\0';
140 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
141 level->author[i] = '\0';
143 strcpy(level->name, NAMELESS_LEVEL_NAME);
144 strcpy(level->author, ANONYMOUS_NAME);
148 level->envelope_text[i][0] = '\0';
149 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
150 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
153 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
154 level->score[i] = 10;
156 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
157 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
160 level->yamyam_content[i][x][y] =
161 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
163 level->field[0][0] = EL_PLAYER_1;
164 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
166 for (i=0; i < MAX_NUM_ELEMENTS; i++)
168 setElementChangePages(&element_info[i], 1);
169 setElementChangeInfoToDefaults(element_info[i].change);
172 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
174 int element = EL_CUSTOM_START + i;
176 for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
177 element_info[element].description[j] = '\0';
178 if (element_info[element].custom_description != NULL)
179 strncpy(element_info[element].description,
180 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
182 strcpy(element_info[element].description,
183 element_info[element].editor_description);
185 element_info[element].use_gfx_element = FALSE;
186 element_info[element].gfx_element = EL_EMPTY_SPACE;
188 element_info[element].collect_score = 10; /* special default */
189 element_info[element].collect_count = 1; /* special default */
191 element_info[element].push_delay_fixed = 2; /* special default */
192 element_info[element].push_delay_random = 8; /* special default */
193 element_info[element].move_delay_fixed = 0;
194 element_info[element].move_delay_random = 0;
196 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
197 element_info[element].move_direction_initial = MV_NO_MOVING;
198 element_info[element].move_stepsize = TILEX / 8;
200 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
204 element_info[element].content[x][y] = EL_EMPTY_SPACE;
206 element_info[element].access_type = 0;
207 element_info[element].access_layer = 0;
208 element_info[element].walk_to_action = 0;
209 element_info[element].smash_targets = 0;
210 element_info[element].deadliness = 0;
211 element_info[element].consistency = 0;
213 element_info[element].can_explode_by_fire = FALSE;
214 element_info[element].can_explode_smashed = FALSE;
215 element_info[element].can_explode_impact = FALSE;
217 element_info[element].current_change_page = 0;
219 /* start with no properties at all */
220 for (j=0; j < NUM_EP_BITFIELDS; j++)
221 Properties[element][j] = EP_BITMASK_DEFAULT;
223 element_info[element].modified_settings = FALSE;
226 BorderElement = EL_STEELWALL;
228 level->no_level_file = FALSE;
230 if (leveldir_current == NULL) /* only when dumping level */
233 /* try to determine better author name than 'anonymous' */
234 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
236 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
237 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
241 switch (LEVELCLASS(leveldir_current))
243 case LEVELCLASS_TUTORIAL:
244 strcpy(level->author, PROGRAM_AUTHOR_STRING);
247 case LEVELCLASS_CONTRIBUTION:
248 strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
249 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
252 case LEVELCLASS_USER:
253 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
254 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
258 /* keep default value */
264 static void ActivateLevelTemplate()
266 /* Currently there is no special action needed to activate the template
267 data, because 'element_info' and 'Properties' overwrite the original
268 level data, while all other variables do not change. */
271 boolean LevelFileExists(int level_nr)
273 char *filename = getLevelFilename(level_nr);
275 return (access(filename, F_OK) == 0);
278 static int checkLevelElement(int element)
280 /* map some (historic, now obsolete) elements */
285 case EL_PLAYER_OBSOLETE:
286 element = EL_PLAYER_1;
289 case EL_KEY_OBSOLETE:
292 case EL_EM_KEY_1_FILE_OBSOLETE:
293 element = EL_EM_KEY_1;
296 case EL_EM_KEY_2_FILE_OBSOLETE:
297 element = EL_EM_KEY_2;
300 case EL_EM_KEY_3_FILE_OBSOLETE:
301 element = EL_EM_KEY_3;
304 case EL_EM_KEY_4_FILE_OBSOLETE:
305 element = EL_EM_KEY_4;
308 case EL_ENVELOPE_OBSOLETE:
309 element = EL_ENVELOPE_1;
317 if (element >= NUM_FILE_ELEMENTS)
319 Error(ERR_WARN, "invalid level element %d", element);
321 element = EL_CHAR_QUESTION;
326 if (element >= NUM_FILE_ELEMENTS)
328 Error(ERR_WARN, "invalid level element %d", element);
330 element = EL_CHAR_QUESTION;
332 else if (element == EL_PLAYER_OBSOLETE)
333 element = EL_PLAYER_1;
334 else if (element == EL_KEY_OBSOLETE)
341 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
343 level->file_version = getFileVersion(file);
344 level->game_version = getFileVersion(file);
349 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
353 level->fieldx = getFile8Bit(file);
354 level->fieldy = getFile8Bit(file);
356 level->time = getFile16BitBE(file);
357 level->gems_needed = getFile16BitBE(file);
359 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
360 level->name[i] = getFile8Bit(file);
361 level->name[MAX_LEVEL_NAME_LEN] = 0;
363 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
364 level->score[i] = getFile8Bit(file);
366 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
367 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
370 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
372 level->amoeba_speed = getFile8Bit(file);
373 level->time_magic_wall = getFile8Bit(file);
374 level->time_wheel = getFile8Bit(file);
375 level->amoeba_content = checkLevelElement(getFile8Bit(file));
376 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
377 level->gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
378 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
379 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
381 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
383 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
388 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
392 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
393 level->author[i] = getFile8Bit(file);
394 level->author[MAX_LEVEL_NAME_LEN] = 0;
399 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
402 int chunk_size_expected = level->fieldx * level->fieldy;
404 /* Note: "chunk_size" was wrong before version 2.0 when elements are
405 stored with 16-bit encoding (and should be twice as big then).
406 Even worse, playfield data was stored 16-bit when only yamyam content
407 contained 16-bit elements and vice versa. */
409 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
410 chunk_size_expected *= 2;
412 if (chunk_size_expected != chunk_size)
414 ReadUnusedBytesFromFile(file, chunk_size);
415 return chunk_size_expected;
418 for(y=0; y<level->fieldy; y++)
419 for(x=0; x<level->fieldx; x++)
421 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
426 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
430 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
431 int chunk_size_expected = header_size + content_size;
433 /* Note: "chunk_size" was wrong before version 2.0 when elements are
434 stored with 16-bit encoding (and should be twice as big then).
435 Even worse, playfield data was stored 16-bit when only yamyam content
436 contained 16-bit elements and vice versa. */
438 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
439 chunk_size_expected += content_size;
441 if (chunk_size_expected != chunk_size)
443 ReadUnusedBytesFromFile(file, chunk_size);
444 return chunk_size_expected;
448 level->num_yamyam_contents = getFile8Bit(file);
452 /* correct invalid number of content fields -- should never happen */
453 if (level->num_yamyam_contents < 1 ||
454 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
455 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
457 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
460 level->yamyam_content[i][x][y] =
461 checkLevelElement(level->encoding_16bit_field ?
462 getFile16BitBE(file) : getFile8Bit(file));
466 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
470 int num_contents, content_xsize, content_ysize;
471 int content_array[MAX_ELEMENT_CONTENTS][3][3];
473 element = checkLevelElement(getFile16BitBE(file));
474 num_contents = getFile8Bit(file);
475 content_xsize = getFile8Bit(file);
476 content_ysize = getFile8Bit(file);
478 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
480 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
483 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
485 /* correct invalid number of content fields -- should never happen */
486 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
487 num_contents = STD_ELEMENT_CONTENTS;
489 if (element == EL_YAMYAM)
491 level->num_yamyam_contents = num_contents;
493 for(i=0; i<num_contents; i++)
496 level->yamyam_content[i][x][y] = content_array[i][x][y];
498 else if (element == EL_BD_AMOEBA)
500 level->amoeba_content = content_array[0][0][0];
504 Error(ERR_WARN, "cannot load content for element '%d'", element);
510 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
516 int chunk_size_expected;
518 element = checkLevelElement(getFile16BitBE(file));
519 if (!IS_ENVELOPE(element))
520 element = EL_ENVELOPE_1;
522 envelope_nr = element - EL_ENVELOPE_1;
524 envelope_len = getFile16BitBE(file);
526 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
527 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
529 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
531 chunk_size_expected = LEVEL_CHUNK_CNT3_HEADER + envelope_len;
533 if (chunk_size_expected != chunk_size)
535 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
536 return chunk_size_expected;
539 for(i=0; i < envelope_len; i++)
540 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
545 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
547 int num_changed_custom_elements = getFile16BitBE(file);
548 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
551 if (chunk_size_expected != chunk_size)
553 ReadUnusedBytesFromFile(file, chunk_size - 2);
554 return chunk_size_expected;
557 for (i=0; i < num_changed_custom_elements; i++)
559 int element = getFile16BitBE(file);
560 int properties = getFile32BitBE(file);
562 if (IS_CUSTOM_ELEMENT(element))
563 Properties[element][EP_BITFIELD_BASE] = properties;
565 Error(ERR_WARN, "invalid custom element number %d", element);
571 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
573 int num_changed_custom_elements = getFile16BitBE(file);
574 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
577 if (chunk_size_expected != chunk_size)
579 ReadUnusedBytesFromFile(file, chunk_size - 2);
580 return chunk_size_expected;
583 for (i=0; i < num_changed_custom_elements; i++)
585 int element = getFile16BitBE(file);
586 int custom_target_element = getFile16BitBE(file);
588 if (IS_CUSTOM_ELEMENT(element))
589 element_info[element].change->target_element = custom_target_element;
591 Error(ERR_WARN, "invalid custom element number %d", element);
597 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
599 int num_changed_custom_elements = getFile16BitBE(file);
600 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
603 if (chunk_size_expected != chunk_size)
605 ReadUnusedBytesFromFile(file, chunk_size - 2);
606 return chunk_size_expected;
609 for (i=0; i < num_changed_custom_elements; i++)
611 int element = getFile16BitBE(file);
613 if (!IS_CUSTOM_ELEMENT(element))
615 Error(ERR_WARN, "invalid custom element number %d", element);
617 element = EL_DEFAULT; /* dummy element used for artwork config */
620 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
621 element_info[element].description[j] = getFile8Bit(file);
622 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
624 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
626 /* some free bytes for future properties and padding */
627 ReadUnusedBytesFromFile(file, 7);
629 element_info[element].use_gfx_element = getFile8Bit(file);
630 element_info[element].gfx_element =
631 checkLevelElement(getFile16BitBE(file));
633 element_info[element].collect_score = getFile8Bit(file);
634 element_info[element].collect_count = getFile8Bit(file);
636 element_info[element].push_delay_fixed = getFile16BitBE(file);
637 element_info[element].push_delay_random = getFile16BitBE(file);
638 element_info[element].move_delay_fixed = getFile16BitBE(file);
639 element_info[element].move_delay_random = getFile16BitBE(file);
641 element_info[element].move_pattern = getFile16BitBE(file);
642 element_info[element].move_direction_initial = getFile8Bit(file);
643 element_info[element].move_stepsize = getFile8Bit(file);
647 element_info[element].content[x][y] =
648 checkLevelElement(getFile16BitBE(file));
650 element_info[element].change->events = getFile32BitBE(file);
652 element_info[element].change->target_element =
653 checkLevelElement(getFile16BitBE(file));
655 element_info[element].change->delay_fixed = getFile16BitBE(file);
656 element_info[element].change->delay_random = getFile16BitBE(file);
657 element_info[element].change->delay_frames = getFile16BitBE(file);
659 element_info[element].change->trigger_element =
660 checkLevelElement(getFile16BitBE(file));
662 element_info[element].change->explode = getFile8Bit(file);
663 element_info[element].change->use_content = getFile8Bit(file);
664 element_info[element].change->only_complete = getFile8Bit(file);
665 element_info[element].change->use_random_change = getFile8Bit(file);
667 element_info[element].change->random = getFile8Bit(file);
668 element_info[element].change->power = getFile8Bit(file);
672 element_info[element].change->content[x][y] =
673 checkLevelElement(getFile16BitBE(file));
675 element_info[element].slippery_type = getFile8Bit(file);
677 /* some free bytes for future properties and padding */
678 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
680 /* mark that this custom element has been modified */
681 element_info[element].modified_settings = TRUE;
687 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
689 struct ElementInfo *ei;
690 int chunk_size_expected;
694 element = getFile16BitBE(file);
696 if (!IS_CUSTOM_ELEMENT(element))
698 Error(ERR_WARN, "invalid custom element number %d", element);
700 element = EL_DEFAULT; /* dummy element used for artwork config */
703 ei = &element_info[element];
705 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
706 ei->description[i] = getFile8Bit(file);
707 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
709 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
710 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
712 ei->num_change_pages = getFile8Bit(file);
714 /* some free bytes for future base property values and padding */
715 ReadUnusedBytesFromFile(file, 5);
717 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
718 if (chunk_size_expected != chunk_size)
720 ReadUnusedBytesFromFile(file, chunk_size - 48);
721 return chunk_size_expected;
724 /* read custom property values */
726 ei->use_gfx_element = getFile8Bit(file);
727 ei->gfx_element = checkLevelElement(getFile16BitBE(file));
729 ei->collect_score = getFile8Bit(file);
730 ei->collect_count = getFile8Bit(file);
732 ei->push_delay_fixed = getFile16BitBE(file);
733 ei->push_delay_random = getFile16BitBE(file);
734 ei->move_delay_fixed = getFile16BitBE(file);
735 ei->move_delay_random = getFile16BitBE(file);
737 ei->move_pattern = getFile16BitBE(file);
738 ei->move_direction_initial = getFile8Bit(file);
739 ei->move_stepsize = getFile8Bit(file);
741 ei->slippery_type = getFile8Bit(file);
745 ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
747 /* some free bytes for future custom property values and padding */
748 ReadUnusedBytesFromFile(file, 12);
750 /* read change property values */
752 setElementChangePages(ei, ei->num_change_pages);
754 for (i=0; i < ei->num_change_pages; i++)
756 struct ElementChangeInfo *change = &ei->change_page[i];
758 /* always start with reliable default values */
759 setElementChangeInfoToDefaults(change);
761 change->events = getFile32BitBE(file);
763 change->target_element = checkLevelElement(getFile16BitBE(file));
765 change->delay_fixed = getFile16BitBE(file);
766 change->delay_random = getFile16BitBE(file);
767 change->delay_frames = getFile16BitBE(file);
769 change->trigger_element = checkLevelElement(getFile16BitBE(file));
771 change->explode = getFile8Bit(file);
772 change->use_content = getFile8Bit(file);
773 change->only_complete = getFile8Bit(file);
774 change->use_random_change = getFile8Bit(file);
776 change->random = getFile8Bit(file);
777 change->power = getFile8Bit(file);
781 change->content[x][y] = checkLevelElement(getFile16BitBE(file));
783 change->can_change = getFile8Bit(file);
785 /* some free bytes for future change property values and padding */
786 ReadUnusedBytesFromFile(file, 9);
789 /* mark this custom element as modified */
790 ei->modified_settings = TRUE;
795 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
797 char cookie[MAX_LINE_LEN];
798 char chunk_name[CHUNK_ID_LEN + 1];
802 /* always start with reliable default values */
803 setLevelInfoToDefaults(level);
805 if (!(file = fopen(filename, MODE_READ)))
807 level->no_level_file = TRUE;
809 if (level != &level_template)
810 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
815 getFileChunkBE(file, chunk_name, NULL);
816 if (strcmp(chunk_name, "RND1") == 0)
818 getFile32BitBE(file); /* not used */
820 getFileChunkBE(file, chunk_name, NULL);
821 if (strcmp(chunk_name, "CAVE") != 0)
823 Error(ERR_WARN, "unknown format of level file '%s'", filename);
828 else /* check for pre-2.0 file format with cookie string */
830 strcpy(cookie, chunk_name);
831 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
832 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
833 cookie[strlen(cookie) - 1] = '\0';
835 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
837 Error(ERR_WARN, "unknown format of level file '%s'", filename);
842 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
844 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
849 /* pre-2.0 level files have no game version, so use file version here */
850 level->game_version = level->file_version;
853 if (level->file_version < FILE_VERSION_1_2)
855 /* level files from versions before 1.2.0 without chunk structure */
856 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
857 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
865 int (*loader)(FILE *, int, struct LevelInfo *);
869 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
870 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
871 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
872 { "BODY", -1, LoadLevel_BODY },
873 { "CONT", -1, LoadLevel_CONT },
874 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
875 { "CNT3", -1, LoadLevel_CNT3 },
876 { "CUS1", -1, LoadLevel_CUS1 },
877 { "CUS2", -1, LoadLevel_CUS2 },
878 { "CUS3", -1, LoadLevel_CUS3 },
879 { "CUS4", -1, LoadLevel_CUS4 },
883 while (getFileChunkBE(file, chunk_name, &chunk_size))
887 while (chunk_info[i].name != NULL &&
888 strcmp(chunk_name, chunk_info[i].name) != 0)
891 if (chunk_info[i].name == NULL)
893 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
894 chunk_name, filename);
895 ReadUnusedBytesFromFile(file, chunk_size);
897 else if (chunk_info[i].size != -1 &&
898 chunk_info[i].size != chunk_size)
900 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
901 chunk_size, chunk_name, filename);
902 ReadUnusedBytesFromFile(file, chunk_size);
906 /* call function to load this level chunk */
907 int chunk_size_expected =
908 (chunk_info[i].loader)(file, chunk_size, level);
910 /* the size of some chunks cannot be checked before reading other
911 chunks first (like "HEAD" and "BODY") that contain some header
912 information, so check them here */
913 if (chunk_size_expected != chunk_size)
915 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
916 chunk_size, chunk_name, filename);
927 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
929 if (leveldir_current == NULL) /* only when dumping level */
932 /* determine correct game engine version of current level */
933 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
934 IS_LEVELCLASS_USER(leveldir_current))
937 printf("\n::: This level is private or contributed: '%s'\n", filename);
940 /* For user contributed and private levels, use the version of
941 the game engine the levels were created for.
942 Since 2.0.1, the game engine version is now directly stored
943 in the level file (chunk "VERS"), so there is no need anymore
944 to set the game version from the file version (except for old,
945 pre-2.0 levels, where the game version is still taken from the
946 file format version used to store the level -- see above). */
948 /* do some special adjustments to support older level versions */
949 if (level->file_version == FILE_VERSION_1_0)
951 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
952 Error(ERR_WARN, "using high speed movement for player");
954 /* player was faster than monsters in (pre-)1.0 levels */
955 level->double_speed = TRUE;
958 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
959 if (level->game_version == VERSION_IDENT(2,0,1))
960 level->em_slippery_gems = TRUE;
965 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
966 leveldir_current->sort_priority, filename);
969 /* Always use the latest version of the game engine for all but
970 user contributed and private levels; this allows for actual
971 corrections in the game engine to take effect for existing,
972 converted levels (from "classic" or other existing games) to
973 make the game emulation more accurate, while (hopefully) not
974 breaking existing levels created from other players. */
976 level->game_version = GAME_VERSION_ACTUAL;
978 /* Set special EM style gems behaviour: EM style gems slip down from
979 normal, steel and growing wall. As this is a more fundamental change,
980 it seems better to set the default behaviour to "off" (as it is more
981 natural) and make it configurable in the level editor (as a property
982 of gem style elements). Already existing converted levels (neither
983 private nor contributed levels) are changed to the new behaviour. */
985 if (level->file_version < FILE_VERSION_2_0)
986 level->em_slippery_gems = TRUE;
990 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
994 /* map custom element change events that have changed in newer versions
995 (these following values were accidentally changed in version 3.0.1) */
996 if (level->game_version <= VERSION_IDENT(3,0,0))
998 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1000 int element = EL_CUSTOM_START + i;
1002 /* order of checking and copying events to be mapped is important */
1003 for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
1005 if (HAS_CHANGE_EVENT(element, j - 2))
1007 SET_CHANGE_EVENT(element, j - 2, FALSE);
1008 SET_CHANGE_EVENT(element, j, TRUE);
1012 /* order of checking and copying events to be mapped is important */
1013 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1015 if (HAS_CHANGE_EVENT(element, j - 1))
1017 SET_CHANGE_EVENT(element, j - 1, FALSE);
1018 SET_CHANGE_EVENT(element, j, TRUE);
1024 /* some custom element change events get mapped since version 3.0.3 */
1025 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1027 int element = EL_CUSTOM_START + i;
1029 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
1030 HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
1032 SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
1033 SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
1035 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1039 /* initialize "can_change" field for old levels with only one change page */
1040 if (level->game_version <= VERSION_IDENT(3,0,2))
1042 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1044 int element = EL_CUSTOM_START + i;
1046 if (CAN_CHANGE(element))
1047 element_info[element].change->can_change = TRUE;
1051 /* initialize element properties for level editor etc. */
1052 InitElementPropertiesEngine(level->game_version);
1055 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1059 /* map elements that have changed in newer versions */
1060 for(y=0; y<level->fieldy; y++)
1062 for(x=0; x<level->fieldx; x++)
1064 int element = level->field[x][y];
1066 if (level->game_version <= VERSION_IDENT(2,2,0))
1068 /* map game font elements */
1069 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1070 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1071 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1072 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1075 if (level->game_version < VERSION_IDENT(3,0,0))
1077 /* map Supaplex gravity tube elements */
1078 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1079 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1080 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1081 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1085 level->field[x][y] = element;
1089 /* copy elements to runtime playfield array */
1090 for(x=0; x<MAX_LEV_FIELDX; x++)
1091 for(y=0; y<MAX_LEV_FIELDY; y++)
1092 Feld[x][y] = level->field[x][y];
1094 /* initialize level size variables for faster access */
1095 lev_fieldx = level->fieldx;
1096 lev_fieldy = level->fieldy;
1098 /* determine border element for this level */
1104 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
1108 if (leveldir_current == NULL) /* only when dumping level */
1111 /* determine correct game engine version of current level */
1112 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
1113 IS_LEVELCLASS_USER(leveldir_current))
1116 printf("\n::: This level is private or contributed: '%s'\n", filename);
1119 /* For user contributed and private levels, use the version of
1120 the game engine the levels were created for.
1121 Since 2.0.1, the game engine version is now directly stored
1122 in the level file (chunk "VERS"), so there is no need anymore
1123 to set the game version from the file version (except for old,
1124 pre-2.0 levels, where the game version is still taken from the
1125 file format version used to store the level -- see above). */
1127 /* do some special adjustments to support older level versions */
1128 if (level->file_version == FILE_VERSION_1_0)
1130 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
1131 Error(ERR_WARN, "using high speed movement for player");
1133 /* player was faster than monsters in (pre-)1.0 levels */
1134 level->double_speed = TRUE;
1137 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1138 if (level->game_version == VERSION_IDENT(2,0,1))
1139 level->em_slippery_gems = TRUE;
1144 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1145 leveldir_current->sort_priority, filename);
1148 /* Always use the latest version of the game engine for all but
1149 user contributed and private levels; this allows for actual
1150 corrections in the game engine to take effect for existing,
1151 converted levels (from "classic" or other existing games) to
1152 make the game emulation more accurate, while (hopefully) not
1153 breaking existing levels created from other players. */
1155 level->game_version = GAME_VERSION_ACTUAL;
1157 /* Set special EM style gems behaviour: EM style gems slip down from
1158 normal, steel and growing wall. As this is a more fundamental change,
1159 it seems better to set the default behaviour to "off" (as it is more
1160 natural) and make it configurable in the level editor (as a property
1161 of gem style elements). Already existing converted levels (neither
1162 private nor contributed levels) are changed to the new behaviour. */
1164 if (level->file_version < FILE_VERSION_2_0)
1165 level->em_slippery_gems = TRUE;
1168 /* map elements that have changed in newer versions */
1169 for(y=0; y<level->fieldy; y++)
1171 for(x=0; x<level->fieldx; x++)
1173 int element = level->field[x][y];
1175 if (level->game_version <= VERSION_IDENT(2,2,0))
1177 /* map game font elements */
1178 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1179 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1180 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1181 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1184 if (level->game_version < VERSION_IDENT(3,0,0))
1186 /* map Supaplex gravity tube elements */
1187 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1188 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1189 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1190 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1194 level->field[x][y] = element;
1198 /* map custom element change events that have changed in newer versions
1199 (these following values have accidentally changed in version 3.0.1) */
1200 if (level->game_version <= VERSION_IDENT(3,0,0))
1202 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1204 int element = EL_CUSTOM_START + i;
1206 /* order of checking events to be mapped is important */
1207 for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
1209 if (HAS_CHANGE_EVENT(element, j - 2))
1211 SET_CHANGE_EVENT(element, j - 2, FALSE);
1212 SET_CHANGE_EVENT(element, j, TRUE);
1216 /* order of checking events to be mapped is important */
1217 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1219 if (HAS_CHANGE_EVENT(element, j - 1))
1221 SET_CHANGE_EVENT(element, j - 1, FALSE);
1222 SET_CHANGE_EVENT(element, j, TRUE);
1228 /* initialize "can_change" field for old levels with only one change page */
1229 if (level->game_version <= VERSION_IDENT(3,0,2))
1231 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1233 int element = EL_CUSTOM_START + i;
1235 if (CAN_CHANGE(element))
1236 element_info[element].change->can_change = TRUE;
1240 /* copy elements to runtime playfield array */
1241 for(x=0; x<MAX_LEV_FIELDX; x++)
1242 for(y=0; y<MAX_LEV_FIELDY; y++)
1243 Feld[x][y] = level->field[x][y];
1245 /* initialize level size variables for faster access */
1246 lev_fieldx = level->fieldx;
1247 lev_fieldy = level->fieldy;
1249 /* determine border element for this level */
1252 /* initialize element properties for level editor etc. */
1253 InitElementPropertiesEngine(level->game_version);
1258 void LoadLevelTemplate(int level_nr)
1260 char *filename = getLevelFilename(level_nr);
1262 LoadLevelFromFilename(&level_template, filename);
1264 LoadLevel_InitVersion(&level, filename);
1265 LoadLevel_InitElements(&level, filename);
1267 ActivateLevelTemplate();
1270 void LoadLevel(int level_nr)
1272 char *filename = getLevelFilename(level_nr);
1274 LoadLevelFromFilename(&level, filename);
1276 if (level.use_custom_template)
1277 LoadLevelTemplate(-1);
1280 LoadLevel_InitVersion(&level, filename);
1281 LoadLevel_InitElements(&level, filename);
1282 LoadLevel_InitPlayfield(&level, filename);
1284 LoadLevel_InitLevel(&level, filename);
1288 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1290 putFileVersion(file, level->file_version);
1291 putFileVersion(file, level->game_version);
1294 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1298 putFile8Bit(file, level->fieldx);
1299 putFile8Bit(file, level->fieldy);
1301 putFile16BitBE(file, level->time);
1302 putFile16BitBE(file, level->gems_needed);
1304 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1305 putFile8Bit(file, level->name[i]);
1307 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1308 putFile8Bit(file, level->score[i]);
1310 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1313 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1314 level->yamyam_content[i][x][y]));
1315 putFile8Bit(file, level->amoeba_speed);
1316 putFile8Bit(file, level->time_magic_wall);
1317 putFile8Bit(file, level->time_wheel);
1318 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1319 level->amoeba_content));
1320 putFile8Bit(file, (level->double_speed ? 1 : 0));
1321 putFile8Bit(file, (level->gravity ? 1 : 0));
1322 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1323 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1325 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1327 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1330 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1334 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1335 putFile8Bit(file, level->author[i]);
1338 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1342 for(y=0; y<level->fieldy; y++)
1343 for(x=0; x<level->fieldx; x++)
1344 if (level->encoding_16bit_field)
1345 putFile16BitBE(file, level->field[x][y]);
1347 putFile8Bit(file, level->field[x][y]);
1351 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1355 putFile8Bit(file, EL_YAMYAM);
1356 putFile8Bit(file, level->num_yamyam_contents);
1357 putFile8Bit(file, 0);
1358 putFile8Bit(file, 0);
1360 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1363 if (level->encoding_16bit_field)
1364 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1366 putFile8Bit(file, level->yamyam_content[i][x][y]);
1370 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1373 int num_contents, content_xsize, content_ysize;
1374 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1376 if (element == EL_YAMYAM)
1378 num_contents = level->num_yamyam_contents;
1382 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1385 content_array[i][x][y] = level->yamyam_content[i][x][y];
1387 else if (element == EL_BD_AMOEBA)
1393 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1396 content_array[i][x][y] = EL_EMPTY;
1397 content_array[0][0][0] = level->amoeba_content;
1401 /* chunk header already written -- write empty chunk data */
1402 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1404 Error(ERR_WARN, "cannot save content for element '%d'", element);
1408 putFile16BitBE(file, element);
1409 putFile8Bit(file, num_contents);
1410 putFile8Bit(file, content_xsize);
1411 putFile8Bit(file, content_ysize);
1413 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1415 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1418 putFile16BitBE(file, content_array[i][x][y]);
1421 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1424 int envelope_nr = element - EL_ENVELOPE_1;
1425 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1427 putFile16BitBE(file, element);
1428 putFile16BitBE(file, envelope_len);
1429 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1430 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1432 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1434 for(i=0; i < envelope_len; i++)
1435 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1439 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1440 int num_changed_custom_elements)
1444 putFile16BitBE(file, num_changed_custom_elements);
1446 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1448 int element = EL_CUSTOM_START + i;
1450 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1452 if (check < num_changed_custom_elements)
1454 putFile16BitBE(file, element);
1455 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1462 if (check != num_changed_custom_elements) /* should not happen */
1463 Error(ERR_WARN, "inconsistent number of custom element properties");
1468 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1469 int num_changed_custom_elements)
1473 putFile16BitBE(file, num_changed_custom_elements);
1475 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1477 int element = EL_CUSTOM_START + i;
1479 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1481 if (check < num_changed_custom_elements)
1483 putFile16BitBE(file, element);
1484 putFile16BitBE(file, element_info[element].change->target_element);
1491 if (check != num_changed_custom_elements) /* should not happen */
1492 Error(ERR_WARN, "inconsistent number of custom target elements");
1497 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1498 int num_changed_custom_elements)
1500 int i, j, x, y, check = 0;
1502 putFile16BitBE(file, num_changed_custom_elements);
1504 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1506 int element = EL_CUSTOM_START + i;
1508 if (element_info[element].modified_settings)
1510 if (check < num_changed_custom_elements)
1512 putFile16BitBE(file, element);
1514 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1515 putFile8Bit(file, element_info[element].description[j]);
1517 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1519 /* some free bytes for future properties and padding */
1520 WriteUnusedBytesToFile(file, 7);
1522 putFile8Bit(file, element_info[element].use_gfx_element);
1523 putFile16BitBE(file, element_info[element].gfx_element);
1525 putFile8Bit(file, element_info[element].collect_score);
1526 putFile8Bit(file, element_info[element].collect_count);
1528 putFile16BitBE(file, element_info[element].push_delay_fixed);
1529 putFile16BitBE(file, element_info[element].push_delay_random);
1530 putFile16BitBE(file, element_info[element].move_delay_fixed);
1531 putFile16BitBE(file, element_info[element].move_delay_random);
1533 putFile16BitBE(file, element_info[element].move_pattern);
1534 putFile8Bit(file, element_info[element].move_direction_initial);
1535 putFile8Bit(file, element_info[element].move_stepsize);
1539 putFile16BitBE(file, element_info[element].content[x][y]);
1541 putFile32BitBE(file, element_info[element].change->events);
1543 putFile16BitBE(file, element_info[element].change->target_element);
1545 putFile16BitBE(file, element_info[element].change->delay_fixed);
1546 putFile16BitBE(file, element_info[element].change->delay_random);
1547 putFile16BitBE(file, element_info[element].change->delay_frames);
1549 putFile16BitBE(file, element_info[element].change->trigger_element);
1551 putFile8Bit(file, element_info[element].change->explode);
1552 putFile8Bit(file, element_info[element].change->use_content);
1553 putFile8Bit(file, element_info[element].change->only_complete);
1554 putFile8Bit(file, element_info[element].change->use_random_change);
1556 putFile8Bit(file, element_info[element].change->random);
1557 putFile8Bit(file, element_info[element].change->power);
1561 putFile16BitBE(file, element_info[element].change->content[x][y]);
1563 putFile8Bit(file, element_info[element].slippery_type);
1565 /* some free bytes for future properties and padding */
1566 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1573 if (check != num_changed_custom_elements) /* should not happen */
1574 Error(ERR_WARN, "inconsistent number of custom element properties");
1578 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1580 struct ElementInfo *ei = &element_info[element];
1583 putFile16BitBE(file, element);
1585 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
1586 putFile8Bit(file, ei->description[i]);
1588 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1589 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
1591 putFile8Bit(file, ei->num_change_pages);
1593 /* some free bytes for future base property values and padding */
1594 WriteUnusedBytesToFile(file, 5);
1596 /* write custom property values */
1598 putFile8Bit(file, ei->use_gfx_element);
1599 putFile16BitBE(file, ei->gfx_element);
1601 putFile8Bit(file, ei->collect_score);
1602 putFile8Bit(file, ei->collect_count);
1604 putFile16BitBE(file, ei->push_delay_fixed);
1605 putFile16BitBE(file, ei->push_delay_random);
1606 putFile16BitBE(file, ei->move_delay_fixed);
1607 putFile16BitBE(file, ei->move_delay_random);
1609 putFile16BitBE(file, ei->move_pattern);
1610 putFile8Bit(file, ei->move_direction_initial);
1611 putFile8Bit(file, ei->move_stepsize);
1613 putFile8Bit(file, ei->slippery_type);
1617 putFile16BitBE(file, ei->content[x][y]);
1619 /* some free bytes for future custom property values and padding */
1620 WriteUnusedBytesToFile(file, 12);
1622 /* write change property values */
1624 for (i=0; i < ei->num_change_pages; i++)
1626 struct ElementChangeInfo *change = &ei->change_page[i];
1628 putFile32BitBE(file, change->events);
1630 putFile16BitBE(file, change->target_element);
1632 putFile16BitBE(file, change->delay_fixed);
1633 putFile16BitBE(file, change->delay_random);
1634 putFile16BitBE(file, change->delay_frames);
1636 putFile16BitBE(file, change->trigger_element);
1638 putFile8Bit(file, change->explode);
1639 putFile8Bit(file, change->use_content);
1640 putFile8Bit(file, change->only_complete);
1641 putFile8Bit(file, change->use_random_change);
1643 putFile8Bit(file, change->random);
1644 putFile8Bit(file, change->power);
1648 putFile16BitBE(file, change->content[x][y]);
1650 putFile8Bit(file, change->can_change);
1652 /* some free bytes for future change property values and padding */
1653 WriteUnusedBytesToFile(file, 9);
1657 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1659 int body_chunk_size;
1663 if (!(file = fopen(filename, MODE_WRITE)))
1665 Error(ERR_WARN, "cannot save level file '%s'", filename);
1669 level->file_version = FILE_VERSION_ACTUAL;
1670 level->game_version = GAME_VERSION_ACTUAL;
1672 /* check level field for 16-bit elements */
1673 level->encoding_16bit_field = FALSE;
1674 for(y=0; y<level->fieldy; y++)
1675 for(x=0; x<level->fieldx; x++)
1676 if (level->field[x][y] > 255)
1677 level->encoding_16bit_field = TRUE;
1679 /* check yamyam content for 16-bit elements */
1680 level->encoding_16bit_yamyam = FALSE;
1681 for(i=0; i<level->num_yamyam_contents; i++)
1684 if (level->yamyam_content[i][x][y] > 255)
1685 level->encoding_16bit_yamyam = TRUE;
1687 /* check amoeba content for 16-bit elements */
1688 level->encoding_16bit_amoeba = FALSE;
1689 if (level->amoeba_content > 255)
1690 level->encoding_16bit_amoeba = TRUE;
1692 /* calculate size of "BODY" chunk */
1694 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1696 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1697 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1699 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1700 SaveLevel_VERS(file, level);
1702 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1703 SaveLevel_HEAD(file, level);
1705 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1706 SaveLevel_AUTH(file, level);
1708 putFileChunkBE(file, "BODY", body_chunk_size);
1709 SaveLevel_BODY(file, level);
1711 if (level->encoding_16bit_yamyam ||
1712 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1714 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1715 SaveLevel_CNT2(file, level, EL_YAMYAM);
1718 if (level->encoding_16bit_amoeba)
1720 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1721 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1724 /* check for envelope content */
1727 if (strlen(level->envelope_text[i]) > 0)
1729 int envelope_len = strlen(level->envelope_text[i]) + 1;
1731 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
1732 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
1736 /* check for non-default custom elements (unless using template level) */
1737 if (!level->use_custom_template)
1739 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1741 int element = EL_CUSTOM_START + i;
1743 if (element_info[element].modified_settings)
1745 int num_change_pages = element_info[element].num_change_pages;
1747 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
1748 SaveLevel_CUS4(file, level, element);
1755 SetFilePermissions(filename, PERMS_PRIVATE);
1758 void SaveLevel(int level_nr)
1760 char *filename = getLevelFilename(level_nr);
1762 SaveLevelFromFilename(&level, filename);
1765 void SaveLevelTemplate()
1767 char *filename = getLevelFilename(-1);
1769 SaveLevelFromFilename(&level, filename);
1772 void DumpLevel(struct LevelInfo *level)
1774 printf_line("-", 79);
1775 printf("Level xxx (file version %08d, game version %08d)\n",
1776 level->file_version, level->game_version);
1777 printf_line("-", 79);
1779 printf("Level Author: '%s'\n", level->author);
1780 printf("Level Title: '%s'\n", level->name);
1782 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1784 printf("Level Time: %d seconds\n", level->time);
1785 printf("Gems needed: %d\n", level->gems_needed);
1787 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1788 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1789 printf("Time for Light: %d seconds\n", level->time_light);
1790 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1792 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1794 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1795 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1796 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1798 printf_line("-", 79);
1802 /* ========================================================================= */
1803 /* tape file functions */
1804 /* ========================================================================= */
1806 static void setTapeInfoToDefaults()
1810 /* always start with reliable default values (empty tape) */
1813 /* default values (also for pre-1.2 tapes) with only the first player */
1814 tape.player_participates[0] = TRUE;
1815 for(i=1; i<MAX_PLAYERS; i++)
1816 tape.player_participates[i] = FALSE;
1818 /* at least one (default: the first) player participates in every tape */
1819 tape.num_participating_players = 1;
1821 tape.level_nr = level_nr;
1823 tape.changed = FALSE;
1825 tape.recording = FALSE;
1826 tape.playing = FALSE;
1827 tape.pausing = FALSE;
1830 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1832 tape->file_version = getFileVersion(file);
1833 tape->game_version = getFileVersion(file);
1838 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1842 tape->random_seed = getFile32BitBE(file);
1843 tape->date = getFile32BitBE(file);
1844 tape->length = getFile32BitBE(file);
1846 /* read header fields that are new since version 1.2 */
1847 if (tape->file_version >= FILE_VERSION_1_2)
1849 byte store_participating_players = getFile8Bit(file);
1852 /* since version 1.2, tapes store which players participate in the tape */
1853 tape->num_participating_players = 0;
1854 for(i=0; i<MAX_PLAYERS; i++)
1856 tape->player_participates[i] = FALSE;
1858 if (store_participating_players & (1 << i))
1860 tape->player_participates[i] = TRUE;
1861 tape->num_participating_players++;
1865 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1867 engine_version = getFileVersion(file);
1868 if (engine_version > 0)
1869 tape->engine_version = engine_version;
1871 tape->engine_version = tape->game_version;
1877 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1879 int level_identifier_size;
1882 level_identifier_size = getFile16BitBE(file);
1884 tape->level_identifier =
1885 checked_realloc(tape->level_identifier, level_identifier_size);
1887 for(i=0; i < level_identifier_size; i++)
1888 tape->level_identifier[i] = getFile8Bit(file);
1890 tape->level_nr = getFile16BitBE(file);
1892 chunk_size = 2 + level_identifier_size + 2;
1897 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1900 int chunk_size_expected =
1901 (tape->num_participating_players + 1) * tape->length;
1903 if (chunk_size_expected != chunk_size)
1905 ReadUnusedBytesFromFile(file, chunk_size);
1906 return chunk_size_expected;
1909 for(i=0; i<tape->length; i++)
1911 if (i >= MAX_TAPELEN)
1914 for(j=0; j<MAX_PLAYERS; j++)
1916 tape->pos[i].action[j] = MV_NO_MOVING;
1918 if (tape->player_participates[j])
1919 tape->pos[i].action[j] = getFile8Bit(file);
1922 tape->pos[i].delay = getFile8Bit(file);
1924 if (tape->file_version == FILE_VERSION_1_0)
1926 /* eliminate possible diagonal moves in old tapes */
1927 /* this is only for backward compatibility */
1929 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1930 byte action = tape->pos[i].action[0];
1931 int k, num_moves = 0;
1935 if (action & joy_dir[k])
1937 tape->pos[i + num_moves].action[0] = joy_dir[k];
1939 tape->pos[i + num_moves].delay = 0;
1948 tape->length += num_moves;
1951 else if (tape->file_version < FILE_VERSION_2_0)
1953 /* convert pre-2.0 tapes to new tape format */
1955 if (tape->pos[i].delay > 1)
1958 tape->pos[i + 1] = tape->pos[i];
1959 tape->pos[i + 1].delay = 1;
1962 for(j=0; j<MAX_PLAYERS; j++)
1963 tape->pos[i].action[j] = MV_NO_MOVING;
1964 tape->pos[i].delay--;
1975 if (i != tape->length)
1976 chunk_size = (tape->num_participating_players + 1) * i;
1981 void LoadTapeFromFilename(char *filename)
1983 char cookie[MAX_LINE_LEN];
1984 char chunk_name[CHUNK_ID_LEN + 1];
1988 /* always start with reliable default values */
1989 setTapeInfoToDefaults();
1991 if (!(file = fopen(filename, MODE_READ)))
1994 getFileChunkBE(file, chunk_name, NULL);
1995 if (strcmp(chunk_name, "RND1") == 0)
1997 getFile32BitBE(file); /* not used */
1999 getFileChunkBE(file, chunk_name, NULL);
2000 if (strcmp(chunk_name, "TAPE") != 0)
2002 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2007 else /* check for pre-2.0 file format with cookie string */
2009 strcpy(cookie, chunk_name);
2010 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
2011 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2012 cookie[strlen(cookie) - 1] = '\0';
2014 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
2016 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2021 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
2023 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
2028 /* pre-2.0 tape files have no game version, so use file version here */
2029 tape.game_version = tape.file_version;
2032 if (tape.file_version < FILE_VERSION_1_2)
2034 /* tape files from versions before 1.2.0 without chunk structure */
2035 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
2036 LoadTape_BODY(file, 2 * tape.length, &tape);
2044 int (*loader)(FILE *, int, struct TapeInfo *);
2048 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
2049 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
2050 { "INFO", -1, LoadTape_INFO },
2051 { "BODY", -1, LoadTape_BODY },
2055 while (getFileChunkBE(file, chunk_name, &chunk_size))
2059 while (chunk_info[i].name != NULL &&
2060 strcmp(chunk_name, chunk_info[i].name) != 0)
2063 if (chunk_info[i].name == NULL)
2065 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
2066 chunk_name, filename);
2067 ReadUnusedBytesFromFile(file, chunk_size);
2069 else if (chunk_info[i].size != -1 &&
2070 chunk_info[i].size != chunk_size)
2072 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2073 chunk_size, chunk_name, filename);
2074 ReadUnusedBytesFromFile(file, chunk_size);
2078 /* call function to load this tape chunk */
2079 int chunk_size_expected =
2080 (chunk_info[i].loader)(file, chunk_size, &tape);
2082 /* the size of some chunks cannot be checked before reading other
2083 chunks first (like "HEAD" and "BODY") that contain some header
2084 information, so check them here */
2085 if (chunk_size_expected != chunk_size)
2087 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2088 chunk_size, chunk_name, filename);
2096 tape.length_seconds = GetTapeLength();
2099 printf("tape game version: %d\n", tape.game_version);
2100 printf("tape engine version: %d\n", tape.engine_version);
2104 void LoadTape(int level_nr)
2106 char *filename = getTapeFilename(level_nr);
2108 LoadTapeFromFilename(filename);
2111 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2113 putFileVersion(file, tape->file_version);
2114 putFileVersion(file, tape->game_version);
2117 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2120 byte store_participating_players = 0;
2122 /* set bits for participating players for compact storage */
2123 for(i=0; i<MAX_PLAYERS; i++)
2124 if (tape->player_participates[i])
2125 store_participating_players |= (1 << i);
2127 putFile32BitBE(file, tape->random_seed);
2128 putFile32BitBE(file, tape->date);
2129 putFile32BitBE(file, tape->length);
2131 putFile8Bit(file, store_participating_players);
2133 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2134 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2136 putFileVersion(file, tape->engine_version);
2139 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2141 int level_identifier_size = strlen(tape->level_identifier) + 1;
2144 putFile16BitBE(file, level_identifier_size);
2146 for(i=0; i < level_identifier_size; i++)
2147 putFile8Bit(file, tape->level_identifier[i]);
2149 putFile16BitBE(file, tape->level_nr);
2152 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2156 for(i=0; i<tape->length; i++)
2158 for(j=0; j<MAX_PLAYERS; j++)
2159 if (tape->player_participates[j])
2160 putFile8Bit(file, tape->pos[i].action[j]);
2162 putFile8Bit(file, tape->pos[i].delay);
2166 void SaveTape(int level_nr)
2168 char *filename = getTapeFilename(level_nr);
2170 boolean new_tape = TRUE;
2171 int num_participating_players = 0;
2172 int info_chunk_size;
2173 int body_chunk_size;
2176 InitTapeDirectory(leveldir_current->filename);
2178 /* if a tape still exists, ask to overwrite it */
2179 if (access(filename, F_OK) == 0)
2182 if (!Request("Replace old tape ?", REQ_ASK))
2186 if (!(file = fopen(filename, MODE_WRITE)))
2188 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2192 tape.file_version = FILE_VERSION_ACTUAL;
2193 tape.game_version = GAME_VERSION_ACTUAL;
2195 /* count number of participating players */
2196 for(i=0; i<MAX_PLAYERS; i++)
2197 if (tape.player_participates[i])
2198 num_participating_players++;
2200 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2201 body_chunk_size = (num_participating_players + 1) * tape.length;
2203 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2204 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2206 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2207 SaveTape_VERS(file, &tape);
2209 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2210 SaveTape_HEAD(file, &tape);
2212 putFileChunkBE(file, "INFO", info_chunk_size);
2213 SaveTape_INFO(file, &tape);
2215 putFileChunkBE(file, "BODY", body_chunk_size);
2216 SaveTape_BODY(file, &tape);
2220 SetFilePermissions(filename, PERMS_PRIVATE);
2222 tape.changed = FALSE;
2225 Request("tape saved !", REQ_CONFIRM);
2228 void DumpTape(struct TapeInfo *tape)
2232 if (TAPE_IS_EMPTY(*tape))
2234 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2238 printf_line("-", 79);
2239 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2240 tape->level_nr, tape->file_version, tape->game_version);
2241 printf("Level series identifier: '%s'\n", tape->level_identifier);
2242 printf_line("-", 79);
2244 for(i=0; i<tape->length; i++)
2246 if (i >= MAX_TAPELEN)
2249 printf("%03d: ", i);
2251 for(j=0; j<MAX_PLAYERS; j++)
2253 if (tape->player_participates[j])
2255 int action = tape->pos[i].action[j];
2257 printf("%d:%02x ", j, action);
2258 printf("[%c%c%c%c|%c%c] - ",
2259 (action & JOY_LEFT ? '<' : ' '),
2260 (action & JOY_RIGHT ? '>' : ' '),
2261 (action & JOY_UP ? '^' : ' '),
2262 (action & JOY_DOWN ? 'v' : ' '),
2263 (action & JOY_BUTTON_1 ? '1' : ' '),
2264 (action & JOY_BUTTON_2 ? '2' : ' '));
2268 printf("(%03d)\n", tape->pos[i].delay);
2271 printf_line("-", 79);
2275 /* ========================================================================= */
2276 /* score file functions */
2277 /* ========================================================================= */
2279 void LoadScore(int level_nr)
2282 char *filename = getScoreFilename(level_nr);
2283 char cookie[MAX_LINE_LEN];
2284 char line[MAX_LINE_LEN];
2288 /* always start with reliable default values */
2289 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2291 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2292 highscore[i].Score = 0;
2295 if (!(file = fopen(filename, MODE_READ)))
2298 /* check file identifier */
2299 fgets(cookie, MAX_LINE_LEN, file);
2300 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2301 cookie[strlen(cookie) - 1] = '\0';
2303 if (!checkCookieString(cookie, SCORE_COOKIE))
2305 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2310 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2312 fscanf(file, "%d", &highscore[i].Score);
2313 fgets(line, MAX_LINE_LEN, file);
2315 if (line[strlen(line) - 1] == '\n')
2316 line[strlen(line) - 1] = '\0';
2318 for (line_ptr = line; *line_ptr; line_ptr++)
2320 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2322 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2323 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2332 void SaveScore(int level_nr)
2335 char *filename = getScoreFilename(level_nr);
2338 InitScoreDirectory(leveldir_current->filename);
2340 if (!(file = fopen(filename, MODE_WRITE)))
2342 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2346 fprintf(file, "%s\n\n", SCORE_COOKIE);
2348 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2349 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2353 SetFilePermissions(filename, PERMS_PUBLIC);
2357 /* ========================================================================= */
2358 /* setup file functions */
2359 /* ========================================================================= */
2361 #define TOKEN_STR_PLAYER_PREFIX "player_"
2364 #define SETUP_TOKEN_PLAYER_NAME 0
2365 #define SETUP_TOKEN_SOUND 1
2366 #define SETUP_TOKEN_SOUND_LOOPS 2
2367 #define SETUP_TOKEN_SOUND_MUSIC 3
2368 #define SETUP_TOKEN_SOUND_SIMPLE 4
2369 #define SETUP_TOKEN_TOONS 5
2370 #define SETUP_TOKEN_SCROLL_DELAY 6
2371 #define SETUP_TOKEN_SOFT_SCROLLING 7
2372 #define SETUP_TOKEN_FADING 8
2373 #define SETUP_TOKEN_AUTORECORD 9
2374 #define SETUP_TOKEN_QUICK_DOORS 10
2375 #define SETUP_TOKEN_TEAM_MODE 11
2376 #define SETUP_TOKEN_HANDICAP 12
2377 #define SETUP_TOKEN_TIME_LIMIT 13
2378 #define SETUP_TOKEN_FULLSCREEN 14
2379 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2380 #define SETUP_TOKEN_GRAPHICS_SET 16
2381 #define SETUP_TOKEN_SOUNDS_SET 17
2382 #define SETUP_TOKEN_MUSIC_SET 18
2383 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2384 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2385 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2387 #define NUM_GLOBAL_SETUP_TOKENS 22
2390 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2391 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2392 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2393 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2394 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2395 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2396 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2397 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2398 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2399 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2400 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2402 #define NUM_EDITOR_SETUP_TOKENS 11
2404 /* shortcut setup */
2405 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2406 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2407 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2409 #define NUM_SHORTCUT_SETUP_TOKENS 3
2412 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2413 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2414 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2415 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2416 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2417 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2418 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2419 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2420 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2421 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
2422 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2423 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2424 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2425 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2426 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2427 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
2429 #define NUM_PLAYER_SETUP_TOKENS 16
2432 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2433 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2435 #define NUM_SYSTEM_SETUP_TOKENS 2
2438 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2440 #define NUM_OPTIONS_SETUP_TOKENS 1
2443 static struct SetupInfo si;
2444 static struct SetupEditorInfo sei;
2445 static struct SetupShortcutInfo ssi;
2446 static struct SetupInputInfo sii;
2447 static struct SetupSystemInfo syi;
2448 static struct OptionInfo soi;
2450 static struct TokenInfo global_setup_tokens[] =
2452 { TYPE_STRING, &si.player_name, "player_name" },
2453 { TYPE_SWITCH, &si.sound, "sound" },
2454 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2455 { TYPE_SWITCH, &si.sound_music, "background_music" },
2456 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2457 { TYPE_SWITCH, &si.toons, "toons" },
2458 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2459 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2460 { TYPE_SWITCH, &si.fading, "screen_fading" },
2461 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2462 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2463 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2464 { TYPE_SWITCH, &si.handicap, "handicap" },
2465 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2466 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2467 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2468 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2469 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2470 { TYPE_STRING, &si.music_set, "music_set" },
2471 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2472 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2473 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2476 static struct TokenInfo editor_setup_tokens[] =
2478 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2479 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2480 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2481 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2482 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2483 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2484 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2485 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2486 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2487 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2488 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2491 static struct TokenInfo shortcut_setup_tokens[] =
2493 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2494 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2495 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2498 static struct TokenInfo player_setup_tokens[] =
2500 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2501 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2502 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2503 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2504 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2505 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2506 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2507 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2508 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2509 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2510 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2511 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2512 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2513 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2514 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2515 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
2518 static struct TokenInfo system_setup_tokens[] =
2520 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2521 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2524 static struct TokenInfo options_setup_tokens[] =
2526 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2529 static char *get_corrected_login_name(char *login_name)
2531 /* needed because player name must be a fixed length string */
2532 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2534 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2535 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2537 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2538 if (strchr(login_name_new, ' '))
2539 *strchr(login_name_new, ' ') = '\0';
2541 return login_name_new;
2544 static void setSetupInfoToDefaults(struct SetupInfo *si)
2548 si->player_name = get_corrected_login_name(getLoginName());
2551 si->sound_loops = TRUE;
2552 si->sound_music = TRUE;
2553 si->sound_simple = TRUE;
2555 si->double_buffering = TRUE;
2556 si->direct_draw = !si->double_buffering;
2557 si->scroll_delay = TRUE;
2558 si->soft_scrolling = TRUE;
2560 si->autorecord = TRUE;
2561 si->quick_doors = FALSE;
2562 si->team_mode = FALSE;
2563 si->handicap = TRUE;
2564 si->time_limit = TRUE;
2565 si->fullscreen = FALSE;
2566 si->ask_on_escape = TRUE;
2568 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2569 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2570 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2571 si->override_level_graphics = FALSE;
2572 si->override_level_sounds = FALSE;
2573 si->override_level_music = FALSE;
2575 si->editor.el_boulderdash = TRUE;
2576 si->editor.el_emerald_mine = TRUE;
2577 si->editor.el_more = TRUE;
2578 si->editor.el_sokoban = TRUE;
2579 si->editor.el_supaplex = TRUE;
2580 si->editor.el_diamond_caves = TRUE;
2581 si->editor.el_dx_boulderdash = TRUE;
2582 si->editor.el_chars = TRUE;
2583 si->editor.el_custom = TRUE;
2584 si->editor.el_custom_more = FALSE;
2586 si->editor.el_headlines = TRUE;
2588 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2589 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2590 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2592 for (i=0; i<MAX_PLAYERS; i++)
2594 si->input[i].use_joystick = FALSE;
2595 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2596 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2597 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2598 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2599 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2600 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2601 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2602 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2603 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2604 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2605 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2606 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2607 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2608 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2609 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2612 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2613 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2615 si->options.verbose = FALSE;
2618 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2622 if (!setup_file_hash)
2627 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2628 setSetupInfo(global_setup_tokens, i,
2629 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2634 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2635 setSetupInfo(editor_setup_tokens, i,
2636 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2639 /* shortcut setup */
2640 ssi = setup.shortcut;
2641 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2642 setSetupInfo(shortcut_setup_tokens, i,
2643 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2644 setup.shortcut = ssi;
2647 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2651 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2653 sii = setup.input[pnr];
2654 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2656 char full_token[100];
2658 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2659 setSetupInfo(player_setup_tokens, i,
2660 getHashEntry(setup_file_hash, full_token));
2662 setup.input[pnr] = sii;
2667 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2668 setSetupInfo(system_setup_tokens, i,
2669 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2673 soi = setup.options;
2674 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2675 setSetupInfo(options_setup_tokens, i,
2676 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2677 setup.options = soi;
2682 char *filename = getSetupFilename();
2683 SetupFileHash *setup_file_hash = NULL;
2685 /* always start with reliable default values */
2686 setSetupInfoToDefaults(&setup);
2688 setup_file_hash = loadSetupFileHash(filename);
2690 if (setup_file_hash)
2692 char *player_name_new;
2694 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2695 decodeSetupFileHash(setup_file_hash);
2697 setup.direct_draw = !setup.double_buffering;
2699 freeSetupFileHash(setup_file_hash);
2701 /* needed to work around problems with fixed length strings */
2702 player_name_new = get_corrected_login_name(setup.player_name);
2703 free(setup.player_name);
2704 setup.player_name = player_name_new;
2707 Error(ERR_WARN, "using default setup values");
2712 char *filename = getSetupFilename();
2716 InitUserDataDirectory();
2718 if (!(file = fopen(filename, MODE_WRITE)))
2720 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2724 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2725 getCookie("SETUP")));
2726 fprintf(file, "\n");
2730 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2732 /* just to make things nicer :) */
2733 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2734 i == SETUP_TOKEN_GRAPHICS_SET)
2735 fprintf(file, "\n");
2737 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2742 fprintf(file, "\n");
2743 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2744 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2746 /* shortcut setup */
2747 ssi = setup.shortcut;
2748 fprintf(file, "\n");
2749 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2750 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2753 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2757 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2758 fprintf(file, "\n");
2760 sii = setup.input[pnr];
2761 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2762 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2767 fprintf(file, "\n");
2768 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2769 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2772 soi = setup.options;
2773 fprintf(file, "\n");
2774 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2775 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2779 SetFilePermissions(filename, PERMS_PRIVATE);
2782 void LoadCustomElementDescriptions()
2784 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2785 SetupFileHash *setup_file_hash;
2788 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2790 if (element_info[i].custom_description != NULL)
2792 free(element_info[i].custom_description);
2793 element_info[i].custom_description = NULL;
2797 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2800 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2802 char *token = getStringCat2(element_info[i].token_name, ".name");
2803 char *value = getHashEntry(setup_file_hash, token);
2806 element_info[i].custom_description = getStringCopy(value);
2811 freeSetupFileHash(setup_file_hash);
2814 void LoadSpecialMenuDesignSettings()
2816 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2817 SetupFileHash *setup_file_hash;
2820 /* always start with reliable default values from default config */
2821 for (i=0; image_config_vars[i].token != NULL; i++)
2822 for (j=0; image_config[j].token != NULL; j++)
2823 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2824 *image_config_vars[i].value =
2825 get_auto_parameter_value(image_config_vars[i].token,
2826 image_config[j].value);
2828 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2831 /* special case: initialize with default values that may be overwritten */
2832 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2834 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2835 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2836 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2838 if (value_x != NULL)
2839 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2840 if (value_y != NULL)
2841 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2842 if (list_size != NULL)
2843 menu.list_size[i] = get_integer_from_string(list_size);
2846 /* read (and overwrite with) values that may be specified in config file */
2847 for (i=0; image_config_vars[i].token != NULL; i++)
2849 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2852 *image_config_vars[i].value =
2853 get_auto_parameter_value(image_config_vars[i].token, value);
2856 freeSetupFileHash(setup_file_hash);