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 for (i=0; i < ei->num_change_pages; i++)
694 struct ElementChangeInfo *change = &ei->change_page[i];
696 change->events = getFile32BitBE(file);
698 change->target_element = checkLevelElement(getFile16BitBE(file));
700 change->delay_fixed = getFile16BitBE(file);
701 change->delay_random = getFile16BitBE(file);
702 change->delay_frames = getFile16BitBE(file);
704 change->trigger_element = checkLevelElement(getFile16BitBE(file));
706 change->explode = getFile8Bit(file);
707 change->use_content = getFile8Bit(file);
708 change->only_complete = getFile8Bit(file);
709 change->use_random_change = getFile8Bit(file);
711 change->random = getFile8Bit(file);
712 change->power = getFile8Bit(file);
716 change->content[x][y] = checkLevelElement(getFile16BitBE(file));
718 change->can_change = getFile8Bit(file);
720 /* some free bytes for future change property values and padding */
721 ReadUnusedBytesFromFile(file, 9);
724 /* mark this custom element as modified */
725 ei->modified_settings = TRUE;
730 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
732 char cookie[MAX_LINE_LEN];
733 char chunk_name[CHUNK_ID_LEN + 1];
737 /* always start with reliable default values */
738 setLevelInfoToDefaults(level);
740 if (!(file = fopen(filename, MODE_READ)))
742 level->no_level_file = TRUE;
744 if (level != &level_template)
745 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
750 getFileChunkBE(file, chunk_name, NULL);
751 if (strcmp(chunk_name, "RND1") == 0)
753 getFile32BitBE(file); /* not used */
755 getFileChunkBE(file, chunk_name, NULL);
756 if (strcmp(chunk_name, "CAVE") != 0)
758 Error(ERR_WARN, "unknown format of level file '%s'", filename);
763 else /* check for pre-2.0 file format with cookie string */
765 strcpy(cookie, chunk_name);
766 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
767 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
768 cookie[strlen(cookie) - 1] = '\0';
770 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
772 Error(ERR_WARN, "unknown format of level file '%s'", filename);
777 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
779 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
784 /* pre-2.0 level files have no game version, so use file version here */
785 level->game_version = level->file_version;
788 if (level->file_version < FILE_VERSION_1_2)
790 /* level files from versions before 1.2.0 without chunk structure */
791 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
792 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
800 int (*loader)(FILE *, int, struct LevelInfo *);
804 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
805 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
806 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
807 { "BODY", -1, LoadLevel_BODY },
808 { "CONT", -1, LoadLevel_CONT },
809 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
810 { "CNT3", -1, LoadLevel_CNT3 },
811 { "CUS1", -1, LoadLevel_CUS1 },
812 { "CUS2", -1, LoadLevel_CUS2 },
813 { "CUS3", -1, LoadLevel_CUS3 },
814 { "CUS4", -1, LoadLevel_CUS4 },
818 while (getFileChunkBE(file, chunk_name, &chunk_size))
822 while (chunk_info[i].name != NULL &&
823 strcmp(chunk_name, chunk_info[i].name) != 0)
826 if (chunk_info[i].name == NULL)
828 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
829 chunk_name, filename);
830 ReadUnusedBytesFromFile(file, chunk_size);
832 else if (chunk_info[i].size != -1 &&
833 chunk_info[i].size != chunk_size)
835 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
836 chunk_size, chunk_name, filename);
837 ReadUnusedBytesFromFile(file, chunk_size);
841 /* call function to load this level chunk */
842 int chunk_size_expected =
843 (chunk_info[i].loader)(file, chunk_size, level);
845 /* the size of some chunks cannot be checked before reading other
846 chunks first (like "HEAD" and "BODY") that contain some header
847 information, so check them here */
848 if (chunk_size_expected != chunk_size)
850 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
851 chunk_size, chunk_name, filename);
862 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
864 if (leveldir_current == NULL) /* only when dumping level */
867 /* determine correct game engine version of current level */
868 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
869 IS_LEVELCLASS_USER(leveldir_current))
872 printf("\n::: This level is private or contributed: '%s'\n", filename);
875 /* For user contributed and private levels, use the version of
876 the game engine the levels were created for.
877 Since 2.0.1, the game engine version is now directly stored
878 in the level file (chunk "VERS"), so there is no need anymore
879 to set the game version from the file version (except for old,
880 pre-2.0 levels, where the game version is still taken from the
881 file format version used to store the level -- see above). */
883 /* do some special adjustments to support older level versions */
884 if (level->file_version == FILE_VERSION_1_0)
886 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
887 Error(ERR_WARN, "using high speed movement for player");
889 /* player was faster than monsters in (pre-)1.0 levels */
890 level->double_speed = TRUE;
893 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
894 if (level->game_version == VERSION_IDENT(2,0,1))
895 level->em_slippery_gems = TRUE;
900 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
901 leveldir_current->sort_priority, filename);
904 /* Always use the latest version of the game engine for all but
905 user contributed and private levels; this allows for actual
906 corrections in the game engine to take effect for existing,
907 converted levels (from "classic" or other existing games) to
908 make the game emulation more accurate, while (hopefully) not
909 breaking existing levels created from other players. */
911 level->game_version = GAME_VERSION_ACTUAL;
913 /* Set special EM style gems behaviour: EM style gems slip down from
914 normal, steel and growing wall. As this is a more fundamental change,
915 it seems better to set the default behaviour to "off" (as it is more
916 natural) and make it configurable in the level editor (as a property
917 of gem style elements). Already existing converted levels (neither
918 private nor contributed levels) are changed to the new behaviour. */
920 if (level->file_version < FILE_VERSION_2_0)
921 level->em_slippery_gems = TRUE;
925 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
929 /* map custom element change events that have changed in newer versions
930 (these following values were accidentally changed in version 3.0.1) */
931 if (level->game_version <= VERSION_IDENT(3,0,0))
933 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
935 int element = EL_CUSTOM_START + i;
937 /* order of checking and copying events to be mapped is important */
938 for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
940 if (HAS_CHANGE_EVENT(element, j - 2))
942 SET_CHANGE_EVENT(element, j - 2, FALSE);
943 SET_CHANGE_EVENT(element, j, TRUE);
947 /* order of checking and copying events to be mapped is important */
948 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
950 if (HAS_CHANGE_EVENT(element, j - 1))
952 SET_CHANGE_EVENT(element, j - 1, FALSE);
953 SET_CHANGE_EVENT(element, j, TRUE);
959 /* some custom element change events get mapped since version 3.0.3 */
960 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
962 int element = EL_CUSTOM_START + i;
964 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
965 HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
967 SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
968 SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
970 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
974 /* initialize "can_change" field for old levels with only one change page */
975 if (level->game_version <= VERSION_IDENT(3,0,2))
977 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
979 int element = EL_CUSTOM_START + i;
981 if (CAN_CHANGE(element))
982 element_info[element].change->can_change = TRUE;
986 /* initialize element properties for level editor etc. */
987 InitElementPropertiesEngine(level->game_version);
990 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
994 /* map elements that have changed in newer versions */
995 for(y=0; y<level->fieldy; y++)
997 for(x=0; x<level->fieldx; x++)
999 int element = level->field[x][y];
1001 if (level->game_version <= VERSION_IDENT(2,2,0))
1003 /* map game font elements */
1004 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1005 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1006 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1007 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1010 if (level->game_version < VERSION_IDENT(3,0,0))
1012 /* map Supaplex gravity tube elements */
1013 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1014 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1015 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1016 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1020 level->field[x][y] = element;
1024 /* copy elements to runtime playfield array */
1025 for(x=0; x<MAX_LEV_FIELDX; x++)
1026 for(y=0; y<MAX_LEV_FIELDY; y++)
1027 Feld[x][y] = level->field[x][y];
1029 /* initialize level size variables for faster access */
1030 lev_fieldx = level->fieldx;
1031 lev_fieldy = level->fieldy;
1033 /* determine border element for this level */
1039 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
1043 if (leveldir_current == NULL) /* only when dumping level */
1046 /* determine correct game engine version of current level */
1047 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
1048 IS_LEVELCLASS_USER(leveldir_current))
1051 printf("\n::: This level is private or contributed: '%s'\n", filename);
1054 /* For user contributed and private levels, use the version of
1055 the game engine the levels were created for.
1056 Since 2.0.1, the game engine version is now directly stored
1057 in the level file (chunk "VERS"), so there is no need anymore
1058 to set the game version from the file version (except for old,
1059 pre-2.0 levels, where the game version is still taken from the
1060 file format version used to store the level -- see above). */
1062 /* do some special adjustments to support older level versions */
1063 if (level->file_version == FILE_VERSION_1_0)
1065 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
1066 Error(ERR_WARN, "using high speed movement for player");
1068 /* player was faster than monsters in (pre-)1.0 levels */
1069 level->double_speed = TRUE;
1072 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1073 if (level->game_version == VERSION_IDENT(2,0,1))
1074 level->em_slippery_gems = TRUE;
1079 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1080 leveldir_current->sort_priority, filename);
1083 /* Always use the latest version of the game engine for all but
1084 user contributed and private levels; this allows for actual
1085 corrections in the game engine to take effect for existing,
1086 converted levels (from "classic" or other existing games) to
1087 make the game emulation more accurate, while (hopefully) not
1088 breaking existing levels created from other players. */
1090 level->game_version = GAME_VERSION_ACTUAL;
1092 /* Set special EM style gems behaviour: EM style gems slip down from
1093 normal, steel and growing wall. As this is a more fundamental change,
1094 it seems better to set the default behaviour to "off" (as it is more
1095 natural) and make it configurable in the level editor (as a property
1096 of gem style elements). Already existing converted levels (neither
1097 private nor contributed levels) are changed to the new behaviour. */
1099 if (level->file_version < FILE_VERSION_2_0)
1100 level->em_slippery_gems = TRUE;
1103 /* map elements that have changed in newer versions */
1104 for(y=0; y<level->fieldy; y++)
1106 for(x=0; x<level->fieldx; x++)
1108 int element = level->field[x][y];
1110 if (level->game_version <= VERSION_IDENT(2,2,0))
1112 /* map game font elements */
1113 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1114 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1115 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1116 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1119 if (level->game_version < VERSION_IDENT(3,0,0))
1121 /* map Supaplex gravity tube elements */
1122 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1123 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1124 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1125 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1129 level->field[x][y] = element;
1133 /* map custom element change events that have changed in newer versions
1134 (these following values have accidentally changed in version 3.0.1) */
1135 if (level->game_version <= VERSION_IDENT(3,0,0))
1137 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1139 int element = EL_CUSTOM_START + i;
1141 /* order of checking events to be mapped is important */
1142 for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
1144 if (HAS_CHANGE_EVENT(element, j - 2))
1146 SET_CHANGE_EVENT(element, j - 2, FALSE);
1147 SET_CHANGE_EVENT(element, j, TRUE);
1151 /* order of checking events to be mapped is important */
1152 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1154 if (HAS_CHANGE_EVENT(element, j - 1))
1156 SET_CHANGE_EVENT(element, j - 1, FALSE);
1157 SET_CHANGE_EVENT(element, j, TRUE);
1163 /* initialize "can_change" field for old levels with only one change page */
1164 if (level->game_version <= VERSION_IDENT(3,0,2))
1166 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1168 int element = EL_CUSTOM_START + i;
1170 if (CAN_CHANGE(element))
1171 element_info[element].change->can_change = TRUE;
1175 /* copy elements to runtime playfield array */
1176 for(x=0; x<MAX_LEV_FIELDX; x++)
1177 for(y=0; y<MAX_LEV_FIELDY; y++)
1178 Feld[x][y] = level->field[x][y];
1180 /* initialize level size variables for faster access */
1181 lev_fieldx = level->fieldx;
1182 lev_fieldy = level->fieldy;
1184 /* determine border element for this level */
1187 /* initialize element properties for level editor etc. */
1188 InitElementPropertiesEngine(level->game_version);
1193 void LoadLevelTemplate(int level_nr)
1195 char *filename = getLevelFilename(level_nr);
1197 LoadLevelFromFilename(&level_template, filename);
1199 LoadLevel_InitVersion(&level, filename);
1200 LoadLevel_InitElements(&level, filename);
1202 ActivateLevelTemplate();
1205 void LoadLevel(int level_nr)
1207 char *filename = getLevelFilename(level_nr);
1209 LoadLevelFromFilename(&level, filename);
1211 if (level.use_custom_template)
1212 LoadLevelTemplate(-1);
1215 LoadLevel_InitVersion(&level, filename);
1216 LoadLevel_InitElements(&level, filename);
1217 LoadLevel_InitPlayfield(&level, filename);
1219 LoadLevel_InitLevel(&level, filename);
1223 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1225 putFileVersion(file, level->file_version);
1226 putFileVersion(file, level->game_version);
1229 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1233 putFile8Bit(file, level->fieldx);
1234 putFile8Bit(file, level->fieldy);
1236 putFile16BitBE(file, level->time);
1237 putFile16BitBE(file, level->gems_needed);
1239 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1240 putFile8Bit(file, level->name[i]);
1242 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1243 putFile8Bit(file, level->score[i]);
1245 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1248 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1249 level->yamyam_content[i][x][y]));
1250 putFile8Bit(file, level->amoeba_speed);
1251 putFile8Bit(file, level->time_magic_wall);
1252 putFile8Bit(file, level->time_wheel);
1253 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1254 level->amoeba_content));
1255 putFile8Bit(file, (level->double_speed ? 1 : 0));
1256 putFile8Bit(file, (level->gravity ? 1 : 0));
1257 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1258 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1260 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1262 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1265 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1269 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1270 putFile8Bit(file, level->author[i]);
1273 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1277 for(y=0; y<level->fieldy; y++)
1278 for(x=0; x<level->fieldx; x++)
1279 if (level->encoding_16bit_field)
1280 putFile16BitBE(file, level->field[x][y]);
1282 putFile8Bit(file, level->field[x][y]);
1286 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1290 putFile8Bit(file, EL_YAMYAM);
1291 putFile8Bit(file, level->num_yamyam_contents);
1292 putFile8Bit(file, 0);
1293 putFile8Bit(file, 0);
1295 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1298 if (level->encoding_16bit_field)
1299 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1301 putFile8Bit(file, level->yamyam_content[i][x][y]);
1305 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1308 int num_contents, content_xsize, content_ysize;
1309 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1311 if (element == EL_YAMYAM)
1313 num_contents = level->num_yamyam_contents;
1317 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1320 content_array[i][x][y] = level->yamyam_content[i][x][y];
1322 else if (element == EL_BD_AMOEBA)
1328 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1331 content_array[i][x][y] = EL_EMPTY;
1332 content_array[0][0][0] = level->amoeba_content;
1336 /* chunk header already written -- write empty chunk data */
1337 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1339 Error(ERR_WARN, "cannot save content for element '%d'", element);
1343 putFile16BitBE(file, element);
1344 putFile8Bit(file, num_contents);
1345 putFile8Bit(file, content_xsize);
1346 putFile8Bit(file, content_ysize);
1348 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1350 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1353 putFile16BitBE(file, content_array[i][x][y]);
1356 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1359 int envelope_len = strlen(level->envelope) + 1;
1361 putFile16BitBE(file, element);
1362 putFile16BitBE(file, envelope_len);
1363 putFile8Bit(file, level->envelope_xsize);
1364 putFile8Bit(file, level->envelope_ysize);
1366 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1368 for(i=0; i < envelope_len; i++)
1369 putFile8Bit(file, level->envelope[i]);
1373 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1374 int num_changed_custom_elements)
1378 putFile16BitBE(file, num_changed_custom_elements);
1380 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1382 int element = EL_CUSTOM_START + i;
1384 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1386 if (check < num_changed_custom_elements)
1388 putFile16BitBE(file, element);
1389 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1396 if (check != num_changed_custom_elements) /* should not happen */
1397 Error(ERR_WARN, "inconsistent number of custom element properties");
1402 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1403 int num_changed_custom_elements)
1407 putFile16BitBE(file, num_changed_custom_elements);
1409 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1411 int element = EL_CUSTOM_START + i;
1413 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1415 if (check < num_changed_custom_elements)
1417 putFile16BitBE(file, element);
1418 putFile16BitBE(file, element_info[element].change->target_element);
1425 if (check != num_changed_custom_elements) /* should not happen */
1426 Error(ERR_WARN, "inconsistent number of custom target elements");
1431 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1432 int num_changed_custom_elements)
1434 int i, j, x, y, check = 0;
1436 putFile16BitBE(file, num_changed_custom_elements);
1438 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1440 int element = EL_CUSTOM_START + i;
1442 if (element_info[element].modified_settings)
1444 if (check < num_changed_custom_elements)
1446 putFile16BitBE(file, element);
1448 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1449 putFile8Bit(file, element_info[element].description[j]);
1451 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1453 /* some free bytes for future properties and padding */
1454 WriteUnusedBytesToFile(file, 7);
1456 putFile8Bit(file, element_info[element].use_gfx_element);
1457 putFile16BitBE(file, element_info[element].gfx_element);
1459 putFile8Bit(file, element_info[element].collect_score);
1460 putFile8Bit(file, element_info[element].collect_count);
1462 putFile16BitBE(file, element_info[element].push_delay_fixed);
1463 putFile16BitBE(file, element_info[element].push_delay_random);
1464 putFile16BitBE(file, element_info[element].move_delay_fixed);
1465 putFile16BitBE(file, element_info[element].move_delay_random);
1467 putFile16BitBE(file, element_info[element].move_pattern);
1468 putFile8Bit(file, element_info[element].move_direction_initial);
1469 putFile8Bit(file, element_info[element].move_stepsize);
1473 putFile16BitBE(file, element_info[element].content[x][y]);
1475 putFile32BitBE(file, element_info[element].change->events);
1477 putFile16BitBE(file, element_info[element].change->target_element);
1479 putFile16BitBE(file, element_info[element].change->delay_fixed);
1480 putFile16BitBE(file, element_info[element].change->delay_random);
1481 putFile16BitBE(file, element_info[element].change->delay_frames);
1483 putFile16BitBE(file, element_info[element].change->trigger_element);
1485 putFile8Bit(file, element_info[element].change->explode);
1486 putFile8Bit(file, element_info[element].change->use_content);
1487 putFile8Bit(file, element_info[element].change->only_complete);
1488 putFile8Bit(file, element_info[element].change->use_random_change);
1490 putFile8Bit(file, element_info[element].change->random);
1491 putFile8Bit(file, element_info[element].change->power);
1495 putFile16BitBE(file, element_info[element].change->content[x][y]);
1497 putFile8Bit(file, element_info[element].slippery_type);
1499 /* some free bytes for future properties and padding */
1500 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1507 if (check != num_changed_custom_elements) /* should not happen */
1508 Error(ERR_WARN, "inconsistent number of custom element properties");
1512 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1514 struct ElementInfo *ei = &element_info[element];
1517 putFile16BitBE(file, element);
1519 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
1520 putFile8Bit(file, ei->description[i]);
1522 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1523 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
1525 putFile8Bit(file, ei->num_change_pages);
1527 /* some free bytes for future base property values and padding */
1528 WriteUnusedBytesToFile(file, 5);
1530 /* write custom property values */
1532 putFile8Bit(file, ei->use_gfx_element);
1533 putFile16BitBE(file, ei->gfx_element);
1535 putFile8Bit(file, ei->collect_score);
1536 putFile8Bit(file, ei->collect_count);
1538 putFile16BitBE(file, ei->push_delay_fixed);
1539 putFile16BitBE(file, ei->push_delay_random);
1540 putFile16BitBE(file, ei->move_delay_fixed);
1541 putFile16BitBE(file, ei->move_delay_random);
1543 putFile16BitBE(file, ei->move_pattern);
1544 putFile8Bit(file, ei->move_direction_initial);
1545 putFile8Bit(file, ei->move_stepsize);
1547 putFile8Bit(file, ei->slippery_type);
1551 putFile16BitBE(file, ei->content[x][y]);
1553 /* some free bytes for future custom property values and padding */
1554 WriteUnusedBytesToFile(file, 12);
1556 /* write change property values */
1558 for (i=0; i < ei->num_change_pages; i++)
1560 struct ElementChangeInfo *change = &ei->change_page[i];
1562 putFile32BitBE(file, change->events);
1564 putFile16BitBE(file, change->target_element);
1566 putFile16BitBE(file, change->delay_fixed);
1567 putFile16BitBE(file, change->delay_random);
1568 putFile16BitBE(file, change->delay_frames);
1570 putFile16BitBE(file, change->trigger_element);
1572 putFile8Bit(file, change->explode);
1573 putFile8Bit(file, change->use_content);
1574 putFile8Bit(file, change->only_complete);
1575 putFile8Bit(file, change->use_random_change);
1577 putFile8Bit(file, change->random);
1578 putFile8Bit(file, change->power);
1582 putFile16BitBE(file, change->content[x][y]);
1584 putFile8Bit(file, change->can_change);
1586 /* some free bytes for future change property values and padding */
1587 WriteUnusedBytesToFile(file, 9);
1591 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1593 int body_chunk_size;
1597 if (!(file = fopen(filename, MODE_WRITE)))
1599 Error(ERR_WARN, "cannot save level file '%s'", filename);
1603 level->file_version = FILE_VERSION_ACTUAL;
1604 level->game_version = GAME_VERSION_ACTUAL;
1606 /* check level field for 16-bit elements */
1607 level->encoding_16bit_field = FALSE;
1608 for(y=0; y<level->fieldy; y++)
1609 for(x=0; x<level->fieldx; x++)
1610 if (level->field[x][y] > 255)
1611 level->encoding_16bit_field = TRUE;
1613 /* check yamyam content for 16-bit elements */
1614 level->encoding_16bit_yamyam = FALSE;
1615 for(i=0; i<level->num_yamyam_contents; i++)
1618 if (level->yamyam_content[i][x][y] > 255)
1619 level->encoding_16bit_yamyam = TRUE;
1621 /* check amoeba content for 16-bit elements */
1622 level->encoding_16bit_amoeba = FALSE;
1623 if (level->amoeba_content > 255)
1624 level->encoding_16bit_amoeba = TRUE;
1626 /* calculate size of "BODY" chunk */
1628 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1630 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1631 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1633 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1634 SaveLevel_VERS(file, level);
1636 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1637 SaveLevel_HEAD(file, level);
1639 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1640 SaveLevel_AUTH(file, level);
1642 putFileChunkBE(file, "BODY", body_chunk_size);
1643 SaveLevel_BODY(file, level);
1645 if (level->encoding_16bit_yamyam ||
1646 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1648 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1649 SaveLevel_CNT2(file, level, EL_YAMYAM);
1652 if (level->encoding_16bit_amoeba)
1654 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1655 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1658 /* check for envelope content */
1659 if (strlen(level->envelope) > 0)
1661 int envelope_len = strlen(level->envelope) + 1;
1663 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
1664 SaveLevel_CNT3(file, level, EL_ENVELOPE);
1667 /* check for non-default custom elements (unless using template level) */
1668 if (!level->use_custom_template)
1670 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1672 int element = EL_CUSTOM_START + i;
1674 if (element_info[element].modified_settings)
1676 int num_change_pages = element_info[element].num_change_pages;
1678 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
1679 SaveLevel_CUS4(file, level, element);
1686 SetFilePermissions(filename, PERMS_PRIVATE);
1689 void SaveLevel(int level_nr)
1691 char *filename = getLevelFilename(level_nr);
1693 SaveLevelFromFilename(&level, filename);
1696 void SaveLevelTemplate()
1698 char *filename = getLevelFilename(-1);
1700 SaveLevelFromFilename(&level, filename);
1703 void DumpLevel(struct LevelInfo *level)
1705 printf_line("-", 79);
1706 printf("Level xxx (file version %08d, game version %08d)\n",
1707 level->file_version, level->game_version);
1708 printf_line("-", 79);
1710 printf("Level Author: '%s'\n", level->author);
1711 printf("Level Title: '%s'\n", level->name);
1713 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1715 printf("Level Time: %d seconds\n", level->time);
1716 printf("Gems needed: %d\n", level->gems_needed);
1718 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1719 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1720 printf("Time for Light: %d seconds\n", level->time_light);
1721 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1723 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1725 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1726 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1727 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1729 printf_line("-", 79);
1733 /* ========================================================================= */
1734 /* tape file functions */
1735 /* ========================================================================= */
1737 static void setTapeInfoToDefaults()
1741 /* always start with reliable default values (empty tape) */
1744 /* default values (also for pre-1.2 tapes) with only the first player */
1745 tape.player_participates[0] = TRUE;
1746 for(i=1; i<MAX_PLAYERS; i++)
1747 tape.player_participates[i] = FALSE;
1749 /* at least one (default: the first) player participates in every tape */
1750 tape.num_participating_players = 1;
1752 tape.level_nr = level_nr;
1754 tape.changed = FALSE;
1756 tape.recording = FALSE;
1757 tape.playing = FALSE;
1758 tape.pausing = FALSE;
1761 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1763 tape->file_version = getFileVersion(file);
1764 tape->game_version = getFileVersion(file);
1769 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1773 tape->random_seed = getFile32BitBE(file);
1774 tape->date = getFile32BitBE(file);
1775 tape->length = getFile32BitBE(file);
1777 /* read header fields that are new since version 1.2 */
1778 if (tape->file_version >= FILE_VERSION_1_2)
1780 byte store_participating_players = getFile8Bit(file);
1783 /* since version 1.2, tapes store which players participate in the tape */
1784 tape->num_participating_players = 0;
1785 for(i=0; i<MAX_PLAYERS; i++)
1787 tape->player_participates[i] = FALSE;
1789 if (store_participating_players & (1 << i))
1791 tape->player_participates[i] = TRUE;
1792 tape->num_participating_players++;
1796 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1798 engine_version = getFileVersion(file);
1799 if (engine_version > 0)
1800 tape->engine_version = engine_version;
1802 tape->engine_version = tape->game_version;
1808 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1810 int level_identifier_size;
1813 level_identifier_size = getFile16BitBE(file);
1815 tape->level_identifier =
1816 checked_realloc(tape->level_identifier, level_identifier_size);
1818 for(i=0; i < level_identifier_size; i++)
1819 tape->level_identifier[i] = getFile8Bit(file);
1821 tape->level_nr = getFile16BitBE(file);
1823 chunk_size = 2 + level_identifier_size + 2;
1828 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1831 int chunk_size_expected =
1832 (tape->num_participating_players + 1) * tape->length;
1834 if (chunk_size_expected != chunk_size)
1836 ReadUnusedBytesFromFile(file, chunk_size);
1837 return chunk_size_expected;
1840 for(i=0; i<tape->length; i++)
1842 if (i >= MAX_TAPELEN)
1845 for(j=0; j<MAX_PLAYERS; j++)
1847 tape->pos[i].action[j] = MV_NO_MOVING;
1849 if (tape->player_participates[j])
1850 tape->pos[i].action[j] = getFile8Bit(file);
1853 tape->pos[i].delay = getFile8Bit(file);
1855 if (tape->file_version == FILE_VERSION_1_0)
1857 /* eliminate possible diagonal moves in old tapes */
1858 /* this is only for backward compatibility */
1860 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1861 byte action = tape->pos[i].action[0];
1862 int k, num_moves = 0;
1866 if (action & joy_dir[k])
1868 tape->pos[i + num_moves].action[0] = joy_dir[k];
1870 tape->pos[i + num_moves].delay = 0;
1879 tape->length += num_moves;
1882 else if (tape->file_version < FILE_VERSION_2_0)
1884 /* convert pre-2.0 tapes to new tape format */
1886 if (tape->pos[i].delay > 1)
1889 tape->pos[i + 1] = tape->pos[i];
1890 tape->pos[i + 1].delay = 1;
1893 for(j=0; j<MAX_PLAYERS; j++)
1894 tape->pos[i].action[j] = MV_NO_MOVING;
1895 tape->pos[i].delay--;
1906 if (i != tape->length)
1907 chunk_size = (tape->num_participating_players + 1) * i;
1912 void LoadTapeFromFilename(char *filename)
1914 char cookie[MAX_LINE_LEN];
1915 char chunk_name[CHUNK_ID_LEN + 1];
1919 /* always start with reliable default values */
1920 setTapeInfoToDefaults();
1922 if (!(file = fopen(filename, MODE_READ)))
1925 getFileChunkBE(file, chunk_name, NULL);
1926 if (strcmp(chunk_name, "RND1") == 0)
1928 getFile32BitBE(file); /* not used */
1930 getFileChunkBE(file, chunk_name, NULL);
1931 if (strcmp(chunk_name, "TAPE") != 0)
1933 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1938 else /* check for pre-2.0 file format with cookie string */
1940 strcpy(cookie, chunk_name);
1941 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1942 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1943 cookie[strlen(cookie) - 1] = '\0';
1945 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1947 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1952 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1954 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1959 /* pre-2.0 tape files have no game version, so use file version here */
1960 tape.game_version = tape.file_version;
1963 if (tape.file_version < FILE_VERSION_1_2)
1965 /* tape files from versions before 1.2.0 without chunk structure */
1966 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1967 LoadTape_BODY(file, 2 * tape.length, &tape);
1975 int (*loader)(FILE *, int, struct TapeInfo *);
1979 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1980 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1981 { "INFO", -1, LoadTape_INFO },
1982 { "BODY", -1, LoadTape_BODY },
1986 while (getFileChunkBE(file, chunk_name, &chunk_size))
1990 while (chunk_info[i].name != NULL &&
1991 strcmp(chunk_name, chunk_info[i].name) != 0)
1994 if (chunk_info[i].name == NULL)
1996 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1997 chunk_name, filename);
1998 ReadUnusedBytesFromFile(file, chunk_size);
2000 else if (chunk_info[i].size != -1 &&
2001 chunk_info[i].size != chunk_size)
2003 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2004 chunk_size, chunk_name, filename);
2005 ReadUnusedBytesFromFile(file, chunk_size);
2009 /* call function to load this tape chunk */
2010 int chunk_size_expected =
2011 (chunk_info[i].loader)(file, chunk_size, &tape);
2013 /* the size of some chunks cannot be checked before reading other
2014 chunks first (like "HEAD" and "BODY") that contain some header
2015 information, so check them here */
2016 if (chunk_size_expected != chunk_size)
2018 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2019 chunk_size, chunk_name, filename);
2027 tape.length_seconds = GetTapeLength();
2030 printf("tape game version: %d\n", tape.game_version);
2031 printf("tape engine version: %d\n", tape.engine_version);
2035 void LoadTape(int level_nr)
2037 char *filename = getTapeFilename(level_nr);
2039 LoadTapeFromFilename(filename);
2042 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2044 putFileVersion(file, tape->file_version);
2045 putFileVersion(file, tape->game_version);
2048 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2051 byte store_participating_players = 0;
2053 /* set bits for participating players for compact storage */
2054 for(i=0; i<MAX_PLAYERS; i++)
2055 if (tape->player_participates[i])
2056 store_participating_players |= (1 << i);
2058 putFile32BitBE(file, tape->random_seed);
2059 putFile32BitBE(file, tape->date);
2060 putFile32BitBE(file, tape->length);
2062 putFile8Bit(file, store_participating_players);
2064 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2065 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2067 putFileVersion(file, tape->engine_version);
2070 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2072 int level_identifier_size = strlen(tape->level_identifier) + 1;
2075 putFile16BitBE(file, level_identifier_size);
2077 for(i=0; i < level_identifier_size; i++)
2078 putFile8Bit(file, tape->level_identifier[i]);
2080 putFile16BitBE(file, tape->level_nr);
2083 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2087 for(i=0; i<tape->length; i++)
2089 for(j=0; j<MAX_PLAYERS; j++)
2090 if (tape->player_participates[j])
2091 putFile8Bit(file, tape->pos[i].action[j]);
2093 putFile8Bit(file, tape->pos[i].delay);
2097 void SaveTape(int level_nr)
2099 char *filename = getTapeFilename(level_nr);
2101 boolean new_tape = TRUE;
2102 int num_participating_players = 0;
2103 int info_chunk_size;
2104 int body_chunk_size;
2107 InitTapeDirectory(leveldir_current->filename);
2109 /* if a tape still exists, ask to overwrite it */
2110 if (access(filename, F_OK) == 0)
2113 if (!Request("Replace old tape ?", REQ_ASK))
2117 if (!(file = fopen(filename, MODE_WRITE)))
2119 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2123 tape.file_version = FILE_VERSION_ACTUAL;
2124 tape.game_version = GAME_VERSION_ACTUAL;
2126 /* count number of participating players */
2127 for(i=0; i<MAX_PLAYERS; i++)
2128 if (tape.player_participates[i])
2129 num_participating_players++;
2131 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2132 body_chunk_size = (num_participating_players + 1) * tape.length;
2134 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2135 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2137 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2138 SaveTape_VERS(file, &tape);
2140 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2141 SaveTape_HEAD(file, &tape);
2143 putFileChunkBE(file, "INFO", info_chunk_size);
2144 SaveTape_INFO(file, &tape);
2146 putFileChunkBE(file, "BODY", body_chunk_size);
2147 SaveTape_BODY(file, &tape);
2151 SetFilePermissions(filename, PERMS_PRIVATE);
2153 tape.changed = FALSE;
2156 Request("tape saved !", REQ_CONFIRM);
2159 void DumpTape(struct TapeInfo *tape)
2163 if (TAPE_IS_EMPTY(*tape))
2165 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2169 printf_line("-", 79);
2170 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2171 tape->level_nr, tape->file_version, tape->game_version);
2172 printf("Level series identifier: '%s'\n", tape->level_identifier);
2173 printf_line("-", 79);
2175 for(i=0; i<tape->length; i++)
2177 if (i >= MAX_TAPELEN)
2180 printf("%03d: ", i);
2182 for(j=0; j<MAX_PLAYERS; j++)
2184 if (tape->player_participates[j])
2186 int action = tape->pos[i].action[j];
2188 printf("%d:%02x ", j, action);
2189 printf("[%c%c%c%c|%c%c] - ",
2190 (action & JOY_LEFT ? '<' : ' '),
2191 (action & JOY_RIGHT ? '>' : ' '),
2192 (action & JOY_UP ? '^' : ' '),
2193 (action & JOY_DOWN ? 'v' : ' '),
2194 (action & JOY_BUTTON_1 ? '1' : ' '),
2195 (action & JOY_BUTTON_2 ? '2' : ' '));
2199 printf("(%03d)\n", tape->pos[i].delay);
2202 printf_line("-", 79);
2206 /* ========================================================================= */
2207 /* score file functions */
2208 /* ========================================================================= */
2210 void LoadScore(int level_nr)
2213 char *filename = getScoreFilename(level_nr);
2214 char cookie[MAX_LINE_LEN];
2215 char line[MAX_LINE_LEN];
2219 /* always start with reliable default values */
2220 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2222 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2223 highscore[i].Score = 0;
2226 if (!(file = fopen(filename, MODE_READ)))
2229 /* check file identifier */
2230 fgets(cookie, MAX_LINE_LEN, file);
2231 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2232 cookie[strlen(cookie) - 1] = '\0';
2234 if (!checkCookieString(cookie, SCORE_COOKIE))
2236 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2241 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2243 fscanf(file, "%d", &highscore[i].Score);
2244 fgets(line, MAX_LINE_LEN, file);
2246 if (line[strlen(line) - 1] == '\n')
2247 line[strlen(line) - 1] = '\0';
2249 for (line_ptr = line; *line_ptr; line_ptr++)
2251 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2253 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2254 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2263 void SaveScore(int level_nr)
2266 char *filename = getScoreFilename(level_nr);
2269 InitScoreDirectory(leveldir_current->filename);
2271 if (!(file = fopen(filename, MODE_WRITE)))
2273 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2277 fprintf(file, "%s\n\n", SCORE_COOKIE);
2279 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2280 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2284 SetFilePermissions(filename, PERMS_PUBLIC);
2288 /* ========================================================================= */
2289 /* setup file functions */
2290 /* ========================================================================= */
2292 #define TOKEN_STR_PLAYER_PREFIX "player_"
2295 #define SETUP_TOKEN_PLAYER_NAME 0
2296 #define SETUP_TOKEN_SOUND 1
2297 #define SETUP_TOKEN_SOUND_LOOPS 2
2298 #define SETUP_TOKEN_SOUND_MUSIC 3
2299 #define SETUP_TOKEN_SOUND_SIMPLE 4
2300 #define SETUP_TOKEN_TOONS 5
2301 #define SETUP_TOKEN_SCROLL_DELAY 6
2302 #define SETUP_TOKEN_SOFT_SCROLLING 7
2303 #define SETUP_TOKEN_FADING 8
2304 #define SETUP_TOKEN_AUTORECORD 9
2305 #define SETUP_TOKEN_QUICK_DOORS 10
2306 #define SETUP_TOKEN_TEAM_MODE 11
2307 #define SETUP_TOKEN_HANDICAP 12
2308 #define SETUP_TOKEN_TIME_LIMIT 13
2309 #define SETUP_TOKEN_FULLSCREEN 14
2310 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2311 #define SETUP_TOKEN_GRAPHICS_SET 16
2312 #define SETUP_TOKEN_SOUNDS_SET 17
2313 #define SETUP_TOKEN_MUSIC_SET 18
2314 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2315 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2316 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2318 #define NUM_GLOBAL_SETUP_TOKENS 22
2321 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2322 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2323 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2324 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2325 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2326 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2327 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2328 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2329 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2330 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2331 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2333 #define NUM_EDITOR_SETUP_TOKENS 11
2335 /* shortcut setup */
2336 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2337 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2338 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2340 #define NUM_SHORTCUT_SETUP_TOKENS 3
2343 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2344 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2345 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2346 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2347 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2348 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2349 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2350 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2351 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2352 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
2353 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2354 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2355 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2356 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2357 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2358 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
2360 #define NUM_PLAYER_SETUP_TOKENS 16
2363 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2364 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2366 #define NUM_SYSTEM_SETUP_TOKENS 2
2369 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2371 #define NUM_OPTIONS_SETUP_TOKENS 1
2374 static struct SetupInfo si;
2375 static struct SetupEditorInfo sei;
2376 static struct SetupShortcutInfo ssi;
2377 static struct SetupInputInfo sii;
2378 static struct SetupSystemInfo syi;
2379 static struct OptionInfo soi;
2381 static struct TokenInfo global_setup_tokens[] =
2383 { TYPE_STRING, &si.player_name, "player_name" },
2384 { TYPE_SWITCH, &si.sound, "sound" },
2385 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2386 { TYPE_SWITCH, &si.sound_music, "background_music" },
2387 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2388 { TYPE_SWITCH, &si.toons, "toons" },
2389 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2390 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2391 { TYPE_SWITCH, &si.fading, "screen_fading" },
2392 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2393 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2394 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2395 { TYPE_SWITCH, &si.handicap, "handicap" },
2396 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2397 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2398 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2399 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2400 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2401 { TYPE_STRING, &si.music_set, "music_set" },
2402 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2403 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2404 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2407 static struct TokenInfo editor_setup_tokens[] =
2409 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2410 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2411 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2412 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2413 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2414 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2415 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2416 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2417 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2418 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2419 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2422 static struct TokenInfo shortcut_setup_tokens[] =
2424 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2425 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2426 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2429 static struct TokenInfo player_setup_tokens[] =
2431 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2432 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2433 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2434 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2435 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2436 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2437 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2438 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2439 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2440 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2441 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2442 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2443 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2444 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2445 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2446 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
2449 static struct TokenInfo system_setup_tokens[] =
2451 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2452 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2455 static struct TokenInfo options_setup_tokens[] =
2457 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2460 static char *get_corrected_login_name(char *login_name)
2462 /* needed because player name must be a fixed length string */
2463 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2465 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2466 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2468 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2469 if (strchr(login_name_new, ' '))
2470 *strchr(login_name_new, ' ') = '\0';
2472 return login_name_new;
2475 static void setSetupInfoToDefaults(struct SetupInfo *si)
2479 si->player_name = get_corrected_login_name(getLoginName());
2482 si->sound_loops = TRUE;
2483 si->sound_music = TRUE;
2484 si->sound_simple = TRUE;
2486 si->double_buffering = TRUE;
2487 si->direct_draw = !si->double_buffering;
2488 si->scroll_delay = TRUE;
2489 si->soft_scrolling = TRUE;
2491 si->autorecord = TRUE;
2492 si->quick_doors = FALSE;
2493 si->team_mode = FALSE;
2494 si->handicap = TRUE;
2495 si->time_limit = TRUE;
2496 si->fullscreen = FALSE;
2497 si->ask_on_escape = TRUE;
2499 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2500 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2501 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2502 si->override_level_graphics = FALSE;
2503 si->override_level_sounds = FALSE;
2504 si->override_level_music = FALSE;
2506 si->editor.el_boulderdash = TRUE;
2507 si->editor.el_emerald_mine = TRUE;
2508 si->editor.el_more = TRUE;
2509 si->editor.el_sokoban = TRUE;
2510 si->editor.el_supaplex = TRUE;
2511 si->editor.el_diamond_caves = TRUE;
2512 si->editor.el_dx_boulderdash = TRUE;
2513 si->editor.el_chars = TRUE;
2514 si->editor.el_custom = TRUE;
2515 si->editor.el_custom_more = FALSE;
2517 si->editor.el_headlines = TRUE;
2519 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2520 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2521 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2523 for (i=0; i<MAX_PLAYERS; i++)
2525 si->input[i].use_joystick = FALSE;
2526 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2527 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2528 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2529 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2530 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2531 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2532 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2533 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2534 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2535 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2536 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2537 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2538 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2539 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2540 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2543 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2544 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2546 si->options.verbose = FALSE;
2549 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2553 if (!setup_file_hash)
2558 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2559 setSetupInfo(global_setup_tokens, i,
2560 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2565 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2566 setSetupInfo(editor_setup_tokens, i,
2567 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2570 /* shortcut setup */
2571 ssi = setup.shortcut;
2572 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2573 setSetupInfo(shortcut_setup_tokens, i,
2574 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2575 setup.shortcut = ssi;
2578 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2582 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2584 sii = setup.input[pnr];
2585 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2587 char full_token[100];
2589 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2590 setSetupInfo(player_setup_tokens, i,
2591 getHashEntry(setup_file_hash, full_token));
2593 setup.input[pnr] = sii;
2598 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2599 setSetupInfo(system_setup_tokens, i,
2600 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2604 soi = setup.options;
2605 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2606 setSetupInfo(options_setup_tokens, i,
2607 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2608 setup.options = soi;
2613 char *filename = getSetupFilename();
2614 SetupFileHash *setup_file_hash = NULL;
2616 /* always start with reliable default values */
2617 setSetupInfoToDefaults(&setup);
2619 setup_file_hash = loadSetupFileHash(filename);
2621 if (setup_file_hash)
2623 char *player_name_new;
2625 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2626 decodeSetupFileHash(setup_file_hash);
2628 setup.direct_draw = !setup.double_buffering;
2630 freeSetupFileHash(setup_file_hash);
2632 /* needed to work around problems with fixed length strings */
2633 player_name_new = get_corrected_login_name(setup.player_name);
2634 free(setup.player_name);
2635 setup.player_name = player_name_new;
2638 Error(ERR_WARN, "using default setup values");
2643 char *filename = getSetupFilename();
2647 InitUserDataDirectory();
2649 if (!(file = fopen(filename, MODE_WRITE)))
2651 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2655 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2656 getCookie("SETUP")));
2657 fprintf(file, "\n");
2661 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2663 /* just to make things nicer :) */
2664 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2665 i == SETUP_TOKEN_GRAPHICS_SET)
2666 fprintf(file, "\n");
2668 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2673 fprintf(file, "\n");
2674 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2675 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2677 /* shortcut setup */
2678 ssi = setup.shortcut;
2679 fprintf(file, "\n");
2680 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2681 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2684 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2688 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2689 fprintf(file, "\n");
2691 sii = setup.input[pnr];
2692 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2693 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2698 fprintf(file, "\n");
2699 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2700 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2703 soi = setup.options;
2704 fprintf(file, "\n");
2705 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2706 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2710 SetFilePermissions(filename, PERMS_PRIVATE);
2713 void LoadCustomElementDescriptions()
2715 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2716 SetupFileHash *setup_file_hash;
2719 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2721 if (element_info[i].custom_description != NULL)
2723 free(element_info[i].custom_description);
2724 element_info[i].custom_description = NULL;
2728 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2731 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2733 char *token = getStringCat2(element_info[i].token_name, ".name");
2734 char *value = getHashEntry(setup_file_hash, token);
2737 element_info[i].custom_description = getStringCopy(value);
2742 freeSetupFileHash(setup_file_hash);
2745 void LoadSpecialMenuDesignSettings()
2747 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2748 SetupFileHash *setup_file_hash;
2751 /* always start with reliable default values from default config */
2752 for (i=0; image_config_vars[i].token != NULL; i++)
2753 for (j=0; image_config[j].token != NULL; j++)
2754 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2755 *image_config_vars[i].value =
2756 get_integer_from_string(image_config[j].value);
2758 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2761 /* special case: initialize with default values that may be overwritten */
2762 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2764 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2765 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2766 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2768 if (value_x != NULL)
2769 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2770 if (value_y != NULL)
2771 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2772 if (list_size != NULL)
2773 menu.list_size[i] = get_integer_from_string(list_size);
2776 /* read (and overwrite with) values that may be specified in config file */
2777 for (i=0; image_config_vars[i].token != NULL; i++)
2779 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2782 *image_config_vars[i].value = get_integer_from_string(value);
2785 freeSetupFileHash(setup_file_hash);