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 change->events = getFile32BitBE(file);
700 change->target_element = checkLevelElement(getFile16BitBE(file));
702 change->delay_fixed = getFile16BitBE(file);
703 change->delay_random = getFile16BitBE(file);
704 change->delay_frames = getFile16BitBE(file);
706 change->trigger_element = checkLevelElement(getFile16BitBE(file));
708 change->explode = getFile8Bit(file);
709 change->use_content = getFile8Bit(file);
710 change->only_complete = getFile8Bit(file);
711 change->use_random_change = getFile8Bit(file);
713 change->random = getFile8Bit(file);
714 change->power = getFile8Bit(file);
718 change->content[x][y] = checkLevelElement(getFile16BitBE(file));
720 change->can_change = getFile8Bit(file);
722 /* some free bytes for future change property values and padding */
723 ReadUnusedBytesFromFile(file, 9);
726 /* mark this custom element as modified */
727 ei->modified_settings = TRUE;
732 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
734 char cookie[MAX_LINE_LEN];
735 char chunk_name[CHUNK_ID_LEN + 1];
739 /* always start with reliable default values */
740 setLevelInfoToDefaults(level);
742 if (!(file = fopen(filename, MODE_READ)))
744 level->no_level_file = TRUE;
746 if (level != &level_template)
747 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
752 getFileChunkBE(file, chunk_name, NULL);
753 if (strcmp(chunk_name, "RND1") == 0)
755 getFile32BitBE(file); /* not used */
757 getFileChunkBE(file, chunk_name, NULL);
758 if (strcmp(chunk_name, "CAVE") != 0)
760 Error(ERR_WARN, "unknown format of level file '%s'", filename);
765 else /* check for pre-2.0 file format with cookie string */
767 strcpy(cookie, chunk_name);
768 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
769 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
770 cookie[strlen(cookie) - 1] = '\0';
772 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
774 Error(ERR_WARN, "unknown format of level file '%s'", filename);
779 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
781 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
786 /* pre-2.0 level files have no game version, so use file version here */
787 level->game_version = level->file_version;
790 if (level->file_version < FILE_VERSION_1_2)
792 /* level files from versions before 1.2.0 without chunk structure */
793 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
794 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
802 int (*loader)(FILE *, int, struct LevelInfo *);
806 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
807 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
808 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
809 { "BODY", -1, LoadLevel_BODY },
810 { "CONT", -1, LoadLevel_CONT },
811 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
812 { "CNT3", -1, LoadLevel_CNT3 },
813 { "CUS1", -1, LoadLevel_CUS1 },
814 { "CUS2", -1, LoadLevel_CUS2 },
815 { "CUS3", -1, LoadLevel_CUS3 },
816 { "CUS4", -1, LoadLevel_CUS4 },
820 while (getFileChunkBE(file, chunk_name, &chunk_size))
824 while (chunk_info[i].name != NULL &&
825 strcmp(chunk_name, chunk_info[i].name) != 0)
828 if (chunk_info[i].name == NULL)
830 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
831 chunk_name, filename);
832 ReadUnusedBytesFromFile(file, chunk_size);
834 else if (chunk_info[i].size != -1 &&
835 chunk_info[i].size != chunk_size)
837 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
838 chunk_size, chunk_name, filename);
839 ReadUnusedBytesFromFile(file, chunk_size);
843 /* call function to load this level chunk */
844 int chunk_size_expected =
845 (chunk_info[i].loader)(file, chunk_size, level);
847 /* the size of some chunks cannot be checked before reading other
848 chunks first (like "HEAD" and "BODY") that contain some header
849 information, so check them here */
850 if (chunk_size_expected != chunk_size)
852 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
853 chunk_size, chunk_name, filename);
864 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
866 if (leveldir_current == NULL) /* only when dumping level */
869 /* determine correct game engine version of current level */
870 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
871 IS_LEVELCLASS_USER(leveldir_current))
874 printf("\n::: This level is private or contributed: '%s'\n", filename);
877 /* For user contributed and private levels, use the version of
878 the game engine the levels were created for.
879 Since 2.0.1, the game engine version is now directly stored
880 in the level file (chunk "VERS"), so there is no need anymore
881 to set the game version from the file version (except for old,
882 pre-2.0 levels, where the game version is still taken from the
883 file format version used to store the level -- see above). */
885 /* do some special adjustments to support older level versions */
886 if (level->file_version == FILE_VERSION_1_0)
888 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
889 Error(ERR_WARN, "using high speed movement for player");
891 /* player was faster than monsters in (pre-)1.0 levels */
892 level->double_speed = TRUE;
895 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
896 if (level->game_version == VERSION_IDENT(2,0,1))
897 level->em_slippery_gems = TRUE;
902 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
903 leveldir_current->sort_priority, filename);
906 /* Always use the latest version of the game engine for all but
907 user contributed and private levels; this allows for actual
908 corrections in the game engine to take effect for existing,
909 converted levels (from "classic" or other existing games) to
910 make the game emulation more accurate, while (hopefully) not
911 breaking existing levels created from other players. */
913 level->game_version = GAME_VERSION_ACTUAL;
915 /* Set special EM style gems behaviour: EM style gems slip down from
916 normal, steel and growing wall. As this is a more fundamental change,
917 it seems better to set the default behaviour to "off" (as it is more
918 natural) and make it configurable in the level editor (as a property
919 of gem style elements). Already existing converted levels (neither
920 private nor contributed levels) are changed to the new behaviour. */
922 if (level->file_version < FILE_VERSION_2_0)
923 level->em_slippery_gems = TRUE;
927 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
931 /* map custom element change events that have changed in newer versions
932 (these following values were accidentally changed in version 3.0.1) */
933 if (level->game_version <= VERSION_IDENT(3,0,0))
935 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
937 int element = EL_CUSTOM_START + i;
939 /* order of checking and copying events to be mapped is important */
940 for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
942 if (HAS_CHANGE_EVENT(element, j - 2))
944 SET_CHANGE_EVENT(element, j - 2, FALSE);
945 SET_CHANGE_EVENT(element, j, TRUE);
949 /* order of checking and copying events to be mapped is important */
950 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
952 if (HAS_CHANGE_EVENT(element, j - 1))
954 SET_CHANGE_EVENT(element, j - 1, FALSE);
955 SET_CHANGE_EVENT(element, j, TRUE);
961 /* some custom element change events get mapped since version 3.0.3 */
962 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
964 int element = EL_CUSTOM_START + i;
966 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
967 HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
969 SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
970 SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
972 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
976 /* initialize "can_change" field for old levels with only one change page */
977 if (level->game_version <= VERSION_IDENT(3,0,2))
979 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
981 int element = EL_CUSTOM_START + i;
983 if (CAN_CHANGE(element))
984 element_info[element].change->can_change = TRUE;
988 /* initialize element properties for level editor etc. */
989 InitElementPropertiesEngine(level->game_version);
992 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
996 /* map elements that have changed in newer versions */
997 for(y=0; y<level->fieldy; y++)
999 for(x=0; x<level->fieldx; x++)
1001 int element = level->field[x][y];
1003 if (level->game_version <= VERSION_IDENT(2,2,0))
1005 /* map game font elements */
1006 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1007 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1008 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1009 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1012 if (level->game_version < VERSION_IDENT(3,0,0))
1014 /* map Supaplex gravity tube elements */
1015 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1016 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1017 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1018 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1022 level->field[x][y] = element;
1026 /* copy elements to runtime playfield array */
1027 for(x=0; x<MAX_LEV_FIELDX; x++)
1028 for(y=0; y<MAX_LEV_FIELDY; y++)
1029 Feld[x][y] = level->field[x][y];
1031 /* initialize level size variables for faster access */
1032 lev_fieldx = level->fieldx;
1033 lev_fieldy = level->fieldy;
1035 /* determine border element for this level */
1041 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
1045 if (leveldir_current == NULL) /* only when dumping level */
1048 /* determine correct game engine version of current level */
1049 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
1050 IS_LEVELCLASS_USER(leveldir_current))
1053 printf("\n::: This level is private or contributed: '%s'\n", filename);
1056 /* For user contributed and private levels, use the version of
1057 the game engine the levels were created for.
1058 Since 2.0.1, the game engine version is now directly stored
1059 in the level file (chunk "VERS"), so there is no need anymore
1060 to set the game version from the file version (except for old,
1061 pre-2.0 levels, where the game version is still taken from the
1062 file format version used to store the level -- see above). */
1064 /* do some special adjustments to support older level versions */
1065 if (level->file_version == FILE_VERSION_1_0)
1067 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
1068 Error(ERR_WARN, "using high speed movement for player");
1070 /* player was faster than monsters in (pre-)1.0 levels */
1071 level->double_speed = TRUE;
1074 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1075 if (level->game_version == VERSION_IDENT(2,0,1))
1076 level->em_slippery_gems = TRUE;
1081 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1082 leveldir_current->sort_priority, filename);
1085 /* Always use the latest version of the game engine for all but
1086 user contributed and private levels; this allows for actual
1087 corrections in the game engine to take effect for existing,
1088 converted levels (from "classic" or other existing games) to
1089 make the game emulation more accurate, while (hopefully) not
1090 breaking existing levels created from other players. */
1092 level->game_version = GAME_VERSION_ACTUAL;
1094 /* Set special EM style gems behaviour: EM style gems slip down from
1095 normal, steel and growing wall. As this is a more fundamental change,
1096 it seems better to set the default behaviour to "off" (as it is more
1097 natural) and make it configurable in the level editor (as a property
1098 of gem style elements). Already existing converted levels (neither
1099 private nor contributed levels) are changed to the new behaviour. */
1101 if (level->file_version < FILE_VERSION_2_0)
1102 level->em_slippery_gems = TRUE;
1105 /* map elements that have changed in newer versions */
1106 for(y=0; y<level->fieldy; y++)
1108 for(x=0; x<level->fieldx; x++)
1110 int element = level->field[x][y];
1112 if (level->game_version <= VERSION_IDENT(2,2,0))
1114 /* map game font elements */
1115 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1116 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1117 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1118 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1121 if (level->game_version < VERSION_IDENT(3,0,0))
1123 /* map Supaplex gravity tube elements */
1124 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1125 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1126 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1127 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1131 level->field[x][y] = element;
1135 /* map custom element change events that have changed in newer versions
1136 (these following values have accidentally changed in version 3.0.1) */
1137 if (level->game_version <= VERSION_IDENT(3,0,0))
1139 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1141 int element = EL_CUSTOM_START + i;
1143 /* order of checking events to be mapped is important */
1144 for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
1146 if (HAS_CHANGE_EVENT(element, j - 2))
1148 SET_CHANGE_EVENT(element, j - 2, FALSE);
1149 SET_CHANGE_EVENT(element, j, TRUE);
1153 /* order of checking events to be mapped is important */
1154 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1156 if (HAS_CHANGE_EVENT(element, j - 1))
1158 SET_CHANGE_EVENT(element, j - 1, FALSE);
1159 SET_CHANGE_EVENT(element, j, TRUE);
1165 /* initialize "can_change" field for old levels with only one change page */
1166 if (level->game_version <= VERSION_IDENT(3,0,2))
1168 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1170 int element = EL_CUSTOM_START + i;
1172 if (CAN_CHANGE(element))
1173 element_info[element].change->can_change = TRUE;
1177 /* copy elements to runtime playfield array */
1178 for(x=0; x<MAX_LEV_FIELDX; x++)
1179 for(y=0; y<MAX_LEV_FIELDY; y++)
1180 Feld[x][y] = level->field[x][y];
1182 /* initialize level size variables for faster access */
1183 lev_fieldx = level->fieldx;
1184 lev_fieldy = level->fieldy;
1186 /* determine border element for this level */
1189 /* initialize element properties for level editor etc. */
1190 InitElementPropertiesEngine(level->game_version);
1195 void LoadLevelTemplate(int level_nr)
1197 char *filename = getLevelFilename(level_nr);
1199 LoadLevelFromFilename(&level_template, filename);
1201 LoadLevel_InitVersion(&level, filename);
1202 LoadLevel_InitElements(&level, filename);
1204 ActivateLevelTemplate();
1207 void LoadLevel(int level_nr)
1209 char *filename = getLevelFilename(level_nr);
1211 LoadLevelFromFilename(&level, filename);
1213 if (level.use_custom_template)
1214 LoadLevelTemplate(-1);
1217 LoadLevel_InitVersion(&level, filename);
1218 LoadLevel_InitElements(&level, filename);
1219 LoadLevel_InitPlayfield(&level, filename);
1221 LoadLevel_InitLevel(&level, filename);
1225 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1227 putFileVersion(file, level->file_version);
1228 putFileVersion(file, level->game_version);
1231 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1235 putFile8Bit(file, level->fieldx);
1236 putFile8Bit(file, level->fieldy);
1238 putFile16BitBE(file, level->time);
1239 putFile16BitBE(file, level->gems_needed);
1241 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1242 putFile8Bit(file, level->name[i]);
1244 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1245 putFile8Bit(file, level->score[i]);
1247 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1250 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1251 level->yamyam_content[i][x][y]));
1252 putFile8Bit(file, level->amoeba_speed);
1253 putFile8Bit(file, level->time_magic_wall);
1254 putFile8Bit(file, level->time_wheel);
1255 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1256 level->amoeba_content));
1257 putFile8Bit(file, (level->double_speed ? 1 : 0));
1258 putFile8Bit(file, (level->gravity ? 1 : 0));
1259 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1260 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1262 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1264 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1267 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1271 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1272 putFile8Bit(file, level->author[i]);
1275 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1279 for(y=0; y<level->fieldy; y++)
1280 for(x=0; x<level->fieldx; x++)
1281 if (level->encoding_16bit_field)
1282 putFile16BitBE(file, level->field[x][y]);
1284 putFile8Bit(file, level->field[x][y]);
1288 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1292 putFile8Bit(file, EL_YAMYAM);
1293 putFile8Bit(file, level->num_yamyam_contents);
1294 putFile8Bit(file, 0);
1295 putFile8Bit(file, 0);
1297 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1300 if (level->encoding_16bit_field)
1301 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1303 putFile8Bit(file, level->yamyam_content[i][x][y]);
1307 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1310 int num_contents, content_xsize, content_ysize;
1311 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1313 if (element == EL_YAMYAM)
1315 num_contents = level->num_yamyam_contents;
1319 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1322 content_array[i][x][y] = level->yamyam_content[i][x][y];
1324 else if (element == EL_BD_AMOEBA)
1330 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1333 content_array[i][x][y] = EL_EMPTY;
1334 content_array[0][0][0] = level->amoeba_content;
1338 /* chunk header already written -- write empty chunk data */
1339 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1341 Error(ERR_WARN, "cannot save content for element '%d'", element);
1345 putFile16BitBE(file, element);
1346 putFile8Bit(file, num_contents);
1347 putFile8Bit(file, content_xsize);
1348 putFile8Bit(file, content_ysize);
1350 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1352 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1355 putFile16BitBE(file, content_array[i][x][y]);
1358 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1361 int envelope_len = strlen(level->envelope) + 1;
1363 putFile16BitBE(file, element);
1364 putFile16BitBE(file, envelope_len);
1365 putFile8Bit(file, level->envelope_xsize);
1366 putFile8Bit(file, level->envelope_ysize);
1368 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1370 for(i=0; i < envelope_len; i++)
1371 putFile8Bit(file, level->envelope[i]);
1375 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1376 int num_changed_custom_elements)
1380 putFile16BitBE(file, num_changed_custom_elements);
1382 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1384 int element = EL_CUSTOM_START + i;
1386 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1388 if (check < num_changed_custom_elements)
1390 putFile16BitBE(file, element);
1391 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1398 if (check != num_changed_custom_elements) /* should not happen */
1399 Error(ERR_WARN, "inconsistent number of custom element properties");
1404 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1405 int num_changed_custom_elements)
1409 putFile16BitBE(file, num_changed_custom_elements);
1411 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1413 int element = EL_CUSTOM_START + i;
1415 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1417 if (check < num_changed_custom_elements)
1419 putFile16BitBE(file, element);
1420 putFile16BitBE(file, element_info[element].change->target_element);
1427 if (check != num_changed_custom_elements) /* should not happen */
1428 Error(ERR_WARN, "inconsistent number of custom target elements");
1433 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1434 int num_changed_custom_elements)
1436 int i, j, x, y, check = 0;
1438 putFile16BitBE(file, num_changed_custom_elements);
1440 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1442 int element = EL_CUSTOM_START + i;
1444 if (element_info[element].modified_settings)
1446 if (check < num_changed_custom_elements)
1448 putFile16BitBE(file, element);
1450 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1451 putFile8Bit(file, element_info[element].description[j]);
1453 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1455 /* some free bytes for future properties and padding */
1456 WriteUnusedBytesToFile(file, 7);
1458 putFile8Bit(file, element_info[element].use_gfx_element);
1459 putFile16BitBE(file, element_info[element].gfx_element);
1461 putFile8Bit(file, element_info[element].collect_score);
1462 putFile8Bit(file, element_info[element].collect_count);
1464 putFile16BitBE(file, element_info[element].push_delay_fixed);
1465 putFile16BitBE(file, element_info[element].push_delay_random);
1466 putFile16BitBE(file, element_info[element].move_delay_fixed);
1467 putFile16BitBE(file, element_info[element].move_delay_random);
1469 putFile16BitBE(file, element_info[element].move_pattern);
1470 putFile8Bit(file, element_info[element].move_direction_initial);
1471 putFile8Bit(file, element_info[element].move_stepsize);
1475 putFile16BitBE(file, element_info[element].content[x][y]);
1477 putFile32BitBE(file, element_info[element].change->events);
1479 putFile16BitBE(file, element_info[element].change->target_element);
1481 putFile16BitBE(file, element_info[element].change->delay_fixed);
1482 putFile16BitBE(file, element_info[element].change->delay_random);
1483 putFile16BitBE(file, element_info[element].change->delay_frames);
1485 putFile16BitBE(file, element_info[element].change->trigger_element);
1487 putFile8Bit(file, element_info[element].change->explode);
1488 putFile8Bit(file, element_info[element].change->use_content);
1489 putFile8Bit(file, element_info[element].change->only_complete);
1490 putFile8Bit(file, element_info[element].change->use_random_change);
1492 putFile8Bit(file, element_info[element].change->random);
1493 putFile8Bit(file, element_info[element].change->power);
1497 putFile16BitBE(file, element_info[element].change->content[x][y]);
1499 putFile8Bit(file, element_info[element].slippery_type);
1501 /* some free bytes for future properties and padding */
1502 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1509 if (check != num_changed_custom_elements) /* should not happen */
1510 Error(ERR_WARN, "inconsistent number of custom element properties");
1514 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1516 struct ElementInfo *ei = &element_info[element];
1519 putFile16BitBE(file, element);
1521 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
1522 putFile8Bit(file, ei->description[i]);
1524 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1525 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
1527 putFile8Bit(file, ei->num_change_pages);
1529 /* some free bytes for future base property values and padding */
1530 WriteUnusedBytesToFile(file, 5);
1532 /* write custom property values */
1534 putFile8Bit(file, ei->use_gfx_element);
1535 putFile16BitBE(file, ei->gfx_element);
1537 putFile8Bit(file, ei->collect_score);
1538 putFile8Bit(file, ei->collect_count);
1540 putFile16BitBE(file, ei->push_delay_fixed);
1541 putFile16BitBE(file, ei->push_delay_random);
1542 putFile16BitBE(file, ei->move_delay_fixed);
1543 putFile16BitBE(file, ei->move_delay_random);
1545 putFile16BitBE(file, ei->move_pattern);
1546 putFile8Bit(file, ei->move_direction_initial);
1547 putFile8Bit(file, ei->move_stepsize);
1549 putFile8Bit(file, ei->slippery_type);
1553 putFile16BitBE(file, ei->content[x][y]);
1555 /* some free bytes for future custom property values and padding */
1556 WriteUnusedBytesToFile(file, 12);
1558 /* write change property values */
1560 for (i=0; i < ei->num_change_pages; i++)
1562 struct ElementChangeInfo *change = &ei->change_page[i];
1564 putFile32BitBE(file, change->events);
1566 putFile16BitBE(file, change->target_element);
1568 putFile16BitBE(file, change->delay_fixed);
1569 putFile16BitBE(file, change->delay_random);
1570 putFile16BitBE(file, change->delay_frames);
1572 putFile16BitBE(file, change->trigger_element);
1574 putFile8Bit(file, change->explode);
1575 putFile8Bit(file, change->use_content);
1576 putFile8Bit(file, change->only_complete);
1577 putFile8Bit(file, change->use_random_change);
1579 putFile8Bit(file, change->random);
1580 putFile8Bit(file, change->power);
1584 putFile16BitBE(file, change->content[x][y]);
1586 putFile8Bit(file, change->can_change);
1588 /* some free bytes for future change property values and padding */
1589 WriteUnusedBytesToFile(file, 9);
1593 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1595 int body_chunk_size;
1599 if (!(file = fopen(filename, MODE_WRITE)))
1601 Error(ERR_WARN, "cannot save level file '%s'", filename);
1605 level->file_version = FILE_VERSION_ACTUAL;
1606 level->game_version = GAME_VERSION_ACTUAL;
1608 /* check level field for 16-bit elements */
1609 level->encoding_16bit_field = FALSE;
1610 for(y=0; y<level->fieldy; y++)
1611 for(x=0; x<level->fieldx; x++)
1612 if (level->field[x][y] > 255)
1613 level->encoding_16bit_field = TRUE;
1615 /* check yamyam content for 16-bit elements */
1616 level->encoding_16bit_yamyam = FALSE;
1617 for(i=0; i<level->num_yamyam_contents; i++)
1620 if (level->yamyam_content[i][x][y] > 255)
1621 level->encoding_16bit_yamyam = TRUE;
1623 /* check amoeba content for 16-bit elements */
1624 level->encoding_16bit_amoeba = FALSE;
1625 if (level->amoeba_content > 255)
1626 level->encoding_16bit_amoeba = TRUE;
1628 /* calculate size of "BODY" chunk */
1630 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1632 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1633 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1635 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1636 SaveLevel_VERS(file, level);
1638 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1639 SaveLevel_HEAD(file, level);
1641 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1642 SaveLevel_AUTH(file, level);
1644 putFileChunkBE(file, "BODY", body_chunk_size);
1645 SaveLevel_BODY(file, level);
1647 if (level->encoding_16bit_yamyam ||
1648 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1650 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1651 SaveLevel_CNT2(file, level, EL_YAMYAM);
1654 if (level->encoding_16bit_amoeba)
1656 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1657 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1660 /* check for envelope content */
1661 if (strlen(level->envelope) > 0)
1663 int envelope_len = strlen(level->envelope) + 1;
1665 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
1666 SaveLevel_CNT3(file, level, EL_ENVELOPE);
1669 /* check for non-default custom elements (unless using template level) */
1670 if (!level->use_custom_template)
1672 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1674 int element = EL_CUSTOM_START + i;
1676 if (element_info[element].modified_settings)
1678 int num_change_pages = element_info[element].num_change_pages;
1680 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
1681 SaveLevel_CUS4(file, level, element);
1688 SetFilePermissions(filename, PERMS_PRIVATE);
1691 void SaveLevel(int level_nr)
1693 char *filename = getLevelFilename(level_nr);
1695 SaveLevelFromFilename(&level, filename);
1698 void SaveLevelTemplate()
1700 char *filename = getLevelFilename(-1);
1702 SaveLevelFromFilename(&level, filename);
1705 void DumpLevel(struct LevelInfo *level)
1707 printf_line("-", 79);
1708 printf("Level xxx (file version %08d, game version %08d)\n",
1709 level->file_version, level->game_version);
1710 printf_line("-", 79);
1712 printf("Level Author: '%s'\n", level->author);
1713 printf("Level Title: '%s'\n", level->name);
1715 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1717 printf("Level Time: %d seconds\n", level->time);
1718 printf("Gems needed: %d\n", level->gems_needed);
1720 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1721 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1722 printf("Time for Light: %d seconds\n", level->time_light);
1723 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1725 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1727 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1728 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1729 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1731 printf_line("-", 79);
1735 /* ========================================================================= */
1736 /* tape file functions */
1737 /* ========================================================================= */
1739 static void setTapeInfoToDefaults()
1743 /* always start with reliable default values (empty tape) */
1746 /* default values (also for pre-1.2 tapes) with only the first player */
1747 tape.player_participates[0] = TRUE;
1748 for(i=1; i<MAX_PLAYERS; i++)
1749 tape.player_participates[i] = FALSE;
1751 /* at least one (default: the first) player participates in every tape */
1752 tape.num_participating_players = 1;
1754 tape.level_nr = level_nr;
1756 tape.changed = FALSE;
1758 tape.recording = FALSE;
1759 tape.playing = FALSE;
1760 tape.pausing = FALSE;
1763 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1765 tape->file_version = getFileVersion(file);
1766 tape->game_version = getFileVersion(file);
1771 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1775 tape->random_seed = getFile32BitBE(file);
1776 tape->date = getFile32BitBE(file);
1777 tape->length = getFile32BitBE(file);
1779 /* read header fields that are new since version 1.2 */
1780 if (tape->file_version >= FILE_VERSION_1_2)
1782 byte store_participating_players = getFile8Bit(file);
1785 /* since version 1.2, tapes store which players participate in the tape */
1786 tape->num_participating_players = 0;
1787 for(i=0; i<MAX_PLAYERS; i++)
1789 tape->player_participates[i] = FALSE;
1791 if (store_participating_players & (1 << i))
1793 tape->player_participates[i] = TRUE;
1794 tape->num_participating_players++;
1798 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1800 engine_version = getFileVersion(file);
1801 if (engine_version > 0)
1802 tape->engine_version = engine_version;
1804 tape->engine_version = tape->game_version;
1810 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1812 int level_identifier_size;
1815 level_identifier_size = getFile16BitBE(file);
1817 tape->level_identifier =
1818 checked_realloc(tape->level_identifier, level_identifier_size);
1820 for(i=0; i < level_identifier_size; i++)
1821 tape->level_identifier[i] = getFile8Bit(file);
1823 tape->level_nr = getFile16BitBE(file);
1825 chunk_size = 2 + level_identifier_size + 2;
1830 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1833 int chunk_size_expected =
1834 (tape->num_participating_players + 1) * tape->length;
1836 if (chunk_size_expected != chunk_size)
1838 ReadUnusedBytesFromFile(file, chunk_size);
1839 return chunk_size_expected;
1842 for(i=0; i<tape->length; i++)
1844 if (i >= MAX_TAPELEN)
1847 for(j=0; j<MAX_PLAYERS; j++)
1849 tape->pos[i].action[j] = MV_NO_MOVING;
1851 if (tape->player_participates[j])
1852 tape->pos[i].action[j] = getFile8Bit(file);
1855 tape->pos[i].delay = getFile8Bit(file);
1857 if (tape->file_version == FILE_VERSION_1_0)
1859 /* eliminate possible diagonal moves in old tapes */
1860 /* this is only for backward compatibility */
1862 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1863 byte action = tape->pos[i].action[0];
1864 int k, num_moves = 0;
1868 if (action & joy_dir[k])
1870 tape->pos[i + num_moves].action[0] = joy_dir[k];
1872 tape->pos[i + num_moves].delay = 0;
1881 tape->length += num_moves;
1884 else if (tape->file_version < FILE_VERSION_2_0)
1886 /* convert pre-2.0 tapes to new tape format */
1888 if (tape->pos[i].delay > 1)
1891 tape->pos[i + 1] = tape->pos[i];
1892 tape->pos[i + 1].delay = 1;
1895 for(j=0; j<MAX_PLAYERS; j++)
1896 tape->pos[i].action[j] = MV_NO_MOVING;
1897 tape->pos[i].delay--;
1908 if (i != tape->length)
1909 chunk_size = (tape->num_participating_players + 1) * i;
1914 void LoadTapeFromFilename(char *filename)
1916 char cookie[MAX_LINE_LEN];
1917 char chunk_name[CHUNK_ID_LEN + 1];
1921 /* always start with reliable default values */
1922 setTapeInfoToDefaults();
1924 if (!(file = fopen(filename, MODE_READ)))
1927 getFileChunkBE(file, chunk_name, NULL);
1928 if (strcmp(chunk_name, "RND1") == 0)
1930 getFile32BitBE(file); /* not used */
1932 getFileChunkBE(file, chunk_name, NULL);
1933 if (strcmp(chunk_name, "TAPE") != 0)
1935 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1940 else /* check for pre-2.0 file format with cookie string */
1942 strcpy(cookie, chunk_name);
1943 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1944 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1945 cookie[strlen(cookie) - 1] = '\0';
1947 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1949 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1954 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1956 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1961 /* pre-2.0 tape files have no game version, so use file version here */
1962 tape.game_version = tape.file_version;
1965 if (tape.file_version < FILE_VERSION_1_2)
1967 /* tape files from versions before 1.2.0 without chunk structure */
1968 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1969 LoadTape_BODY(file, 2 * tape.length, &tape);
1977 int (*loader)(FILE *, int, struct TapeInfo *);
1981 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1982 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1983 { "INFO", -1, LoadTape_INFO },
1984 { "BODY", -1, LoadTape_BODY },
1988 while (getFileChunkBE(file, chunk_name, &chunk_size))
1992 while (chunk_info[i].name != NULL &&
1993 strcmp(chunk_name, chunk_info[i].name) != 0)
1996 if (chunk_info[i].name == NULL)
1998 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1999 chunk_name, filename);
2000 ReadUnusedBytesFromFile(file, chunk_size);
2002 else if (chunk_info[i].size != -1 &&
2003 chunk_info[i].size != chunk_size)
2005 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2006 chunk_size, chunk_name, filename);
2007 ReadUnusedBytesFromFile(file, chunk_size);
2011 /* call function to load this tape chunk */
2012 int chunk_size_expected =
2013 (chunk_info[i].loader)(file, chunk_size, &tape);
2015 /* the size of some chunks cannot be checked before reading other
2016 chunks first (like "HEAD" and "BODY") that contain some header
2017 information, so check them here */
2018 if (chunk_size_expected != chunk_size)
2020 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2021 chunk_size, chunk_name, filename);
2029 tape.length_seconds = GetTapeLength();
2032 printf("tape game version: %d\n", tape.game_version);
2033 printf("tape engine version: %d\n", tape.engine_version);
2037 void LoadTape(int level_nr)
2039 char *filename = getTapeFilename(level_nr);
2041 LoadTapeFromFilename(filename);
2044 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2046 putFileVersion(file, tape->file_version);
2047 putFileVersion(file, tape->game_version);
2050 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2053 byte store_participating_players = 0;
2055 /* set bits for participating players for compact storage */
2056 for(i=0; i<MAX_PLAYERS; i++)
2057 if (tape->player_participates[i])
2058 store_participating_players |= (1 << i);
2060 putFile32BitBE(file, tape->random_seed);
2061 putFile32BitBE(file, tape->date);
2062 putFile32BitBE(file, tape->length);
2064 putFile8Bit(file, store_participating_players);
2066 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2067 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2069 putFileVersion(file, tape->engine_version);
2072 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2074 int level_identifier_size = strlen(tape->level_identifier) + 1;
2077 putFile16BitBE(file, level_identifier_size);
2079 for(i=0; i < level_identifier_size; i++)
2080 putFile8Bit(file, tape->level_identifier[i]);
2082 putFile16BitBE(file, tape->level_nr);
2085 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2089 for(i=0; i<tape->length; i++)
2091 for(j=0; j<MAX_PLAYERS; j++)
2092 if (tape->player_participates[j])
2093 putFile8Bit(file, tape->pos[i].action[j]);
2095 putFile8Bit(file, tape->pos[i].delay);
2099 void SaveTape(int level_nr)
2101 char *filename = getTapeFilename(level_nr);
2103 boolean new_tape = TRUE;
2104 int num_participating_players = 0;
2105 int info_chunk_size;
2106 int body_chunk_size;
2109 InitTapeDirectory(leveldir_current->filename);
2111 /* if a tape still exists, ask to overwrite it */
2112 if (access(filename, F_OK) == 0)
2115 if (!Request("Replace old tape ?", REQ_ASK))
2119 if (!(file = fopen(filename, MODE_WRITE)))
2121 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2125 tape.file_version = FILE_VERSION_ACTUAL;
2126 tape.game_version = GAME_VERSION_ACTUAL;
2128 /* count number of participating players */
2129 for(i=0; i<MAX_PLAYERS; i++)
2130 if (tape.player_participates[i])
2131 num_participating_players++;
2133 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2134 body_chunk_size = (num_participating_players + 1) * tape.length;
2136 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2137 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2139 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2140 SaveTape_VERS(file, &tape);
2142 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2143 SaveTape_HEAD(file, &tape);
2145 putFileChunkBE(file, "INFO", info_chunk_size);
2146 SaveTape_INFO(file, &tape);
2148 putFileChunkBE(file, "BODY", body_chunk_size);
2149 SaveTape_BODY(file, &tape);
2153 SetFilePermissions(filename, PERMS_PRIVATE);
2155 tape.changed = FALSE;
2158 Request("tape saved !", REQ_CONFIRM);
2161 void DumpTape(struct TapeInfo *tape)
2165 if (TAPE_IS_EMPTY(*tape))
2167 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2171 printf_line("-", 79);
2172 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2173 tape->level_nr, tape->file_version, tape->game_version);
2174 printf("Level series identifier: '%s'\n", tape->level_identifier);
2175 printf_line("-", 79);
2177 for(i=0; i<tape->length; i++)
2179 if (i >= MAX_TAPELEN)
2182 printf("%03d: ", i);
2184 for(j=0; j<MAX_PLAYERS; j++)
2186 if (tape->player_participates[j])
2188 int action = tape->pos[i].action[j];
2190 printf("%d:%02x ", j, action);
2191 printf("[%c%c%c%c|%c%c] - ",
2192 (action & JOY_LEFT ? '<' : ' '),
2193 (action & JOY_RIGHT ? '>' : ' '),
2194 (action & JOY_UP ? '^' : ' '),
2195 (action & JOY_DOWN ? 'v' : ' '),
2196 (action & JOY_BUTTON_1 ? '1' : ' '),
2197 (action & JOY_BUTTON_2 ? '2' : ' '));
2201 printf("(%03d)\n", tape->pos[i].delay);
2204 printf_line("-", 79);
2208 /* ========================================================================= */
2209 /* score file functions */
2210 /* ========================================================================= */
2212 void LoadScore(int level_nr)
2215 char *filename = getScoreFilename(level_nr);
2216 char cookie[MAX_LINE_LEN];
2217 char line[MAX_LINE_LEN];
2221 /* always start with reliable default values */
2222 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2224 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2225 highscore[i].Score = 0;
2228 if (!(file = fopen(filename, MODE_READ)))
2231 /* check file identifier */
2232 fgets(cookie, MAX_LINE_LEN, file);
2233 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2234 cookie[strlen(cookie) - 1] = '\0';
2236 if (!checkCookieString(cookie, SCORE_COOKIE))
2238 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2243 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2245 fscanf(file, "%d", &highscore[i].Score);
2246 fgets(line, MAX_LINE_LEN, file);
2248 if (line[strlen(line) - 1] == '\n')
2249 line[strlen(line) - 1] = '\0';
2251 for (line_ptr = line; *line_ptr; line_ptr++)
2253 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2255 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2256 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2265 void SaveScore(int level_nr)
2268 char *filename = getScoreFilename(level_nr);
2271 InitScoreDirectory(leveldir_current->filename);
2273 if (!(file = fopen(filename, MODE_WRITE)))
2275 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2279 fprintf(file, "%s\n\n", SCORE_COOKIE);
2281 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2282 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2286 SetFilePermissions(filename, PERMS_PUBLIC);
2290 /* ========================================================================= */
2291 /* setup file functions */
2292 /* ========================================================================= */
2294 #define TOKEN_STR_PLAYER_PREFIX "player_"
2297 #define SETUP_TOKEN_PLAYER_NAME 0
2298 #define SETUP_TOKEN_SOUND 1
2299 #define SETUP_TOKEN_SOUND_LOOPS 2
2300 #define SETUP_TOKEN_SOUND_MUSIC 3
2301 #define SETUP_TOKEN_SOUND_SIMPLE 4
2302 #define SETUP_TOKEN_TOONS 5
2303 #define SETUP_TOKEN_SCROLL_DELAY 6
2304 #define SETUP_TOKEN_SOFT_SCROLLING 7
2305 #define SETUP_TOKEN_FADING 8
2306 #define SETUP_TOKEN_AUTORECORD 9
2307 #define SETUP_TOKEN_QUICK_DOORS 10
2308 #define SETUP_TOKEN_TEAM_MODE 11
2309 #define SETUP_TOKEN_HANDICAP 12
2310 #define SETUP_TOKEN_TIME_LIMIT 13
2311 #define SETUP_TOKEN_FULLSCREEN 14
2312 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2313 #define SETUP_TOKEN_GRAPHICS_SET 16
2314 #define SETUP_TOKEN_SOUNDS_SET 17
2315 #define SETUP_TOKEN_MUSIC_SET 18
2316 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2317 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2318 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2320 #define NUM_GLOBAL_SETUP_TOKENS 22
2323 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2324 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2325 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2326 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2327 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2328 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2329 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2330 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2331 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2332 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2333 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2335 #define NUM_EDITOR_SETUP_TOKENS 11
2337 /* shortcut setup */
2338 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2339 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2340 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2342 #define NUM_SHORTCUT_SETUP_TOKENS 3
2345 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2346 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2347 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2348 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2349 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2350 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2351 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2352 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2353 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2354 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
2355 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2356 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2357 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2358 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2359 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2360 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
2362 #define NUM_PLAYER_SETUP_TOKENS 16
2365 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2366 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2368 #define NUM_SYSTEM_SETUP_TOKENS 2
2371 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2373 #define NUM_OPTIONS_SETUP_TOKENS 1
2376 static struct SetupInfo si;
2377 static struct SetupEditorInfo sei;
2378 static struct SetupShortcutInfo ssi;
2379 static struct SetupInputInfo sii;
2380 static struct SetupSystemInfo syi;
2381 static struct OptionInfo soi;
2383 static struct TokenInfo global_setup_tokens[] =
2385 { TYPE_STRING, &si.player_name, "player_name" },
2386 { TYPE_SWITCH, &si.sound, "sound" },
2387 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2388 { TYPE_SWITCH, &si.sound_music, "background_music" },
2389 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2390 { TYPE_SWITCH, &si.toons, "toons" },
2391 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2392 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2393 { TYPE_SWITCH, &si.fading, "screen_fading" },
2394 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2395 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2396 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2397 { TYPE_SWITCH, &si.handicap, "handicap" },
2398 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2399 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2400 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2401 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2402 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2403 { TYPE_STRING, &si.music_set, "music_set" },
2404 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2405 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2406 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2409 static struct TokenInfo editor_setup_tokens[] =
2411 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2412 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2413 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2414 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2415 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2416 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2417 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2418 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2419 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2420 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2421 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2424 static struct TokenInfo shortcut_setup_tokens[] =
2426 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2427 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2428 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2431 static struct TokenInfo player_setup_tokens[] =
2433 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2434 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2435 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2436 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2437 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2438 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2439 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2440 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2441 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2442 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2443 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2444 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2445 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2446 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2447 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2448 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
2451 static struct TokenInfo system_setup_tokens[] =
2453 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2454 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2457 static struct TokenInfo options_setup_tokens[] =
2459 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2462 static char *get_corrected_login_name(char *login_name)
2464 /* needed because player name must be a fixed length string */
2465 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2467 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2468 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2470 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2471 if (strchr(login_name_new, ' '))
2472 *strchr(login_name_new, ' ') = '\0';
2474 return login_name_new;
2477 static void setSetupInfoToDefaults(struct SetupInfo *si)
2481 si->player_name = get_corrected_login_name(getLoginName());
2484 si->sound_loops = TRUE;
2485 si->sound_music = TRUE;
2486 si->sound_simple = TRUE;
2488 si->double_buffering = TRUE;
2489 si->direct_draw = !si->double_buffering;
2490 si->scroll_delay = TRUE;
2491 si->soft_scrolling = TRUE;
2493 si->autorecord = TRUE;
2494 si->quick_doors = FALSE;
2495 si->team_mode = FALSE;
2496 si->handicap = TRUE;
2497 si->time_limit = TRUE;
2498 si->fullscreen = FALSE;
2499 si->ask_on_escape = TRUE;
2501 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2502 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2503 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2504 si->override_level_graphics = FALSE;
2505 si->override_level_sounds = FALSE;
2506 si->override_level_music = FALSE;
2508 si->editor.el_boulderdash = TRUE;
2509 si->editor.el_emerald_mine = TRUE;
2510 si->editor.el_more = TRUE;
2511 si->editor.el_sokoban = TRUE;
2512 si->editor.el_supaplex = TRUE;
2513 si->editor.el_diamond_caves = TRUE;
2514 si->editor.el_dx_boulderdash = TRUE;
2515 si->editor.el_chars = TRUE;
2516 si->editor.el_custom = TRUE;
2517 si->editor.el_custom_more = FALSE;
2519 si->editor.el_headlines = TRUE;
2521 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2522 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2523 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2525 for (i=0; i<MAX_PLAYERS; i++)
2527 si->input[i].use_joystick = FALSE;
2528 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2529 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2530 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2531 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2532 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2533 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2534 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2535 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2536 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2537 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2538 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2539 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2540 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2541 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2542 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2545 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2546 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2548 si->options.verbose = FALSE;
2551 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2555 if (!setup_file_hash)
2560 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2561 setSetupInfo(global_setup_tokens, i,
2562 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2567 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2568 setSetupInfo(editor_setup_tokens, i,
2569 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2572 /* shortcut setup */
2573 ssi = setup.shortcut;
2574 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2575 setSetupInfo(shortcut_setup_tokens, i,
2576 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2577 setup.shortcut = ssi;
2580 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2584 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2586 sii = setup.input[pnr];
2587 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2589 char full_token[100];
2591 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2592 setSetupInfo(player_setup_tokens, i,
2593 getHashEntry(setup_file_hash, full_token));
2595 setup.input[pnr] = sii;
2600 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2601 setSetupInfo(system_setup_tokens, i,
2602 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2606 soi = setup.options;
2607 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2608 setSetupInfo(options_setup_tokens, i,
2609 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2610 setup.options = soi;
2615 char *filename = getSetupFilename();
2616 SetupFileHash *setup_file_hash = NULL;
2618 /* always start with reliable default values */
2619 setSetupInfoToDefaults(&setup);
2621 setup_file_hash = loadSetupFileHash(filename);
2623 if (setup_file_hash)
2625 char *player_name_new;
2627 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2628 decodeSetupFileHash(setup_file_hash);
2630 setup.direct_draw = !setup.double_buffering;
2632 freeSetupFileHash(setup_file_hash);
2634 /* needed to work around problems with fixed length strings */
2635 player_name_new = get_corrected_login_name(setup.player_name);
2636 free(setup.player_name);
2637 setup.player_name = player_name_new;
2640 Error(ERR_WARN, "using default setup values");
2645 char *filename = getSetupFilename();
2649 InitUserDataDirectory();
2651 if (!(file = fopen(filename, MODE_WRITE)))
2653 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2657 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2658 getCookie("SETUP")));
2659 fprintf(file, "\n");
2663 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2665 /* just to make things nicer :) */
2666 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2667 i == SETUP_TOKEN_GRAPHICS_SET)
2668 fprintf(file, "\n");
2670 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2675 fprintf(file, "\n");
2676 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2677 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2679 /* shortcut setup */
2680 ssi = setup.shortcut;
2681 fprintf(file, "\n");
2682 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2683 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2686 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2690 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2691 fprintf(file, "\n");
2693 sii = setup.input[pnr];
2694 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2695 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2700 fprintf(file, "\n");
2701 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2702 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2705 soi = setup.options;
2706 fprintf(file, "\n");
2707 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2708 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2712 SetFilePermissions(filename, PERMS_PRIVATE);
2715 void LoadCustomElementDescriptions()
2717 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2718 SetupFileHash *setup_file_hash;
2721 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2723 if (element_info[i].custom_description != NULL)
2725 free(element_info[i].custom_description);
2726 element_info[i].custom_description = NULL;
2730 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2733 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2735 char *token = getStringCat2(element_info[i].token_name, ".name");
2736 char *value = getHashEntry(setup_file_hash, token);
2739 element_info[i].custom_description = getStringCopy(value);
2744 freeSetupFileHash(setup_file_hash);
2747 void LoadSpecialMenuDesignSettings()
2749 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2750 SetupFileHash *setup_file_hash;
2753 /* always start with reliable default values from default config */
2754 for (i=0; image_config_vars[i].token != NULL; i++)
2755 for (j=0; image_config[j].token != NULL; j++)
2756 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2757 *image_config_vars[i].value =
2758 get_integer_from_string(image_config[j].value);
2760 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2763 /* special case: initialize with default values that may be overwritten */
2764 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2766 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2767 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2768 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2770 if (value_x != NULL)
2771 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2772 if (value_y != NULL)
2773 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2774 if (list_size != NULL)
2775 menu.list_size[i] = get_integer_from_string(list_size);
2778 /* read (and overwrite with) values that may be specified in config file */
2779 for (i=0; image_config_vars[i].token != NULL; i++)
2781 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2784 *image_config_vars[i].value = get_integer_from_string(value);
2787 freeSetupFileHash(setup_file_hash);