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 ***********************************************************/
18 #include "libgame/libgame.h"
26 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
27 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
28 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
29 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
30 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
31 #define LEVEL_HEADER_UNUSED 13 /* unused level header bytes */
32 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
33 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
34 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
35 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
36 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
37 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
38 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
39 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
41 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
42 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
44 /* file identifier strings */
45 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
46 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
47 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
50 /* ========================================================================= */
51 /* level file functions */
52 /* ========================================================================= */
54 void setElementChangePages(struct ElementInfo *ei, int change_pages)
56 int change_page_size = sizeof(struct ElementChangeInfo);
58 ei->num_change_pages = MAX(1, change_pages);
61 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
63 if (ei->current_change_page >= ei->num_change_pages)
64 ei->current_change_page = ei->num_change_pages - 1;
66 ei->change = &ei->change_page[ei->current_change_page];
69 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
73 change->can_change = FALSE;
75 change->events = CE_BITMASK_DEFAULT;
76 change->sides = CH_SIDE_ANY;
78 change->target_element = EL_EMPTY_SPACE;
80 change->delay_fixed = 0;
81 change->delay_random = 0;
82 change->delay_frames = 1;
84 change->trigger_element = EL_EMPTY_SPACE;
86 change->explode = FALSE;
87 change->use_content = FALSE;
88 change->only_complete = FALSE;
89 change->use_random_change = FALSE;
91 change->power = CP_NON_DESTRUCTIVE;
95 change->content[x][y] = EL_EMPTY_SPACE;
97 change->direct_action = 0;
98 change->other_action = 0;
100 change->pre_change_function = NULL;
101 change->change_function = NULL;
102 change->post_change_function = NULL;
105 static void setLevelInfoToDefaults(struct LevelInfo *level)
109 level->file_version = FILE_VERSION_ACTUAL;
110 level->game_version = GAME_VERSION_ACTUAL;
112 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
113 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
114 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
116 level->fieldx = STD_LEV_FIELDX;
117 level->fieldy = STD_LEV_FIELDY;
119 for(x=0; x<MAX_LEV_FIELDX; x++)
120 for(y=0; y<MAX_LEV_FIELDY; y++)
121 level->field[x][y] = EL_SAND;
124 level->gems_needed = 0;
125 level->amoeba_speed = 10;
126 level->time_magic_wall = 10;
127 level->time_wheel = 10;
128 level->time_light = 10;
129 level->time_timegate = 10;
130 level->amoeba_content = EL_DIAMOND;
131 level->double_speed = FALSE;
132 level->initial_gravity = FALSE;
133 level->em_slippery_gems = FALSE;
135 level->use_custom_template = FALSE;
137 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
138 level->name[i] = '\0';
139 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
140 level->author[i] = '\0';
142 strcpy(level->name, NAMELESS_LEVEL_NAME);
143 strcpy(level->author, ANONYMOUS_NAME);
147 level->envelope_text[i][0] = '\0';
148 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
149 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
152 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
153 level->score[i] = 10;
155 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
156 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
159 level->yamyam_content[i][x][y] =
160 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
162 level->field[0][0] = EL_PLAYER_1;
163 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
165 for (i=0; i < MAX_NUM_ELEMENTS; i++)
167 setElementChangePages(&element_info[i], 1);
168 setElementChangeInfoToDefaults(element_info[i].change);
171 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
173 int element = EL_CUSTOM_START + i;
175 for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
176 element_info[element].description[j] = '\0';
177 if (element_info[element].custom_description != NULL)
178 strncpy(element_info[element].description,
179 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
181 strcpy(element_info[element].description,
182 element_info[element].editor_description);
184 element_info[element].use_gfx_element = FALSE;
185 element_info[element].gfx_element = EL_EMPTY_SPACE;
187 element_info[element].collect_score = 10; /* special default */
188 element_info[element].collect_count = 1; /* special default */
190 element_info[element].push_delay_fixed = -1; /* initialize later */
191 element_info[element].push_delay_random = -1; /* initialize later */
192 element_info[element].move_delay_fixed = 0;
193 element_info[element].move_delay_random = 0;
195 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
196 element_info[element].move_direction_initial = MV_NO_MOVING;
197 element_info[element].move_stepsize = TILEX / 8;
199 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
203 element_info[element].content[x][y] = EL_EMPTY_SPACE;
205 element_info[element].access_type = 0;
206 element_info[element].access_layer = 0;
207 element_info[element].walk_to_action = 0;
208 element_info[element].smash_targets = 0;
209 element_info[element].deadliness = 0;
210 element_info[element].consistency = 0;
212 element_info[element].can_explode_by_fire = FALSE;
213 element_info[element].can_explode_smashed = FALSE;
214 element_info[element].can_explode_impact = FALSE;
216 element_info[element].current_change_page = 0;
218 /* start with no properties at all */
219 for (j=0; j < NUM_EP_BITFIELDS; j++)
220 Properties[element][j] = EP_BITMASK_DEFAULT;
222 element_info[element].modified_settings = FALSE;
225 BorderElement = EL_STEELWALL;
227 level->no_level_file = FALSE;
229 if (leveldir_current == NULL) /* only when dumping level */
232 /* try to determine better author name than 'anonymous' */
233 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
235 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
236 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
240 switch (LEVELCLASS(leveldir_current))
242 case LEVELCLASS_TUTORIAL:
243 strcpy(level->author, PROGRAM_AUTHOR_STRING);
246 case LEVELCLASS_CONTRIB:
247 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
248 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
251 case LEVELCLASS_PRIVATE:
252 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
253 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
257 /* keep default value */
263 static void ActivateLevelTemplate()
265 /* Currently there is no special action needed to activate the template
266 data, because 'element_info' and 'Properties' overwrite the original
267 level data, while all other variables do not change. */
270 boolean LevelFileExists(int level_nr)
272 char *filename = getLevelFilename(level_nr);
274 return (access(filename, F_OK) == 0);
277 static int checkLevelElement(int element)
279 /* map some (historic, now obsolete) elements */
284 case EL_PLAYER_OBSOLETE:
285 element = EL_PLAYER_1;
288 case EL_KEY_OBSOLETE:
291 case EL_EM_KEY_1_FILE_OBSOLETE:
292 element = EL_EM_KEY_1;
295 case EL_EM_KEY_2_FILE_OBSOLETE:
296 element = EL_EM_KEY_2;
299 case EL_EM_KEY_3_FILE_OBSOLETE:
300 element = EL_EM_KEY_3;
303 case EL_EM_KEY_4_FILE_OBSOLETE:
304 element = EL_EM_KEY_4;
307 case EL_ENVELOPE_OBSOLETE:
308 element = EL_ENVELOPE_1;
316 if (element >= NUM_FILE_ELEMENTS)
318 Error(ERR_WARN, "invalid level element %d", element);
320 element = EL_CHAR_QUESTION;
325 if (element >= NUM_FILE_ELEMENTS)
327 Error(ERR_WARN, "invalid level element %d", element);
329 element = EL_CHAR_QUESTION;
331 else if (element == EL_PLAYER_OBSOLETE)
332 element = EL_PLAYER_1;
333 else if (element == EL_KEY_OBSOLETE)
340 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
342 level->file_version = getFileVersion(file);
343 level->game_version = getFileVersion(file);
348 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
352 level->fieldx = getFile8Bit(file);
353 level->fieldy = getFile8Bit(file);
355 level->time = getFile16BitBE(file);
356 level->gems_needed = getFile16BitBE(file);
358 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
359 level->name[i] = getFile8Bit(file);
360 level->name[MAX_LEVEL_NAME_LEN] = 0;
362 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
363 level->score[i] = getFile8Bit(file);
365 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
366 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
369 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
371 level->amoeba_speed = getFile8Bit(file);
372 level->time_magic_wall = getFile8Bit(file);
373 level->time_wheel = getFile8Bit(file);
374 level->amoeba_content = checkLevelElement(getFile8Bit(file));
375 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
376 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
377 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
378 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
380 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
382 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
387 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
391 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
392 level->author[i] = getFile8Bit(file);
393 level->author[MAX_LEVEL_NAME_LEN] = 0;
398 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
401 int chunk_size_expected = level->fieldx * level->fieldy;
403 /* Note: "chunk_size" was wrong before version 2.0 when elements are
404 stored with 16-bit encoding (and should be twice as big then).
405 Even worse, playfield data was stored 16-bit when only yamyam content
406 contained 16-bit elements and vice versa. */
408 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
409 chunk_size_expected *= 2;
411 if (chunk_size_expected != chunk_size)
413 ReadUnusedBytesFromFile(file, chunk_size);
414 return chunk_size_expected;
417 for(y=0; y<level->fieldy; y++)
418 for(x=0; x<level->fieldx; x++)
420 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
425 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
429 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
430 int chunk_size_expected = header_size + content_size;
432 /* Note: "chunk_size" was wrong before version 2.0 when elements are
433 stored with 16-bit encoding (and should be twice as big then).
434 Even worse, playfield data was stored 16-bit when only yamyam content
435 contained 16-bit elements and vice versa. */
437 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
438 chunk_size_expected += content_size;
440 if (chunk_size_expected != chunk_size)
442 ReadUnusedBytesFromFile(file, chunk_size);
443 return chunk_size_expected;
447 level->num_yamyam_contents = getFile8Bit(file);
451 /* correct invalid number of content fields -- should never happen */
452 if (level->num_yamyam_contents < 1 ||
453 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
454 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
456 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
459 level->yamyam_content[i][x][y] =
460 checkLevelElement(level->encoding_16bit_field ?
461 getFile16BitBE(file) : getFile8Bit(file));
465 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
469 int num_contents, content_xsize, content_ysize;
470 int content_array[MAX_ELEMENT_CONTENTS][3][3];
472 element = checkLevelElement(getFile16BitBE(file));
473 num_contents = getFile8Bit(file);
474 content_xsize = getFile8Bit(file);
475 content_ysize = getFile8Bit(file);
477 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
479 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
482 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
484 /* correct invalid number of content fields -- should never happen */
485 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
486 num_contents = STD_ELEMENT_CONTENTS;
488 if (element == EL_YAMYAM)
490 level->num_yamyam_contents = num_contents;
492 for(i=0; i<num_contents; i++)
495 level->yamyam_content[i][x][y] = content_array[i][x][y];
497 else if (element == EL_BD_AMOEBA)
499 level->amoeba_content = content_array[0][0][0];
503 Error(ERR_WARN, "cannot load content for element '%d'", element);
509 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
515 int chunk_size_expected;
517 element = checkLevelElement(getFile16BitBE(file));
518 if (!IS_ENVELOPE(element))
519 element = EL_ENVELOPE_1;
521 envelope_nr = element - EL_ENVELOPE_1;
523 envelope_len = getFile16BitBE(file);
525 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
526 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
528 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
530 chunk_size_expected = LEVEL_CHUNK_CNT3_HEADER + envelope_len;
532 if (chunk_size_expected != chunk_size)
534 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
535 return chunk_size_expected;
538 for(i=0; i < envelope_len; i++)
539 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
544 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
546 int num_changed_custom_elements = getFile16BitBE(file);
547 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
550 if (chunk_size_expected != chunk_size)
552 ReadUnusedBytesFromFile(file, chunk_size - 2);
553 return chunk_size_expected;
556 for (i=0; i < num_changed_custom_elements; i++)
558 int element = getFile16BitBE(file);
559 int properties = getFile32BitBE(file);
561 if (IS_CUSTOM_ELEMENT(element))
562 Properties[element][EP_BITFIELD_BASE] = properties;
564 Error(ERR_WARN, "invalid custom element number %d", element);
570 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
572 int num_changed_custom_elements = getFile16BitBE(file);
573 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
576 if (chunk_size_expected != chunk_size)
578 ReadUnusedBytesFromFile(file, chunk_size - 2);
579 return chunk_size_expected;
582 for (i=0; i < num_changed_custom_elements; i++)
584 int element = getFile16BitBE(file);
585 int custom_target_element = getFile16BitBE(file);
587 if (IS_CUSTOM_ELEMENT(element))
588 element_info[element].change->target_element = custom_target_element;
590 Error(ERR_WARN, "invalid custom element number %d", element);
596 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
598 int num_changed_custom_elements = getFile16BitBE(file);
599 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
602 if (chunk_size_expected != chunk_size)
604 ReadUnusedBytesFromFile(file, chunk_size - 2);
605 return chunk_size_expected;
608 for (i=0; i < num_changed_custom_elements; i++)
610 int element = getFile16BitBE(file);
612 if (!IS_CUSTOM_ELEMENT(element))
614 Error(ERR_WARN, "invalid custom element number %d", element);
616 element = EL_DEFAULT; /* dummy element used for artwork config */
619 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
620 element_info[element].description[j] = getFile8Bit(file);
621 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
623 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
625 /* some free bytes for future properties and padding */
626 ReadUnusedBytesFromFile(file, 7);
628 element_info[element].use_gfx_element = getFile8Bit(file);
629 element_info[element].gfx_element =
630 checkLevelElement(getFile16BitBE(file));
632 element_info[element].collect_score = getFile8Bit(file);
633 element_info[element].collect_count = getFile8Bit(file);
635 element_info[element].push_delay_fixed = getFile16BitBE(file);
636 element_info[element].push_delay_random = getFile16BitBE(file);
637 element_info[element].move_delay_fixed = getFile16BitBE(file);
638 element_info[element].move_delay_random = getFile16BitBE(file);
640 element_info[element].move_pattern = getFile16BitBE(file);
641 element_info[element].move_direction_initial = getFile8Bit(file);
642 element_info[element].move_stepsize = getFile8Bit(file);
646 element_info[element].content[x][y] =
647 checkLevelElement(getFile16BitBE(file));
649 element_info[element].change->events = getFile32BitBE(file);
651 element_info[element].change->target_element =
652 checkLevelElement(getFile16BitBE(file));
654 element_info[element].change->delay_fixed = getFile16BitBE(file);
655 element_info[element].change->delay_random = getFile16BitBE(file);
656 element_info[element].change->delay_frames = getFile16BitBE(file);
658 element_info[element].change->trigger_element =
659 checkLevelElement(getFile16BitBE(file));
661 element_info[element].change->explode = getFile8Bit(file);
662 element_info[element].change->use_content = getFile8Bit(file);
663 element_info[element].change->only_complete = getFile8Bit(file);
664 element_info[element].change->use_random_change = getFile8Bit(file);
666 element_info[element].change->random = getFile8Bit(file);
667 element_info[element].change->power = getFile8Bit(file);
671 element_info[element].change->content[x][y] =
672 checkLevelElement(getFile16BitBE(file));
674 element_info[element].slippery_type = getFile8Bit(file);
676 /* some free bytes for future properties and padding */
677 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
679 /* mark that this custom element has been modified */
680 element_info[element].modified_settings = TRUE;
686 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
688 struct ElementInfo *ei;
689 int chunk_size_expected;
693 element = getFile16BitBE(file);
695 if (!IS_CUSTOM_ELEMENT(element))
697 Error(ERR_WARN, "invalid custom element number %d", element);
699 element = EL_DEFAULT; /* dummy element used for artwork config */
702 ei = &element_info[element];
704 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
705 ei->description[i] = getFile8Bit(file);
706 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
708 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
709 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
711 ei->num_change_pages = getFile8Bit(file);
713 /* some free bytes for future base property values and padding */
714 ReadUnusedBytesFromFile(file, 5);
716 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
717 if (chunk_size_expected != chunk_size)
719 ReadUnusedBytesFromFile(file, chunk_size - 48);
720 return chunk_size_expected;
723 /* read custom property values */
725 ei->use_gfx_element = getFile8Bit(file);
726 ei->gfx_element = checkLevelElement(getFile16BitBE(file));
728 ei->collect_score = getFile8Bit(file);
729 ei->collect_count = getFile8Bit(file);
731 ei->push_delay_fixed = getFile16BitBE(file);
732 ei->push_delay_random = getFile16BitBE(file);
733 ei->move_delay_fixed = getFile16BitBE(file);
734 ei->move_delay_random = getFile16BitBE(file);
736 ei->move_pattern = getFile16BitBE(file);
737 ei->move_direction_initial = getFile8Bit(file);
738 ei->move_stepsize = getFile8Bit(file);
740 ei->slippery_type = getFile8Bit(file);
744 ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
746 /* some free bytes for future custom property values and padding */
747 ReadUnusedBytesFromFile(file, 12);
749 /* read change property values */
751 setElementChangePages(ei, ei->num_change_pages);
753 for (i=0; i < ei->num_change_pages; i++)
755 struct ElementChangeInfo *change = &ei->change_page[i];
757 /* always start with reliable default values */
758 setElementChangeInfoToDefaults(change);
760 change->events = getFile32BitBE(file);
762 change->target_element = checkLevelElement(getFile16BitBE(file));
764 change->delay_fixed = getFile16BitBE(file);
765 change->delay_random = getFile16BitBE(file);
766 change->delay_frames = getFile16BitBE(file);
768 change->trigger_element = checkLevelElement(getFile16BitBE(file));
770 change->explode = getFile8Bit(file);
771 change->use_content = getFile8Bit(file);
772 change->only_complete = getFile8Bit(file);
773 change->use_random_change = getFile8Bit(file);
775 change->random = getFile8Bit(file);
776 change->power = getFile8Bit(file);
780 change->content[x][y] = checkLevelElement(getFile16BitBE(file));
782 change->can_change = getFile8Bit(file);
784 change->sides = getFile8Bit(file);
786 if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
787 change->sides = CH_SIDE_ANY;
789 /* some free bytes for future change property values and padding */
790 ReadUnusedBytesFromFile(file, 8);
793 /* mark this custom element as modified */
794 ei->modified_settings = TRUE;
799 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
801 char cookie[MAX_LINE_LEN];
802 char chunk_name[CHUNK_ID_LEN + 1];
806 /* always start with reliable default values */
807 setLevelInfoToDefaults(level);
809 if (!(file = fopen(filename, MODE_READ)))
811 level->no_level_file = TRUE;
813 if (level != &level_template)
814 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
819 getFileChunkBE(file, chunk_name, NULL);
820 if (strcmp(chunk_name, "RND1") == 0)
822 getFile32BitBE(file); /* not used */
824 getFileChunkBE(file, chunk_name, NULL);
825 if (strcmp(chunk_name, "CAVE") != 0)
827 Error(ERR_WARN, "unknown format of level file '%s'", filename);
832 else /* check for pre-2.0 file format with cookie string */
834 strcpy(cookie, chunk_name);
835 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
836 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
837 cookie[strlen(cookie) - 1] = '\0';
839 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
841 Error(ERR_WARN, "unknown format of level file '%s'", filename);
846 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
848 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
853 /* pre-2.0 level files have no game version, so use file version here */
854 level->game_version = level->file_version;
857 if (level->file_version < FILE_VERSION_1_2)
859 /* level files from versions before 1.2.0 without chunk structure */
860 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
861 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
869 int (*loader)(FILE *, int, struct LevelInfo *);
873 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
874 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
875 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
876 { "BODY", -1, LoadLevel_BODY },
877 { "CONT", -1, LoadLevel_CONT },
878 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
879 { "CNT3", -1, LoadLevel_CNT3 },
880 { "CUS1", -1, LoadLevel_CUS1 },
881 { "CUS2", -1, LoadLevel_CUS2 },
882 { "CUS3", -1, LoadLevel_CUS3 },
883 { "CUS4", -1, LoadLevel_CUS4 },
887 while (getFileChunkBE(file, chunk_name, &chunk_size))
891 while (chunk_info[i].name != NULL &&
892 strcmp(chunk_name, chunk_info[i].name) != 0)
895 if (chunk_info[i].name == NULL)
897 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
898 chunk_name, filename);
899 ReadUnusedBytesFromFile(file, chunk_size);
901 else if (chunk_info[i].size != -1 &&
902 chunk_info[i].size != chunk_size)
904 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
905 chunk_size, chunk_name, filename);
906 ReadUnusedBytesFromFile(file, chunk_size);
910 /* call function to load this level chunk */
911 int chunk_size_expected =
912 (chunk_info[i].loader)(file, chunk_size, level);
914 /* the size of some chunks cannot be checked before reading other
915 chunks first (like "HEAD" and "BODY") that contain some header
916 information, so check them here */
917 if (chunk_size_expected != chunk_size)
919 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
920 chunk_size, chunk_name, filename);
929 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
931 if (leveldir_current == NULL) /* only when dumping level */
935 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
938 /* determine correct game engine version of current level */
940 if (!leveldir_current->latest_engine)
942 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
943 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
944 IS_LEVELCLASS_UNDEFINED(leveldir_current))
948 printf("\n::: This level is private or contributed: '%s'\n", filename);
952 printf("\n::: Use the stored game engine version for this level\n");
955 /* For all levels which are not forced to use the latest game engine
956 version (normally user contributed, private and undefined levels),
957 use the version of the game engine the levels were created for.
959 Since 2.0.1, the game engine version is now directly stored
960 in the level file (chunk "VERS"), so there is no need anymore
961 to set the game version from the file version (except for old,
962 pre-2.0 levels, where the game version is still taken from the
963 file format version used to store the level -- see above). */
965 /* do some special adjustments to support older level versions */
966 if (level->file_version == FILE_VERSION_1_0)
968 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
969 Error(ERR_WARN, "using high speed movement for player");
971 /* player was faster than monsters in (pre-)1.0 levels */
972 level->double_speed = TRUE;
975 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
976 if (level->game_version == VERSION_IDENT(2,0,1,0))
977 level->em_slippery_gems = TRUE;
982 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
983 leveldir_current->sort_priority, filename);
987 printf("\n::: Use latest game engine version for this level.\n");
990 /* For all levels which are forced to use the latest game engine version
991 (normally all but user contributed, private and undefined levels), set
992 the game engine version to the actual version; this allows for actual
993 corrections in the game engine to take effect for existing, converted
994 levels (from "classic" or other existing games) to make the emulation
995 of the corresponding game more accurate, while (hopefully) not breaking
996 existing levels created from other players. */
999 printf("::: changing engine from %d to %d\n",
1000 level->game_version, GAME_VERSION_ACTUAL);
1003 level->game_version = GAME_VERSION_ACTUAL;
1005 /* Set special EM style gems behaviour: EM style gems slip down from
1006 normal, steel and growing wall. As this is a more fundamental change,
1007 it seems better to set the default behaviour to "off" (as it is more
1008 natural) and make it configurable in the level editor (as a property
1009 of gem style elements). Already existing converted levels (neither
1010 private nor contributed levels) are changed to the new behaviour. */
1012 if (level->file_version < FILE_VERSION_2_0)
1013 level->em_slippery_gems = TRUE;
1017 printf("::: => %d\n", level->game_version);
1021 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
1025 /* map custom element change events that have changed in newer versions
1026 (these following values were accidentally changed in version 3.0.1) */
1027 if (level->game_version <= VERSION_IDENT(3,0,0,0))
1029 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1031 int element = EL_CUSTOM_START + i;
1033 /* order of checking and copying events to be mapped is important */
1034 for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
1036 if (HAS_CHANGE_EVENT(element, j - 2))
1038 SET_CHANGE_EVENT(element, j - 2, FALSE);
1039 SET_CHANGE_EVENT(element, j, TRUE);
1043 /* order of checking and copying events to be mapped is important */
1044 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1046 if (HAS_CHANGE_EVENT(element, j - 1))
1048 SET_CHANGE_EVENT(element, j - 1, FALSE);
1049 SET_CHANGE_EVENT(element, j, TRUE);
1055 /* some custom element change events get mapped since version 3.0.3 */
1056 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1058 int element = EL_CUSTOM_START + i;
1060 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
1061 HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
1063 SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
1064 SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
1066 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1070 /* initialize "can_change" field for old levels with only one change page */
1071 if (level->game_version <= VERSION_IDENT(3,0,2,0))
1073 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1075 int element = EL_CUSTOM_START + i;
1077 if (CAN_CHANGE(element))
1078 element_info[element].change->can_change = TRUE;
1083 /* set default push delay values (corrected since version 3.0.7-1) */
1084 if (level->game_version < VERSION_IDENT(3,0,7,1))
1086 game.default_push_delay_fixed = 2;
1087 game.default_push_delay_random = 8;
1091 game.default_push_delay_fixed = 8;
1092 game.default_push_delay_random = 8;
1095 /* set uninitialized push delay values of custom elements in older levels */
1096 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1098 int element = EL_CUSTOM_START + i;
1100 if (element_info[element].push_delay_fixed == -1)
1101 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
1102 if (element_info[element].push_delay_random == -1)
1103 element_info[element].push_delay_random = game.default_push_delay_random;
1107 /* initialize element properties for level editor etc. */
1108 InitElementPropertiesEngine(level->game_version);
1111 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
1115 /* map elements that have changed in newer versions */
1116 for(y=0; y<level->fieldy; y++)
1118 for(x=0; x<level->fieldx; x++)
1120 int element = level->field[x][y];
1122 if (level->game_version <= VERSION_IDENT(2,2,0,0))
1124 /* map game font elements */
1125 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
1126 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1127 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
1128 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
1131 if (level->game_version < VERSION_IDENT(3,0,0,0))
1133 /* map Supaplex gravity tube elements */
1134 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
1135 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1136 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
1137 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
1141 level->field[x][y] = element;
1145 /* copy elements to runtime playfield array */
1146 for(x=0; x<MAX_LEV_FIELDX; x++)
1147 for(y=0; y<MAX_LEV_FIELDY; y++)
1148 Feld[x][y] = level->field[x][y];
1150 /* initialize level size variables for faster access */
1151 lev_fieldx = level->fieldx;
1152 lev_fieldy = level->fieldy;
1154 /* determine border element for this level */
1158 void LoadLevelTemplate(int level_nr)
1160 char *filename = getLevelFilename(level_nr);
1162 LoadLevelFromFilename(&level_template, filename);
1164 LoadLevel_InitVersion(&level, filename);
1165 LoadLevel_InitElements(&level, filename);
1167 ActivateLevelTemplate();
1170 void LoadLevel(int level_nr)
1172 char *filename = getLevelFilename(level_nr);
1174 LoadLevelFromFilename(&level, filename);
1176 if (level.use_custom_template)
1177 LoadLevelTemplate(-1);
1180 LoadLevel_InitVersion(&level, filename);
1181 LoadLevel_InitElements(&level, filename);
1182 LoadLevel_InitPlayfield(&level, filename);
1184 LoadLevel_InitLevel(&level, filename);
1188 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1190 putFileVersion(file, level->file_version);
1191 putFileVersion(file, level->game_version);
1194 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1198 putFile8Bit(file, level->fieldx);
1199 putFile8Bit(file, level->fieldy);
1201 putFile16BitBE(file, level->time);
1202 putFile16BitBE(file, level->gems_needed);
1204 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1205 putFile8Bit(file, level->name[i]);
1207 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1208 putFile8Bit(file, level->score[i]);
1210 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1213 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1214 level->yamyam_content[i][x][y]));
1215 putFile8Bit(file, level->amoeba_speed);
1216 putFile8Bit(file, level->time_magic_wall);
1217 putFile8Bit(file, level->time_wheel);
1218 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1219 level->amoeba_content));
1220 putFile8Bit(file, (level->double_speed ? 1 : 0));
1221 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
1222 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1223 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1225 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1227 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1230 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1234 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1235 putFile8Bit(file, level->author[i]);
1238 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1242 for(y=0; y<level->fieldy; y++)
1243 for(x=0; x<level->fieldx; x++)
1244 if (level->encoding_16bit_field)
1245 putFile16BitBE(file, level->field[x][y]);
1247 putFile8Bit(file, level->field[x][y]);
1251 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1255 putFile8Bit(file, EL_YAMYAM);
1256 putFile8Bit(file, level->num_yamyam_contents);
1257 putFile8Bit(file, 0);
1258 putFile8Bit(file, 0);
1260 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1263 if (level->encoding_16bit_field)
1264 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1266 putFile8Bit(file, level->yamyam_content[i][x][y]);
1270 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1273 int num_contents, content_xsize, content_ysize;
1274 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1276 if (element == EL_YAMYAM)
1278 num_contents = level->num_yamyam_contents;
1282 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1285 content_array[i][x][y] = level->yamyam_content[i][x][y];
1287 else if (element == EL_BD_AMOEBA)
1293 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1296 content_array[i][x][y] = EL_EMPTY;
1297 content_array[0][0][0] = level->amoeba_content;
1301 /* chunk header already written -- write empty chunk data */
1302 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1304 Error(ERR_WARN, "cannot save content for element '%d'", element);
1308 putFile16BitBE(file, element);
1309 putFile8Bit(file, num_contents);
1310 putFile8Bit(file, content_xsize);
1311 putFile8Bit(file, content_ysize);
1313 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1315 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1318 putFile16BitBE(file, content_array[i][x][y]);
1321 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1324 int envelope_nr = element - EL_ENVELOPE_1;
1325 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
1327 putFile16BitBE(file, element);
1328 putFile16BitBE(file, envelope_len);
1329 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
1330 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
1332 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1334 for(i=0; i < envelope_len; i++)
1335 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
1339 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1340 int num_changed_custom_elements)
1344 putFile16BitBE(file, num_changed_custom_elements);
1346 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1348 int element = EL_CUSTOM_START + i;
1350 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1352 if (check < num_changed_custom_elements)
1354 putFile16BitBE(file, element);
1355 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1362 if (check != num_changed_custom_elements) /* should not happen */
1363 Error(ERR_WARN, "inconsistent number of custom element properties");
1368 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1369 int num_changed_custom_elements)
1373 putFile16BitBE(file, num_changed_custom_elements);
1375 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1377 int element = EL_CUSTOM_START + i;
1379 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1381 if (check < num_changed_custom_elements)
1383 putFile16BitBE(file, element);
1384 putFile16BitBE(file, element_info[element].change->target_element);
1391 if (check != num_changed_custom_elements) /* should not happen */
1392 Error(ERR_WARN, "inconsistent number of custom target elements");
1397 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1398 int num_changed_custom_elements)
1400 int i, j, x, y, check = 0;
1402 putFile16BitBE(file, num_changed_custom_elements);
1404 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1406 int element = EL_CUSTOM_START + i;
1408 if (element_info[element].modified_settings)
1410 if (check < num_changed_custom_elements)
1412 putFile16BitBE(file, element);
1414 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1415 putFile8Bit(file, element_info[element].description[j]);
1417 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1419 /* some free bytes for future properties and padding */
1420 WriteUnusedBytesToFile(file, 7);
1422 putFile8Bit(file, element_info[element].use_gfx_element);
1423 putFile16BitBE(file, element_info[element].gfx_element);
1425 putFile8Bit(file, element_info[element].collect_score);
1426 putFile8Bit(file, element_info[element].collect_count);
1428 putFile16BitBE(file, element_info[element].push_delay_fixed);
1429 putFile16BitBE(file, element_info[element].push_delay_random);
1430 putFile16BitBE(file, element_info[element].move_delay_fixed);
1431 putFile16BitBE(file, element_info[element].move_delay_random);
1433 putFile16BitBE(file, element_info[element].move_pattern);
1434 putFile8Bit(file, element_info[element].move_direction_initial);
1435 putFile8Bit(file, element_info[element].move_stepsize);
1439 putFile16BitBE(file, element_info[element].content[x][y]);
1441 putFile32BitBE(file, element_info[element].change->events);
1443 putFile16BitBE(file, element_info[element].change->target_element);
1445 putFile16BitBE(file, element_info[element].change->delay_fixed);
1446 putFile16BitBE(file, element_info[element].change->delay_random);
1447 putFile16BitBE(file, element_info[element].change->delay_frames);
1449 putFile16BitBE(file, element_info[element].change->trigger_element);
1451 putFile8Bit(file, element_info[element].change->explode);
1452 putFile8Bit(file, element_info[element].change->use_content);
1453 putFile8Bit(file, element_info[element].change->only_complete);
1454 putFile8Bit(file, element_info[element].change->use_random_change);
1456 putFile8Bit(file, element_info[element].change->random);
1457 putFile8Bit(file, element_info[element].change->power);
1461 putFile16BitBE(file, element_info[element].change->content[x][y]);
1463 putFile8Bit(file, element_info[element].slippery_type);
1465 /* some free bytes for future properties and padding */
1466 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1473 if (check != num_changed_custom_elements) /* should not happen */
1474 Error(ERR_WARN, "inconsistent number of custom element properties");
1478 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1480 struct ElementInfo *ei = &element_info[element];
1483 putFile16BitBE(file, element);
1485 for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
1486 putFile8Bit(file, ei->description[i]);
1488 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1489 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
1491 putFile8Bit(file, ei->num_change_pages);
1493 /* some free bytes for future base property values and padding */
1494 WriteUnusedBytesToFile(file, 5);
1496 /* write custom property values */
1498 putFile8Bit(file, ei->use_gfx_element);
1499 putFile16BitBE(file, ei->gfx_element);
1501 putFile8Bit(file, ei->collect_score);
1502 putFile8Bit(file, ei->collect_count);
1504 putFile16BitBE(file, ei->push_delay_fixed);
1505 putFile16BitBE(file, ei->push_delay_random);
1506 putFile16BitBE(file, ei->move_delay_fixed);
1507 putFile16BitBE(file, ei->move_delay_random);
1509 putFile16BitBE(file, ei->move_pattern);
1510 putFile8Bit(file, ei->move_direction_initial);
1511 putFile8Bit(file, ei->move_stepsize);
1513 putFile8Bit(file, ei->slippery_type);
1517 putFile16BitBE(file, ei->content[x][y]);
1519 /* some free bytes for future custom property values and padding */
1520 WriteUnusedBytesToFile(file, 12);
1522 /* write change property values */
1524 for (i=0; i < ei->num_change_pages; i++)
1526 struct ElementChangeInfo *change = &ei->change_page[i];
1528 putFile32BitBE(file, change->events);
1530 putFile16BitBE(file, change->target_element);
1532 putFile16BitBE(file, change->delay_fixed);
1533 putFile16BitBE(file, change->delay_random);
1534 putFile16BitBE(file, change->delay_frames);
1536 putFile16BitBE(file, change->trigger_element);
1538 putFile8Bit(file, change->explode);
1539 putFile8Bit(file, change->use_content);
1540 putFile8Bit(file, change->only_complete);
1541 putFile8Bit(file, change->use_random_change);
1543 putFile8Bit(file, change->random);
1544 putFile8Bit(file, change->power);
1548 putFile16BitBE(file, change->content[x][y]);
1550 putFile8Bit(file, change->can_change);
1552 putFile8Bit(file, change->sides);
1554 /* some free bytes for future change property values and padding */
1555 WriteUnusedBytesToFile(file, 8);
1559 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1561 int body_chunk_size;
1565 if (!(file = fopen(filename, MODE_WRITE)))
1567 Error(ERR_WARN, "cannot save level file '%s'", filename);
1571 level->file_version = FILE_VERSION_ACTUAL;
1572 level->game_version = GAME_VERSION_ACTUAL;
1574 /* check level field for 16-bit elements */
1575 level->encoding_16bit_field = FALSE;
1576 for(y=0; y<level->fieldy; y++)
1577 for(x=0; x<level->fieldx; x++)
1578 if (level->field[x][y] > 255)
1579 level->encoding_16bit_field = TRUE;
1581 /* check yamyam content for 16-bit elements */
1582 level->encoding_16bit_yamyam = FALSE;
1583 for(i=0; i<level->num_yamyam_contents; i++)
1586 if (level->yamyam_content[i][x][y] > 255)
1587 level->encoding_16bit_yamyam = TRUE;
1589 /* check amoeba content for 16-bit elements */
1590 level->encoding_16bit_amoeba = FALSE;
1591 if (level->amoeba_content > 255)
1592 level->encoding_16bit_amoeba = TRUE;
1594 /* calculate size of "BODY" chunk */
1596 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1598 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1599 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1601 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1602 SaveLevel_VERS(file, level);
1604 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1605 SaveLevel_HEAD(file, level);
1607 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1608 SaveLevel_AUTH(file, level);
1610 putFileChunkBE(file, "BODY", body_chunk_size);
1611 SaveLevel_BODY(file, level);
1613 if (level->encoding_16bit_yamyam ||
1614 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1616 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1617 SaveLevel_CNT2(file, level, EL_YAMYAM);
1620 if (level->encoding_16bit_amoeba)
1622 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1623 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1626 /* check for envelope content */
1629 if (strlen(level->envelope_text[i]) > 0)
1631 int envelope_len = strlen(level->envelope_text[i]) + 1;
1633 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
1634 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
1638 /* check for non-default custom elements (unless using template level) */
1639 if (!level->use_custom_template)
1641 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1643 int element = EL_CUSTOM_START + i;
1645 if (element_info[element].modified_settings)
1647 int num_change_pages = element_info[element].num_change_pages;
1649 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
1650 SaveLevel_CUS4(file, level, element);
1657 SetFilePermissions(filename, PERMS_PRIVATE);
1660 void SaveLevel(int level_nr)
1662 char *filename = getLevelFilename(level_nr);
1664 SaveLevelFromFilename(&level, filename);
1667 void SaveLevelTemplate()
1669 char *filename = getLevelFilename(-1);
1671 SaveLevelFromFilename(&level, filename);
1674 void DumpLevel(struct LevelInfo *level)
1676 printf_line("-", 79);
1677 printf("Level xxx (file version %08d, game version %08d)\n",
1678 level->file_version, level->game_version);
1679 printf_line("-", 79);
1681 printf("Level Author: '%s'\n", level->author);
1682 printf("Level Title: '%s'\n", level->name);
1684 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1686 printf("Level Time: %d seconds\n", level->time);
1687 printf("Gems needed: %d\n", level->gems_needed);
1689 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1690 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1691 printf("Time for Light: %d seconds\n", level->time_light);
1692 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1694 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1696 printf("Gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
1697 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1698 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1700 printf_line("-", 79);
1704 /* ========================================================================= */
1705 /* tape file functions */
1706 /* ========================================================================= */
1708 static void setTapeInfoToDefaults()
1712 /* always start with reliable default values (empty tape) */
1715 /* default values (also for pre-1.2 tapes) with only the first player */
1716 tape.player_participates[0] = TRUE;
1717 for(i=1; i<MAX_PLAYERS; i++)
1718 tape.player_participates[i] = FALSE;
1720 /* at least one (default: the first) player participates in every tape */
1721 tape.num_participating_players = 1;
1723 tape.level_nr = level_nr;
1725 tape.changed = FALSE;
1727 tape.recording = FALSE;
1728 tape.playing = FALSE;
1729 tape.pausing = FALSE;
1732 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1734 tape->file_version = getFileVersion(file);
1735 tape->game_version = getFileVersion(file);
1740 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1744 tape->random_seed = getFile32BitBE(file);
1745 tape->date = getFile32BitBE(file);
1746 tape->length = getFile32BitBE(file);
1748 /* read header fields that are new since version 1.2 */
1749 if (tape->file_version >= FILE_VERSION_1_2)
1751 byte store_participating_players = getFile8Bit(file);
1754 /* since version 1.2, tapes store which players participate in the tape */
1755 tape->num_participating_players = 0;
1756 for(i=0; i<MAX_PLAYERS; i++)
1758 tape->player_participates[i] = FALSE;
1760 if (store_participating_players & (1 << i))
1762 tape->player_participates[i] = TRUE;
1763 tape->num_participating_players++;
1767 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1769 engine_version = getFileVersion(file);
1770 if (engine_version > 0)
1771 tape->engine_version = engine_version;
1773 tape->engine_version = tape->game_version;
1779 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1781 int level_identifier_size;
1784 level_identifier_size = getFile16BitBE(file);
1786 tape->level_identifier =
1787 checked_realloc(tape->level_identifier, level_identifier_size);
1789 for(i=0; i < level_identifier_size; i++)
1790 tape->level_identifier[i] = getFile8Bit(file);
1792 tape->level_nr = getFile16BitBE(file);
1794 chunk_size = 2 + level_identifier_size + 2;
1799 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1802 int chunk_size_expected =
1803 (tape->num_participating_players + 1) * tape->length;
1805 if (chunk_size_expected != chunk_size)
1807 ReadUnusedBytesFromFile(file, chunk_size);
1808 return chunk_size_expected;
1811 for(i=0; i<tape->length; i++)
1813 if (i >= MAX_TAPELEN)
1816 for(j=0; j<MAX_PLAYERS; j++)
1818 tape->pos[i].action[j] = MV_NO_MOVING;
1820 if (tape->player_participates[j])
1821 tape->pos[i].action[j] = getFile8Bit(file);
1824 tape->pos[i].delay = getFile8Bit(file);
1826 if (tape->file_version == FILE_VERSION_1_0)
1828 /* eliminate possible diagonal moves in old tapes */
1829 /* this is only for backward compatibility */
1831 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1832 byte action = tape->pos[i].action[0];
1833 int k, num_moves = 0;
1837 if (action & joy_dir[k])
1839 tape->pos[i + num_moves].action[0] = joy_dir[k];
1841 tape->pos[i + num_moves].delay = 0;
1850 tape->length += num_moves;
1853 else if (tape->file_version < FILE_VERSION_2_0)
1855 /* convert pre-2.0 tapes to new tape format */
1857 if (tape->pos[i].delay > 1)
1860 tape->pos[i + 1] = tape->pos[i];
1861 tape->pos[i + 1].delay = 1;
1864 for(j=0; j<MAX_PLAYERS; j++)
1865 tape->pos[i].action[j] = MV_NO_MOVING;
1866 tape->pos[i].delay--;
1877 if (i != tape->length)
1878 chunk_size = (tape->num_participating_players + 1) * i;
1883 void LoadTapeFromFilename(char *filename)
1885 char cookie[MAX_LINE_LEN];
1886 char chunk_name[CHUNK_ID_LEN + 1];
1890 /* always start with reliable default values */
1891 setTapeInfoToDefaults();
1893 if (!(file = fopen(filename, MODE_READ)))
1896 getFileChunkBE(file, chunk_name, NULL);
1897 if (strcmp(chunk_name, "RND1") == 0)
1899 getFile32BitBE(file); /* not used */
1901 getFileChunkBE(file, chunk_name, NULL);
1902 if (strcmp(chunk_name, "TAPE") != 0)
1904 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1909 else /* check for pre-2.0 file format with cookie string */
1911 strcpy(cookie, chunk_name);
1912 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1913 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1914 cookie[strlen(cookie) - 1] = '\0';
1916 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1918 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1923 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1925 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1930 /* pre-2.0 tape files have no game version, so use file version here */
1931 tape.game_version = tape.file_version;
1934 if (tape.file_version < FILE_VERSION_1_2)
1936 /* tape files from versions before 1.2.0 without chunk structure */
1937 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1938 LoadTape_BODY(file, 2 * tape.length, &tape);
1946 int (*loader)(FILE *, int, struct TapeInfo *);
1950 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1951 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1952 { "INFO", -1, LoadTape_INFO },
1953 { "BODY", -1, LoadTape_BODY },
1957 while (getFileChunkBE(file, chunk_name, &chunk_size))
1961 while (chunk_info[i].name != NULL &&
1962 strcmp(chunk_name, chunk_info[i].name) != 0)
1965 if (chunk_info[i].name == NULL)
1967 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1968 chunk_name, filename);
1969 ReadUnusedBytesFromFile(file, chunk_size);
1971 else if (chunk_info[i].size != -1 &&
1972 chunk_info[i].size != chunk_size)
1974 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1975 chunk_size, chunk_name, filename);
1976 ReadUnusedBytesFromFile(file, chunk_size);
1980 /* call function to load this tape chunk */
1981 int chunk_size_expected =
1982 (chunk_info[i].loader)(file, chunk_size, &tape);
1984 /* the size of some chunks cannot be checked before reading other
1985 chunks first (like "HEAD" and "BODY") that contain some header
1986 information, so check them here */
1987 if (chunk_size_expected != chunk_size)
1989 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1990 chunk_size, chunk_name, filename);
1998 tape.length_seconds = GetTapeLength();
2001 printf("::: tape game version: %d\n", tape.game_version);
2002 printf("::: tape engine version: %d\n", tape.engine_version);
2006 void LoadTape(int level_nr)
2008 char *filename = getTapeFilename(level_nr);
2010 LoadTapeFromFilename(filename);
2013 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2015 putFileVersion(file, tape->file_version);
2016 putFileVersion(file, tape->game_version);
2019 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2022 byte store_participating_players = 0;
2024 /* set bits for participating players for compact storage */
2025 for(i=0; i<MAX_PLAYERS; i++)
2026 if (tape->player_participates[i])
2027 store_participating_players |= (1 << i);
2029 putFile32BitBE(file, tape->random_seed);
2030 putFile32BitBE(file, tape->date);
2031 putFile32BitBE(file, tape->length);
2033 putFile8Bit(file, store_participating_players);
2035 /* unused bytes not at the end here for 4-byte alignment of engine_version */
2036 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2038 putFileVersion(file, tape->engine_version);
2041 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2043 int level_identifier_size = strlen(tape->level_identifier) + 1;
2046 putFile16BitBE(file, level_identifier_size);
2048 for(i=0; i < level_identifier_size; i++)
2049 putFile8Bit(file, tape->level_identifier[i]);
2051 putFile16BitBE(file, tape->level_nr);
2054 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2058 for(i=0; i<tape->length; i++)
2060 for(j=0; j<MAX_PLAYERS; j++)
2061 if (tape->player_participates[j])
2062 putFile8Bit(file, tape->pos[i].action[j]);
2064 putFile8Bit(file, tape->pos[i].delay);
2068 void SaveTape(int level_nr)
2070 char *filename = getTapeFilename(level_nr);
2072 boolean new_tape = TRUE;
2073 int num_participating_players = 0;
2074 int info_chunk_size;
2075 int body_chunk_size;
2078 InitTapeDirectory(leveldir_current->filename);
2080 /* if a tape still exists, ask to overwrite it */
2081 if (access(filename, F_OK) == 0)
2084 if (!Request("Replace old tape ?", REQ_ASK))
2088 if (!(file = fopen(filename, MODE_WRITE)))
2090 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2094 tape.file_version = FILE_VERSION_ACTUAL;
2095 tape.game_version = GAME_VERSION_ACTUAL;
2097 /* count number of participating players */
2098 for(i=0; i<MAX_PLAYERS; i++)
2099 if (tape.player_participates[i])
2100 num_participating_players++;
2102 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2103 body_chunk_size = (num_participating_players + 1) * tape.length;
2105 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2106 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2108 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2109 SaveTape_VERS(file, &tape);
2111 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2112 SaveTape_HEAD(file, &tape);
2114 putFileChunkBE(file, "INFO", info_chunk_size);
2115 SaveTape_INFO(file, &tape);
2117 putFileChunkBE(file, "BODY", body_chunk_size);
2118 SaveTape_BODY(file, &tape);
2122 SetFilePermissions(filename, PERMS_PRIVATE);
2124 tape.changed = FALSE;
2127 Request("tape saved !", REQ_CONFIRM);
2130 void DumpTape(struct TapeInfo *tape)
2134 if (TAPE_IS_EMPTY(*tape))
2136 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2140 printf_line("-", 79);
2141 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2142 tape->level_nr, tape->file_version, tape->game_version);
2143 printf("Level series identifier: '%s'\n", tape->level_identifier);
2144 printf_line("-", 79);
2146 for(i=0; i<tape->length; i++)
2148 if (i >= MAX_TAPELEN)
2151 printf("%03d: ", i);
2153 for(j=0; j<MAX_PLAYERS; j++)
2155 if (tape->player_participates[j])
2157 int action = tape->pos[i].action[j];
2159 printf("%d:%02x ", j, action);
2160 printf("[%c%c%c%c|%c%c] - ",
2161 (action & JOY_LEFT ? '<' : ' '),
2162 (action & JOY_RIGHT ? '>' : ' '),
2163 (action & JOY_UP ? '^' : ' '),
2164 (action & JOY_DOWN ? 'v' : ' '),
2165 (action & JOY_BUTTON_1 ? '1' : ' '),
2166 (action & JOY_BUTTON_2 ? '2' : ' '));
2170 printf("(%03d)\n", tape->pos[i].delay);
2173 printf_line("-", 79);
2177 /* ========================================================================= */
2178 /* score file functions */
2179 /* ========================================================================= */
2181 void LoadScore(int level_nr)
2184 char *filename = getScoreFilename(level_nr);
2185 char cookie[MAX_LINE_LEN];
2186 char line[MAX_LINE_LEN];
2190 /* always start with reliable default values */
2191 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2193 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2194 highscore[i].Score = 0;
2197 if (!(file = fopen(filename, MODE_READ)))
2200 /* check file identifier */
2201 fgets(cookie, MAX_LINE_LEN, file);
2202 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2203 cookie[strlen(cookie) - 1] = '\0';
2205 if (!checkCookieString(cookie, SCORE_COOKIE))
2207 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2212 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2214 fscanf(file, "%d", &highscore[i].Score);
2215 fgets(line, MAX_LINE_LEN, file);
2217 if (line[strlen(line) - 1] == '\n')
2218 line[strlen(line) - 1] = '\0';
2220 for (line_ptr = line; *line_ptr; line_ptr++)
2222 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2224 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2225 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2234 void SaveScore(int level_nr)
2237 char *filename = getScoreFilename(level_nr);
2240 InitScoreDirectory(leveldir_current->filename);
2242 if (!(file = fopen(filename, MODE_WRITE)))
2244 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2248 fprintf(file, "%s\n\n", SCORE_COOKIE);
2250 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2251 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2255 SetFilePermissions(filename, PERMS_PUBLIC);
2259 /* ========================================================================= */
2260 /* setup file functions */
2261 /* ========================================================================= */
2263 #define TOKEN_STR_PLAYER_PREFIX "player_"
2266 #define SETUP_TOKEN_PLAYER_NAME 0
2267 #define SETUP_TOKEN_SOUND 1
2268 #define SETUP_TOKEN_SOUND_LOOPS 2
2269 #define SETUP_TOKEN_SOUND_MUSIC 3
2270 #define SETUP_TOKEN_SOUND_SIMPLE 4
2271 #define SETUP_TOKEN_TOONS 5
2272 #define SETUP_TOKEN_SCROLL_DELAY 6
2273 #define SETUP_TOKEN_SOFT_SCROLLING 7
2274 #define SETUP_TOKEN_FADING 8
2275 #define SETUP_TOKEN_AUTORECORD 9
2276 #define SETUP_TOKEN_QUICK_DOORS 10
2277 #define SETUP_TOKEN_TEAM_MODE 11
2278 #define SETUP_TOKEN_HANDICAP 12
2279 #define SETUP_TOKEN_TIME_LIMIT 13
2280 #define SETUP_TOKEN_FULLSCREEN 14
2281 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2282 #define SETUP_TOKEN_GRAPHICS_SET 16
2283 #define SETUP_TOKEN_SOUNDS_SET 17
2284 #define SETUP_TOKEN_MUSIC_SET 18
2285 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2286 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2287 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2289 #define NUM_GLOBAL_SETUP_TOKENS 22
2292 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2293 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2294 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2295 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2296 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2297 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2298 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2299 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2300 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2301 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2302 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2303 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
2305 #define NUM_EDITOR_SETUP_TOKENS 12
2307 /* shortcut setup */
2308 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2309 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2310 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2312 #define NUM_SHORTCUT_SETUP_TOKENS 3
2315 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2316 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2317 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2318 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2319 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2320 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2321 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2322 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2323 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2324 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
2325 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2326 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2327 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2328 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2329 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2330 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
2332 #define NUM_PLAYER_SETUP_TOKENS 16
2335 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2336 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2338 #define NUM_SYSTEM_SETUP_TOKENS 2
2341 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2343 #define NUM_OPTIONS_SETUP_TOKENS 1
2346 static struct SetupInfo si;
2347 static struct SetupEditorInfo sei;
2348 static struct SetupShortcutInfo ssi;
2349 static struct SetupInputInfo sii;
2350 static struct SetupSystemInfo syi;
2351 static struct OptionInfo soi;
2353 static struct TokenInfo global_setup_tokens[] =
2355 { TYPE_STRING, &si.player_name, "player_name" },
2356 { TYPE_SWITCH, &si.sound, "sound" },
2357 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2358 { TYPE_SWITCH, &si.sound_music, "background_music" },
2359 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2360 { TYPE_SWITCH, &si.toons, "toons" },
2361 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2362 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2363 { TYPE_SWITCH, &si.fading, "screen_fading" },
2364 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2365 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2366 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2367 { TYPE_SWITCH, &si.handicap, "handicap" },
2368 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2369 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2370 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2371 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2372 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2373 { TYPE_STRING, &si.music_set, "music_set" },
2374 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2375 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2376 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2379 static struct TokenInfo editor_setup_tokens[] =
2381 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2382 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2383 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2384 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2385 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2386 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2387 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2388 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2389 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2390 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2391 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2392 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
2395 static struct TokenInfo shortcut_setup_tokens[] =
2397 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2398 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2399 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2402 static struct TokenInfo player_setup_tokens[] =
2404 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2405 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2406 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2407 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2408 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2409 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2410 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2411 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2412 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2413 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2414 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2415 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2416 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2417 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2418 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2419 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
2422 static struct TokenInfo system_setup_tokens[] =
2424 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2425 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2428 static struct TokenInfo options_setup_tokens[] =
2430 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2433 static char *get_corrected_login_name(char *login_name)
2435 /* needed because player name must be a fixed length string */
2436 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2438 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2439 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2441 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2442 if (strchr(login_name_new, ' '))
2443 *strchr(login_name_new, ' ') = '\0';
2445 return login_name_new;
2448 static void setSetupInfoToDefaults(struct SetupInfo *si)
2452 si->player_name = get_corrected_login_name(getLoginName());
2455 si->sound_loops = TRUE;
2456 si->sound_music = TRUE;
2457 si->sound_simple = TRUE;
2459 si->double_buffering = TRUE;
2460 si->direct_draw = !si->double_buffering;
2461 si->scroll_delay = TRUE;
2462 si->soft_scrolling = TRUE;
2464 si->autorecord = TRUE;
2465 si->quick_doors = FALSE;
2466 si->team_mode = FALSE;
2467 si->handicap = TRUE;
2468 si->time_limit = TRUE;
2469 si->fullscreen = FALSE;
2470 si->ask_on_escape = TRUE;
2472 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2473 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2474 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2475 si->override_level_graphics = FALSE;
2476 si->override_level_sounds = FALSE;
2477 si->override_level_music = FALSE;
2479 si->editor.el_boulderdash = TRUE;
2480 si->editor.el_emerald_mine = TRUE;
2481 si->editor.el_more = TRUE;
2482 si->editor.el_sokoban = TRUE;
2483 si->editor.el_supaplex = TRUE;
2484 si->editor.el_diamond_caves = TRUE;
2485 si->editor.el_dx_boulderdash = TRUE;
2486 si->editor.el_chars = TRUE;
2487 si->editor.el_custom = TRUE;
2488 si->editor.el_custom_more = FALSE;
2490 si->editor.el_headlines = TRUE;
2491 si->editor.el_user_defined = FALSE;
2493 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2494 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2495 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2497 for (i=0; i<MAX_PLAYERS; i++)
2499 si->input[i].use_joystick = FALSE;
2500 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2501 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2502 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2503 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2504 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2505 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2506 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2507 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2508 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2509 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2510 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2511 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2512 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2513 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2514 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2517 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2518 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2520 si->options.verbose = FALSE;
2523 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2527 if (!setup_file_hash)
2532 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2533 setSetupInfo(global_setup_tokens, i,
2534 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2539 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2540 setSetupInfo(editor_setup_tokens, i,
2541 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2544 /* shortcut setup */
2545 ssi = setup.shortcut;
2546 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2547 setSetupInfo(shortcut_setup_tokens, i,
2548 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2549 setup.shortcut = ssi;
2552 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2556 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2558 sii = setup.input[pnr];
2559 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2561 char full_token[100];
2563 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2564 setSetupInfo(player_setup_tokens, i,
2565 getHashEntry(setup_file_hash, full_token));
2567 setup.input[pnr] = sii;
2572 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2573 setSetupInfo(system_setup_tokens, i,
2574 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2578 soi = setup.options;
2579 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2580 setSetupInfo(options_setup_tokens, i,
2581 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2582 setup.options = soi;
2587 char *filename = getSetupFilename();
2588 SetupFileHash *setup_file_hash = NULL;
2590 /* always start with reliable default values */
2591 setSetupInfoToDefaults(&setup);
2593 setup_file_hash = loadSetupFileHash(filename);
2595 if (setup_file_hash)
2597 char *player_name_new;
2599 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2600 decodeSetupFileHash(setup_file_hash);
2602 setup.direct_draw = !setup.double_buffering;
2604 freeSetupFileHash(setup_file_hash);
2606 /* needed to work around problems with fixed length strings */
2607 player_name_new = get_corrected_login_name(setup.player_name);
2608 free(setup.player_name);
2609 setup.player_name = player_name_new;
2612 Error(ERR_WARN, "using default setup values");
2617 char *filename = getSetupFilename();
2621 InitUserDataDirectory();
2623 if (!(file = fopen(filename, MODE_WRITE)))
2625 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2629 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2630 getCookie("SETUP")));
2631 fprintf(file, "\n");
2635 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2637 /* just to make things nicer :) */
2638 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2639 i == SETUP_TOKEN_GRAPHICS_SET)
2640 fprintf(file, "\n");
2642 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2647 fprintf(file, "\n");
2648 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2649 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2651 /* shortcut setup */
2652 ssi = setup.shortcut;
2653 fprintf(file, "\n");
2654 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2655 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2658 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2662 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2663 fprintf(file, "\n");
2665 sii = setup.input[pnr];
2666 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2667 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2672 fprintf(file, "\n");
2673 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2674 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2677 soi = setup.options;
2678 fprintf(file, "\n");
2679 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2680 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2684 SetFilePermissions(filename, PERMS_PRIVATE);
2687 void LoadCustomElementDescriptions()
2689 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2690 SetupFileHash *setup_file_hash;
2693 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2695 if (element_info[i].custom_description != NULL)
2697 free(element_info[i].custom_description);
2698 element_info[i].custom_description = NULL;
2702 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2705 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2707 char *token = getStringCat2(element_info[i].token_name, ".name");
2708 char *value = getHashEntry(setup_file_hash, token);
2711 element_info[i].custom_description = getStringCopy(value);
2716 freeSetupFileHash(setup_file_hash);
2719 void LoadSpecialMenuDesignSettings()
2721 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2722 SetupFileHash *setup_file_hash;
2725 /* always start with reliable default values from default config */
2726 for (i=0; image_config_vars[i].token != NULL; i++)
2727 for (j=0; image_config[j].token != NULL; j++)
2728 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2729 *image_config_vars[i].value =
2730 get_auto_parameter_value(image_config_vars[i].token,
2731 image_config[j].value);
2733 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2736 /* special case: initialize with default values that may be overwritten */
2737 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2739 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2740 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2741 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2743 if (value_x != NULL)
2744 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2745 if (value_y != NULL)
2746 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2747 if (list_size != NULL)
2748 menu.list_size[i] = get_integer_from_string(list_size);
2751 /* read (and overwrite with) values that may be specified in config file */
2752 for (i=0; image_config_vars[i].token != NULL; i++)
2754 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2757 *image_config_vars[i].value =
2758 get_auto_parameter_value(image_config_vars[i].token, value);
2761 freeSetupFileHash(setup_file_hash);
2764 static char *itoa(unsigned int i)
2766 static char *a = NULL;
2771 if (i > 2147483647) /* yes, this is a kludge */
2774 a = checked_malloc(10 + 1);
2776 sprintf(a, "%d", i);
2781 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
2783 char *filename = getEditorSetupFilename();
2784 SetupFileList *setup_file_list, *list;
2785 SetupFileHash *element_hash;
2786 int num_unknown_tokens = 0;
2789 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
2792 element_hash = newSetupFileHash();
2794 for (i=0; i < NUM_FILE_ELEMENTS; i++)
2795 setHashEntry(element_hash, element_info[i].token_name, itoa(i));
2797 /* determined size may be larger than needed (due to unknown elements) */
2799 for (list = setup_file_list; list != NULL; list = list->next)
2802 /* add space for up to 3 more elements for padding that may be needed */
2805 *elements = checked_malloc(*num_elements * sizeof(int));
2808 for (list = setup_file_list; list != NULL; list = list->next)
2810 char *value = getHashEntry(element_hash, list->token);
2814 (*elements)[(*num_elements)++] = atoi(value);
2818 if (num_unknown_tokens == 0)
2820 Error(ERR_RETURN_LINE, "-");
2821 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
2822 Error(ERR_RETURN, "- config file: '%s'", filename);
2824 num_unknown_tokens++;
2827 Error(ERR_RETURN, "- token: '%s'", list->token);
2831 if (num_unknown_tokens > 0)
2832 Error(ERR_RETURN_LINE, "-");
2834 while (*num_elements % 4) /* pad with empty elements, if needed */
2835 (*elements)[(*num_elements)++] = EL_EMPTY;
2837 freeSetupFileList(setup_file_list);
2838 freeSetupFileHash(element_hash);
2842 for (i=0; i < *num_elements; i++)
2843 printf("editor: element '%s' [%d]\n",
2844 element_info[(*elements)[i]].token_name, (*elements)[i]);