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_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
34 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
35 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
36 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
38 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + x * LEVEL_CPART_CUS3_SIZE)
40 /* file identifier strings */
41 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
42 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
43 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
46 /* ========================================================================= */
47 /* level file functions */
48 /* ========================================================================= */
50 void setElementChangePages(struct ElementInfo *ei, int change_pages)
52 int change_page_size = sizeof(struct ElementChangeInfo);
54 ei->num_change_pages = MAX(1, change_pages);
57 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
59 if (ei->current_change_page >= ei->num_change_pages)
60 ei->current_change_page = ei->num_change_pages - 1;
62 ei->change = &ei->change_page[ei->current_change_page];
65 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
69 change->can_change = FALSE;
71 change->events = CE_BITMASK_DEFAULT;
72 change->target_element = EL_EMPTY_SPACE;
74 change->delay_fixed = 0;
75 change->delay_random = 0;
76 change->delay_frames = -1; /* later set to reliable default value */
78 change->trigger_element = EL_EMPTY_SPACE;
80 change->explode = FALSE;
81 change->use_content = FALSE;
82 change->only_complete = FALSE;
83 change->use_random_change = FALSE;
85 change->power = CP_NON_DESTRUCTIVE;
89 change->content[x][y] = EL_EMPTY_SPACE;
91 change->player_action = 0;
92 change->collide_action = 0;
93 change->other_action = 0;
95 change->pre_change_function = NULL;
96 change->change_function = NULL;
97 change->post_change_function = NULL;
100 static void setLevelInfoToDefaults(struct LevelInfo *level)
104 level->file_version = FILE_VERSION_ACTUAL;
105 level->game_version = GAME_VERSION_ACTUAL;
107 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
108 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
109 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
111 level->fieldx = STD_LEV_FIELDX;
112 level->fieldy = STD_LEV_FIELDY;
114 for(x=0; x<MAX_LEV_FIELDX; x++)
115 for(y=0; y<MAX_LEV_FIELDY; y++)
116 level->field[x][y] = EL_SAND;
119 level->gems_needed = 0;
120 level->amoeba_speed = 10;
121 level->time_magic_wall = 10;
122 level->time_wheel = 10;
123 level->time_light = 10;
124 level->time_timegate = 10;
125 level->amoeba_content = EL_DIAMOND;
126 level->double_speed = FALSE;
127 level->gravity = FALSE;
128 level->em_slippery_gems = FALSE;
130 level->use_custom_template = FALSE;
132 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
133 level->name[i] = '\0';
134 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
135 level->author[i] = '\0';
137 strcpy(level->name, NAMELESS_LEVEL_NAME);
138 strcpy(level->author, ANONYMOUS_NAME);
140 level->envelope[0] = '\0';
141 level->envelope_xsize = MAX_ENVELOPE_XSIZE;
142 level->envelope_ysize = MAX_ENVELOPE_YSIZE;
144 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
145 level->score[i] = 10;
147 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
148 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
151 level->yamyam_content[i][x][y] =
152 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
154 level->field[0][0] = EL_PLAYER_1;
155 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
157 for (i=0; i < MAX_NUM_ELEMENTS; i++)
159 setElementChangePages(&element_info[i], 1);
160 setElementChangeInfoToDefaults(element_info[i].change);
163 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
165 int element = EL_CUSTOM_START + i;
167 for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
168 element_info[element].description[j] = '\0';
169 if (element_info[element].custom_description != NULL)
170 strncpy(element_info[element].description,
171 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
173 strcpy(element_info[element].description,
174 element_info[element].editor_description);
176 element_info[element].use_gfx_element = FALSE;
177 element_info[element].gfx_element = EL_EMPTY_SPACE;
179 element_info[element].collect_score = 10; /* special default */
180 element_info[element].collect_count = 1; /* special default */
182 element_info[element].push_delay_fixed = 2; /* special default */
183 element_info[element].push_delay_random = 8; /* special default */
184 element_info[element].move_delay_fixed = 0;
185 element_info[element].move_delay_random = 0;
187 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
188 element_info[element].move_direction_initial = MV_NO_MOVING;
189 element_info[element].move_stepsize = TILEX / 8;
191 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
195 element_info[element].content[x][y] = EL_EMPTY_SPACE;
197 element_info[element].access_type = 0;
198 element_info[element].access_layer = 0;
199 element_info[element].walk_to_action = 0;
200 element_info[element].smash_targets = 0;
201 element_info[element].deadliness = 0;
202 element_info[element].consistency = 0;
204 element_info[element].can_explode_by_fire = FALSE;
205 element_info[element].can_explode_smashed = FALSE;
206 element_info[element].can_explode_impact = FALSE;
208 element_info[element].current_change_page = 0;
210 /* start with no properties at all */
211 for (j=0; j < NUM_EP_BITFIELDS; j++)
212 Properties[element][j] = EP_BITMASK_DEFAULT;
214 element_info[element].modified_settings = FALSE;
217 BorderElement = EL_STEELWALL;
219 level->no_level_file = FALSE;
221 if (leveldir_current == NULL) /* only when dumping level */
224 /* try to determine better author name than 'anonymous' */
225 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
227 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
228 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
232 switch (LEVELCLASS(leveldir_current))
234 case LEVELCLASS_TUTORIAL:
235 strcpy(level->author, PROGRAM_AUTHOR_STRING);
238 case LEVELCLASS_CONTRIBUTION:
239 strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
240 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
243 case LEVELCLASS_USER:
244 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
245 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
249 /* keep default value */
255 static void ActivateLevelTemplate()
257 /* Currently there is no special action needed to activate the template
258 data, because 'element_info' and 'Properties' overwrite the original
259 level data, while all other variables do not change. */
262 boolean LevelFileExists(int level_nr)
264 char *filename = getLevelFilename(level_nr);
266 return (access(filename, F_OK) == 0);
269 static int checkLevelElement(int element)
271 if (element >= NUM_FILE_ELEMENTS)
273 Error(ERR_WARN, "invalid level element %d", element);
274 element = EL_CHAR_QUESTION;
276 else if (element == EL_PLAYER_OBSOLETE)
277 element = EL_PLAYER_1;
278 else if (element == EL_KEY_OBSOLETE)
284 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
286 level->file_version = getFileVersion(file);
287 level->game_version = getFileVersion(file);
292 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
296 level->fieldx = getFile8Bit(file);
297 level->fieldy = getFile8Bit(file);
299 level->time = getFile16BitBE(file);
300 level->gems_needed = getFile16BitBE(file);
302 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
303 level->name[i] = getFile8Bit(file);
304 level->name[MAX_LEVEL_NAME_LEN] = 0;
306 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
307 level->score[i] = getFile8Bit(file);
309 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
310 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
313 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
315 level->amoeba_speed = getFile8Bit(file);
316 level->time_magic_wall = getFile8Bit(file);
317 level->time_wheel = getFile8Bit(file);
318 level->amoeba_content = checkLevelElement(getFile8Bit(file));
319 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
320 level->gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
321 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
322 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
324 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
326 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
331 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
335 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
336 level->author[i] = getFile8Bit(file);
337 level->author[MAX_LEVEL_NAME_LEN] = 0;
342 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
345 int chunk_size_expected = level->fieldx * level->fieldy;
347 /* Note: "chunk_size" was wrong before version 2.0 when elements are
348 stored with 16-bit encoding (and should be twice as big then).
349 Even worse, playfield data was stored 16-bit when only yamyam content
350 contained 16-bit elements and vice versa. */
352 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
353 chunk_size_expected *= 2;
355 if (chunk_size_expected != chunk_size)
357 ReadUnusedBytesFromFile(file, chunk_size);
358 return chunk_size_expected;
361 for(y=0; y<level->fieldy; y++)
362 for(x=0; x<level->fieldx; x++)
364 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
369 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
373 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
374 int chunk_size_expected = header_size + content_size;
376 /* Note: "chunk_size" was wrong before version 2.0 when elements are
377 stored with 16-bit encoding (and should be twice as big then).
378 Even worse, playfield data was stored 16-bit when only yamyam content
379 contained 16-bit elements and vice versa. */
381 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
382 chunk_size_expected += content_size;
384 if (chunk_size_expected != chunk_size)
386 ReadUnusedBytesFromFile(file, chunk_size);
387 return chunk_size_expected;
391 level->num_yamyam_contents = getFile8Bit(file);
395 /* correct invalid number of content fields -- should never happen */
396 if (level->num_yamyam_contents < 1 ||
397 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
398 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
400 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
403 level->yamyam_content[i][x][y] =
404 checkLevelElement(level->encoding_16bit_field ?
405 getFile16BitBE(file) : getFile8Bit(file));
409 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
413 int num_contents, content_xsize, content_ysize;
414 int content_array[MAX_ELEMENT_CONTENTS][3][3];
416 element = checkLevelElement(getFile16BitBE(file));
417 num_contents = getFile8Bit(file);
418 content_xsize = getFile8Bit(file);
419 content_ysize = getFile8Bit(file);
420 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
422 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
425 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
427 /* correct invalid number of content fields -- should never happen */
428 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
429 num_contents = STD_ELEMENT_CONTENTS;
431 if (element == EL_YAMYAM)
433 level->num_yamyam_contents = num_contents;
435 for(i=0; i<num_contents; i++)
438 level->yamyam_content[i][x][y] = content_array[i][x][y];
440 else if (element == EL_BD_AMOEBA)
442 level->amoeba_content = content_array[0][0][0];
446 Error(ERR_WARN, "cannot load content for element '%d'", element);
452 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
454 int num_changed_custom_elements = getFile16BitBE(file);
455 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
458 if (chunk_size_expected != chunk_size)
460 ReadUnusedBytesFromFile(file, chunk_size - 2);
461 return chunk_size_expected;
464 for (i=0; i < num_changed_custom_elements; i++)
466 int element = getFile16BitBE(file);
467 int properties = getFile32BitBE(file);
469 if (IS_CUSTOM_ELEMENT(element))
470 Properties[element][EP_BITFIELD_BASE] = properties;
472 Error(ERR_WARN, "invalid custom element number %d", element);
478 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
480 int num_changed_custom_elements = getFile16BitBE(file);
481 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
484 if (chunk_size_expected != chunk_size)
486 ReadUnusedBytesFromFile(file, chunk_size - 2);
487 return chunk_size_expected;
490 for (i=0; i < num_changed_custom_elements; i++)
492 int element = getFile16BitBE(file);
493 int custom_target_element = getFile16BitBE(file);
495 if (IS_CUSTOM_ELEMENT(element))
496 element_info[element].change->target_element = custom_target_element;
498 Error(ERR_WARN, "invalid custom element number %d", element);
504 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
506 int num_changed_custom_elements = getFile16BitBE(file);
507 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
510 if (chunk_size_expected != chunk_size)
512 ReadUnusedBytesFromFile(file, chunk_size - 2);
513 return chunk_size_expected;
516 for (i=0; i < num_changed_custom_elements; i++)
518 int element = getFile16BitBE(file);
520 if (!IS_CUSTOM_ELEMENT(element))
522 Error(ERR_WARN, "invalid custom element number %d", element);
524 element = EL_DEFAULT; /* dummy element used for artwork config */
527 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
528 element_info[element].description[j] = getFile8Bit(file);
529 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
531 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
533 /* some free bytes for future properties and padding */
534 ReadUnusedBytesFromFile(file, 7);
536 element_info[element].use_gfx_element = getFile8Bit(file);
537 element_info[element].gfx_element =
538 checkLevelElement(getFile16BitBE(file));
540 element_info[element].collect_score = getFile8Bit(file);
541 element_info[element].collect_count = getFile8Bit(file);
543 element_info[element].push_delay_fixed = getFile16BitBE(file);
544 element_info[element].push_delay_random = getFile16BitBE(file);
545 element_info[element].move_delay_fixed = getFile16BitBE(file);
546 element_info[element].move_delay_random = getFile16BitBE(file);
548 element_info[element].move_pattern = getFile16BitBE(file);
549 element_info[element].move_direction_initial = getFile8Bit(file);
550 element_info[element].move_stepsize = getFile8Bit(file);
554 element_info[element].content[x][y] =
555 checkLevelElement(getFile16BitBE(file));
557 element_info[element].change->events = getFile32BitBE(file);
559 element_info[element].change->target_element =
560 checkLevelElement(getFile16BitBE(file));
562 element_info[element].change->delay_fixed = getFile16BitBE(file);
563 element_info[element].change->delay_random = getFile16BitBE(file);
564 element_info[element].change->delay_frames = getFile16BitBE(file);
566 element_info[element].change->trigger_element =
567 checkLevelElement(getFile16BitBE(file));
569 element_info[element].change->explode = getFile8Bit(file);
570 element_info[element].change->use_content = getFile8Bit(file);
571 element_info[element].change->only_complete = getFile8Bit(file);
572 element_info[element].change->use_random_change = getFile8Bit(file);
574 element_info[element].change->random = getFile8Bit(file);
575 element_info[element].change->power = getFile8Bit(file);
579 element_info[element].change->content[x][y] =
580 checkLevelElement(getFile16BitBE(file));
582 element_info[element].slippery_type = getFile8Bit(file);
584 /* some free bytes for future properties and padding */
585 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
587 /* mark that this custom element has been modified */
588 element_info[element].modified_settings = TRUE;
594 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
596 char cookie[MAX_LINE_LEN];
597 char chunk_name[CHUNK_ID_LEN + 1];
601 /* always start with reliable default values */
602 setLevelInfoToDefaults(level);
604 if (!(file = fopen(filename, MODE_READ)))
606 level->no_level_file = TRUE;
608 if (level != &level_template)
609 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
614 getFileChunkBE(file, chunk_name, NULL);
615 if (strcmp(chunk_name, "RND1") == 0)
617 getFile32BitBE(file); /* not used */
619 getFileChunkBE(file, chunk_name, NULL);
620 if (strcmp(chunk_name, "CAVE") != 0)
622 Error(ERR_WARN, "unknown format of level file '%s'", filename);
627 else /* check for pre-2.0 file format with cookie string */
629 strcpy(cookie, chunk_name);
630 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
631 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
632 cookie[strlen(cookie) - 1] = '\0';
634 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
636 Error(ERR_WARN, "unknown format of level file '%s'", filename);
641 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
643 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
648 /* pre-2.0 level files have no game version, so use file version here */
649 level->game_version = level->file_version;
652 if (level->file_version < FILE_VERSION_1_2)
654 /* level files from versions before 1.2.0 without chunk structure */
655 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
656 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
664 int (*loader)(FILE *, int, struct LevelInfo *);
668 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
669 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
670 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
671 { "BODY", -1, LoadLevel_BODY },
672 { "CONT", -1, LoadLevel_CONT },
673 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
674 { "CUS1", -1, LoadLevel_CUS1 },
675 { "CUS2", -1, LoadLevel_CUS2 },
676 { "CUS3", -1, LoadLevel_CUS3 },
680 while (getFileChunkBE(file, chunk_name, &chunk_size))
684 while (chunk_info[i].name != NULL &&
685 strcmp(chunk_name, chunk_info[i].name) != 0)
688 if (chunk_info[i].name == NULL)
690 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
691 chunk_name, filename);
692 ReadUnusedBytesFromFile(file, chunk_size);
694 else if (chunk_info[i].size != -1 &&
695 chunk_info[i].size != chunk_size)
697 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
698 chunk_size, chunk_name, filename);
699 ReadUnusedBytesFromFile(file, chunk_size);
703 /* call function to load this level chunk */
704 int chunk_size_expected =
705 (chunk_info[i].loader)(file, chunk_size, level);
707 /* the size of some chunks cannot be checked before reading other
708 chunks first (like "HEAD" and "BODY") that contain some header
709 information, so check them here */
710 if (chunk_size_expected != chunk_size)
712 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
713 chunk_size, chunk_name, filename);
724 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
726 if (leveldir_current == NULL) /* only when dumping level */
729 /* determine correct game engine version of current level */
730 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
731 IS_LEVELCLASS_USER(leveldir_current))
734 printf("\n::: This level is private or contributed: '%s'\n", filename);
737 /* For user contributed and private levels, use the version of
738 the game engine the levels were created for.
739 Since 2.0.1, the game engine version is now directly stored
740 in the level file (chunk "VERS"), so there is no need anymore
741 to set the game version from the file version (except for old,
742 pre-2.0 levels, where the game version is still taken from the
743 file format version used to store the level -- see above). */
745 /* do some special adjustments to support older level versions */
746 if (level->file_version == FILE_VERSION_1_0)
748 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
749 Error(ERR_WARN, "using high speed movement for player");
751 /* player was faster than monsters in (pre-)1.0 levels */
752 level->double_speed = TRUE;
755 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
756 if (level->game_version == VERSION_IDENT(2,0,1))
757 level->em_slippery_gems = TRUE;
762 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
763 leveldir_current->sort_priority, filename);
766 /* Always use the latest version of the game engine for all but
767 user contributed and private levels; this allows for actual
768 corrections in the game engine to take effect for existing,
769 converted levels (from "classic" or other existing games) to
770 make the game emulation more accurate, while (hopefully) not
771 breaking existing levels created from other players. */
773 level->game_version = GAME_VERSION_ACTUAL;
775 /* Set special EM style gems behaviour: EM style gems slip down from
776 normal, steel and growing wall. As this is a more fundamental change,
777 it seems better to set the default behaviour to "off" (as it is more
778 natural) and make it configurable in the level editor (as a property
779 of gem style elements). Already existing converted levels (neither
780 private nor contributed levels) are changed to the new behaviour. */
782 if (level->file_version < FILE_VERSION_2_0)
783 level->em_slippery_gems = TRUE;
787 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
791 /* map custom element change events that have changed in newer versions
792 (these following values have accidentally changed in version 3.0.1) */
793 if (level->game_version <= VERSION_IDENT(3,0,0))
795 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
797 int element = EL_CUSTOM_START + i;
799 /* order of checking events to be mapped is important */
800 for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
802 if (HAS_CHANGE_EVENT(element, j - 2))
804 SET_CHANGE_EVENT(element, j - 2, FALSE);
805 SET_CHANGE_EVENT(element, j, TRUE);
809 /* order of checking events to be mapped is important */
810 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
812 if (HAS_CHANGE_EVENT(element, j - 1))
814 SET_CHANGE_EVENT(element, j - 1, FALSE);
815 SET_CHANGE_EVENT(element, j, TRUE);
821 /* initialize "can_change" field for old levels with only one change page */
822 if (level->game_version <= VERSION_IDENT(3,0,2))
824 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
826 int element = EL_CUSTOM_START + i;
828 if (CAN_CHANGE(element))
829 element_info[element].change->can_change = TRUE;
833 /* initialize element properties for level editor etc. */
834 InitElementPropertiesEngine(level->game_version);
837 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
841 /* map elements that have changed in newer versions */
842 for(y=0; y<level->fieldy; y++)
844 for(x=0; x<level->fieldx; x++)
846 int element = level->field[x][y];
848 if (level->game_version <= VERSION_IDENT(2,2,0))
850 /* map game font elements */
851 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
852 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
853 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
854 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
857 if (level->game_version < VERSION_IDENT(3,0,0))
859 /* map Supaplex gravity tube elements */
860 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
861 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
862 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
863 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
867 level->field[x][y] = element;
871 /* copy elements to runtime playfield array */
872 for(x=0; x<MAX_LEV_FIELDX; x++)
873 for(y=0; y<MAX_LEV_FIELDY; y++)
874 Feld[x][y] = level->field[x][y];
876 /* initialize level size variables for faster access */
877 lev_fieldx = level->fieldx;
878 lev_fieldy = level->fieldy;
880 /* determine border element for this level */
886 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
890 if (leveldir_current == NULL) /* only when dumping level */
893 /* determine correct game engine version of current level */
894 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
895 IS_LEVELCLASS_USER(leveldir_current))
898 printf("\n::: This level is private or contributed: '%s'\n", filename);
901 /* For user contributed and private levels, use the version of
902 the game engine the levels were created for.
903 Since 2.0.1, the game engine version is now directly stored
904 in the level file (chunk "VERS"), so there is no need anymore
905 to set the game version from the file version (except for old,
906 pre-2.0 levels, where the game version is still taken from the
907 file format version used to store the level -- see above). */
909 /* do some special adjustments to support older level versions */
910 if (level->file_version == FILE_VERSION_1_0)
912 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
913 Error(ERR_WARN, "using high speed movement for player");
915 /* player was faster than monsters in (pre-)1.0 levels */
916 level->double_speed = TRUE;
919 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
920 if (level->game_version == VERSION_IDENT(2,0,1))
921 level->em_slippery_gems = TRUE;
926 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
927 leveldir_current->sort_priority, filename);
930 /* Always use the latest version of the game engine for all but
931 user contributed and private levels; this allows for actual
932 corrections in the game engine to take effect for existing,
933 converted levels (from "classic" or other existing games) to
934 make the game emulation more accurate, while (hopefully) not
935 breaking existing levels created from other players. */
937 level->game_version = GAME_VERSION_ACTUAL;
939 /* Set special EM style gems behaviour: EM style gems slip down from
940 normal, steel and growing wall. As this is a more fundamental change,
941 it seems better to set the default behaviour to "off" (as it is more
942 natural) and make it configurable in the level editor (as a property
943 of gem style elements). Already existing converted levels (neither
944 private nor contributed levels) are changed to the new behaviour. */
946 if (level->file_version < FILE_VERSION_2_0)
947 level->em_slippery_gems = TRUE;
950 /* map elements that have changed in newer versions */
951 for(y=0; y<level->fieldy; y++)
953 for(x=0; x<level->fieldx; x++)
955 int element = level->field[x][y];
957 if (level->game_version <= VERSION_IDENT(2,2,0))
959 /* map game font elements */
960 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
961 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
962 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
963 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
966 if (level->game_version < VERSION_IDENT(3,0,0))
968 /* map Supaplex gravity tube elements */
969 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
970 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
971 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
972 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
976 level->field[x][y] = element;
980 /* map custom element change events that have changed in newer versions
981 (these following values have accidentally changed in version 3.0.1) */
982 if (level->game_version <= VERSION_IDENT(3,0,0))
984 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
986 int element = EL_CUSTOM_START + i;
988 /* order of checking events to be mapped is important */
989 for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
991 if (HAS_CHANGE_EVENT(element, j - 2))
993 SET_CHANGE_EVENT(element, j - 2, FALSE);
994 SET_CHANGE_EVENT(element, j, TRUE);
998 /* order of checking events to be mapped is important */
999 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1001 if (HAS_CHANGE_EVENT(element, j - 1))
1003 SET_CHANGE_EVENT(element, j - 1, FALSE);
1004 SET_CHANGE_EVENT(element, j, TRUE);
1010 /* initialize "can_change" field for old levels with only one change page */
1011 if (level->game_version <= VERSION_IDENT(3,0,2))
1013 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1015 int element = EL_CUSTOM_START + i;
1017 if (CAN_CHANGE(element))
1018 element_info[element].change->can_change = TRUE;
1022 /* copy elements to runtime playfield array */
1023 for(x=0; x<MAX_LEV_FIELDX; x++)
1024 for(y=0; y<MAX_LEV_FIELDY; y++)
1025 Feld[x][y] = level->field[x][y];
1027 /* initialize level size variables for faster access */
1028 lev_fieldx = level->fieldx;
1029 lev_fieldy = level->fieldy;
1031 /* determine border element for this level */
1034 /* initialize element properties for level editor etc. */
1035 InitElementPropertiesEngine(level->game_version);
1040 void LoadLevelTemplate(int level_nr)
1042 char *filename = getLevelFilename(level_nr);
1044 LoadLevelFromFilename(&level_template, filename);
1046 LoadLevel_InitVersion(&level, filename);
1047 LoadLevel_InitElements(&level, filename);
1049 ActivateLevelTemplate();
1052 void LoadLevel(int level_nr)
1054 char *filename = getLevelFilename(level_nr);
1056 LoadLevelFromFilename(&level, filename);
1058 if (level.use_custom_template)
1059 LoadLevelTemplate(-1);
1062 LoadLevel_InitVersion(&level, filename);
1063 LoadLevel_InitElements(&level, filename);
1064 LoadLevel_InitPlayfield(&level, filename);
1066 LoadLevel_InitLevel(&level, filename);
1070 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1072 putFileVersion(file, level->file_version);
1073 putFileVersion(file, level->game_version);
1076 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1080 putFile8Bit(file, level->fieldx);
1081 putFile8Bit(file, level->fieldy);
1083 putFile16BitBE(file, level->time);
1084 putFile16BitBE(file, level->gems_needed);
1086 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1087 putFile8Bit(file, level->name[i]);
1089 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1090 putFile8Bit(file, level->score[i]);
1092 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1095 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1096 level->yamyam_content[i][x][y]));
1097 putFile8Bit(file, level->amoeba_speed);
1098 putFile8Bit(file, level->time_magic_wall);
1099 putFile8Bit(file, level->time_wheel);
1100 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1101 level->amoeba_content));
1102 putFile8Bit(file, (level->double_speed ? 1 : 0));
1103 putFile8Bit(file, (level->gravity ? 1 : 0));
1104 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1105 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1107 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1109 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1112 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1116 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1117 putFile8Bit(file, level->author[i]);
1120 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1124 for(y=0; y<level->fieldy; y++)
1125 for(x=0; x<level->fieldx; x++)
1126 if (level->encoding_16bit_field)
1127 putFile16BitBE(file, level->field[x][y]);
1129 putFile8Bit(file, level->field[x][y]);
1133 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1137 putFile8Bit(file, EL_YAMYAM);
1138 putFile8Bit(file, level->num_yamyam_contents);
1139 putFile8Bit(file, 0);
1140 putFile8Bit(file, 0);
1142 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1145 if (level->encoding_16bit_field)
1146 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1148 putFile8Bit(file, level->yamyam_content[i][x][y]);
1152 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1155 int num_contents, content_xsize, content_ysize;
1156 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1158 if (element == EL_YAMYAM)
1160 num_contents = level->num_yamyam_contents;
1164 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1167 content_array[i][x][y] = level->yamyam_content[i][x][y];
1169 else if (element == EL_BD_AMOEBA)
1175 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1178 content_array[i][x][y] = EL_EMPTY;
1179 content_array[0][0][0] = level->amoeba_content;
1183 /* chunk header already written -- write empty chunk data */
1184 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1186 Error(ERR_WARN, "cannot save content for element '%d'", element);
1190 putFile16BitBE(file, element);
1191 putFile8Bit(file, num_contents);
1192 putFile8Bit(file, content_xsize);
1193 putFile8Bit(file, content_ysize);
1195 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1197 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1200 putFile16BitBE(file, content_array[i][x][y]);
1204 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1205 int num_changed_custom_elements)
1209 putFile16BitBE(file, num_changed_custom_elements);
1211 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1213 int element = EL_CUSTOM_START + i;
1215 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1217 if (check < num_changed_custom_elements)
1219 putFile16BitBE(file, element);
1220 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1227 if (check != num_changed_custom_elements) /* should not happen */
1228 Error(ERR_WARN, "inconsistent number of custom element properties");
1233 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1234 int num_changed_custom_elements)
1238 putFile16BitBE(file, num_changed_custom_elements);
1240 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1242 int element = EL_CUSTOM_START + i;
1244 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1246 if (check < num_changed_custom_elements)
1248 putFile16BitBE(file, element);
1249 putFile16BitBE(file, element_info[element].change->target_element);
1256 if (check != num_changed_custom_elements) /* should not happen */
1257 Error(ERR_WARN, "inconsistent number of custom target elements");
1261 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1262 int num_changed_custom_elements)
1264 int i, j, x, y, check = 0;
1266 putFile16BitBE(file, num_changed_custom_elements);
1268 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1270 int element = EL_CUSTOM_START + i;
1272 if (element_info[element].modified_settings)
1274 if (check < num_changed_custom_elements)
1276 putFile16BitBE(file, element);
1278 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1279 putFile8Bit(file, element_info[element].description[j]);
1281 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1283 /* some free bytes for future properties and padding */
1284 WriteUnusedBytesToFile(file, 7);
1286 putFile8Bit(file, element_info[element].use_gfx_element);
1287 putFile16BitBE(file, element_info[element].gfx_element);
1289 putFile8Bit(file, element_info[element].collect_score);
1290 putFile8Bit(file, element_info[element].collect_count);
1292 putFile16BitBE(file, element_info[element].push_delay_fixed);
1293 putFile16BitBE(file, element_info[element].push_delay_random);
1294 putFile16BitBE(file, element_info[element].move_delay_fixed);
1295 putFile16BitBE(file, element_info[element].move_delay_random);
1297 putFile16BitBE(file, element_info[element].move_pattern);
1298 putFile8Bit(file, element_info[element].move_direction_initial);
1299 putFile8Bit(file, element_info[element].move_stepsize);
1303 putFile16BitBE(file, element_info[element].content[x][y]);
1305 putFile32BitBE(file, element_info[element].change->events);
1307 putFile16BitBE(file, element_info[element].change->target_element);
1309 putFile16BitBE(file, element_info[element].change->delay_fixed);
1310 putFile16BitBE(file, element_info[element].change->delay_random);
1311 putFile16BitBE(file, element_info[element].change->delay_frames);
1313 putFile16BitBE(file, element_info[element].change->trigger_element);
1315 putFile8Bit(file, element_info[element].change->explode);
1316 putFile8Bit(file, element_info[element].change->use_content);
1317 putFile8Bit(file, element_info[element].change->only_complete);
1318 putFile8Bit(file, element_info[element].change->use_random_change);
1320 putFile8Bit(file, element_info[element].change->random);
1321 putFile8Bit(file, element_info[element].change->power);
1325 putFile16BitBE(file, element_info[element].change->content[x][y]);
1327 putFile8Bit(file, element_info[element].slippery_type);
1329 /* some free bytes for future properties and padding */
1330 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1337 if (check != num_changed_custom_elements) /* should not happen */
1338 Error(ERR_WARN, "inconsistent number of custom element properties");
1341 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1343 int body_chunk_size;
1344 int num_changed_custom_elements = 0;
1345 int level_chunk_CUS3_size;
1349 if (!(file = fopen(filename, MODE_WRITE)))
1351 Error(ERR_WARN, "cannot save level file '%s'", filename);
1355 level->file_version = FILE_VERSION_ACTUAL;
1356 level->game_version = GAME_VERSION_ACTUAL;
1358 /* check level field for 16-bit elements */
1359 level->encoding_16bit_field = FALSE;
1360 for(y=0; y<level->fieldy; y++)
1361 for(x=0; x<level->fieldx; x++)
1362 if (level->field[x][y] > 255)
1363 level->encoding_16bit_field = TRUE;
1365 /* check yamyam content for 16-bit elements */
1366 level->encoding_16bit_yamyam = FALSE;
1367 for(i=0; i<level->num_yamyam_contents; i++)
1370 if (level->yamyam_content[i][x][y] > 255)
1371 level->encoding_16bit_yamyam = TRUE;
1373 /* check amoeba content for 16-bit elements */
1374 level->encoding_16bit_amoeba = FALSE;
1375 if (level->amoeba_content > 255)
1376 level->encoding_16bit_amoeba = TRUE;
1378 /* calculate size of "BODY" chunk */
1380 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1382 /* check for non-standard custom elements and calculate "CUS3" chunk size */
1383 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1384 if (element_info[EL_CUSTOM_START + i].modified_settings)
1385 num_changed_custom_elements++;
1386 level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1388 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1389 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1391 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1392 SaveLevel_VERS(file, level);
1394 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1395 SaveLevel_HEAD(file, level);
1397 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1398 SaveLevel_AUTH(file, level);
1400 putFileChunkBE(file, "BODY", body_chunk_size);
1401 SaveLevel_BODY(file, level);
1403 if (level->encoding_16bit_yamyam ||
1404 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1406 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1407 SaveLevel_CNT2(file, level, EL_YAMYAM);
1410 if (level->encoding_16bit_amoeba)
1412 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1413 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1416 if (num_changed_custom_elements > 0 && !level->use_custom_template)
1418 putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1419 SaveLevel_CUS3(file, level, num_changed_custom_elements);
1424 SetFilePermissions(filename, PERMS_PRIVATE);
1427 void SaveLevel(int level_nr)
1429 char *filename = getLevelFilename(level_nr);
1431 SaveLevelFromFilename(&level, filename);
1434 void SaveLevelTemplate()
1436 char *filename = getLevelFilename(-1);
1438 SaveLevelFromFilename(&level, filename);
1441 void DumpLevel(struct LevelInfo *level)
1443 printf_line("-", 79);
1444 printf("Level xxx (file version %08d, game version %08d)\n",
1445 level->file_version, level->game_version);
1446 printf_line("-", 79);
1448 printf("Level Author: '%s'\n", level->author);
1449 printf("Level Title: '%s'\n", level->name);
1451 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1453 printf("Level Time: %d seconds\n", level->time);
1454 printf("Gems needed: %d\n", level->gems_needed);
1456 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1457 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1458 printf("Time for Light: %d seconds\n", level->time_light);
1459 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1461 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1463 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1464 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1465 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1467 printf_line("-", 79);
1471 /* ========================================================================= */
1472 /* tape file functions */
1473 /* ========================================================================= */
1475 static void setTapeInfoToDefaults()
1479 /* always start with reliable default values (empty tape) */
1482 /* default values (also for pre-1.2 tapes) with only the first player */
1483 tape.player_participates[0] = TRUE;
1484 for(i=1; i<MAX_PLAYERS; i++)
1485 tape.player_participates[i] = FALSE;
1487 /* at least one (default: the first) player participates in every tape */
1488 tape.num_participating_players = 1;
1490 tape.level_nr = level_nr;
1492 tape.changed = FALSE;
1494 tape.recording = FALSE;
1495 tape.playing = FALSE;
1496 tape.pausing = FALSE;
1499 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1501 tape->file_version = getFileVersion(file);
1502 tape->game_version = getFileVersion(file);
1507 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1511 tape->random_seed = getFile32BitBE(file);
1512 tape->date = getFile32BitBE(file);
1513 tape->length = getFile32BitBE(file);
1515 /* read header fields that are new since version 1.2 */
1516 if (tape->file_version >= FILE_VERSION_1_2)
1518 byte store_participating_players = getFile8Bit(file);
1521 /* since version 1.2, tapes store which players participate in the tape */
1522 tape->num_participating_players = 0;
1523 for(i=0; i<MAX_PLAYERS; i++)
1525 tape->player_participates[i] = FALSE;
1527 if (store_participating_players & (1 << i))
1529 tape->player_participates[i] = TRUE;
1530 tape->num_participating_players++;
1534 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1536 engine_version = getFileVersion(file);
1537 if (engine_version > 0)
1538 tape->engine_version = engine_version;
1540 tape->engine_version = tape->game_version;
1546 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1548 int level_identifier_size;
1551 level_identifier_size = getFile16BitBE(file);
1553 tape->level_identifier =
1554 checked_realloc(tape->level_identifier, level_identifier_size);
1556 for(i=0; i < level_identifier_size; i++)
1557 tape->level_identifier[i] = getFile8Bit(file);
1559 tape->level_nr = getFile16BitBE(file);
1561 chunk_size = 2 + level_identifier_size + 2;
1566 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1569 int chunk_size_expected =
1570 (tape->num_participating_players + 1) * tape->length;
1572 if (chunk_size_expected != chunk_size)
1574 ReadUnusedBytesFromFile(file, chunk_size);
1575 return chunk_size_expected;
1578 for(i=0; i<tape->length; i++)
1580 if (i >= MAX_TAPELEN)
1583 for(j=0; j<MAX_PLAYERS; j++)
1585 tape->pos[i].action[j] = MV_NO_MOVING;
1587 if (tape->player_participates[j])
1588 tape->pos[i].action[j] = getFile8Bit(file);
1591 tape->pos[i].delay = getFile8Bit(file);
1593 if (tape->file_version == FILE_VERSION_1_0)
1595 /* eliminate possible diagonal moves in old tapes */
1596 /* this is only for backward compatibility */
1598 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1599 byte action = tape->pos[i].action[0];
1600 int k, num_moves = 0;
1604 if (action & joy_dir[k])
1606 tape->pos[i + num_moves].action[0] = joy_dir[k];
1608 tape->pos[i + num_moves].delay = 0;
1617 tape->length += num_moves;
1620 else if (tape->file_version < FILE_VERSION_2_0)
1622 /* convert pre-2.0 tapes to new tape format */
1624 if (tape->pos[i].delay > 1)
1627 tape->pos[i + 1] = tape->pos[i];
1628 tape->pos[i + 1].delay = 1;
1631 for(j=0; j<MAX_PLAYERS; j++)
1632 tape->pos[i].action[j] = MV_NO_MOVING;
1633 tape->pos[i].delay--;
1644 if (i != tape->length)
1645 chunk_size = (tape->num_participating_players + 1) * i;
1650 void LoadTapeFromFilename(char *filename)
1652 char cookie[MAX_LINE_LEN];
1653 char chunk_name[CHUNK_ID_LEN + 1];
1657 /* always start with reliable default values */
1658 setTapeInfoToDefaults();
1660 if (!(file = fopen(filename, MODE_READ)))
1663 getFileChunkBE(file, chunk_name, NULL);
1664 if (strcmp(chunk_name, "RND1") == 0)
1666 getFile32BitBE(file); /* not used */
1668 getFileChunkBE(file, chunk_name, NULL);
1669 if (strcmp(chunk_name, "TAPE") != 0)
1671 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1676 else /* check for pre-2.0 file format with cookie string */
1678 strcpy(cookie, chunk_name);
1679 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1680 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1681 cookie[strlen(cookie) - 1] = '\0';
1683 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1685 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1690 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1692 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1697 /* pre-2.0 tape files have no game version, so use file version here */
1698 tape.game_version = tape.file_version;
1701 if (tape.file_version < FILE_VERSION_1_2)
1703 /* tape files from versions before 1.2.0 without chunk structure */
1704 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1705 LoadTape_BODY(file, 2 * tape.length, &tape);
1713 int (*loader)(FILE *, int, struct TapeInfo *);
1717 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1718 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1719 { "INFO", -1, LoadTape_INFO },
1720 { "BODY", -1, LoadTape_BODY },
1724 while (getFileChunkBE(file, chunk_name, &chunk_size))
1728 while (chunk_info[i].name != NULL &&
1729 strcmp(chunk_name, chunk_info[i].name) != 0)
1732 if (chunk_info[i].name == NULL)
1734 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1735 chunk_name, filename);
1736 ReadUnusedBytesFromFile(file, chunk_size);
1738 else if (chunk_info[i].size != -1 &&
1739 chunk_info[i].size != chunk_size)
1741 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1742 chunk_size, chunk_name, filename);
1743 ReadUnusedBytesFromFile(file, chunk_size);
1747 /* call function to load this tape chunk */
1748 int chunk_size_expected =
1749 (chunk_info[i].loader)(file, chunk_size, &tape);
1751 /* the size of some chunks cannot be checked before reading other
1752 chunks first (like "HEAD" and "BODY") that contain some header
1753 information, so check them here */
1754 if (chunk_size_expected != chunk_size)
1756 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1757 chunk_size, chunk_name, filename);
1765 tape.length_seconds = GetTapeLength();
1768 printf("tape game version: %d\n", tape.game_version);
1769 printf("tape engine version: %d\n", tape.engine_version);
1773 void LoadTape(int level_nr)
1775 char *filename = getTapeFilename(level_nr);
1777 LoadTapeFromFilename(filename);
1780 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1782 putFileVersion(file, tape->file_version);
1783 putFileVersion(file, tape->game_version);
1786 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1789 byte store_participating_players = 0;
1791 /* set bits for participating players for compact storage */
1792 for(i=0; i<MAX_PLAYERS; i++)
1793 if (tape->player_participates[i])
1794 store_participating_players |= (1 << i);
1796 putFile32BitBE(file, tape->random_seed);
1797 putFile32BitBE(file, tape->date);
1798 putFile32BitBE(file, tape->length);
1800 putFile8Bit(file, store_participating_players);
1802 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1803 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1805 putFileVersion(file, tape->engine_version);
1808 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1810 int level_identifier_size = strlen(tape->level_identifier) + 1;
1813 putFile16BitBE(file, level_identifier_size);
1815 for(i=0; i < level_identifier_size; i++)
1816 putFile8Bit(file, tape->level_identifier[i]);
1818 putFile16BitBE(file, tape->level_nr);
1821 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1825 for(i=0; i<tape->length; i++)
1827 for(j=0; j<MAX_PLAYERS; j++)
1828 if (tape->player_participates[j])
1829 putFile8Bit(file, tape->pos[i].action[j]);
1831 putFile8Bit(file, tape->pos[i].delay);
1835 void SaveTape(int level_nr)
1837 char *filename = getTapeFilename(level_nr);
1839 boolean new_tape = TRUE;
1840 int num_participating_players = 0;
1841 int info_chunk_size;
1842 int body_chunk_size;
1845 InitTapeDirectory(leveldir_current->filename);
1847 /* if a tape still exists, ask to overwrite it */
1848 if (access(filename, F_OK) == 0)
1851 if (!Request("Replace old tape ?", REQ_ASK))
1855 if (!(file = fopen(filename, MODE_WRITE)))
1857 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1861 tape.file_version = FILE_VERSION_ACTUAL;
1862 tape.game_version = GAME_VERSION_ACTUAL;
1864 /* count number of participating players */
1865 for(i=0; i<MAX_PLAYERS; i++)
1866 if (tape.player_participates[i])
1867 num_participating_players++;
1869 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1870 body_chunk_size = (num_participating_players + 1) * tape.length;
1872 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1873 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1875 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1876 SaveTape_VERS(file, &tape);
1878 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1879 SaveTape_HEAD(file, &tape);
1881 putFileChunkBE(file, "INFO", info_chunk_size);
1882 SaveTape_INFO(file, &tape);
1884 putFileChunkBE(file, "BODY", body_chunk_size);
1885 SaveTape_BODY(file, &tape);
1889 SetFilePermissions(filename, PERMS_PRIVATE);
1891 tape.changed = FALSE;
1894 Request("tape saved !", REQ_CONFIRM);
1897 void DumpTape(struct TapeInfo *tape)
1901 if (TAPE_IS_EMPTY(*tape))
1903 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1907 printf_line("-", 79);
1908 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1909 tape->level_nr, tape->file_version, tape->game_version);
1910 printf("Level series identifier: '%s'\n", tape->level_identifier);
1911 printf_line("-", 79);
1913 for(i=0; i<tape->length; i++)
1915 if (i >= MAX_TAPELEN)
1918 printf("%03d: ", i);
1920 for(j=0; j<MAX_PLAYERS; j++)
1922 if (tape->player_participates[j])
1924 int action = tape->pos[i].action[j];
1926 printf("%d:%02x ", j, action);
1927 printf("[%c%c%c%c|%c%c] - ",
1928 (action & JOY_LEFT ? '<' : ' '),
1929 (action & JOY_RIGHT ? '>' : ' '),
1930 (action & JOY_UP ? '^' : ' '),
1931 (action & JOY_DOWN ? 'v' : ' '),
1932 (action & JOY_BUTTON_1 ? '1' : ' '),
1933 (action & JOY_BUTTON_2 ? '2' : ' '));
1937 printf("(%03d)\n", tape->pos[i].delay);
1940 printf_line("-", 79);
1944 /* ========================================================================= */
1945 /* score file functions */
1946 /* ========================================================================= */
1948 void LoadScore(int level_nr)
1951 char *filename = getScoreFilename(level_nr);
1952 char cookie[MAX_LINE_LEN];
1953 char line[MAX_LINE_LEN];
1957 /* always start with reliable default values */
1958 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1960 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1961 highscore[i].Score = 0;
1964 if (!(file = fopen(filename, MODE_READ)))
1967 /* check file identifier */
1968 fgets(cookie, MAX_LINE_LEN, file);
1969 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1970 cookie[strlen(cookie) - 1] = '\0';
1972 if (!checkCookieString(cookie, SCORE_COOKIE))
1974 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1979 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1981 fscanf(file, "%d", &highscore[i].Score);
1982 fgets(line, MAX_LINE_LEN, file);
1984 if (line[strlen(line) - 1] == '\n')
1985 line[strlen(line) - 1] = '\0';
1987 for (line_ptr = line; *line_ptr; line_ptr++)
1989 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1991 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1992 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2001 void SaveScore(int level_nr)
2004 char *filename = getScoreFilename(level_nr);
2007 InitScoreDirectory(leveldir_current->filename);
2009 if (!(file = fopen(filename, MODE_WRITE)))
2011 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2015 fprintf(file, "%s\n\n", SCORE_COOKIE);
2017 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2018 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2022 SetFilePermissions(filename, PERMS_PUBLIC);
2026 /* ========================================================================= */
2027 /* setup file functions */
2028 /* ========================================================================= */
2030 #define TOKEN_STR_PLAYER_PREFIX "player_"
2033 #define SETUP_TOKEN_PLAYER_NAME 0
2034 #define SETUP_TOKEN_SOUND 1
2035 #define SETUP_TOKEN_SOUND_LOOPS 2
2036 #define SETUP_TOKEN_SOUND_MUSIC 3
2037 #define SETUP_TOKEN_SOUND_SIMPLE 4
2038 #define SETUP_TOKEN_TOONS 5
2039 #define SETUP_TOKEN_SCROLL_DELAY 6
2040 #define SETUP_TOKEN_SOFT_SCROLLING 7
2041 #define SETUP_TOKEN_FADING 8
2042 #define SETUP_TOKEN_AUTORECORD 9
2043 #define SETUP_TOKEN_QUICK_DOORS 10
2044 #define SETUP_TOKEN_TEAM_MODE 11
2045 #define SETUP_TOKEN_HANDICAP 12
2046 #define SETUP_TOKEN_TIME_LIMIT 13
2047 #define SETUP_TOKEN_FULLSCREEN 14
2048 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2049 #define SETUP_TOKEN_GRAPHICS_SET 16
2050 #define SETUP_TOKEN_SOUNDS_SET 17
2051 #define SETUP_TOKEN_MUSIC_SET 18
2052 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2053 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2054 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2056 #define NUM_GLOBAL_SETUP_TOKENS 22
2059 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2060 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2061 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2062 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2063 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2064 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2065 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2066 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2067 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2068 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 9
2070 #define NUM_EDITOR_SETUP_TOKENS 10
2072 /* shortcut setup */
2073 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2074 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2075 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2077 #define NUM_SHORTCUT_SETUP_TOKENS 3
2080 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2081 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2082 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2083 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2084 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2085 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2086 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2087 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2088 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2089 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
2090 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2091 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2092 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2093 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2094 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2095 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
2097 #define NUM_PLAYER_SETUP_TOKENS 16
2100 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2101 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2103 #define NUM_SYSTEM_SETUP_TOKENS 2
2106 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2108 #define NUM_OPTIONS_SETUP_TOKENS 1
2111 static struct SetupInfo si;
2112 static struct SetupEditorInfo sei;
2113 static struct SetupShortcutInfo ssi;
2114 static struct SetupInputInfo sii;
2115 static struct SetupSystemInfo syi;
2116 static struct OptionInfo soi;
2118 static struct TokenInfo global_setup_tokens[] =
2120 { TYPE_STRING, &si.player_name, "player_name" },
2121 { TYPE_SWITCH, &si.sound, "sound" },
2122 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2123 { TYPE_SWITCH, &si.sound_music, "background_music" },
2124 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2125 { TYPE_SWITCH, &si.toons, "toons" },
2126 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2127 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2128 { TYPE_SWITCH, &si.fading, "screen_fading" },
2129 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2130 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2131 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2132 { TYPE_SWITCH, &si.handicap, "handicap" },
2133 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2134 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2135 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2136 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2137 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2138 { TYPE_STRING, &si.music_set, "music_set" },
2139 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2140 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2141 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2144 static struct TokenInfo editor_setup_tokens[] =
2146 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2147 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2148 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2149 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2150 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2151 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2152 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2153 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2154 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2155 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2156 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2159 static struct TokenInfo shortcut_setup_tokens[] =
2161 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2162 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2163 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2166 static struct TokenInfo player_setup_tokens[] =
2168 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2169 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2170 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2171 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2172 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2173 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2174 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2175 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2176 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2177 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2178 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2179 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2180 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2181 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2182 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2183 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
2186 static struct TokenInfo system_setup_tokens[] =
2188 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2189 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2192 static struct TokenInfo options_setup_tokens[] =
2194 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2197 static char *get_corrected_login_name(char *login_name)
2199 /* needed because player name must be a fixed length string */
2200 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2202 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2203 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2205 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2206 if (strchr(login_name_new, ' '))
2207 *strchr(login_name_new, ' ') = '\0';
2209 return login_name_new;
2212 static void setSetupInfoToDefaults(struct SetupInfo *si)
2216 si->player_name = get_corrected_login_name(getLoginName());
2219 si->sound_loops = TRUE;
2220 si->sound_music = TRUE;
2221 si->sound_simple = TRUE;
2223 si->double_buffering = TRUE;
2224 si->direct_draw = !si->double_buffering;
2225 si->scroll_delay = TRUE;
2226 si->soft_scrolling = TRUE;
2228 si->autorecord = TRUE;
2229 si->quick_doors = FALSE;
2230 si->team_mode = FALSE;
2231 si->handicap = TRUE;
2232 si->time_limit = TRUE;
2233 si->fullscreen = FALSE;
2234 si->ask_on_escape = TRUE;
2236 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2237 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2238 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2239 si->override_level_graphics = FALSE;
2240 si->override_level_sounds = FALSE;
2241 si->override_level_music = FALSE;
2243 si->editor.el_boulderdash = TRUE;
2244 si->editor.el_emerald_mine = TRUE;
2245 si->editor.el_more = TRUE;
2246 si->editor.el_sokoban = TRUE;
2247 si->editor.el_supaplex = TRUE;
2248 si->editor.el_diamond_caves = TRUE;
2249 si->editor.el_dx_boulderdash = TRUE;
2250 si->editor.el_chars = TRUE;
2251 si->editor.el_custom = TRUE;
2252 si->editor.el_custom_more = FALSE;
2254 si->editor.el_headlines = TRUE;
2256 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2257 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2258 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2260 for (i=0; i<MAX_PLAYERS; i++)
2262 si->input[i].use_joystick = FALSE;
2263 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2264 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2265 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2266 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2267 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2268 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2269 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2270 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2271 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2272 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2273 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2274 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2275 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2276 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2277 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2280 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2281 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2283 si->options.verbose = FALSE;
2286 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2290 if (!setup_file_hash)
2295 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2296 setSetupInfo(global_setup_tokens, i,
2297 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2302 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2303 setSetupInfo(editor_setup_tokens, i,
2304 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2307 /* shortcut setup */
2308 ssi = setup.shortcut;
2309 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2310 setSetupInfo(shortcut_setup_tokens, i,
2311 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2312 setup.shortcut = ssi;
2315 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2319 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2321 sii = setup.input[pnr];
2322 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2324 char full_token[100];
2326 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2327 setSetupInfo(player_setup_tokens, i,
2328 getHashEntry(setup_file_hash, full_token));
2330 setup.input[pnr] = sii;
2335 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2336 setSetupInfo(system_setup_tokens, i,
2337 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2341 soi = setup.options;
2342 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2343 setSetupInfo(options_setup_tokens, i,
2344 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2345 setup.options = soi;
2350 char *filename = getSetupFilename();
2351 SetupFileHash *setup_file_hash = NULL;
2353 /* always start with reliable default values */
2354 setSetupInfoToDefaults(&setup);
2356 setup_file_hash = loadSetupFileHash(filename);
2358 if (setup_file_hash)
2360 char *player_name_new;
2362 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2363 decodeSetupFileHash(setup_file_hash);
2365 setup.direct_draw = !setup.double_buffering;
2367 freeSetupFileHash(setup_file_hash);
2369 /* needed to work around problems with fixed length strings */
2370 player_name_new = get_corrected_login_name(setup.player_name);
2371 free(setup.player_name);
2372 setup.player_name = player_name_new;
2375 Error(ERR_WARN, "using default setup values");
2380 char *filename = getSetupFilename();
2384 InitUserDataDirectory();
2386 if (!(file = fopen(filename, MODE_WRITE)))
2388 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2392 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2393 getCookie("SETUP")));
2394 fprintf(file, "\n");
2398 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2400 /* just to make things nicer :) */
2401 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2402 i == SETUP_TOKEN_GRAPHICS_SET)
2403 fprintf(file, "\n");
2405 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2410 fprintf(file, "\n");
2411 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2412 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2414 /* shortcut setup */
2415 ssi = setup.shortcut;
2416 fprintf(file, "\n");
2417 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2418 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2421 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2425 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2426 fprintf(file, "\n");
2428 sii = setup.input[pnr];
2429 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2430 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2435 fprintf(file, "\n");
2436 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2437 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2440 soi = setup.options;
2441 fprintf(file, "\n");
2442 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2443 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2447 SetFilePermissions(filename, PERMS_PRIVATE);
2450 void LoadCustomElementDescriptions()
2452 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2453 SetupFileHash *setup_file_hash;
2456 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2458 if (element_info[i].custom_description != NULL)
2460 free(element_info[i].custom_description);
2461 element_info[i].custom_description = NULL;
2465 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2468 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2470 char *token = getStringCat2(element_info[i].token_name, ".name");
2471 char *value = getHashEntry(setup_file_hash, token);
2474 element_info[i].custom_description = getStringCopy(value);
2479 freeSetupFileHash(setup_file_hash);
2482 void LoadSpecialMenuDesignSettings()
2484 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2485 SetupFileHash *setup_file_hash;
2488 /* always start with reliable default values from default config */
2489 for (i=0; image_config_vars[i].token != NULL; i++)
2490 for (j=0; image_config[j].token != NULL; j++)
2491 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2492 *image_config_vars[i].value =
2493 get_integer_from_string(image_config[j].value);
2495 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2498 /* special case: initialize with default values that may be overwritten */
2499 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2501 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2502 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2503 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2505 if (value_x != NULL)
2506 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2507 if (value_y != NULL)
2508 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2509 if (list_size != NULL)
2510 menu.list_size[i] = get_integer_from_string(list_size);
2513 /* read (and overwrite with) values that may be specified in config file */
2514 for (i=0; image_config_vars[i].token != NULL; i++)
2516 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2519 *image_config_vars[i].value = get_integer_from_string(value);
2522 freeSetupFileHash(setup_file_hash);