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->target_element = EL_EMPTY_SPACE;
79 change->delay_fixed = 0;
80 change->delay_random = 0;
81 change->delay_frames = -1; /* later set to reliable default value */
83 change->trigger_element = EL_EMPTY_SPACE;
85 change->explode = FALSE;
86 change->use_content = FALSE;
87 change->only_complete = FALSE;
88 change->use_random_change = FALSE;
90 change->power = CP_NON_DESTRUCTIVE;
94 change->content[x][y] = EL_EMPTY_SPACE;
96 change->direct_action = 0;
97 change->other_action = 0;
99 change->pre_change_function = NULL;
100 change->change_function = NULL;
101 change->post_change_function = NULL;
104 static void setLevelInfoToDefaults(struct LevelInfo *level)
108 level->file_version = FILE_VERSION_ACTUAL;
109 level->game_version = GAME_VERSION_ACTUAL;
111 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
112 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
113 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
115 level->fieldx = STD_LEV_FIELDX;
116 level->fieldy = STD_LEV_FIELDY;
118 for(x=0; x<MAX_LEV_FIELDX; x++)
119 for(y=0; y<MAX_LEV_FIELDY; y++)
120 level->field[x][y] = EL_SAND;
123 level->gems_needed = 0;
124 level->amoeba_speed = 10;
125 level->time_magic_wall = 10;
126 level->time_wheel = 10;
127 level->time_light = 10;
128 level->time_timegate = 10;
129 level->amoeba_content = EL_DIAMOND;
130 level->double_speed = FALSE;
131 level->gravity = FALSE;
132 level->em_slippery_gems = FALSE;
134 level->use_custom_template = FALSE;
136 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
137 level->name[i] = '\0';
138 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
139 level->author[i] = '\0';
141 strcpy(level->name, NAMELESS_LEVEL_NAME);
142 strcpy(level->author, ANONYMOUS_NAME);
144 level->envelope[0] = '\0';
145 level->envelope_xsize = MAX_ENVELOPE_XSIZE;
146 level->envelope_ysize = MAX_ENVELOPE_YSIZE;
148 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
149 level->score[i] = 10;
151 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
152 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
155 level->yamyam_content[i][x][y] =
156 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
158 level->field[0][0] = EL_PLAYER_1;
159 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
161 for (i=0; i < MAX_NUM_ELEMENTS; i++)
163 setElementChangePages(&element_info[i], 1);
164 setElementChangeInfoToDefaults(element_info[i].change);
167 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
169 int element = EL_CUSTOM_START + i;
171 for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
172 element_info[element].description[j] = '\0';
173 if (element_info[element].custom_description != NULL)
174 strncpy(element_info[element].description,
175 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
177 strcpy(element_info[element].description,
178 element_info[element].editor_description);
180 element_info[element].use_gfx_element = FALSE;
181 element_info[element].gfx_element = EL_EMPTY_SPACE;
183 element_info[element].collect_score = 10; /* special default */
184 element_info[element].collect_count = 1; /* special default */
186 element_info[element].push_delay_fixed = 2; /* special default */
187 element_info[element].push_delay_random = 8; /* special default */
188 element_info[element].move_delay_fixed = 0;
189 element_info[element].move_delay_random = 0;
191 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
192 element_info[element].move_direction_initial = MV_NO_MOVING;
193 element_info[element].move_stepsize = TILEX / 8;
195 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
199 element_info[element].content[x][y] = EL_EMPTY_SPACE;
201 element_info[element].access_type = 0;
202 element_info[element].access_layer = 0;
203 element_info[element].walk_to_action = 0;
204 element_info[element].smash_targets = 0;
205 element_info[element].deadliness = 0;
206 element_info[element].consistency = 0;
208 element_info[element].can_explode_by_fire = FALSE;
209 element_info[element].can_explode_smashed = FALSE;
210 element_info[element].can_explode_impact = FALSE;
212 element_info[element].current_change_page = 0;
214 /* start with no properties at all */
215 for (j=0; j < NUM_EP_BITFIELDS; j++)
216 Properties[element][j] = EP_BITMASK_DEFAULT;
218 element_info[element].modified_settings = FALSE;
221 BorderElement = EL_STEELWALL;
223 level->no_level_file = FALSE;
225 if (leveldir_current == NULL) /* only when dumping level */
228 /* try to determine better author name than 'anonymous' */
229 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
231 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
232 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
236 switch (LEVELCLASS(leveldir_current))
238 case LEVELCLASS_TUTORIAL:
239 strcpy(level->author, PROGRAM_AUTHOR_STRING);
242 case LEVELCLASS_CONTRIBUTION:
243 strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
244 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
247 case LEVELCLASS_USER:
248 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
249 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
253 /* keep default value */
259 static void ActivateLevelTemplate()
261 /* Currently there is no special action needed to activate the template
262 data, because 'element_info' and 'Properties' overwrite the original
263 level data, while all other variables do not change. */
266 boolean LevelFileExists(int level_nr)
268 char *filename = getLevelFilename(level_nr);
270 return (access(filename, F_OK) == 0);
273 static int checkLevelElement(int element)
275 if (element >= NUM_FILE_ELEMENTS)
277 Error(ERR_WARN, "invalid level element %d", element);
278 element = EL_CHAR_QUESTION;
280 else if (element == EL_PLAYER_OBSOLETE)
281 element = EL_PLAYER_1;
282 else if (element == EL_KEY_OBSOLETE)
288 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
290 level->file_version = getFileVersion(file);
291 level->game_version = getFileVersion(file);
296 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
300 level->fieldx = getFile8Bit(file);
301 level->fieldy = getFile8Bit(file);
303 level->time = getFile16BitBE(file);
304 level->gems_needed = getFile16BitBE(file);
306 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
307 level->name[i] = getFile8Bit(file);
308 level->name[MAX_LEVEL_NAME_LEN] = 0;
310 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
311 level->score[i] = getFile8Bit(file);
313 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
314 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
317 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
319 level->amoeba_speed = getFile8Bit(file);
320 level->time_magic_wall = getFile8Bit(file);
321 level->time_wheel = getFile8Bit(file);
322 level->amoeba_content = checkLevelElement(getFile8Bit(file));
323 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
324 level->gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
325 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
326 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
328 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
330 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
335 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
339 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
340 level->author[i] = getFile8Bit(file);
341 level->author[MAX_LEVEL_NAME_LEN] = 0;
346 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
349 int chunk_size_expected = level->fieldx * level->fieldy;
351 /* Note: "chunk_size" was wrong before version 2.0 when elements are
352 stored with 16-bit encoding (and should be twice as big then).
353 Even worse, playfield data was stored 16-bit when only yamyam content
354 contained 16-bit elements and vice versa. */
356 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
357 chunk_size_expected *= 2;
359 if (chunk_size_expected != chunk_size)
361 ReadUnusedBytesFromFile(file, chunk_size);
362 return chunk_size_expected;
365 for(y=0; y<level->fieldy; y++)
366 for(x=0; x<level->fieldx; x++)
368 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
373 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
377 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
378 int chunk_size_expected = header_size + content_size;
380 /* Note: "chunk_size" was wrong before version 2.0 when elements are
381 stored with 16-bit encoding (and should be twice as big then).
382 Even worse, playfield data was stored 16-bit when only yamyam content
383 contained 16-bit elements and vice versa. */
385 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
386 chunk_size_expected += content_size;
388 if (chunk_size_expected != chunk_size)
390 ReadUnusedBytesFromFile(file, chunk_size);
391 return chunk_size_expected;
395 level->num_yamyam_contents = getFile8Bit(file);
399 /* correct invalid number of content fields -- should never happen */
400 if (level->num_yamyam_contents < 1 ||
401 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
402 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
404 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
407 level->yamyam_content[i][x][y] =
408 checkLevelElement(level->encoding_16bit_field ?
409 getFile16BitBE(file) : getFile8Bit(file));
413 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
417 int num_contents, content_xsize, content_ysize;
418 int content_array[MAX_ELEMENT_CONTENTS][3][3];
420 element = checkLevelElement(getFile16BitBE(file));
421 num_contents = getFile8Bit(file);
422 content_xsize = getFile8Bit(file);
423 content_ysize = getFile8Bit(file);
425 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
427 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
430 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
432 /* correct invalid number of content fields -- should never happen */
433 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
434 num_contents = STD_ELEMENT_CONTENTS;
436 if (element == EL_YAMYAM)
438 level->num_yamyam_contents = num_contents;
440 for(i=0; i<num_contents; i++)
443 level->yamyam_content[i][x][y] = content_array[i][x][y];
445 else if (element == EL_BD_AMOEBA)
447 level->amoeba_content = content_array[0][0][0];
451 Error(ERR_WARN, "cannot load content for element '%d'", element);
457 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
462 int chunk_size_expected;
464 element = checkLevelElement(getFile16BitBE(file));
465 envelope_len = getFile16BitBE(file);
466 level->envelope_xsize = getFile8Bit(file);
467 level->envelope_ysize = getFile8Bit(file);
469 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
471 chunk_size_expected = LEVEL_CHUNK_CNT3_HEADER + envelope_len;
473 if (chunk_size_expected != chunk_size)
475 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
476 return chunk_size_expected;
479 for(i=0; i < envelope_len; i++)
480 level->envelope[i] = getFile8Bit(file);
485 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
487 int num_changed_custom_elements = getFile16BitBE(file);
488 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
491 if (chunk_size_expected != chunk_size)
493 ReadUnusedBytesFromFile(file, chunk_size - 2);
494 return chunk_size_expected;
497 for (i=0; i < num_changed_custom_elements; i++)
499 int element = getFile16BitBE(file);
500 int properties = getFile32BitBE(file);
502 if (IS_CUSTOM_ELEMENT(element))
503 Properties[element][EP_BITFIELD_BASE] = properties;
505 Error(ERR_WARN, "invalid custom element number %d", element);
511 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
513 int num_changed_custom_elements = getFile16BitBE(file);
514 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
517 if (chunk_size_expected != chunk_size)
519 ReadUnusedBytesFromFile(file, chunk_size - 2);
520 return chunk_size_expected;
523 for (i=0; i < num_changed_custom_elements; i++)
525 int element = getFile16BitBE(file);
526 int custom_target_element = getFile16BitBE(file);
528 if (IS_CUSTOM_ELEMENT(element))
529 element_info[element].change->target_element = custom_target_element;
531 Error(ERR_WARN, "invalid custom element number %d", element);
537 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
539 int num_changed_custom_elements = getFile16BitBE(file);
540 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
543 if (chunk_size_expected != chunk_size)
545 ReadUnusedBytesFromFile(file, chunk_size - 2);
546 return chunk_size_expected;
549 for (i=0; i < num_changed_custom_elements; i++)
551 int element = getFile16BitBE(file);
553 if (!IS_CUSTOM_ELEMENT(element))
555 Error(ERR_WARN, "invalid custom element number %d", element);
557 element = EL_DEFAULT; /* dummy element used for artwork config */
560 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
561 element_info[element].description[j] = getFile8Bit(file);
562 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
564 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
566 /* some free bytes for future properties and padding */
567 ReadUnusedBytesFromFile(file, 7);
569 element_info[element].use_gfx_element = getFile8Bit(file);
570 element_info[element].gfx_element =
571 checkLevelElement(getFile16BitBE(file));
573 element_info[element].collect_score = getFile8Bit(file);
574 element_info[element].collect_count = getFile8Bit(file);
576 element_info[element].push_delay_fixed = getFile16BitBE(file);
577 element_info[element].push_delay_random = getFile16BitBE(file);
578 element_info[element].move_delay_fixed = getFile16BitBE(file);
579 element_info[element].move_delay_random = getFile16BitBE(file);
581 element_info[element].move_pattern = getFile16BitBE(file);
582 element_info[element].move_direction_initial = getFile8Bit(file);
583 element_info[element].move_stepsize = getFile8Bit(file);
587 element_info[element].content[x][y] =
588 checkLevelElement(getFile16BitBE(file));
590 element_info[element].change->events = getFile32BitBE(file);
592 element_info[element].change->target_element =
593 checkLevelElement(getFile16BitBE(file));
595 element_info[element].change->delay_fixed = getFile16BitBE(file);
596 element_info[element].change->delay_random = getFile16BitBE(file);
597 element_info[element].change->delay_frames = getFile16BitBE(file);
599 element_info[element].change->trigger_element =
600 checkLevelElement(getFile16BitBE(file));
602 element_info[element].change->explode = getFile8Bit(file);
603 element_info[element].change->use_content = getFile8Bit(file);
604 element_info[element].change->only_complete = getFile8Bit(file);
605 element_info[element].change->use_random_change = getFile8Bit(file);
607 element_info[element].change->random = getFile8Bit(file);
608 element_info[element].change->power = getFile8Bit(file);
612 element_info[element].change->content[x][y] =
613 checkLevelElement(getFile16BitBE(file));
615 element_info[element].slippery_type = getFile8Bit(file);
617 /* some free bytes for future properties and padding */
618 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
620 /* mark that this custom element has been modified */
621 element_info[element].modified_settings = TRUE;
627 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
629 struct ElementInfo *ei;
630 int chunk_size_expected;
634 element = getFile16BitBE(file);
636 if (!IS_CUSTOM_ELEMENT(element))
638 Error(ERR_WARN, "invalid custom element number %d", element);
640 element = EL_DEFAULT; /* dummy element used for artwork config */
643 ei = &element_info[element];
645 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
646 ei->description[i] = getFile8Bit(file);
647 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
649 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
650 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
652 ei->num_change_pages = getFile8Bit(file);
654 /* some free bytes for future base property values and padding */
655 ReadUnusedBytesFromFile(file, 5);
657 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
658 if (chunk_size_expected != chunk_size)
660 ReadUnusedBytesFromFile(file, chunk_size - 48);
661 return chunk_size_expected;
664 /* read custom property values */
666 ei->use_gfx_element = getFile8Bit(file);
667 ei->gfx_element = checkLevelElement(getFile16BitBE(file));
669 ei->collect_score = getFile8Bit(file);
670 ei->collect_count = getFile8Bit(file);
672 ei->push_delay_fixed = getFile16BitBE(file);
673 ei->push_delay_random = getFile16BitBE(file);
674 ei->move_delay_fixed = getFile16BitBE(file);
675 ei->move_delay_random = getFile16BitBE(file);
677 ei->move_pattern = getFile16BitBE(file);
678 ei->move_direction_initial = getFile8Bit(file);
679 ei->move_stepsize = getFile8Bit(file);
681 ei->slippery_type = getFile8Bit(file);
685 ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
687 /* some free bytes for future custom property values and padding */
688 ReadUnusedBytesFromFile(file, 12);
690 /* read change property values */
692 setElementChangePages(ei, ei->num_change_pages);
694 for (i=0; i < ei->num_change_pages; i++)
696 struct ElementChangeInfo *change = &ei->change_page[i];
698 /* always start with reliable default values */
699 setElementChangeInfoToDefaults(change);
701 change->events = getFile32BitBE(file);
703 change->target_element = checkLevelElement(getFile16BitBE(file));
705 change->delay_fixed = getFile16BitBE(file);
706 change->delay_random = getFile16BitBE(file);
707 change->delay_frames = getFile16BitBE(file);
709 change->trigger_element = checkLevelElement(getFile16BitBE(file));
711 change->explode = getFile8Bit(file);
712 change->use_content = getFile8Bit(file);
713 change->only_complete = getFile8Bit(file);
714 change->use_random_change = getFile8Bit(file);
716 change->random = getFile8Bit(file);
717 change->power = getFile8Bit(file);
721 change->content[x][y] = checkLevelElement(getFile16BitBE(file));
723 change->can_change = getFile8Bit(file);
725 /* some free bytes for future change property values and padding */
726 ReadUnusedBytesFromFile(file, 9);
729 /* mark this custom element as modified */
730 ei->modified_settings = TRUE;
735 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
737 char cookie[MAX_LINE_LEN];
738 char chunk_name[CHUNK_ID_LEN + 1];
742 /* always start with reliable default values */
743 setLevelInfoToDefaults(level);
745 if (!(file = fopen(filename, MODE_READ)))
747 level->no_level_file = TRUE;
749 if (level != &level_template)
750 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
755 getFileChunkBE(file, chunk_name, NULL);
756 if (strcmp(chunk_name, "RND1") == 0)
758 getFile32BitBE(file); /* not used */
760 getFileChunkBE(file, chunk_name, NULL);
761 if (strcmp(chunk_name, "CAVE") != 0)
763 Error(ERR_WARN, "unknown format of level file '%s'", filename);
768 else /* check for pre-2.0 file format with cookie string */
770 strcpy(cookie, chunk_name);
771 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
772 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
773 cookie[strlen(cookie) - 1] = '\0';
775 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
777 Error(ERR_WARN, "unknown format of level file '%s'", filename);
782 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
784 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
789 /* pre-2.0 level files have no game version, so use file version here */
790 level->game_version = level->file_version;
793 if (level->file_version < FILE_VERSION_1_2)
795 /* level files from versions before 1.2.0 without chunk structure */
796 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
797 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
805 int (*loader)(FILE *, int, struct LevelInfo *);
809 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
810 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
811 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
812 { "BODY", -1, LoadLevel_BODY },
813 { "CONT", -1, LoadLevel_CONT },
814 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
815 { "CNT3", -1, LoadLevel_CNT3 },
816 { "CUS1", -1, LoadLevel_CUS1 },
817 { "CUS2", -1, LoadLevel_CUS2 },
818 { "CUS3", -1, LoadLevel_CUS3 },
819 { "CUS4", -1, LoadLevel_CUS4 },
823 while (getFileChunkBE(file, chunk_name, &chunk_size))
827 while (chunk_info[i].name != NULL &&
828 strcmp(chunk_name, chunk_info[i].name) != 0)
831 if (chunk_info[i].name == NULL)
833 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
834 chunk_name, filename);
835 ReadUnusedBytesFromFile(file, chunk_size);
837 else if (chunk_info[i].size != -1 &&
838 chunk_info[i].size != chunk_size)
840 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
841 chunk_size, chunk_name, filename);
842 ReadUnusedBytesFromFile(file, chunk_size);
846 /* call function to load this level chunk */
847 int chunk_size_expected =
848 (chunk_info[i].loader)(file, chunk_size, level);
850 /* the size of some chunks cannot be checked before reading other
851 chunks first (like "HEAD" and "BODY") that contain some header
852 information, so check them here */
853 if (chunk_size_expected != chunk_size)
855 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
856 chunk_size, chunk_name, filename);
867 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
869 if (leveldir_current == NULL) /* only when dumping level */
872 /* determine correct game engine version of current level */
873 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
874 IS_LEVELCLASS_USER(leveldir_current))
877 printf("\n::: This level is private or contributed: '%s'\n", filename);
880 /* For user contributed and private levels, use the version of
881 the game engine the levels were created for.
882 Since 2.0.1, the game engine version is now directly stored
883 in the level file (chunk "VERS"), so there is no need anymore
884 to set the game version from the file version (except for old,
885 pre-2.0 levels, where the game version is still taken from the
886 file format version used to store the level -- see above). */
888 /* do some special adjustments to support older level versions */
889 if (level->file_version == FILE_VERSION_1_0)
891 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
892 Error(ERR_WARN, "using high speed movement for player");
894 /* player was faster than monsters in (pre-)1.0 levels */
895 level->double_speed = TRUE;
898 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
899 if (level->game_version == VERSION_IDENT(2,0,1))
900 level->em_slippery_gems = TRUE;
905 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
906 leveldir_current->sort_priority, filename);
909 /* Always use the latest version of the game engine for all but
910 user contributed and private levels; this allows for actual
911 corrections in the game engine to take effect for existing,
912 converted levels (from "classic" or other existing games) to
913 make the game emulation more accurate, while (hopefully) not
914 breaking existing levels created from other players. */
916 level->game_version = GAME_VERSION_ACTUAL;
918 /* Set special EM style gems behaviour: EM style gems slip down from
919 normal, steel and growing wall. As this is a more fundamental change,
920 it seems better to set the default behaviour to "off" (as it is more
921 natural) and make it configurable in the level editor (as a property
922 of gem style elements). Already existing converted levels (neither
923 private nor contributed levels) are changed to the new behaviour. */
925 if (level->file_version < FILE_VERSION_2_0)
926 level->em_slippery_gems = TRUE;
930 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
934 /* map custom element change events that have changed in newer versions
935 (these following values were accidentally changed in version 3.0.1) */
936 if (level->game_version <= VERSION_IDENT(3,0,0))
938 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
940 int element = EL_CUSTOM_START + i;
942 /* order of checking and copying events to be mapped is important */
943 for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
945 if (HAS_CHANGE_EVENT(element, j - 2))
947 SET_CHANGE_EVENT(element, j - 2, FALSE);
948 SET_CHANGE_EVENT(element, j, TRUE);
952 /* order of checking and copying events to be mapped is important */
953 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
955 if (HAS_CHANGE_EVENT(element, j - 1))
957 SET_CHANGE_EVENT(element, j - 1, FALSE);
958 SET_CHANGE_EVENT(element, j, TRUE);
964 /* some custom element change events get mapped since version 3.0.3 */
965 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
967 int element = EL_CUSTOM_START + i;
969 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
970 HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
972 SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
973 SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
975 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
979 /* initialize "can_change" field for old levels with only one change page */
980 if (level->game_version <= VERSION_IDENT(3,0,2))
982 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
984 int element = EL_CUSTOM_START + i;
986 if (CAN_CHANGE(element))
987 element_info[element].change->can_change = TRUE;
991 /* initialize element properties for level editor etc. */
992 InitElementPropertiesEngine(level->game_version);
995 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
999 /* map elements that have changed in newer versions */
1000 for(y=0; y<level->fieldy; y++)
1002 for(x=0; x<level->fieldx; x++)
1004 int element = level->field[x][y];
1006 if (level->game_version <= VERSION_IDENT(2,2,0))
1008 /* map game font elements */
1009 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1010 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1011 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1012 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1015 if (level->game_version < VERSION_IDENT(3,0,0))
1017 /* map Supaplex gravity tube elements */
1018 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1019 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1020 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1021 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1025 level->field[x][y] = element;
1029 /* copy elements to runtime playfield array */
1030 for(x=0; x<MAX_LEV_FIELDX; x++)
1031 for(y=0; y<MAX_LEV_FIELDY; y++)
1032 Feld[x][y] = level->field[x][y];
1034 /* initialize level size variables for faster access */
1035 lev_fieldx = level->fieldx;
1036 lev_fieldy = level->fieldy;
1038 /* determine border element for this level */
1044 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
1048 if (leveldir_current == NULL) /* only when dumping level */
1051 /* determine correct game engine version of current level */
1052 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
1053 IS_LEVELCLASS_USER(leveldir_current))
1056 printf("\n::: This level is private or contributed: '%s'\n", filename);
1059 /* For user contributed and private levels, use the version of
1060 the game engine the levels were created for.
1061 Since 2.0.1, the game engine version is now directly stored
1062 in the level file (chunk "VERS"), so there is no need anymore
1063 to set the game version from the file version (except for old,
1064 pre-2.0 levels, where the game version is still taken from the
1065 file format version used to store the level -- see above). */
1067 /* do some special adjustments to support older level versions */
1068 if (level->file_version == FILE_VERSION_1_0)
1070 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
1071 Error(ERR_WARN, "using high speed movement for player");
1073 /* player was faster than monsters in (pre-)1.0 levels */
1074 level->double_speed = TRUE;
1077 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1078 if (level->game_version == VERSION_IDENT(2,0,1))
1079 level->em_slippery_gems = TRUE;
1084 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1085 leveldir_current->sort_priority, filename);
1088 /* Always use the latest version of the game engine for all but
1089 user contributed and private levels; this allows for actual
1090 corrections in the game engine to take effect for existing,
1091 converted levels (from "classic" or other existing games) to
1092 make the game emulation more accurate, while (hopefully) not
1093 breaking existing levels created from other players. */
1095 level->game_version = GAME_VERSION_ACTUAL;
1097 /* Set special EM style gems behaviour: EM style gems slip down from
1098 normal, steel and growing wall. As this is a more fundamental change,
1099 it seems better to set the default behaviour to "off" (as it is more
1100 natural) and make it configurable in the level editor (as a property
1101 of gem style elements). Already existing converted levels (neither
1102 private nor contributed levels) are changed to the new behaviour. */
1104 if (level->file_version < FILE_VERSION_2_0)
1105 level->em_slippery_gems = TRUE;
1108 /* map elements that have changed in newer versions */
1109 for(y=0; y<level->fieldy; y++)
1111 for(x=0; x<level->fieldx; x++)
1113 int element = level->field[x][y];
1115 if (level->game_version <= VERSION_IDENT(2,2,0))
1117 /* map game font elements */
1118 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1119 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1120 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1121 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1124 if (level->game_version < VERSION_IDENT(3,0,0))
1126 /* map Supaplex gravity tube elements */
1127 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1128 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1129 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1130 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1134 level->field[x][y] = element;
1138 /* map custom element change events that have changed in newer versions
1139 (these following values have accidentally changed in version 3.0.1) */
1140 if (level->game_version <= VERSION_IDENT(3,0,0))
1142 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1144 int element = EL_CUSTOM_START + i;
1146 /* order of checking events to be mapped is important */
1147 for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
1149 if (HAS_CHANGE_EVENT(element, j - 2))
1151 SET_CHANGE_EVENT(element, j - 2, FALSE);
1152 SET_CHANGE_EVENT(element, j, TRUE);
1156 /* order of checking events to be mapped is important */
1157 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1159 if (HAS_CHANGE_EVENT(element, j - 1))
1161 SET_CHANGE_EVENT(element, j - 1, FALSE);
1162 SET_CHANGE_EVENT(element, j, TRUE);
1168 /* initialize "can_change" field for old levels with only one change page */
1169 if (level->game_version <= VERSION_IDENT(3,0,2))
1171 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1173 int element = EL_CUSTOM_START + i;
1175 if (CAN_CHANGE(element))
1176 element_info[element].change->can_change = TRUE;
1180 /* copy elements to runtime playfield array */
1181 for(x=0; x<MAX_LEV_FIELDX; x++)
1182 for(y=0; y<MAX_LEV_FIELDY; y++)
1183 Feld[x][y] = level->field[x][y];
1185 /* initialize level size variables for faster access */
1186 lev_fieldx = level->fieldx;
1187 lev_fieldy = level->fieldy;
1189 /* determine border element for this level */
1192 /* initialize element properties for level editor etc. */
1193 InitElementPropertiesEngine(level->game_version);
1198 void LoadLevelTemplate(int level_nr)
1200 char *filename = getLevelFilename(level_nr);
1202 LoadLevelFromFilename(&level_template, filename);
1204 LoadLevel_InitVersion(&level, filename);
1205 LoadLevel_InitElements(&level, filename);
1207 ActivateLevelTemplate();
1210 void LoadLevel(int level_nr)
1212 char *filename = getLevelFilename(level_nr);
1214 LoadLevelFromFilename(&level, filename);
1216 if (level.use_custom_template)
1217 LoadLevelTemplate(-1);
1220 LoadLevel_InitVersion(&level, filename);
1221 LoadLevel_InitElements(&level, filename);
1222 LoadLevel_InitPlayfield(&level, filename);
1224 LoadLevel_InitLevel(&level, filename);
1228 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1230 putFileVersion(file, level->file_version);
1231 putFileVersion(file, level->game_version);
1234 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1238 putFile8Bit(file, level->fieldx);
1239 putFile8Bit(file, level->fieldy);
1241 putFile16BitBE(file, level->time);
1242 putFile16BitBE(file, level->gems_needed);
1244 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1245 putFile8Bit(file, level->name[i]);
1247 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1248 putFile8Bit(file, level->score[i]);
1250 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1253 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1254 level->yamyam_content[i][x][y]));
1255 putFile8Bit(file, level->amoeba_speed);
1256 putFile8Bit(file, level->time_magic_wall);
1257 putFile8Bit(file, level->time_wheel);
1258 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1259 level->amoeba_content));
1260 putFile8Bit(file, (level->double_speed ? 1 : 0));
1261 putFile8Bit(file, (level->gravity ? 1 : 0));
1262 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1263 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1265 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1267 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1270 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1274 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1275 putFile8Bit(file, level->author[i]);
1278 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1282 for(y=0; y<level->fieldy; y++)
1283 for(x=0; x<level->fieldx; x++)
1284 if (level->encoding_16bit_field)
1285 putFile16BitBE(file, level->field[x][y]);
1287 putFile8Bit(file, level->field[x][y]);
1291 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1295 putFile8Bit(file, EL_YAMYAM);
1296 putFile8Bit(file, level->num_yamyam_contents);
1297 putFile8Bit(file, 0);
1298 putFile8Bit(file, 0);
1300 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1303 if (level->encoding_16bit_field)
1304 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1306 putFile8Bit(file, level->yamyam_content[i][x][y]);
1310 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1313 int num_contents, content_xsize, content_ysize;
1314 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1316 if (element == EL_YAMYAM)
1318 num_contents = level->num_yamyam_contents;
1322 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1325 content_array[i][x][y] = level->yamyam_content[i][x][y];
1327 else if (element == EL_BD_AMOEBA)
1333 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1336 content_array[i][x][y] = EL_EMPTY;
1337 content_array[0][0][0] = level->amoeba_content;
1341 /* chunk header already written -- write empty chunk data */
1342 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1344 Error(ERR_WARN, "cannot save content for element '%d'", element);
1348 putFile16BitBE(file, element);
1349 putFile8Bit(file, num_contents);
1350 putFile8Bit(file, content_xsize);
1351 putFile8Bit(file, content_ysize);
1353 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1355 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1358 putFile16BitBE(file, content_array[i][x][y]);
1361 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1364 int envelope_len = strlen(level->envelope) + 1;
1366 putFile16BitBE(file, element);
1367 putFile16BitBE(file, envelope_len);
1368 putFile8Bit(file, level->envelope_xsize);
1369 putFile8Bit(file, level->envelope_ysize);
1371 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1373 for(i=0; i < envelope_len; i++)
1374 putFile8Bit(file, level->envelope[i]);
1378 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1379 int num_changed_custom_elements)
1383 putFile16BitBE(file, num_changed_custom_elements);
1385 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1387 int element = EL_CUSTOM_START + i;
1389 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1391 if (check < num_changed_custom_elements)
1393 putFile16BitBE(file, element);
1394 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1401 if (check != num_changed_custom_elements) /* should not happen */
1402 Error(ERR_WARN, "inconsistent number of custom element properties");
1407 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1408 int num_changed_custom_elements)
1412 putFile16BitBE(file, num_changed_custom_elements);
1414 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1416 int element = EL_CUSTOM_START + i;
1418 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1420 if (check < num_changed_custom_elements)
1422 putFile16BitBE(file, element);
1423 putFile16BitBE(file, element_info[element].change->target_element);
1430 if (check != num_changed_custom_elements) /* should not happen */
1431 Error(ERR_WARN, "inconsistent number of custom target elements");
1436 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1437 int num_changed_custom_elements)
1439 int i, j, x, y, check = 0;
1441 putFile16BitBE(file, num_changed_custom_elements);
1443 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1445 int element = EL_CUSTOM_START + i;
1447 if (element_info[element].modified_settings)
1449 if (check < num_changed_custom_elements)
1451 putFile16BitBE(file, element);
1453 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1454 putFile8Bit(file, element_info[element].description[j]);
1456 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1458 /* some free bytes for future properties and padding */
1459 WriteUnusedBytesToFile(file, 7);
1461 putFile8Bit(file, element_info[element].use_gfx_element);
1462 putFile16BitBE(file, element_info[element].gfx_element);
1464 putFile8Bit(file, element_info[element].collect_score);
1465 putFile8Bit(file, element_info[element].collect_count);
1467 putFile16BitBE(file, element_info[element].push_delay_fixed);
1468 putFile16BitBE(file, element_info[element].push_delay_random);
1469 putFile16BitBE(file, element_info[element].move_delay_fixed);
1470 putFile16BitBE(file, element_info[element].move_delay_random);
1472 putFile16BitBE(file, element_info[element].move_pattern);
1473 putFile8Bit(file, element_info[element].move_direction_initial);
1474 putFile8Bit(file, element_info[element].move_stepsize);
1478 putFile16BitBE(file, element_info[element].content[x][y]);
1480 putFile32BitBE(file, element_info[element].change->events);
1482 putFile16BitBE(file, element_info[element].change->target_element);
1484 putFile16BitBE(file, element_info[element].change->delay_fixed);
1485 putFile16BitBE(file, element_info[element].change->delay_random);
1486 putFile16BitBE(file, element_info[element].change->delay_frames);
1488 putFile16BitBE(file, element_info[element].change->trigger_element);
1490 putFile8Bit(file, element_info[element].change->explode);
1491 putFile8Bit(file, element_info[element].change->use_content);
1492 putFile8Bit(file, element_info[element].change->only_complete);
1493 putFile8Bit(file, element_info[element].change->use_random_change);
1495 putFile8Bit(file, element_info[element].change->random);
1496 putFile8Bit(file, element_info[element].change->power);
1500 putFile16BitBE(file, element_info[element].change->content[x][y]);
1502 putFile8Bit(file, element_info[element].slippery_type);
1504 /* some free bytes for future properties and padding */
1505 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1512 if (check != num_changed_custom_elements) /* should not happen */
1513 Error(ERR_WARN, "inconsistent number of custom element properties");
1517 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1519 struct ElementInfo *ei = &element_info[element];
1522 putFile16BitBE(file, element);
1524 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
1525 putFile8Bit(file, ei->description[i]);
1527 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1528 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
1530 putFile8Bit(file, ei->num_change_pages);
1532 /* some free bytes for future base property values and padding */
1533 WriteUnusedBytesToFile(file, 5);
1535 /* write custom property values */
1537 putFile8Bit(file, ei->use_gfx_element);
1538 putFile16BitBE(file, ei->gfx_element);
1540 putFile8Bit(file, ei->collect_score);
1541 putFile8Bit(file, ei->collect_count);
1543 putFile16BitBE(file, ei->push_delay_fixed);
1544 putFile16BitBE(file, ei->push_delay_random);
1545 putFile16BitBE(file, ei->move_delay_fixed);
1546 putFile16BitBE(file, ei->move_delay_random);
1548 putFile16BitBE(file, ei->move_pattern);
1549 putFile8Bit(file, ei->move_direction_initial);
1550 putFile8Bit(file, ei->move_stepsize);
1552 putFile8Bit(file, ei->slippery_type);
1556 putFile16BitBE(file, ei->content[x][y]);
1558 /* some free bytes for future custom property values and padding */
1559 WriteUnusedBytesToFile(file, 12);
1561 /* write change property values */
1563 for (i=0; i < ei->num_change_pages; i++)
1565 struct ElementChangeInfo *change = &ei->change_page[i];
1567 putFile32BitBE(file, change->events);
1569 putFile16BitBE(file, change->target_element);
1571 putFile16BitBE(file, change->delay_fixed);
1572 putFile16BitBE(file, change->delay_random);
1573 putFile16BitBE(file, change->delay_frames);
1575 putFile16BitBE(file, change->trigger_element);
1577 putFile8Bit(file, change->explode);
1578 putFile8Bit(file, change->use_content);
1579 putFile8Bit(file, change->only_complete);
1580 putFile8Bit(file, change->use_random_change);
1582 putFile8Bit(file, change->random);
1583 putFile8Bit(file, change->power);
1587 putFile16BitBE(file, change->content[x][y]);
1589 putFile8Bit(file, change->can_change);
1591 /* some free bytes for future change property values and padding */
1592 WriteUnusedBytesToFile(file, 9);
1596 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1598 int body_chunk_size;
1602 if (!(file = fopen(filename, MODE_WRITE)))
1604 Error(ERR_WARN, "cannot save level file '%s'", filename);
1608 level->file_version = FILE_VERSION_ACTUAL;
1609 level->game_version = GAME_VERSION_ACTUAL;
1611 /* check level field for 16-bit elements */
1612 level->encoding_16bit_field = FALSE;
1613 for(y=0; y<level->fieldy; y++)
1614 for(x=0; x<level->fieldx; x++)
1615 if (level->field[x][y] > 255)
1616 level->encoding_16bit_field = TRUE;
1618 /* check yamyam content for 16-bit elements */
1619 level->encoding_16bit_yamyam = FALSE;
1620 for(i=0; i<level->num_yamyam_contents; i++)
1623 if (level->yamyam_content[i][x][y] > 255)
1624 level->encoding_16bit_yamyam = TRUE;
1626 /* check amoeba content for 16-bit elements */
1627 level->encoding_16bit_amoeba = FALSE;
1628 if (level->amoeba_content > 255)
1629 level->encoding_16bit_amoeba = TRUE;
1631 /* calculate size of "BODY" chunk */
1633 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1635 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1636 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1638 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1639 SaveLevel_VERS(file, level);
1641 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1642 SaveLevel_HEAD(file, level);
1644 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1645 SaveLevel_AUTH(file, level);
1647 putFileChunkBE(file, "BODY", body_chunk_size);
1648 SaveLevel_BODY(file, level);
1650 if (level->encoding_16bit_yamyam ||
1651 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1653 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1654 SaveLevel_CNT2(file, level, EL_YAMYAM);
1657 if (level->encoding_16bit_amoeba)
1659 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1660 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1663 /* check for envelope content */
1664 if (strlen(level->envelope) > 0)
1666 int envelope_len = strlen(level->envelope) + 1;
1668 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
1669 SaveLevel_CNT3(file, level, EL_ENVELOPE);
1672 /* check for non-default custom elements (unless using template level) */
1673 if (!level->use_custom_template)
1675 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1677 int element = EL_CUSTOM_START + i;
1679 if (element_info[element].modified_settings)
1681 int num_change_pages = element_info[element].num_change_pages;
1683 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
1684 SaveLevel_CUS4(file, level, element);
1691 SetFilePermissions(filename, PERMS_PRIVATE);
1694 void SaveLevel(int level_nr)
1696 char *filename = getLevelFilename(level_nr);
1698 SaveLevelFromFilename(&level, filename);
1701 void SaveLevelTemplate()
1703 char *filename = getLevelFilename(-1);
1705 SaveLevelFromFilename(&level, filename);
1708 void DumpLevel(struct LevelInfo *level)
1710 printf_line("-", 79);
1711 printf("Level xxx (file version %08d, game version %08d)\n",
1712 level->file_version, level->game_version);
1713 printf_line("-", 79);
1715 printf("Level Author: '%s'\n", level->author);
1716 printf("Level Title: '%s'\n", level->name);
1718 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1720 printf("Level Time: %d seconds\n", level->time);
1721 printf("Gems needed: %d\n", level->gems_needed);
1723 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1724 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1725 printf("Time for Light: %d seconds\n", level->time_light);
1726 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1728 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1730 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1731 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1732 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1734 printf_line("-", 79);
1738 /* ========================================================================= */
1739 /* tape file functions */
1740 /* ========================================================================= */
1742 static void setTapeInfoToDefaults()
1746 /* always start with reliable default values (empty tape) */
1749 /* default values (also for pre-1.2 tapes) with only the first player */
1750 tape.player_participates[0] = TRUE;
1751 for(i=1; i<MAX_PLAYERS; i++)
1752 tape.player_participates[i] = FALSE;
1754 /* at least one (default: the first) player participates in every tape */
1755 tape.num_participating_players = 1;
1757 tape.level_nr = level_nr;
1759 tape.changed = FALSE;
1761 tape.recording = FALSE;
1762 tape.playing = FALSE;
1763 tape.pausing = FALSE;
1766 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1768 tape->file_version = getFileVersion(file);
1769 tape->game_version = getFileVersion(file);
1774 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1778 tape->random_seed = getFile32BitBE(file);
1779 tape->date = getFile32BitBE(file);
1780 tape->length = getFile32BitBE(file);
1782 /* read header fields that are new since version 1.2 */
1783 if (tape->file_version >= FILE_VERSION_1_2)
1785 byte store_participating_players = getFile8Bit(file);
1788 /* since version 1.2, tapes store which players participate in the tape */
1789 tape->num_participating_players = 0;
1790 for(i=0; i<MAX_PLAYERS; i++)
1792 tape->player_participates[i] = FALSE;
1794 if (store_participating_players & (1 << i))
1796 tape->player_participates[i] = TRUE;
1797 tape->num_participating_players++;
1801 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1803 engine_version = getFileVersion(file);
1804 if (engine_version > 0)
1805 tape->engine_version = engine_version;
1807 tape->engine_version = tape->game_version;
1813 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1815 int level_identifier_size;
1818 level_identifier_size = getFile16BitBE(file);
1820 tape->level_identifier =
1821 checked_realloc(tape->level_identifier, level_identifier_size);
1823 for(i=0; i < level_identifier_size; i++)
1824 tape->level_identifier[i] = getFile8Bit(file);
1826 tape->level_nr = getFile16BitBE(file);
1828 chunk_size = 2 + level_identifier_size + 2;
1833 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1836 int chunk_size_expected =
1837 (tape->num_participating_players + 1) * tape->length;
1839 if (chunk_size_expected != chunk_size)
1841 ReadUnusedBytesFromFile(file, chunk_size);
1842 return chunk_size_expected;
1845 for(i=0; i<tape->length; i++)
1847 if (i >= MAX_TAPELEN)
1850 for(j=0; j<MAX_PLAYERS; j++)
1852 tape->pos[i].action[j] = MV_NO_MOVING;
1854 if (tape->player_participates[j])
1855 tape->pos[i].action[j] = getFile8Bit(file);
1858 tape->pos[i].delay = getFile8Bit(file);
1860 if (tape->file_version == FILE_VERSION_1_0)
1862 /* eliminate possible diagonal moves in old tapes */
1863 /* this is only for backward compatibility */
1865 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1866 byte action = tape->pos[i].action[0];
1867 int k, num_moves = 0;
1871 if (action & joy_dir[k])
1873 tape->pos[i + num_moves].action[0] = joy_dir[k];
1875 tape->pos[i + num_moves].delay = 0;
1884 tape->length += num_moves;
1887 else if (tape->file_version < FILE_VERSION_2_0)
1889 /* convert pre-2.0 tapes to new tape format */
1891 if (tape->pos[i].delay > 1)
1894 tape->pos[i + 1] = tape->pos[i];
1895 tape->pos[i + 1].delay = 1;
1898 for(j=0; j<MAX_PLAYERS; j++)
1899 tape->pos[i].action[j] = MV_NO_MOVING;
1900 tape->pos[i].delay--;
1911 if (i != tape->length)
1912 chunk_size = (tape->num_participating_players + 1) * i;
1917 void LoadTapeFromFilename(char *filename)
1919 char cookie[MAX_LINE_LEN];
1920 char chunk_name[CHUNK_ID_LEN + 1];
1924 /* always start with reliable default values */
1925 setTapeInfoToDefaults();
1927 if (!(file = fopen(filename, MODE_READ)))
1930 getFileChunkBE(file, chunk_name, NULL);
1931 if (strcmp(chunk_name, "RND1") == 0)
1933 getFile32BitBE(file); /* not used */
1935 getFileChunkBE(file, chunk_name, NULL);
1936 if (strcmp(chunk_name, "TAPE") != 0)
1938 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1943 else /* check for pre-2.0 file format with cookie string */
1945 strcpy(cookie, chunk_name);
1946 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1947 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1948 cookie[strlen(cookie) - 1] = '\0';
1950 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1952 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1957 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1959 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1964 /* pre-2.0 tape files have no game version, so use file version here */
1965 tape.game_version = tape.file_version;
1968 if (tape.file_version < FILE_VERSION_1_2)
1970 /* tape files from versions before 1.2.0 without chunk structure */
1971 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1972 LoadTape_BODY(file, 2 * tape.length, &tape);
1980 int (*loader)(FILE *, int, struct TapeInfo *);
1984 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1985 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1986 { "INFO", -1, LoadTape_INFO },
1987 { "BODY", -1, LoadTape_BODY },
1991 while (getFileChunkBE(file, chunk_name, &chunk_size))
1995 while (chunk_info[i].name != NULL &&
1996 strcmp(chunk_name, chunk_info[i].name) != 0)
1999 if (chunk_info[i].name == NULL)
2001 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
2002 chunk_name, filename);
2003 ReadUnusedBytesFromFile(file, chunk_size);
2005 else if (chunk_info[i].size != -1 &&
2006 chunk_info[i].size != chunk_size)
2008 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2009 chunk_size, chunk_name, filename);
2010 ReadUnusedBytesFromFile(file, chunk_size);
2014 /* call function to load this tape chunk */
2015 int chunk_size_expected =
2016 (chunk_info[i].loader)(file, chunk_size, &tape);
2018 /* the size of some chunks cannot be checked before reading other
2019 chunks first (like "HEAD" and "BODY") that contain some header
2020 information, so check them here */
2021 if (chunk_size_expected != chunk_size)
2023 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2024 chunk_size, chunk_name, filename);
2032 tape.length_seconds = GetTapeLength();
2035 printf("tape game version: %d\n", tape.game_version);
2036 printf("tape engine version: %d\n", tape.engine_version);
2040 void LoadTape(int level_nr)
2042 char *filename = getTapeFilename(level_nr);
2044 LoadTapeFromFilename(filename);
2047 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2049 putFileVersion(file, tape->file_version);
2050 putFileVersion(file, tape->game_version);
2053 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2056 byte store_participating_players = 0;
2058 /* set bits for participating players for compact storage */
2059 for(i=0; i<MAX_PLAYERS; i++)
2060 if (tape->player_participates[i])
2061 store_participating_players |= (1 << i);
2063 putFile32BitBE(file, tape->random_seed);
2064 putFile32BitBE(file, tape->date);
2065 putFile32BitBE(file, tape->length);
2067 putFile8Bit(file, store_participating_players);
2069 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2070 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2072 putFileVersion(file, tape->engine_version);
2075 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2077 int level_identifier_size = strlen(tape->level_identifier) + 1;
2080 putFile16BitBE(file, level_identifier_size);
2082 for(i=0; i < level_identifier_size; i++)
2083 putFile8Bit(file, tape->level_identifier[i]);
2085 putFile16BitBE(file, tape->level_nr);
2088 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2092 for(i=0; i<tape->length; i++)
2094 for(j=0; j<MAX_PLAYERS; j++)
2095 if (tape->player_participates[j])
2096 putFile8Bit(file, tape->pos[i].action[j]);
2098 putFile8Bit(file, tape->pos[i].delay);
2102 void SaveTape(int level_nr)
2104 char *filename = getTapeFilename(level_nr);
2106 boolean new_tape = TRUE;
2107 int num_participating_players = 0;
2108 int info_chunk_size;
2109 int body_chunk_size;
2112 InitTapeDirectory(leveldir_current->filename);
2114 /* if a tape still exists, ask to overwrite it */
2115 if (access(filename, F_OK) == 0)
2118 if (!Request("Replace old tape ?", REQ_ASK))
2122 if (!(file = fopen(filename, MODE_WRITE)))
2124 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2128 tape.file_version = FILE_VERSION_ACTUAL;
2129 tape.game_version = GAME_VERSION_ACTUAL;
2131 /* count number of participating players */
2132 for(i=0; i<MAX_PLAYERS; i++)
2133 if (tape.player_participates[i])
2134 num_participating_players++;
2136 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2137 body_chunk_size = (num_participating_players + 1) * tape.length;
2139 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2140 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2142 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2143 SaveTape_VERS(file, &tape);
2145 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2146 SaveTape_HEAD(file, &tape);
2148 putFileChunkBE(file, "INFO", info_chunk_size);
2149 SaveTape_INFO(file, &tape);
2151 putFileChunkBE(file, "BODY", body_chunk_size);
2152 SaveTape_BODY(file, &tape);
2156 SetFilePermissions(filename, PERMS_PRIVATE);
2158 tape.changed = FALSE;
2161 Request("tape saved !", REQ_CONFIRM);
2164 void DumpTape(struct TapeInfo *tape)
2168 if (TAPE_IS_EMPTY(*tape))
2170 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2174 printf_line("-", 79);
2175 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2176 tape->level_nr, tape->file_version, tape->game_version);
2177 printf("Level series identifier: '%s'\n", tape->level_identifier);
2178 printf_line("-", 79);
2180 for(i=0; i<tape->length; i++)
2182 if (i >= MAX_TAPELEN)
2185 printf("%03d: ", i);
2187 for(j=0; j<MAX_PLAYERS; j++)
2189 if (tape->player_participates[j])
2191 int action = tape->pos[i].action[j];
2193 printf("%d:%02x ", j, action);
2194 printf("[%c%c%c%c|%c%c] - ",
2195 (action & JOY_LEFT ? '<' : ' '),
2196 (action & JOY_RIGHT ? '>' : ' '),
2197 (action & JOY_UP ? '^' : ' '),
2198 (action & JOY_DOWN ? 'v' : ' '),
2199 (action & JOY_BUTTON_1 ? '1' : ' '),
2200 (action & JOY_BUTTON_2 ? '2' : ' '));
2204 printf("(%03d)\n", tape->pos[i].delay);
2207 printf_line("-", 79);
2211 /* ========================================================================= */
2212 /* score file functions */
2213 /* ========================================================================= */
2215 void LoadScore(int level_nr)
2218 char *filename = getScoreFilename(level_nr);
2219 char cookie[MAX_LINE_LEN];
2220 char line[MAX_LINE_LEN];
2224 /* always start with reliable default values */
2225 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2227 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2228 highscore[i].Score = 0;
2231 if (!(file = fopen(filename, MODE_READ)))
2234 /* check file identifier */
2235 fgets(cookie, MAX_LINE_LEN, file);
2236 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2237 cookie[strlen(cookie) - 1] = '\0';
2239 if (!checkCookieString(cookie, SCORE_COOKIE))
2241 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2246 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2248 fscanf(file, "%d", &highscore[i].Score);
2249 fgets(line, MAX_LINE_LEN, file);
2251 if (line[strlen(line) - 1] == '\n')
2252 line[strlen(line) - 1] = '\0';
2254 for (line_ptr = line; *line_ptr; line_ptr++)
2256 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2258 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2259 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2268 void SaveScore(int level_nr)
2271 char *filename = getScoreFilename(level_nr);
2274 InitScoreDirectory(leveldir_current->filename);
2276 if (!(file = fopen(filename, MODE_WRITE)))
2278 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2282 fprintf(file, "%s\n\n", SCORE_COOKIE);
2284 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2285 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2289 SetFilePermissions(filename, PERMS_PUBLIC);
2293 /* ========================================================================= */
2294 /* setup file functions */
2295 /* ========================================================================= */
2297 #define TOKEN_STR_PLAYER_PREFIX "player_"
2300 #define SETUP_TOKEN_PLAYER_NAME 0
2301 #define SETUP_TOKEN_SOUND 1
2302 #define SETUP_TOKEN_SOUND_LOOPS 2
2303 #define SETUP_TOKEN_SOUND_MUSIC 3
2304 #define SETUP_TOKEN_SOUND_SIMPLE 4
2305 #define SETUP_TOKEN_TOONS 5
2306 #define SETUP_TOKEN_SCROLL_DELAY 6
2307 #define SETUP_TOKEN_SOFT_SCROLLING 7
2308 #define SETUP_TOKEN_FADING 8
2309 #define SETUP_TOKEN_AUTORECORD 9
2310 #define SETUP_TOKEN_QUICK_DOORS 10
2311 #define SETUP_TOKEN_TEAM_MODE 11
2312 #define SETUP_TOKEN_HANDICAP 12
2313 #define SETUP_TOKEN_TIME_LIMIT 13
2314 #define SETUP_TOKEN_FULLSCREEN 14
2315 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2316 #define SETUP_TOKEN_GRAPHICS_SET 16
2317 #define SETUP_TOKEN_SOUNDS_SET 17
2318 #define SETUP_TOKEN_MUSIC_SET 18
2319 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2320 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2321 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2323 #define NUM_GLOBAL_SETUP_TOKENS 22
2326 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2327 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2328 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2329 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2330 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2331 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2332 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2333 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2334 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2335 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2336 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2338 #define NUM_EDITOR_SETUP_TOKENS 11
2340 /* shortcut setup */
2341 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2342 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2343 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2345 #define NUM_SHORTCUT_SETUP_TOKENS 3
2348 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2349 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2350 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2351 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2352 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2353 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2354 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2355 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2356 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2357 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
2358 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2359 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2360 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2361 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2362 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2363 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
2365 #define NUM_PLAYER_SETUP_TOKENS 16
2368 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2369 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2371 #define NUM_SYSTEM_SETUP_TOKENS 2
2374 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2376 #define NUM_OPTIONS_SETUP_TOKENS 1
2379 static struct SetupInfo si;
2380 static struct SetupEditorInfo sei;
2381 static struct SetupShortcutInfo ssi;
2382 static struct SetupInputInfo sii;
2383 static struct SetupSystemInfo syi;
2384 static struct OptionInfo soi;
2386 static struct TokenInfo global_setup_tokens[] =
2388 { TYPE_STRING, &si.player_name, "player_name" },
2389 { TYPE_SWITCH, &si.sound, "sound" },
2390 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2391 { TYPE_SWITCH, &si.sound_music, "background_music" },
2392 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2393 { TYPE_SWITCH, &si.toons, "toons" },
2394 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2395 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2396 { TYPE_SWITCH, &si.fading, "screen_fading" },
2397 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2398 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2399 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2400 { TYPE_SWITCH, &si.handicap, "handicap" },
2401 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2402 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2403 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2404 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2405 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2406 { TYPE_STRING, &si.music_set, "music_set" },
2407 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2408 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2409 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2412 static struct TokenInfo editor_setup_tokens[] =
2414 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2415 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2416 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2417 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2418 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2419 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2420 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2421 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2422 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2423 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2424 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2427 static struct TokenInfo shortcut_setup_tokens[] =
2429 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2430 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2431 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2434 static struct TokenInfo player_setup_tokens[] =
2436 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2437 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2438 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2439 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2440 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2441 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2442 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2443 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2444 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2445 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2446 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2447 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2448 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2449 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2450 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2451 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
2454 static struct TokenInfo system_setup_tokens[] =
2456 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2457 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2460 static struct TokenInfo options_setup_tokens[] =
2462 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2465 static char *get_corrected_login_name(char *login_name)
2467 /* needed because player name must be a fixed length string */
2468 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2470 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2471 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2473 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2474 if (strchr(login_name_new, ' '))
2475 *strchr(login_name_new, ' ') = '\0';
2477 return login_name_new;
2480 static void setSetupInfoToDefaults(struct SetupInfo *si)
2484 si->player_name = get_corrected_login_name(getLoginName());
2487 si->sound_loops = TRUE;
2488 si->sound_music = TRUE;
2489 si->sound_simple = TRUE;
2491 si->double_buffering = TRUE;
2492 si->direct_draw = !si->double_buffering;
2493 si->scroll_delay = TRUE;
2494 si->soft_scrolling = TRUE;
2496 si->autorecord = TRUE;
2497 si->quick_doors = FALSE;
2498 si->team_mode = FALSE;
2499 si->handicap = TRUE;
2500 si->time_limit = TRUE;
2501 si->fullscreen = FALSE;
2502 si->ask_on_escape = TRUE;
2504 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2505 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2506 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2507 si->override_level_graphics = FALSE;
2508 si->override_level_sounds = FALSE;
2509 si->override_level_music = FALSE;
2511 si->editor.el_boulderdash = TRUE;
2512 si->editor.el_emerald_mine = TRUE;
2513 si->editor.el_more = TRUE;
2514 si->editor.el_sokoban = TRUE;
2515 si->editor.el_supaplex = TRUE;
2516 si->editor.el_diamond_caves = TRUE;
2517 si->editor.el_dx_boulderdash = TRUE;
2518 si->editor.el_chars = TRUE;
2519 si->editor.el_custom = TRUE;
2520 si->editor.el_custom_more = FALSE;
2522 si->editor.el_headlines = TRUE;
2524 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2525 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2526 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2528 for (i=0; i<MAX_PLAYERS; i++)
2530 si->input[i].use_joystick = FALSE;
2531 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2532 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2533 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2534 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2535 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2536 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2537 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2538 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2539 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2540 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2541 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2542 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2543 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2544 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2545 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2548 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2549 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2551 si->options.verbose = FALSE;
2554 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2558 if (!setup_file_hash)
2563 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2564 setSetupInfo(global_setup_tokens, i,
2565 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2570 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2571 setSetupInfo(editor_setup_tokens, i,
2572 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2575 /* shortcut setup */
2576 ssi = setup.shortcut;
2577 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2578 setSetupInfo(shortcut_setup_tokens, i,
2579 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2580 setup.shortcut = ssi;
2583 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2587 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2589 sii = setup.input[pnr];
2590 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2592 char full_token[100];
2594 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2595 setSetupInfo(player_setup_tokens, i,
2596 getHashEntry(setup_file_hash, full_token));
2598 setup.input[pnr] = sii;
2603 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2604 setSetupInfo(system_setup_tokens, i,
2605 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2609 soi = setup.options;
2610 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2611 setSetupInfo(options_setup_tokens, i,
2612 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2613 setup.options = soi;
2618 char *filename = getSetupFilename();
2619 SetupFileHash *setup_file_hash = NULL;
2621 /* always start with reliable default values */
2622 setSetupInfoToDefaults(&setup);
2624 setup_file_hash = loadSetupFileHash(filename);
2626 if (setup_file_hash)
2628 char *player_name_new;
2630 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2631 decodeSetupFileHash(setup_file_hash);
2633 setup.direct_draw = !setup.double_buffering;
2635 freeSetupFileHash(setup_file_hash);
2637 /* needed to work around problems with fixed length strings */
2638 player_name_new = get_corrected_login_name(setup.player_name);
2639 free(setup.player_name);
2640 setup.player_name = player_name_new;
2643 Error(ERR_WARN, "using default setup values");
2648 char *filename = getSetupFilename();
2652 InitUserDataDirectory();
2654 if (!(file = fopen(filename, MODE_WRITE)))
2656 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2660 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2661 getCookie("SETUP")));
2662 fprintf(file, "\n");
2666 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2668 /* just to make things nicer :) */
2669 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2670 i == SETUP_TOKEN_GRAPHICS_SET)
2671 fprintf(file, "\n");
2673 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2678 fprintf(file, "\n");
2679 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2680 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2682 /* shortcut setup */
2683 ssi = setup.shortcut;
2684 fprintf(file, "\n");
2685 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2686 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2689 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2693 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2694 fprintf(file, "\n");
2696 sii = setup.input[pnr];
2697 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2698 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2703 fprintf(file, "\n");
2704 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2705 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2708 soi = setup.options;
2709 fprintf(file, "\n");
2710 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2711 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2715 SetFilePermissions(filename, PERMS_PRIVATE);
2718 void LoadCustomElementDescriptions()
2720 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2721 SetupFileHash *setup_file_hash;
2724 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2726 if (element_info[i].custom_description != NULL)
2728 free(element_info[i].custom_description);
2729 element_info[i].custom_description = NULL;
2733 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2736 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2738 char *token = getStringCat2(element_info[i].token_name, ".name");
2739 char *value = getHashEntry(setup_file_hash, token);
2742 element_info[i].custom_description = getStringCopy(value);
2747 freeSetupFileHash(setup_file_hash);
2750 void LoadSpecialMenuDesignSettings()
2752 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2753 SetupFileHash *setup_file_hash;
2756 /* always start with reliable default values from default config */
2757 for (i=0; image_config_vars[i].token != NULL; i++)
2758 for (j=0; image_config[j].token != NULL; j++)
2759 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2760 *image_config_vars[i].value =
2761 get_integer_from_string(image_config[j].value);
2763 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2766 /* special case: initialize with default values that may be overwritten */
2767 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2769 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2770 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2771 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2773 if (value_x != NULL)
2774 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2775 if (value_y != NULL)
2776 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2777 if (list_size != NULL)
2778 menu.list_size[i] = get_integer_from_string(list_size);
2781 /* read (and overwrite with) values that may be specified in config file */
2782 for (i=0; image_config_vars[i].token != NULL; i++)
2784 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2787 *image_config_vars[i].value = get_integer_from_string(value);
2790 freeSetupFileHash(setup_file_hash);