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);
146 level->envelope[0] = '\0';
147 level->envelope_xsize = MAX_ENVELOPE_XSIZE;
148 level->envelope_ysize = MAX_ENVELOPE_YSIZE;
150 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
151 level->score[i] = 10;
153 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
154 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
157 level->yamyam_content[i][x][y] =
158 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
160 level->field[0][0] = EL_PLAYER_1;
161 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
163 for (i=0; i < MAX_NUM_ELEMENTS; i++)
165 setElementChangePages(&element_info[i], 1);
166 setElementChangeInfoToDefaults(element_info[i].change);
169 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
171 int element = EL_CUSTOM_START + i;
173 for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
174 element_info[element].description[j] = '\0';
175 if (element_info[element].custom_description != NULL)
176 strncpy(element_info[element].description,
177 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
179 strcpy(element_info[element].description,
180 element_info[element].editor_description);
182 element_info[element].use_gfx_element = FALSE;
183 element_info[element].gfx_element = EL_EMPTY_SPACE;
185 element_info[element].collect_score = 10; /* special default */
186 element_info[element].collect_count = 1; /* special default */
188 element_info[element].push_delay_fixed = 2; /* special default */
189 element_info[element].push_delay_random = 8; /* special default */
190 element_info[element].move_delay_fixed = 0;
191 element_info[element].move_delay_random = 0;
193 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
194 element_info[element].move_direction_initial = MV_NO_MOVING;
195 element_info[element].move_stepsize = TILEX / 8;
197 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
201 element_info[element].content[x][y] = EL_EMPTY_SPACE;
203 element_info[element].access_type = 0;
204 element_info[element].access_layer = 0;
205 element_info[element].walk_to_action = 0;
206 element_info[element].smash_targets = 0;
207 element_info[element].deadliness = 0;
208 element_info[element].consistency = 0;
210 element_info[element].can_explode_by_fire = FALSE;
211 element_info[element].can_explode_smashed = FALSE;
212 element_info[element].can_explode_impact = FALSE;
214 element_info[element].current_change_page = 0;
216 /* start with no properties at all */
217 for (j=0; j < NUM_EP_BITFIELDS; j++)
218 Properties[element][j] = EP_BITMASK_DEFAULT;
220 element_info[element].modified_settings = FALSE;
223 BorderElement = EL_STEELWALL;
225 level->no_level_file = FALSE;
227 if (leveldir_current == NULL) /* only when dumping level */
230 /* try to determine better author name than 'anonymous' */
231 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
233 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
234 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
238 switch (LEVELCLASS(leveldir_current))
240 case LEVELCLASS_TUTORIAL:
241 strcpy(level->author, PROGRAM_AUTHOR_STRING);
244 case LEVELCLASS_CONTRIBUTION:
245 strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
246 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
249 case LEVELCLASS_USER:
250 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
251 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
255 /* keep default value */
261 static void ActivateLevelTemplate()
263 /* Currently there is no special action needed to activate the template
264 data, because 'element_info' and 'Properties' overwrite the original
265 level data, while all other variables do not change. */
268 boolean LevelFileExists(int level_nr)
270 char *filename = getLevelFilename(level_nr);
272 return (access(filename, F_OK) == 0);
275 static int checkLevelElement(int element)
277 if (element >= NUM_FILE_ELEMENTS)
279 Error(ERR_WARN, "invalid level element %d", element);
280 element = EL_CHAR_QUESTION;
282 else if (element == EL_PLAYER_OBSOLETE)
283 element = EL_PLAYER_1;
284 else if (element == EL_KEY_OBSOLETE)
290 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
292 level->file_version = getFileVersion(file);
293 level->game_version = getFileVersion(file);
298 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
302 level->fieldx = getFile8Bit(file);
303 level->fieldy = getFile8Bit(file);
305 level->time = getFile16BitBE(file);
306 level->gems_needed = getFile16BitBE(file);
308 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
309 level->name[i] = getFile8Bit(file);
310 level->name[MAX_LEVEL_NAME_LEN] = 0;
312 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
313 level->score[i] = getFile8Bit(file);
315 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
316 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
319 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
321 level->amoeba_speed = getFile8Bit(file);
322 level->time_magic_wall = getFile8Bit(file);
323 level->time_wheel = getFile8Bit(file);
324 level->amoeba_content = checkLevelElement(getFile8Bit(file));
325 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
326 level->gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
327 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
328 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
330 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
332 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
337 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
341 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
342 level->author[i] = getFile8Bit(file);
343 level->author[MAX_LEVEL_NAME_LEN] = 0;
348 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
351 int chunk_size_expected = level->fieldx * level->fieldy;
353 /* Note: "chunk_size" was wrong before version 2.0 when elements are
354 stored with 16-bit encoding (and should be twice as big then).
355 Even worse, playfield data was stored 16-bit when only yamyam content
356 contained 16-bit elements and vice versa. */
358 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
359 chunk_size_expected *= 2;
361 if (chunk_size_expected != chunk_size)
363 ReadUnusedBytesFromFile(file, chunk_size);
364 return chunk_size_expected;
367 for(y=0; y<level->fieldy; y++)
368 for(x=0; x<level->fieldx; x++)
370 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
375 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
379 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
380 int chunk_size_expected = header_size + content_size;
382 /* Note: "chunk_size" was wrong before version 2.0 when elements are
383 stored with 16-bit encoding (and should be twice as big then).
384 Even worse, playfield data was stored 16-bit when only yamyam content
385 contained 16-bit elements and vice versa. */
387 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
388 chunk_size_expected += content_size;
390 if (chunk_size_expected != chunk_size)
392 ReadUnusedBytesFromFile(file, chunk_size);
393 return chunk_size_expected;
397 level->num_yamyam_contents = getFile8Bit(file);
401 /* correct invalid number of content fields -- should never happen */
402 if (level->num_yamyam_contents < 1 ||
403 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
404 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
406 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
409 level->yamyam_content[i][x][y] =
410 checkLevelElement(level->encoding_16bit_field ?
411 getFile16BitBE(file) : getFile8Bit(file));
415 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
419 int num_contents, content_xsize, content_ysize;
420 int content_array[MAX_ELEMENT_CONTENTS][3][3];
422 element = checkLevelElement(getFile16BitBE(file));
423 num_contents = getFile8Bit(file);
424 content_xsize = getFile8Bit(file);
425 content_ysize = getFile8Bit(file);
427 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
429 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
432 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
434 /* correct invalid number of content fields -- should never happen */
435 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
436 num_contents = STD_ELEMENT_CONTENTS;
438 if (element == EL_YAMYAM)
440 level->num_yamyam_contents = num_contents;
442 for(i=0; i<num_contents; i++)
445 level->yamyam_content[i][x][y] = content_array[i][x][y];
447 else if (element == EL_BD_AMOEBA)
449 level->amoeba_content = content_array[0][0][0];
453 Error(ERR_WARN, "cannot load content for element '%d'", element);
459 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
464 int chunk_size_expected;
466 element = checkLevelElement(getFile16BitBE(file));
467 envelope_len = getFile16BitBE(file);
468 level->envelope_xsize = getFile8Bit(file);
469 level->envelope_ysize = getFile8Bit(file);
471 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
473 chunk_size_expected = LEVEL_CHUNK_CNT3_HEADER + envelope_len;
475 if (chunk_size_expected != chunk_size)
477 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
478 return chunk_size_expected;
481 for(i=0; i < envelope_len; i++)
482 level->envelope[i] = getFile8Bit(file);
487 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
489 int num_changed_custom_elements = getFile16BitBE(file);
490 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
493 if (chunk_size_expected != chunk_size)
495 ReadUnusedBytesFromFile(file, chunk_size - 2);
496 return chunk_size_expected;
499 for (i=0; i < num_changed_custom_elements; i++)
501 int element = getFile16BitBE(file);
502 int properties = getFile32BitBE(file);
504 if (IS_CUSTOM_ELEMENT(element))
505 Properties[element][EP_BITFIELD_BASE] = properties;
507 Error(ERR_WARN, "invalid custom element number %d", element);
513 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
515 int num_changed_custom_elements = getFile16BitBE(file);
516 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
519 if (chunk_size_expected != chunk_size)
521 ReadUnusedBytesFromFile(file, chunk_size - 2);
522 return chunk_size_expected;
525 for (i=0; i < num_changed_custom_elements; i++)
527 int element = getFile16BitBE(file);
528 int custom_target_element = getFile16BitBE(file);
530 if (IS_CUSTOM_ELEMENT(element))
531 element_info[element].change->target_element = custom_target_element;
533 Error(ERR_WARN, "invalid custom element number %d", element);
539 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
541 int num_changed_custom_elements = getFile16BitBE(file);
542 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
545 if (chunk_size_expected != chunk_size)
547 ReadUnusedBytesFromFile(file, chunk_size - 2);
548 return chunk_size_expected;
551 for (i=0; i < num_changed_custom_elements; i++)
553 int element = getFile16BitBE(file);
555 if (!IS_CUSTOM_ELEMENT(element))
557 Error(ERR_WARN, "invalid custom element number %d", element);
559 element = EL_DEFAULT; /* dummy element used for artwork config */
562 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
563 element_info[element].description[j] = getFile8Bit(file);
564 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
566 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
568 /* some free bytes for future properties and padding */
569 ReadUnusedBytesFromFile(file, 7);
571 element_info[element].use_gfx_element = getFile8Bit(file);
572 element_info[element].gfx_element =
573 checkLevelElement(getFile16BitBE(file));
575 element_info[element].collect_score = getFile8Bit(file);
576 element_info[element].collect_count = getFile8Bit(file);
578 element_info[element].push_delay_fixed = getFile16BitBE(file);
579 element_info[element].push_delay_random = getFile16BitBE(file);
580 element_info[element].move_delay_fixed = getFile16BitBE(file);
581 element_info[element].move_delay_random = getFile16BitBE(file);
583 element_info[element].move_pattern = getFile16BitBE(file);
584 element_info[element].move_direction_initial = getFile8Bit(file);
585 element_info[element].move_stepsize = getFile8Bit(file);
589 element_info[element].content[x][y] =
590 checkLevelElement(getFile16BitBE(file));
592 element_info[element].change->events = getFile32BitBE(file);
594 element_info[element].change->target_element =
595 checkLevelElement(getFile16BitBE(file));
597 element_info[element].change->delay_fixed = getFile16BitBE(file);
598 element_info[element].change->delay_random = getFile16BitBE(file);
599 element_info[element].change->delay_frames = getFile16BitBE(file);
601 element_info[element].change->trigger_element =
602 checkLevelElement(getFile16BitBE(file));
604 element_info[element].change->explode = getFile8Bit(file);
605 element_info[element].change->use_content = getFile8Bit(file);
606 element_info[element].change->only_complete = getFile8Bit(file);
607 element_info[element].change->use_random_change = getFile8Bit(file);
609 element_info[element].change->random = getFile8Bit(file);
610 element_info[element].change->power = getFile8Bit(file);
614 element_info[element].change->content[x][y] =
615 checkLevelElement(getFile16BitBE(file));
617 element_info[element].slippery_type = getFile8Bit(file);
619 /* some free bytes for future properties and padding */
620 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
622 /* mark that this custom element has been modified */
623 element_info[element].modified_settings = TRUE;
629 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
631 struct ElementInfo *ei;
632 int chunk_size_expected;
636 element = getFile16BitBE(file);
638 if (!IS_CUSTOM_ELEMENT(element))
640 Error(ERR_WARN, "invalid custom element number %d", element);
642 element = EL_DEFAULT; /* dummy element used for artwork config */
645 ei = &element_info[element];
647 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
648 ei->description[i] = getFile8Bit(file);
649 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
651 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
652 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
654 ei->num_change_pages = getFile8Bit(file);
656 /* some free bytes for future base property values and padding */
657 ReadUnusedBytesFromFile(file, 5);
659 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
660 if (chunk_size_expected != chunk_size)
662 ReadUnusedBytesFromFile(file, chunk_size - 48);
663 return chunk_size_expected;
666 /* read custom property values */
668 ei->use_gfx_element = getFile8Bit(file);
669 ei->gfx_element = checkLevelElement(getFile16BitBE(file));
671 ei->collect_score = getFile8Bit(file);
672 ei->collect_count = getFile8Bit(file);
674 ei->push_delay_fixed = getFile16BitBE(file);
675 ei->push_delay_random = getFile16BitBE(file);
676 ei->move_delay_fixed = getFile16BitBE(file);
677 ei->move_delay_random = getFile16BitBE(file);
679 ei->move_pattern = getFile16BitBE(file);
680 ei->move_direction_initial = getFile8Bit(file);
681 ei->move_stepsize = getFile8Bit(file);
683 ei->slippery_type = getFile8Bit(file);
687 ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
689 /* some free bytes for future custom property values and padding */
690 ReadUnusedBytesFromFile(file, 12);
692 /* read change property values */
694 setElementChangePages(ei, ei->num_change_pages);
696 for (i=0; i < ei->num_change_pages; i++)
698 struct ElementChangeInfo *change = &ei->change_page[i];
700 /* always start with reliable default values */
701 setElementChangeInfoToDefaults(change);
703 change->events = getFile32BitBE(file);
705 change->target_element = checkLevelElement(getFile16BitBE(file));
707 change->delay_fixed = getFile16BitBE(file);
708 change->delay_random = getFile16BitBE(file);
709 change->delay_frames = getFile16BitBE(file);
711 change->trigger_element = checkLevelElement(getFile16BitBE(file));
713 change->explode = getFile8Bit(file);
714 change->use_content = getFile8Bit(file);
715 change->only_complete = getFile8Bit(file);
716 change->use_random_change = getFile8Bit(file);
718 change->random = getFile8Bit(file);
719 change->power = getFile8Bit(file);
723 change->content[x][y] = checkLevelElement(getFile16BitBE(file));
725 change->can_change = getFile8Bit(file);
727 /* some free bytes for future change property values and padding */
728 ReadUnusedBytesFromFile(file, 9);
731 /* mark this custom element as modified */
732 ei->modified_settings = TRUE;
737 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
739 char cookie[MAX_LINE_LEN];
740 char chunk_name[CHUNK_ID_LEN + 1];
744 /* always start with reliable default values */
745 setLevelInfoToDefaults(level);
747 if (!(file = fopen(filename, MODE_READ)))
749 level->no_level_file = TRUE;
751 if (level != &level_template)
752 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
757 getFileChunkBE(file, chunk_name, NULL);
758 if (strcmp(chunk_name, "RND1") == 0)
760 getFile32BitBE(file); /* not used */
762 getFileChunkBE(file, chunk_name, NULL);
763 if (strcmp(chunk_name, "CAVE") != 0)
765 Error(ERR_WARN, "unknown format of level file '%s'", filename);
770 else /* check for pre-2.0 file format with cookie string */
772 strcpy(cookie, chunk_name);
773 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
774 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
775 cookie[strlen(cookie) - 1] = '\0';
777 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
779 Error(ERR_WARN, "unknown format of level file '%s'", filename);
784 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
786 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
791 /* pre-2.0 level files have no game version, so use file version here */
792 level->game_version = level->file_version;
795 if (level->file_version < FILE_VERSION_1_2)
797 /* level files from versions before 1.2.0 without chunk structure */
798 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
799 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
807 int (*loader)(FILE *, int, struct LevelInfo *);
811 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
812 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
813 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
814 { "BODY", -1, LoadLevel_BODY },
815 { "CONT", -1, LoadLevel_CONT },
816 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
817 { "CNT3", -1, LoadLevel_CNT3 },
818 { "CUS1", -1, LoadLevel_CUS1 },
819 { "CUS2", -1, LoadLevel_CUS2 },
820 { "CUS3", -1, LoadLevel_CUS3 },
821 { "CUS4", -1, LoadLevel_CUS4 },
825 while (getFileChunkBE(file, chunk_name, &chunk_size))
829 while (chunk_info[i].name != NULL &&
830 strcmp(chunk_name, chunk_info[i].name) != 0)
833 if (chunk_info[i].name == NULL)
835 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
836 chunk_name, filename);
837 ReadUnusedBytesFromFile(file, chunk_size);
839 else if (chunk_info[i].size != -1 &&
840 chunk_info[i].size != chunk_size)
842 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
843 chunk_size, chunk_name, filename);
844 ReadUnusedBytesFromFile(file, chunk_size);
848 /* call function to load this level chunk */
849 int chunk_size_expected =
850 (chunk_info[i].loader)(file, chunk_size, level);
852 /* the size of some chunks cannot be checked before reading other
853 chunks first (like "HEAD" and "BODY") that contain some header
854 information, so check them here */
855 if (chunk_size_expected != chunk_size)
857 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
858 chunk_size, chunk_name, filename);
869 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
871 if (leveldir_current == NULL) /* only when dumping level */
874 /* determine correct game engine version of current level */
875 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
876 IS_LEVELCLASS_USER(leveldir_current))
879 printf("\n::: This level is private or contributed: '%s'\n", filename);
882 /* For user contributed and private levels, use the version of
883 the game engine the levels were created for.
884 Since 2.0.1, the game engine version is now directly stored
885 in the level file (chunk "VERS"), so there is no need anymore
886 to set the game version from the file version (except for old,
887 pre-2.0 levels, where the game version is still taken from the
888 file format version used to store the level -- see above). */
890 /* do some special adjustments to support older level versions */
891 if (level->file_version == FILE_VERSION_1_0)
893 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
894 Error(ERR_WARN, "using high speed movement for player");
896 /* player was faster than monsters in (pre-)1.0 levels */
897 level->double_speed = TRUE;
900 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
901 if (level->game_version == VERSION_IDENT(2,0,1))
902 level->em_slippery_gems = TRUE;
907 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
908 leveldir_current->sort_priority, filename);
911 /* Always use the latest version of the game engine for all but
912 user contributed and private levels; this allows for actual
913 corrections in the game engine to take effect for existing,
914 converted levels (from "classic" or other existing games) to
915 make the game emulation more accurate, while (hopefully) not
916 breaking existing levels created from other players. */
918 level->game_version = GAME_VERSION_ACTUAL;
920 /* Set special EM style gems behaviour: EM style gems slip down from
921 normal, steel and growing wall. As this is a more fundamental change,
922 it seems better to set the default behaviour to "off" (as it is more
923 natural) and make it configurable in the level editor (as a property
924 of gem style elements). Already existing converted levels (neither
925 private nor contributed levels) are changed to the new behaviour. */
927 if (level->file_version < FILE_VERSION_2_0)
928 level->em_slippery_gems = TRUE;
932 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
936 /* map custom element change events that have changed in newer versions
937 (these following values were accidentally changed in version 3.0.1) */
938 if (level->game_version <= VERSION_IDENT(3,0,0))
940 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
942 int element = EL_CUSTOM_START + i;
944 /* order of checking and copying events to be mapped is important */
945 for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
947 if (HAS_CHANGE_EVENT(element, j - 2))
949 SET_CHANGE_EVENT(element, j - 2, FALSE);
950 SET_CHANGE_EVENT(element, j, TRUE);
954 /* order of checking and copying events to be mapped is important */
955 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
957 if (HAS_CHANGE_EVENT(element, j - 1))
959 SET_CHANGE_EVENT(element, j - 1, FALSE);
960 SET_CHANGE_EVENT(element, j, TRUE);
966 /* some custom element change events get mapped since version 3.0.3 */
967 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
969 int element = EL_CUSTOM_START + i;
971 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
972 HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
974 SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
975 SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
977 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
981 /* initialize "can_change" field for old levels with only one change page */
982 if (level->game_version <= VERSION_IDENT(3,0,2))
984 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
986 int element = EL_CUSTOM_START + i;
988 if (CAN_CHANGE(element))
989 element_info[element].change->can_change = TRUE;
993 /* initialize element properties for level editor etc. */
994 InitElementPropertiesEngine(level->game_version);
997 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1001 /* map elements that have changed in newer versions */
1002 for(y=0; y<level->fieldy; y++)
1004 for(x=0; x<level->fieldx; x++)
1006 int element = level->field[x][y];
1008 if (level->game_version <= VERSION_IDENT(2,2,0))
1010 /* map game font elements */
1011 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1012 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1013 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1014 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1017 if (level->game_version < VERSION_IDENT(3,0,0))
1019 /* map Supaplex gravity tube elements */
1020 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1021 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1022 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1023 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1027 level->field[x][y] = element;
1031 /* copy elements to runtime playfield array */
1032 for(x=0; x<MAX_LEV_FIELDX; x++)
1033 for(y=0; y<MAX_LEV_FIELDY; y++)
1034 Feld[x][y] = level->field[x][y];
1036 /* initialize level size variables for faster access */
1037 lev_fieldx = level->fieldx;
1038 lev_fieldy = level->fieldy;
1040 /* determine border element for this level */
1046 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
1050 if (leveldir_current == NULL) /* only when dumping level */
1053 /* determine correct game engine version of current level */
1054 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
1055 IS_LEVELCLASS_USER(leveldir_current))
1058 printf("\n::: This level is private or contributed: '%s'\n", filename);
1061 /* For user contributed and private levels, use the version of
1062 the game engine the levels were created for.
1063 Since 2.0.1, the game engine version is now directly stored
1064 in the level file (chunk "VERS"), so there is no need anymore
1065 to set the game version from the file version (except for old,
1066 pre-2.0 levels, where the game version is still taken from the
1067 file format version used to store the level -- see above). */
1069 /* do some special adjustments to support older level versions */
1070 if (level->file_version == FILE_VERSION_1_0)
1072 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
1073 Error(ERR_WARN, "using high speed movement for player");
1075 /* player was faster than monsters in (pre-)1.0 levels */
1076 level->double_speed = TRUE;
1079 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1080 if (level->game_version == VERSION_IDENT(2,0,1))
1081 level->em_slippery_gems = TRUE;
1086 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1087 leveldir_current->sort_priority, filename);
1090 /* Always use the latest version of the game engine for all but
1091 user contributed and private levels; this allows for actual
1092 corrections in the game engine to take effect for existing,
1093 converted levels (from "classic" or other existing games) to
1094 make the game emulation more accurate, while (hopefully) not
1095 breaking existing levels created from other players. */
1097 level->game_version = GAME_VERSION_ACTUAL;
1099 /* Set special EM style gems behaviour: EM style gems slip down from
1100 normal, steel and growing wall. As this is a more fundamental change,
1101 it seems better to set the default behaviour to "off" (as it is more
1102 natural) and make it configurable in the level editor (as a property
1103 of gem style elements). Already existing converted levels (neither
1104 private nor contributed levels) are changed to the new behaviour. */
1106 if (level->file_version < FILE_VERSION_2_0)
1107 level->em_slippery_gems = TRUE;
1110 /* map elements that have changed in newer versions */
1111 for(y=0; y<level->fieldy; y++)
1113 for(x=0; x<level->fieldx; x++)
1115 int element = level->field[x][y];
1117 if (level->game_version <= VERSION_IDENT(2,2,0))
1119 /* map game font elements */
1120 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1121 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1122 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1123 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1126 if (level->game_version < VERSION_IDENT(3,0,0))
1128 /* map Supaplex gravity tube elements */
1129 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1130 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1131 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1132 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1136 level->field[x][y] = element;
1140 /* map custom element change events that have changed in newer versions
1141 (these following values have accidentally changed in version 3.0.1) */
1142 if (level->game_version <= VERSION_IDENT(3,0,0))
1144 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1146 int element = EL_CUSTOM_START + i;
1148 /* order of checking events to be mapped is important */
1149 for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
1151 if (HAS_CHANGE_EVENT(element, j - 2))
1153 SET_CHANGE_EVENT(element, j - 2, FALSE);
1154 SET_CHANGE_EVENT(element, j, TRUE);
1158 /* order of checking events to be mapped is important */
1159 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1161 if (HAS_CHANGE_EVENT(element, j - 1))
1163 SET_CHANGE_EVENT(element, j - 1, FALSE);
1164 SET_CHANGE_EVENT(element, j, TRUE);
1170 /* initialize "can_change" field for old levels with only one change page */
1171 if (level->game_version <= VERSION_IDENT(3,0,2))
1173 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1175 int element = EL_CUSTOM_START + i;
1177 if (CAN_CHANGE(element))
1178 element_info[element].change->can_change = TRUE;
1182 /* copy elements to runtime playfield array */
1183 for(x=0; x<MAX_LEV_FIELDX; x++)
1184 for(y=0; y<MAX_LEV_FIELDY; y++)
1185 Feld[x][y] = level->field[x][y];
1187 /* initialize level size variables for faster access */
1188 lev_fieldx = level->fieldx;
1189 lev_fieldy = level->fieldy;
1191 /* determine border element for this level */
1194 /* initialize element properties for level editor etc. */
1195 InitElementPropertiesEngine(level->game_version);
1200 void LoadLevelTemplate(int level_nr)
1202 char *filename = getLevelFilename(level_nr);
1204 LoadLevelFromFilename(&level_template, filename);
1206 LoadLevel_InitVersion(&level, filename);
1207 LoadLevel_InitElements(&level, filename);
1209 ActivateLevelTemplate();
1212 void LoadLevel(int level_nr)
1214 char *filename = getLevelFilename(level_nr);
1216 LoadLevelFromFilename(&level, filename);
1218 if (level.use_custom_template)
1219 LoadLevelTemplate(-1);
1222 LoadLevel_InitVersion(&level, filename);
1223 LoadLevel_InitElements(&level, filename);
1224 LoadLevel_InitPlayfield(&level, filename);
1226 LoadLevel_InitLevel(&level, filename);
1230 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1232 putFileVersion(file, level->file_version);
1233 putFileVersion(file, level->game_version);
1236 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1240 putFile8Bit(file, level->fieldx);
1241 putFile8Bit(file, level->fieldy);
1243 putFile16BitBE(file, level->time);
1244 putFile16BitBE(file, level->gems_needed);
1246 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1247 putFile8Bit(file, level->name[i]);
1249 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1250 putFile8Bit(file, level->score[i]);
1252 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1255 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1256 level->yamyam_content[i][x][y]));
1257 putFile8Bit(file, level->amoeba_speed);
1258 putFile8Bit(file, level->time_magic_wall);
1259 putFile8Bit(file, level->time_wheel);
1260 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1261 level->amoeba_content));
1262 putFile8Bit(file, (level->double_speed ? 1 : 0));
1263 putFile8Bit(file, (level->gravity ? 1 : 0));
1264 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1265 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1267 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1269 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1272 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1276 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1277 putFile8Bit(file, level->author[i]);
1280 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1284 for(y=0; y<level->fieldy; y++)
1285 for(x=0; x<level->fieldx; x++)
1286 if (level->encoding_16bit_field)
1287 putFile16BitBE(file, level->field[x][y]);
1289 putFile8Bit(file, level->field[x][y]);
1293 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1297 putFile8Bit(file, EL_YAMYAM);
1298 putFile8Bit(file, level->num_yamyam_contents);
1299 putFile8Bit(file, 0);
1300 putFile8Bit(file, 0);
1302 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1305 if (level->encoding_16bit_field)
1306 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1308 putFile8Bit(file, level->yamyam_content[i][x][y]);
1312 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1315 int num_contents, content_xsize, content_ysize;
1316 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1318 if (element == EL_YAMYAM)
1320 num_contents = level->num_yamyam_contents;
1324 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1327 content_array[i][x][y] = level->yamyam_content[i][x][y];
1329 else if (element == EL_BD_AMOEBA)
1335 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1338 content_array[i][x][y] = EL_EMPTY;
1339 content_array[0][0][0] = level->amoeba_content;
1343 /* chunk header already written -- write empty chunk data */
1344 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1346 Error(ERR_WARN, "cannot save content for element '%d'", element);
1350 putFile16BitBE(file, element);
1351 putFile8Bit(file, num_contents);
1352 putFile8Bit(file, content_xsize);
1353 putFile8Bit(file, content_ysize);
1355 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1357 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1360 putFile16BitBE(file, content_array[i][x][y]);
1363 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1366 int envelope_len = strlen(level->envelope) + 1;
1368 putFile16BitBE(file, element);
1369 putFile16BitBE(file, envelope_len);
1370 putFile8Bit(file, level->envelope_xsize);
1371 putFile8Bit(file, level->envelope_ysize);
1373 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1375 for(i=0; i < envelope_len; i++)
1376 putFile8Bit(file, level->envelope[i]);
1380 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1381 int num_changed_custom_elements)
1385 putFile16BitBE(file, num_changed_custom_elements);
1387 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1389 int element = EL_CUSTOM_START + i;
1391 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1393 if (check < num_changed_custom_elements)
1395 putFile16BitBE(file, element);
1396 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1403 if (check != num_changed_custom_elements) /* should not happen */
1404 Error(ERR_WARN, "inconsistent number of custom element properties");
1409 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1410 int num_changed_custom_elements)
1414 putFile16BitBE(file, num_changed_custom_elements);
1416 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1418 int element = EL_CUSTOM_START + i;
1420 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1422 if (check < num_changed_custom_elements)
1424 putFile16BitBE(file, element);
1425 putFile16BitBE(file, element_info[element].change->target_element);
1432 if (check != num_changed_custom_elements) /* should not happen */
1433 Error(ERR_WARN, "inconsistent number of custom target elements");
1438 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1439 int num_changed_custom_elements)
1441 int i, j, x, y, check = 0;
1443 putFile16BitBE(file, num_changed_custom_elements);
1445 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1447 int element = EL_CUSTOM_START + i;
1449 if (element_info[element].modified_settings)
1451 if (check < num_changed_custom_elements)
1453 putFile16BitBE(file, element);
1455 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1456 putFile8Bit(file, element_info[element].description[j]);
1458 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1460 /* some free bytes for future properties and padding */
1461 WriteUnusedBytesToFile(file, 7);
1463 putFile8Bit(file, element_info[element].use_gfx_element);
1464 putFile16BitBE(file, element_info[element].gfx_element);
1466 putFile8Bit(file, element_info[element].collect_score);
1467 putFile8Bit(file, element_info[element].collect_count);
1469 putFile16BitBE(file, element_info[element].push_delay_fixed);
1470 putFile16BitBE(file, element_info[element].push_delay_random);
1471 putFile16BitBE(file, element_info[element].move_delay_fixed);
1472 putFile16BitBE(file, element_info[element].move_delay_random);
1474 putFile16BitBE(file, element_info[element].move_pattern);
1475 putFile8Bit(file, element_info[element].move_direction_initial);
1476 putFile8Bit(file, element_info[element].move_stepsize);
1480 putFile16BitBE(file, element_info[element].content[x][y]);
1482 putFile32BitBE(file, element_info[element].change->events);
1484 putFile16BitBE(file, element_info[element].change->target_element);
1486 putFile16BitBE(file, element_info[element].change->delay_fixed);
1487 putFile16BitBE(file, element_info[element].change->delay_random);
1488 putFile16BitBE(file, element_info[element].change->delay_frames);
1490 putFile16BitBE(file, element_info[element].change->trigger_element);
1492 putFile8Bit(file, element_info[element].change->explode);
1493 putFile8Bit(file, element_info[element].change->use_content);
1494 putFile8Bit(file, element_info[element].change->only_complete);
1495 putFile8Bit(file, element_info[element].change->use_random_change);
1497 putFile8Bit(file, element_info[element].change->random);
1498 putFile8Bit(file, element_info[element].change->power);
1502 putFile16BitBE(file, element_info[element].change->content[x][y]);
1504 putFile8Bit(file, element_info[element].slippery_type);
1506 /* some free bytes for future properties and padding */
1507 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1514 if (check != num_changed_custom_elements) /* should not happen */
1515 Error(ERR_WARN, "inconsistent number of custom element properties");
1519 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1521 struct ElementInfo *ei = &element_info[element];
1524 putFile16BitBE(file, element);
1526 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
1527 putFile8Bit(file, ei->description[i]);
1529 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1530 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
1532 putFile8Bit(file, ei->num_change_pages);
1534 /* some free bytes for future base property values and padding */
1535 WriteUnusedBytesToFile(file, 5);
1537 /* write custom property values */
1539 putFile8Bit(file, ei->use_gfx_element);
1540 putFile16BitBE(file, ei->gfx_element);
1542 putFile8Bit(file, ei->collect_score);
1543 putFile8Bit(file, ei->collect_count);
1545 putFile16BitBE(file, ei->push_delay_fixed);
1546 putFile16BitBE(file, ei->push_delay_random);
1547 putFile16BitBE(file, ei->move_delay_fixed);
1548 putFile16BitBE(file, ei->move_delay_random);
1550 putFile16BitBE(file, ei->move_pattern);
1551 putFile8Bit(file, ei->move_direction_initial);
1552 putFile8Bit(file, ei->move_stepsize);
1554 putFile8Bit(file, ei->slippery_type);
1558 putFile16BitBE(file, ei->content[x][y]);
1560 /* some free bytes for future custom property values and padding */
1561 WriteUnusedBytesToFile(file, 12);
1563 /* write change property values */
1565 for (i=0; i < ei->num_change_pages; i++)
1567 struct ElementChangeInfo *change = &ei->change_page[i];
1569 putFile32BitBE(file, change->events);
1571 putFile16BitBE(file, change->target_element);
1573 putFile16BitBE(file, change->delay_fixed);
1574 putFile16BitBE(file, change->delay_random);
1575 putFile16BitBE(file, change->delay_frames);
1577 putFile16BitBE(file, change->trigger_element);
1579 putFile8Bit(file, change->explode);
1580 putFile8Bit(file, change->use_content);
1581 putFile8Bit(file, change->only_complete);
1582 putFile8Bit(file, change->use_random_change);
1584 putFile8Bit(file, change->random);
1585 putFile8Bit(file, change->power);
1589 putFile16BitBE(file, change->content[x][y]);
1591 putFile8Bit(file, change->can_change);
1593 /* some free bytes for future change property values and padding */
1594 WriteUnusedBytesToFile(file, 9);
1598 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1600 int body_chunk_size;
1604 if (!(file = fopen(filename, MODE_WRITE)))
1606 Error(ERR_WARN, "cannot save level file '%s'", filename);
1610 level->file_version = FILE_VERSION_ACTUAL;
1611 level->game_version = GAME_VERSION_ACTUAL;
1613 /* check level field for 16-bit elements */
1614 level->encoding_16bit_field = FALSE;
1615 for(y=0; y<level->fieldy; y++)
1616 for(x=0; x<level->fieldx; x++)
1617 if (level->field[x][y] > 255)
1618 level->encoding_16bit_field = TRUE;
1620 /* check yamyam content for 16-bit elements */
1621 level->encoding_16bit_yamyam = FALSE;
1622 for(i=0; i<level->num_yamyam_contents; i++)
1625 if (level->yamyam_content[i][x][y] > 255)
1626 level->encoding_16bit_yamyam = TRUE;
1628 /* check amoeba content for 16-bit elements */
1629 level->encoding_16bit_amoeba = FALSE;
1630 if (level->amoeba_content > 255)
1631 level->encoding_16bit_amoeba = TRUE;
1633 /* calculate size of "BODY" chunk */
1635 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1637 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1638 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1640 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1641 SaveLevel_VERS(file, level);
1643 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1644 SaveLevel_HEAD(file, level);
1646 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1647 SaveLevel_AUTH(file, level);
1649 putFileChunkBE(file, "BODY", body_chunk_size);
1650 SaveLevel_BODY(file, level);
1652 if (level->encoding_16bit_yamyam ||
1653 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1655 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1656 SaveLevel_CNT2(file, level, EL_YAMYAM);
1659 if (level->encoding_16bit_amoeba)
1661 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1662 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1665 /* check for envelope content */
1666 if (strlen(level->envelope) > 0)
1668 int envelope_len = strlen(level->envelope) + 1;
1670 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
1671 SaveLevel_CNT3(file, level, EL_ENVELOPE);
1674 /* check for non-default custom elements (unless using template level) */
1675 if (!level->use_custom_template)
1677 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1679 int element = EL_CUSTOM_START + i;
1681 if (element_info[element].modified_settings)
1683 int num_change_pages = element_info[element].num_change_pages;
1685 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
1686 SaveLevel_CUS4(file, level, element);
1693 SetFilePermissions(filename, PERMS_PRIVATE);
1696 void SaveLevel(int level_nr)
1698 char *filename = getLevelFilename(level_nr);
1700 SaveLevelFromFilename(&level, filename);
1703 void SaveLevelTemplate()
1705 char *filename = getLevelFilename(-1);
1707 SaveLevelFromFilename(&level, filename);
1710 void DumpLevel(struct LevelInfo *level)
1712 printf_line("-", 79);
1713 printf("Level xxx (file version %08d, game version %08d)\n",
1714 level->file_version, level->game_version);
1715 printf_line("-", 79);
1717 printf("Level Author: '%s'\n", level->author);
1718 printf("Level Title: '%s'\n", level->name);
1720 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1722 printf("Level Time: %d seconds\n", level->time);
1723 printf("Gems needed: %d\n", level->gems_needed);
1725 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1726 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1727 printf("Time for Light: %d seconds\n", level->time_light);
1728 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1730 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1732 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1733 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1734 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1736 printf_line("-", 79);
1740 /* ========================================================================= */
1741 /* tape file functions */
1742 /* ========================================================================= */
1744 static void setTapeInfoToDefaults()
1748 /* always start with reliable default values (empty tape) */
1751 /* default values (also for pre-1.2 tapes) with only the first player */
1752 tape.player_participates[0] = TRUE;
1753 for(i=1; i<MAX_PLAYERS; i++)
1754 tape.player_participates[i] = FALSE;
1756 /* at least one (default: the first) player participates in every tape */
1757 tape.num_participating_players = 1;
1759 tape.level_nr = level_nr;
1761 tape.changed = FALSE;
1763 tape.recording = FALSE;
1764 tape.playing = FALSE;
1765 tape.pausing = FALSE;
1768 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1770 tape->file_version = getFileVersion(file);
1771 tape->game_version = getFileVersion(file);
1776 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1780 tape->random_seed = getFile32BitBE(file);
1781 tape->date = getFile32BitBE(file);
1782 tape->length = getFile32BitBE(file);
1784 /* read header fields that are new since version 1.2 */
1785 if (tape->file_version >= FILE_VERSION_1_2)
1787 byte store_participating_players = getFile8Bit(file);
1790 /* since version 1.2, tapes store which players participate in the tape */
1791 tape->num_participating_players = 0;
1792 for(i=0; i<MAX_PLAYERS; i++)
1794 tape->player_participates[i] = FALSE;
1796 if (store_participating_players & (1 << i))
1798 tape->player_participates[i] = TRUE;
1799 tape->num_participating_players++;
1803 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1805 engine_version = getFileVersion(file);
1806 if (engine_version > 0)
1807 tape->engine_version = engine_version;
1809 tape->engine_version = tape->game_version;
1815 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1817 int level_identifier_size;
1820 level_identifier_size = getFile16BitBE(file);
1822 tape->level_identifier =
1823 checked_realloc(tape->level_identifier, level_identifier_size);
1825 for(i=0; i < level_identifier_size; i++)
1826 tape->level_identifier[i] = getFile8Bit(file);
1828 tape->level_nr = getFile16BitBE(file);
1830 chunk_size = 2 + level_identifier_size + 2;
1835 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1838 int chunk_size_expected =
1839 (tape->num_participating_players + 1) * tape->length;
1841 if (chunk_size_expected != chunk_size)
1843 ReadUnusedBytesFromFile(file, chunk_size);
1844 return chunk_size_expected;
1847 for(i=0; i<tape->length; i++)
1849 if (i >= MAX_TAPELEN)
1852 for(j=0; j<MAX_PLAYERS; j++)
1854 tape->pos[i].action[j] = MV_NO_MOVING;
1856 if (tape->player_participates[j])
1857 tape->pos[i].action[j] = getFile8Bit(file);
1860 tape->pos[i].delay = getFile8Bit(file);
1862 if (tape->file_version == FILE_VERSION_1_0)
1864 /* eliminate possible diagonal moves in old tapes */
1865 /* this is only for backward compatibility */
1867 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1868 byte action = tape->pos[i].action[0];
1869 int k, num_moves = 0;
1873 if (action & joy_dir[k])
1875 tape->pos[i + num_moves].action[0] = joy_dir[k];
1877 tape->pos[i + num_moves].delay = 0;
1886 tape->length += num_moves;
1889 else if (tape->file_version < FILE_VERSION_2_0)
1891 /* convert pre-2.0 tapes to new tape format */
1893 if (tape->pos[i].delay > 1)
1896 tape->pos[i + 1] = tape->pos[i];
1897 tape->pos[i + 1].delay = 1;
1900 for(j=0; j<MAX_PLAYERS; j++)
1901 tape->pos[i].action[j] = MV_NO_MOVING;
1902 tape->pos[i].delay--;
1913 if (i != tape->length)
1914 chunk_size = (tape->num_participating_players + 1) * i;
1919 void LoadTapeFromFilename(char *filename)
1921 char cookie[MAX_LINE_LEN];
1922 char chunk_name[CHUNK_ID_LEN + 1];
1926 /* always start with reliable default values */
1927 setTapeInfoToDefaults();
1929 if (!(file = fopen(filename, MODE_READ)))
1932 getFileChunkBE(file, chunk_name, NULL);
1933 if (strcmp(chunk_name, "RND1") == 0)
1935 getFile32BitBE(file); /* not used */
1937 getFileChunkBE(file, chunk_name, NULL);
1938 if (strcmp(chunk_name, "TAPE") != 0)
1940 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1945 else /* check for pre-2.0 file format with cookie string */
1947 strcpy(cookie, chunk_name);
1948 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1949 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1950 cookie[strlen(cookie) - 1] = '\0';
1952 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1954 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1959 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1961 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1966 /* pre-2.0 tape files have no game version, so use file version here */
1967 tape.game_version = tape.file_version;
1970 if (tape.file_version < FILE_VERSION_1_2)
1972 /* tape files from versions before 1.2.0 without chunk structure */
1973 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1974 LoadTape_BODY(file, 2 * tape.length, &tape);
1982 int (*loader)(FILE *, int, struct TapeInfo *);
1986 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1987 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1988 { "INFO", -1, LoadTape_INFO },
1989 { "BODY", -1, LoadTape_BODY },
1993 while (getFileChunkBE(file, chunk_name, &chunk_size))
1997 while (chunk_info[i].name != NULL &&
1998 strcmp(chunk_name, chunk_info[i].name) != 0)
2001 if (chunk_info[i].name == NULL)
2003 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
2004 chunk_name, filename);
2005 ReadUnusedBytesFromFile(file, chunk_size);
2007 else if (chunk_info[i].size != -1 &&
2008 chunk_info[i].size != chunk_size)
2010 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2011 chunk_size, chunk_name, filename);
2012 ReadUnusedBytesFromFile(file, chunk_size);
2016 /* call function to load this tape chunk */
2017 int chunk_size_expected =
2018 (chunk_info[i].loader)(file, chunk_size, &tape);
2020 /* the size of some chunks cannot be checked before reading other
2021 chunks first (like "HEAD" and "BODY") that contain some header
2022 information, so check them here */
2023 if (chunk_size_expected != chunk_size)
2025 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2026 chunk_size, chunk_name, filename);
2034 tape.length_seconds = GetTapeLength();
2037 printf("tape game version: %d\n", tape.game_version);
2038 printf("tape engine version: %d\n", tape.engine_version);
2042 void LoadTape(int level_nr)
2044 char *filename = getTapeFilename(level_nr);
2046 LoadTapeFromFilename(filename);
2049 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2051 putFileVersion(file, tape->file_version);
2052 putFileVersion(file, tape->game_version);
2055 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2058 byte store_participating_players = 0;
2060 /* set bits for participating players for compact storage */
2061 for(i=0; i<MAX_PLAYERS; i++)
2062 if (tape->player_participates[i])
2063 store_participating_players |= (1 << i);
2065 putFile32BitBE(file, tape->random_seed);
2066 putFile32BitBE(file, tape->date);
2067 putFile32BitBE(file, tape->length);
2069 putFile8Bit(file, store_participating_players);
2071 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2072 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2074 putFileVersion(file, tape->engine_version);
2077 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2079 int level_identifier_size = strlen(tape->level_identifier) + 1;
2082 putFile16BitBE(file, level_identifier_size);
2084 for(i=0; i < level_identifier_size; i++)
2085 putFile8Bit(file, tape->level_identifier[i]);
2087 putFile16BitBE(file, tape->level_nr);
2090 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2094 for(i=0; i<tape->length; i++)
2096 for(j=0; j<MAX_PLAYERS; j++)
2097 if (tape->player_participates[j])
2098 putFile8Bit(file, tape->pos[i].action[j]);
2100 putFile8Bit(file, tape->pos[i].delay);
2104 void SaveTape(int level_nr)
2106 char *filename = getTapeFilename(level_nr);
2108 boolean new_tape = TRUE;
2109 int num_participating_players = 0;
2110 int info_chunk_size;
2111 int body_chunk_size;
2114 InitTapeDirectory(leveldir_current->filename);
2116 /* if a tape still exists, ask to overwrite it */
2117 if (access(filename, F_OK) == 0)
2120 if (!Request("Replace old tape ?", REQ_ASK))
2124 if (!(file = fopen(filename, MODE_WRITE)))
2126 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2130 tape.file_version = FILE_VERSION_ACTUAL;
2131 tape.game_version = GAME_VERSION_ACTUAL;
2133 /* count number of participating players */
2134 for(i=0; i<MAX_PLAYERS; i++)
2135 if (tape.player_participates[i])
2136 num_participating_players++;
2138 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2139 body_chunk_size = (num_participating_players + 1) * tape.length;
2141 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2142 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2144 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2145 SaveTape_VERS(file, &tape);
2147 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2148 SaveTape_HEAD(file, &tape);
2150 putFileChunkBE(file, "INFO", info_chunk_size);
2151 SaveTape_INFO(file, &tape);
2153 putFileChunkBE(file, "BODY", body_chunk_size);
2154 SaveTape_BODY(file, &tape);
2158 SetFilePermissions(filename, PERMS_PRIVATE);
2160 tape.changed = FALSE;
2163 Request("tape saved !", REQ_CONFIRM);
2166 void DumpTape(struct TapeInfo *tape)
2170 if (TAPE_IS_EMPTY(*tape))
2172 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2176 printf_line("-", 79);
2177 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2178 tape->level_nr, tape->file_version, tape->game_version);
2179 printf("Level series identifier: '%s'\n", tape->level_identifier);
2180 printf_line("-", 79);
2182 for(i=0; i<tape->length; i++)
2184 if (i >= MAX_TAPELEN)
2187 printf("%03d: ", i);
2189 for(j=0; j<MAX_PLAYERS; j++)
2191 if (tape->player_participates[j])
2193 int action = tape->pos[i].action[j];
2195 printf("%d:%02x ", j, action);
2196 printf("[%c%c%c%c|%c%c] - ",
2197 (action & JOY_LEFT ? '<' : ' '),
2198 (action & JOY_RIGHT ? '>' : ' '),
2199 (action & JOY_UP ? '^' : ' '),
2200 (action & JOY_DOWN ? 'v' : ' '),
2201 (action & JOY_BUTTON_1 ? '1' : ' '),
2202 (action & JOY_BUTTON_2 ? '2' : ' '));
2206 printf("(%03d)\n", tape->pos[i].delay);
2209 printf_line("-", 79);
2213 /* ========================================================================= */
2214 /* score file functions */
2215 /* ========================================================================= */
2217 void LoadScore(int level_nr)
2220 char *filename = getScoreFilename(level_nr);
2221 char cookie[MAX_LINE_LEN];
2222 char line[MAX_LINE_LEN];
2226 /* always start with reliable default values */
2227 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2229 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2230 highscore[i].Score = 0;
2233 if (!(file = fopen(filename, MODE_READ)))
2236 /* check file identifier */
2237 fgets(cookie, MAX_LINE_LEN, file);
2238 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2239 cookie[strlen(cookie) - 1] = '\0';
2241 if (!checkCookieString(cookie, SCORE_COOKIE))
2243 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2248 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2250 fscanf(file, "%d", &highscore[i].Score);
2251 fgets(line, MAX_LINE_LEN, file);
2253 if (line[strlen(line) - 1] == '\n')
2254 line[strlen(line) - 1] = '\0';
2256 for (line_ptr = line; *line_ptr; line_ptr++)
2258 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2260 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2261 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2270 void SaveScore(int level_nr)
2273 char *filename = getScoreFilename(level_nr);
2276 InitScoreDirectory(leveldir_current->filename);
2278 if (!(file = fopen(filename, MODE_WRITE)))
2280 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2284 fprintf(file, "%s\n\n", SCORE_COOKIE);
2286 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2287 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2291 SetFilePermissions(filename, PERMS_PUBLIC);
2295 /* ========================================================================= */
2296 /* setup file functions */
2297 /* ========================================================================= */
2299 #define TOKEN_STR_PLAYER_PREFIX "player_"
2302 #define SETUP_TOKEN_PLAYER_NAME 0
2303 #define SETUP_TOKEN_SOUND 1
2304 #define SETUP_TOKEN_SOUND_LOOPS 2
2305 #define SETUP_TOKEN_SOUND_MUSIC 3
2306 #define SETUP_TOKEN_SOUND_SIMPLE 4
2307 #define SETUP_TOKEN_TOONS 5
2308 #define SETUP_TOKEN_SCROLL_DELAY 6
2309 #define SETUP_TOKEN_SOFT_SCROLLING 7
2310 #define SETUP_TOKEN_FADING 8
2311 #define SETUP_TOKEN_AUTORECORD 9
2312 #define SETUP_TOKEN_QUICK_DOORS 10
2313 #define SETUP_TOKEN_TEAM_MODE 11
2314 #define SETUP_TOKEN_HANDICAP 12
2315 #define SETUP_TOKEN_TIME_LIMIT 13
2316 #define SETUP_TOKEN_FULLSCREEN 14
2317 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2318 #define SETUP_TOKEN_GRAPHICS_SET 16
2319 #define SETUP_TOKEN_SOUNDS_SET 17
2320 #define SETUP_TOKEN_MUSIC_SET 18
2321 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2322 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2323 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2325 #define NUM_GLOBAL_SETUP_TOKENS 22
2328 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2329 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2330 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2331 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2332 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2333 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2334 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2335 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2336 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2337 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2338 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2340 #define NUM_EDITOR_SETUP_TOKENS 11
2342 /* shortcut setup */
2343 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2344 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2345 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2347 #define NUM_SHORTCUT_SETUP_TOKENS 3
2350 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2351 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2352 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2353 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2354 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2355 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2356 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2357 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2358 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2359 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
2360 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2361 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2362 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2363 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2364 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2365 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
2367 #define NUM_PLAYER_SETUP_TOKENS 16
2370 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2371 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2373 #define NUM_SYSTEM_SETUP_TOKENS 2
2376 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2378 #define NUM_OPTIONS_SETUP_TOKENS 1
2381 static struct SetupInfo si;
2382 static struct SetupEditorInfo sei;
2383 static struct SetupShortcutInfo ssi;
2384 static struct SetupInputInfo sii;
2385 static struct SetupSystemInfo syi;
2386 static struct OptionInfo soi;
2388 static struct TokenInfo global_setup_tokens[] =
2390 { TYPE_STRING, &si.player_name, "player_name" },
2391 { TYPE_SWITCH, &si.sound, "sound" },
2392 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2393 { TYPE_SWITCH, &si.sound_music, "background_music" },
2394 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2395 { TYPE_SWITCH, &si.toons, "toons" },
2396 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2397 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2398 { TYPE_SWITCH, &si.fading, "screen_fading" },
2399 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2400 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2401 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2402 { TYPE_SWITCH, &si.handicap, "handicap" },
2403 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2404 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2405 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2406 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2407 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2408 { TYPE_STRING, &si.music_set, "music_set" },
2409 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2410 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2411 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2414 static struct TokenInfo editor_setup_tokens[] =
2416 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2417 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2418 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2419 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2420 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2421 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2422 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2423 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2424 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2425 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2426 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2429 static struct TokenInfo shortcut_setup_tokens[] =
2431 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2432 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2433 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2436 static struct TokenInfo player_setup_tokens[] =
2438 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2439 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2440 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2441 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2442 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2443 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2444 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2445 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2446 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2447 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2448 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2449 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2450 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2451 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2452 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2453 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
2456 static struct TokenInfo system_setup_tokens[] =
2458 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2459 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2462 static struct TokenInfo options_setup_tokens[] =
2464 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2467 static char *get_corrected_login_name(char *login_name)
2469 /* needed because player name must be a fixed length string */
2470 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2472 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2473 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2475 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2476 if (strchr(login_name_new, ' '))
2477 *strchr(login_name_new, ' ') = '\0';
2479 return login_name_new;
2482 static void setSetupInfoToDefaults(struct SetupInfo *si)
2486 si->player_name = get_corrected_login_name(getLoginName());
2489 si->sound_loops = TRUE;
2490 si->sound_music = TRUE;
2491 si->sound_simple = TRUE;
2493 si->double_buffering = TRUE;
2494 si->direct_draw = !si->double_buffering;
2495 si->scroll_delay = TRUE;
2496 si->soft_scrolling = TRUE;
2498 si->autorecord = TRUE;
2499 si->quick_doors = FALSE;
2500 si->team_mode = FALSE;
2501 si->handicap = TRUE;
2502 si->time_limit = TRUE;
2503 si->fullscreen = FALSE;
2504 si->ask_on_escape = TRUE;
2506 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2507 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2508 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2509 si->override_level_graphics = FALSE;
2510 si->override_level_sounds = FALSE;
2511 si->override_level_music = FALSE;
2513 si->editor.el_boulderdash = TRUE;
2514 si->editor.el_emerald_mine = TRUE;
2515 si->editor.el_more = TRUE;
2516 si->editor.el_sokoban = TRUE;
2517 si->editor.el_supaplex = TRUE;
2518 si->editor.el_diamond_caves = TRUE;
2519 si->editor.el_dx_boulderdash = TRUE;
2520 si->editor.el_chars = TRUE;
2521 si->editor.el_custom = TRUE;
2522 si->editor.el_custom_more = FALSE;
2524 si->editor.el_headlines = TRUE;
2526 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2527 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2528 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2530 for (i=0; i<MAX_PLAYERS; i++)
2532 si->input[i].use_joystick = FALSE;
2533 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2534 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2535 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2536 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2537 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2538 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2539 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2540 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2541 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2542 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2543 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2544 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2545 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2546 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2547 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2550 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2551 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2553 si->options.verbose = FALSE;
2556 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2560 if (!setup_file_hash)
2565 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2566 setSetupInfo(global_setup_tokens, i,
2567 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2572 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2573 setSetupInfo(editor_setup_tokens, i,
2574 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2577 /* shortcut setup */
2578 ssi = setup.shortcut;
2579 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2580 setSetupInfo(shortcut_setup_tokens, i,
2581 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2582 setup.shortcut = ssi;
2585 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2589 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2591 sii = setup.input[pnr];
2592 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2594 char full_token[100];
2596 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2597 setSetupInfo(player_setup_tokens, i,
2598 getHashEntry(setup_file_hash, full_token));
2600 setup.input[pnr] = sii;
2605 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2606 setSetupInfo(system_setup_tokens, i,
2607 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2611 soi = setup.options;
2612 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2613 setSetupInfo(options_setup_tokens, i,
2614 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2615 setup.options = soi;
2620 char *filename = getSetupFilename();
2621 SetupFileHash *setup_file_hash = NULL;
2623 /* always start with reliable default values */
2624 setSetupInfoToDefaults(&setup);
2626 setup_file_hash = loadSetupFileHash(filename);
2628 if (setup_file_hash)
2630 char *player_name_new;
2632 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2633 decodeSetupFileHash(setup_file_hash);
2635 setup.direct_draw = !setup.double_buffering;
2637 freeSetupFileHash(setup_file_hash);
2639 /* needed to work around problems with fixed length strings */
2640 player_name_new = get_corrected_login_name(setup.player_name);
2641 free(setup.player_name);
2642 setup.player_name = player_name_new;
2645 Error(ERR_WARN, "using default setup values");
2650 char *filename = getSetupFilename();
2654 InitUserDataDirectory();
2656 if (!(file = fopen(filename, MODE_WRITE)))
2658 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2662 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2663 getCookie("SETUP")));
2664 fprintf(file, "\n");
2668 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2670 /* just to make things nicer :) */
2671 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2672 i == SETUP_TOKEN_GRAPHICS_SET)
2673 fprintf(file, "\n");
2675 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2680 fprintf(file, "\n");
2681 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2682 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2684 /* shortcut setup */
2685 ssi = setup.shortcut;
2686 fprintf(file, "\n");
2687 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2688 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2691 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2695 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2696 fprintf(file, "\n");
2698 sii = setup.input[pnr];
2699 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2700 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2705 fprintf(file, "\n");
2706 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2707 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2710 soi = setup.options;
2711 fprintf(file, "\n");
2712 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2713 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2717 SetFilePermissions(filename, PERMS_PRIVATE);
2720 void LoadCustomElementDescriptions()
2722 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2723 SetupFileHash *setup_file_hash;
2726 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2728 if (element_info[i].custom_description != NULL)
2730 free(element_info[i].custom_description);
2731 element_info[i].custom_description = NULL;
2735 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2738 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2740 char *token = getStringCat2(element_info[i].token_name, ".name");
2741 char *value = getHashEntry(setup_file_hash, token);
2744 element_info[i].custom_description = getStringCopy(value);
2749 freeSetupFileHash(setup_file_hash);
2752 void LoadSpecialMenuDesignSettings()
2754 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2755 SetupFileHash *setup_file_hash;
2758 /* always start with reliable default values from default config */
2759 for (i=0; image_config_vars[i].token != NULL; i++)
2760 for (j=0; image_config[j].token != NULL; j++)
2761 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2762 *image_config_vars[i].value =
2763 get_integer_from_string(image_config[j].value);
2765 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2768 /* special case: initialize with default values that may be overwritten */
2769 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2771 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2772 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2773 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2775 if (value_x != NULL)
2776 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2777 if (value_y != NULL)
2778 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2779 if (list_size != NULL)
2780 menu.list_size[i] = get_integer_from_string(list_size);
2783 /* read (and overwrite with) values that may be specified in config file */
2784 for (i=0; image_config_vars[i].token != NULL; i++)
2786 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2789 *image_config_vars[i].value = get_integer_from_string(value);
2792 freeSetupFileHash(setup_file_hash);