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 14 /* 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 TAPE_HEADER_SIZE 20 /* size of tape file header */
34 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
36 /* file identifier strings */
37 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
38 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
39 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
42 /* ========================================================================= */
43 /* level file functions */
44 /* ========================================================================= */
46 static void setLevelInfoToDefaults()
50 level.file_version = FILE_VERSION_ACTUAL;
51 level.game_version = GAME_VERSION_ACTUAL;
53 level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
54 level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
55 level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
57 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
58 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
60 for(x=0; x<MAX_LEV_FIELDX; x++)
61 for(y=0; y<MAX_LEV_FIELDY; y++)
62 Feld[x][y] = Ur[x][y] = EL_SAND;
65 level.gems_needed = 0;
66 level.amoeba_speed = 10;
67 level.time_magic_wall = 10;
68 level.time_wheel = 10;
69 level.time_light = 10;
70 level.time_timegate = 10;
71 level.amoeba_content = EL_DIAMOND;
72 level.double_speed = FALSE;
73 level.gravity = FALSE;
74 level.em_slippery_gems = FALSE;
76 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
78 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
79 level.author[i] = '\0';
81 strcpy(level.name, NAMELESS_LEVEL_NAME);
82 strcpy(level.author, ANONYMOUS_NAME);
84 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
87 level.num_yam_contents = STD_ELEMENT_CONTENTS;
88 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
91 level.yam_content[i][x][y] =
92 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
94 Feld[0][0] = Ur[0][0] = EL_PLAYER_1;
95 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
96 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
98 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
100 level.custom_element[i].change.events = CE_BITMASK_DEFAULT;
101 level.custom_element[i].change.successor = EL_EMPTY_SPACE;
102 level.custom_element[i].change.delay_fixed = 0;
103 level.custom_element[i].change.delay_random = 0;
105 /* start with no properties at all */
107 for (j=0; j < NUM_EP_BITFIELDS; j++)
108 Properties[EL_CUSTOM_START + i][j] = EP_BITMASK_DEFAULT;
110 Properties[EL_CUSTOM_START + i][EP_BITFIELD_BASE] = EP_BITMASK_DEFAULT;
114 BorderElement = EL_STEELWALL;
116 level.no_level_file = FALSE;
118 if (leveldir_current == NULL) /* only when dumping level */
121 /* try to determine better author name than 'anonymous' */
122 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
124 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
125 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
129 switch (LEVELCLASS(leveldir_current))
131 case LEVELCLASS_TUTORIAL:
132 strcpy(level.author, PROGRAM_AUTHOR_STRING);
135 case LEVELCLASS_CONTRIBUTION:
136 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
137 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
140 case LEVELCLASS_USER:
141 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
142 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
146 /* keep default value */
152 static int checkLevelElement(int element)
154 if (element >= NUM_FILE_ELEMENTS)
156 Error(ERR_WARN, "invalid level element %d", element);
157 element = EL_CHAR_QUESTION;
159 else if (element == EL_PLAYER_OBSOLETE)
160 element = EL_PLAYER_1;
161 else if (element == EL_KEY_OBSOLETE)
167 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
169 level->file_version = getFileVersion(file);
170 level->game_version = getFileVersion(file);
175 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
179 lev_fieldx = level->fieldx = fgetc(file);
180 lev_fieldy = level->fieldy = fgetc(file);
182 level->time = getFile16BitBE(file);
183 level->gems_needed = getFile16BitBE(file);
185 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
186 level->name[i] = fgetc(file);
187 level->name[MAX_LEVEL_NAME_LEN] = 0;
189 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
190 level->score[i] = fgetc(file);
192 level->num_yam_contents = STD_ELEMENT_CONTENTS;
193 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
196 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
198 level->amoeba_speed = fgetc(file);
199 level->time_magic_wall = fgetc(file);
200 level->time_wheel = fgetc(file);
201 level->amoeba_content = checkLevelElement(fgetc(file));
202 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
203 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
204 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
205 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
207 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
212 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
216 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
217 level->author[i] = fgetc(file);
218 level->author[MAX_LEVEL_NAME_LEN] = 0;
223 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
226 int chunk_size_expected = level->fieldx * level->fieldy;
228 /* Note: "chunk_size" was wrong before version 2.0 when elements are
229 stored with 16-bit encoding (and should be twice as big then).
230 Even worse, playfield data was stored 16-bit when only yamyam content
231 contained 16-bit elements and vice versa. */
233 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
234 chunk_size_expected *= 2;
236 if (chunk_size_expected != chunk_size)
238 ReadUnusedBytesFromFile(file, chunk_size);
239 return chunk_size_expected;
242 for(y=0; y<level->fieldy; y++)
243 for(x=0; x<level->fieldx; x++)
244 Feld[x][y] = Ur[x][y] =
245 checkLevelElement(level->encoding_16bit_field ?
246 getFile16BitBE(file) : fgetc(file));
250 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
254 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
255 int chunk_size_expected = header_size + content_size;
257 /* Note: "chunk_size" was wrong before version 2.0 when elements are
258 stored with 16-bit encoding (and should be twice as big then).
259 Even worse, playfield data was stored 16-bit when only yamyam content
260 contained 16-bit elements and vice versa. */
262 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
263 chunk_size_expected += content_size;
265 if (chunk_size_expected != chunk_size)
267 ReadUnusedBytesFromFile(file, chunk_size);
268 return chunk_size_expected;
272 level->num_yam_contents = fgetc(file);
276 /* correct invalid number of content fields -- should never happen */
277 if (level->num_yam_contents < 1 ||
278 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
279 level->num_yam_contents = STD_ELEMENT_CONTENTS;
281 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
284 level->yam_content[i][x][y] =
285 checkLevelElement(level->encoding_16bit_field ?
286 getFile16BitBE(file) : fgetc(file));
290 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
294 int num_contents, content_xsize, content_ysize;
295 int content_array[MAX_ELEMENT_CONTENTS][3][3];
297 element = checkLevelElement(getFile16BitBE(file));
298 num_contents = fgetc(file);
299 content_xsize = fgetc(file);
300 content_ysize = fgetc(file);
301 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
303 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
306 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
308 /* correct invalid number of content fields -- should never happen */
309 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
310 num_contents = STD_ELEMENT_CONTENTS;
312 if (element == EL_YAMYAM)
314 level->num_yam_contents = num_contents;
316 for(i=0; i<num_contents; i++)
319 level->yam_content[i][x][y] = content_array[i][x][y];
321 else if (element == EL_BD_AMOEBA)
323 level->amoeba_content = content_array[0][0][0];
327 Error(ERR_WARN, "cannot load content for element '%d'", element);
333 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
335 int num_changed_custom_elements = getFile16BitBE(file);
336 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
339 if (chunk_size_expected != chunk_size)
341 ReadUnusedBytesFromFile(file, chunk_size - 2);
342 return chunk_size_expected;
345 for (i=0; i < num_changed_custom_elements; i++)
347 int element = getFile16BitBE(file);
348 int properties = getFile32BitBE(file);
350 if (IS_CUSTOM_ELEMENT(element))
351 Properties[element][EP_BITFIELD_BASE] = properties;
353 Error(ERR_WARN, "invalid custom element number %d", element);
359 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
361 int num_changed_custom_elements = getFile16BitBE(file);
362 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
365 if (chunk_size_expected != chunk_size)
367 ReadUnusedBytesFromFile(file, chunk_size - 2);
368 return chunk_size_expected;
371 for (i=0; i < num_changed_custom_elements; i++)
373 int element = getFile16BitBE(file);
374 int custom_element_successor = getFile16BitBE(file);
375 int i = element - EL_CUSTOM_START;
377 if (IS_CUSTOM_ELEMENT(element))
378 level->custom_element[i].change.successor = custom_element_successor;
380 Error(ERR_WARN, "invalid custom element number %d", element);
386 void LoadLevelFromFilename(char *filename)
388 char cookie[MAX_LINE_LEN];
389 char chunk_name[CHUNK_ID_LEN + 1];
393 /* always start with reliable default values */
394 setLevelInfoToDefaults();
396 if (!(file = fopen(filename, MODE_READ)))
398 level.no_level_file = TRUE;
400 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
404 getFileChunkBE(file, chunk_name, NULL);
405 if (strcmp(chunk_name, "RND1") == 0)
407 getFile32BitBE(file); /* not used */
409 getFileChunkBE(file, chunk_name, NULL);
410 if (strcmp(chunk_name, "CAVE") != 0)
412 Error(ERR_WARN, "unknown format of level file '%s'", filename);
417 else /* check for pre-2.0 file format with cookie string */
419 strcpy(cookie, chunk_name);
420 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
421 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
422 cookie[strlen(cookie) - 1] = '\0';
424 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
426 Error(ERR_WARN, "unknown format of level file '%s'", filename);
431 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
433 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
438 /* pre-2.0 level files have no game version, so use file version here */
439 level.game_version = level.file_version;
442 if (level.file_version < FILE_VERSION_1_2)
444 /* level files from versions before 1.2.0 without chunk structure */
445 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
446 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
454 int (*loader)(FILE *, int, struct LevelInfo *);
458 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
459 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
460 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
461 { "BODY", -1, LoadLevel_BODY },
462 { "CONT", -1, LoadLevel_CONT },
463 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
464 { "CUS1", -1, LoadLevel_CUS1 },
465 { "CUS2", -1, LoadLevel_CUS2 },
469 while (getFileChunkBE(file, chunk_name, &chunk_size))
473 while (chunk_info[i].name != NULL &&
474 strcmp(chunk_name, chunk_info[i].name) != 0)
477 if (chunk_info[i].name == NULL)
479 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
480 chunk_name, filename);
481 ReadUnusedBytesFromFile(file, chunk_size);
483 else if (chunk_info[i].size != -1 &&
484 chunk_info[i].size != chunk_size)
486 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
487 chunk_size, chunk_name, filename);
488 ReadUnusedBytesFromFile(file, chunk_size);
492 /* call function to load this level chunk */
493 int chunk_size_expected =
494 (chunk_info[i].loader)(file, chunk_size, &level);
496 /* the size of some chunks cannot be checked before reading other
497 chunks first (like "HEAD" and "BODY") that contain some header
498 information, so check them here */
499 if (chunk_size_expected != chunk_size)
501 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
502 chunk_size, chunk_name, filename);
510 if (leveldir_current == NULL) /* only when dumping level */
513 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
514 IS_LEVELCLASS_USER(leveldir_current))
516 /* For user contributed and private levels, use the version of
517 the game engine the levels were created for.
518 Since 2.0.1, the game engine version is now directly stored
519 in the level file (chunk "VERS"), so there is no need anymore
520 to set the game version from the file version (except for old,
521 pre-2.0 levels, where the game version is still taken from the
522 file format version used to store the level -- see above). */
524 /* do some special adjustments to support older level versions */
525 if (level.file_version == FILE_VERSION_1_0)
527 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
528 Error(ERR_WARN, "using high speed movement for player");
530 /* player was faster than monsters in (pre-)1.0 levels */
531 level.double_speed = TRUE;
534 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
535 if (level.game_version == VERSION_IDENT(2,0,1))
536 level.em_slippery_gems = TRUE;
540 /* Always use the latest version of the game engine for all but
541 user contributed and private levels; this allows for actual
542 corrections in the game engine to take effect for existing,
543 converted levels (from "classic" or other existing games) to
544 make the game emulation more accurate, while (hopefully) not
545 breaking existing levels created from other players. */
547 level.game_version = GAME_VERSION_ACTUAL;
549 /* Set special EM style gems behaviour: EM style gems slip down from
550 normal, steel and growing wall. As this is a more fundamental change,
551 it seems better to set the default behaviour to "off" (as it is more
552 natural) and make it configurable in the level editor (as a property
553 of gem style elements). Already existing converted levels (neither
554 private nor contributed levels) are changed to the new behaviour. */
556 if (level.file_version < FILE_VERSION_2_0)
557 level.em_slippery_gems = TRUE;
560 /* map some elements which have changed in newer versions */
561 if (level.game_version <= VERSION_IDENT(2,2,0))
565 /* map game font elements */
566 for(y=0; y<level.fieldy; y++)
568 for(x=0; x<level.fieldx; x++)
570 int element = Ur[x][y];
572 if (element == EL_CHAR('['))
573 element = EL_CHAR_AUMLAUT;
574 else if (element == EL_CHAR('\\'))
575 element = EL_CHAR_OUMLAUT;
576 else if (element == EL_CHAR(']'))
577 element = EL_CHAR_UUMLAUT;
578 else if (element == EL_CHAR('^'))
579 element = EL_CHAR_COPYRIGHT;
581 Feld[x][y] = Ur[x][y] = element;
586 /* determine border element for this level */
590 void LoadLevel(int level_nr)
592 char *filename = getLevelFilename(level_nr);
594 LoadLevelFromFilename(filename);
595 InitElementPropertiesEngine(level.game_version);
598 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
600 putFileVersion(file, level->file_version);
601 putFileVersion(file, level->game_version);
604 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
608 fputc(level->fieldx, file);
609 fputc(level->fieldy, file);
611 putFile16BitBE(file, level->time);
612 putFile16BitBE(file, level->gems_needed);
614 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
615 fputc(level->name[i], file);
617 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
618 fputc(level->score[i], file);
620 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
623 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
624 level->yam_content[i][x][y]),
626 fputc(level->amoeba_speed, file);
627 fputc(level->time_magic_wall, file);
628 fputc(level->time_wheel, file);
629 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
631 fputc((level->double_speed ? 1 : 0), file);
632 fputc((level->gravity ? 1 : 0), file);
633 fputc((level->encoding_16bit_field ? 1 : 0), file);
634 fputc((level->em_slippery_gems ? 1 : 0), file);
636 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
639 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
643 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
644 fputc(level->author[i], file);
647 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
651 for(y=0; y<level->fieldy; y++)
652 for(x=0; x<level->fieldx; x++)
653 if (level->encoding_16bit_field)
654 putFile16BitBE(file, Ur[x][y]);
656 fputc(Ur[x][y], file);
660 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
664 fputc(EL_YAMYAM, file);
665 fputc(level->num_yam_contents, file);
669 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
672 if (level->encoding_16bit_field)
673 putFile16BitBE(file, level->yam_content[i][x][y]);
675 fputc(level->yam_content[i][x][y], file);
679 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
682 int num_contents, content_xsize, content_ysize;
683 int content_array[MAX_ELEMENT_CONTENTS][3][3];
685 if (element == EL_YAMYAM)
687 num_contents = level->num_yam_contents;
691 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
694 content_array[i][x][y] = level->yam_content[i][x][y];
696 else if (element == EL_BD_AMOEBA)
702 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
705 content_array[i][x][y] = EL_EMPTY;
706 content_array[0][0][0] = level->amoeba_content;
710 /* chunk header already written -- write empty chunk data */
711 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
713 Error(ERR_WARN, "cannot save content for element '%d'", element);
717 putFile16BitBE(file, element);
718 fputc(num_contents, file);
719 fputc(content_xsize, file);
720 fputc(content_ysize, file);
722 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
724 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
727 putFile16BitBE(file, content_array[i][x][y]);
730 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
731 int num_changed_custom_elements)
735 putFile16BitBE(file, num_changed_custom_elements);
737 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
739 int element = EL_CUSTOM_START + i;
741 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
743 if (check < num_changed_custom_elements)
745 putFile16BitBE(file, element);
746 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
753 if (check != num_changed_custom_elements) /* should not happen */
754 Error(ERR_WARN, "inconsistent number of custom element properties");
757 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
758 int num_changed_custom_elements)
762 putFile16BitBE(file, num_changed_custom_elements);
764 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
766 int element = EL_CUSTOM_START + i;
768 if (level->custom_element[i].change.successor != EL_EMPTY_SPACE)
770 if (check < num_changed_custom_elements)
772 putFile16BitBE(file, element);
773 putFile16BitBE(file, level->custom_element[i].change.successor);
780 if (check != num_changed_custom_elements) /* should not happen */
781 Error(ERR_WARN, "inconsistent number of custom element successors");
784 void SaveLevel(int level_nr)
786 char *filename = getLevelFilename(level_nr);
788 int num_changed_custom_elements1 = 0;
789 int num_changed_custom_elements2 = 0;
793 if (!(file = fopen(filename, MODE_WRITE)))
795 Error(ERR_WARN, "cannot save level file '%s'", filename);
799 level.file_version = FILE_VERSION_ACTUAL;
800 level.game_version = GAME_VERSION_ACTUAL;
802 /* check level field for 16-bit elements */
803 level.encoding_16bit_field = FALSE;
804 for(y=0; y<level.fieldy; y++)
805 for(x=0; x<level.fieldx; x++)
807 level.encoding_16bit_field = TRUE;
809 /* check yamyam content for 16-bit elements */
810 level.encoding_16bit_yamyam = FALSE;
811 for(i=0; i<level.num_yam_contents; i++)
814 if (level.yam_content[i][x][y] > 255)
815 level.encoding_16bit_yamyam = TRUE;
817 /* check amoeba content for 16-bit elements */
818 level.encoding_16bit_amoeba = FALSE;
819 if (level.amoeba_content > 255)
820 level.encoding_16bit_amoeba = TRUE;
822 /* calculate size of "BODY" chunk */
824 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
826 /* check for non-standard custom elements and calculate "CUS1" chunk size */
827 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
828 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
829 num_changed_custom_elements1++;
831 /* check for non-standard custom elements and calculate "CUS2" chunk size */
832 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
833 if (level.custom_element[i].change.successor != EL_EMPTY_SPACE)
834 num_changed_custom_elements2++;
836 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
837 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
839 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
840 SaveLevel_VERS(file, &level);
842 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
843 SaveLevel_HEAD(file, &level);
845 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
846 SaveLevel_AUTH(file, &level);
848 putFileChunkBE(file, "BODY", body_chunk_size);
849 SaveLevel_BODY(file, &level);
851 if (level.encoding_16bit_yamyam ||
852 level.num_yam_contents != STD_ELEMENT_CONTENTS)
854 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
855 SaveLevel_CNT2(file, &level, EL_YAMYAM);
858 if (level.encoding_16bit_amoeba)
860 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
861 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
864 if (num_changed_custom_elements1 > 0)
866 putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements1 * 6);
867 SaveLevel_CUS1(file, &level, num_changed_custom_elements1);
870 if (num_changed_custom_elements2 > 0)
872 putFileChunkBE(file, "CUS2", 2 + num_changed_custom_elements2 * 4);
873 SaveLevel_CUS2(file, &level, num_changed_custom_elements2);
878 SetFilePermissions(filename, PERMS_PRIVATE);
881 void DumpLevel(struct LevelInfo *level)
883 printf_line("-", 79);
884 printf("Level xxx (file version %06d, game version %06d)\n",
885 level->file_version, level->game_version);
886 printf_line("-", 79);
888 printf("Level Author: '%s'\n", level->author);
889 printf("Level Title: '%s'\n", level->name);
891 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
893 printf("Level Time: %d seconds\n", level->time);
894 printf("Gems needed: %d\n", level->gems_needed);
896 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
897 printf("Time for Wheel: %d seconds\n", level->time_wheel);
898 printf("Time for Light: %d seconds\n", level->time_light);
899 printf("Time for Timegate: %d seconds\n", level->time_timegate);
901 printf("Amoeba Speed: %d\n", level->amoeba_speed);
903 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
904 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
905 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
907 printf_line("-", 79);
911 /* ========================================================================= */
912 /* tape file functions */
913 /* ========================================================================= */
915 static void setTapeInfoToDefaults()
919 /* always start with reliable default values (empty tape) */
922 /* default values (also for pre-1.2 tapes) with only the first player */
923 tape.player_participates[0] = TRUE;
924 for(i=1; i<MAX_PLAYERS; i++)
925 tape.player_participates[i] = FALSE;
927 /* at least one (default: the first) player participates in every tape */
928 tape.num_participating_players = 1;
930 tape.level_nr = level_nr;
932 tape.changed = FALSE;
934 tape.recording = FALSE;
935 tape.playing = FALSE;
936 tape.pausing = FALSE;
939 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
941 tape->file_version = getFileVersion(file);
942 tape->game_version = getFileVersion(file);
947 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
951 tape->random_seed = getFile32BitBE(file);
952 tape->date = getFile32BitBE(file);
953 tape->length = getFile32BitBE(file);
955 /* read header fields that are new since version 1.2 */
956 if (tape->file_version >= FILE_VERSION_1_2)
958 byte store_participating_players = fgetc(file);
961 /* since version 1.2, tapes store which players participate in the tape */
962 tape->num_participating_players = 0;
963 for(i=0; i<MAX_PLAYERS; i++)
965 tape->player_participates[i] = FALSE;
967 if (store_participating_players & (1 << i))
969 tape->player_participates[i] = TRUE;
970 tape->num_participating_players++;
974 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
976 engine_version = getFileVersion(file);
977 if (engine_version > 0)
978 tape->engine_version = engine_version;
984 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
986 int level_identifier_size;
989 level_identifier_size = getFile16BitBE(file);
991 tape->level_identifier =
992 checked_realloc(tape->level_identifier, level_identifier_size);
994 for(i=0; i < level_identifier_size; i++)
995 tape->level_identifier[i] = fgetc(file);
997 tape->level_nr = getFile16BitBE(file);
999 chunk_size = 2 + level_identifier_size + 2;
1004 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1007 int chunk_size_expected =
1008 (tape->num_participating_players + 1) * tape->length;
1010 if (chunk_size_expected != chunk_size)
1012 ReadUnusedBytesFromFile(file, chunk_size);
1013 return chunk_size_expected;
1016 for(i=0; i<tape->length; i++)
1018 if (i >= MAX_TAPELEN)
1021 for(j=0; j<MAX_PLAYERS; j++)
1023 tape->pos[i].action[j] = MV_NO_MOVING;
1025 if (tape->player_participates[j])
1026 tape->pos[i].action[j] = fgetc(file);
1029 tape->pos[i].delay = fgetc(file);
1031 if (tape->file_version == FILE_VERSION_1_0)
1033 /* eliminate possible diagonal moves in old tapes */
1034 /* this is only for backward compatibility */
1036 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1037 byte action = tape->pos[i].action[0];
1038 int k, num_moves = 0;
1042 if (action & joy_dir[k])
1044 tape->pos[i + num_moves].action[0] = joy_dir[k];
1046 tape->pos[i + num_moves].delay = 0;
1055 tape->length += num_moves;
1058 else if (tape->file_version < FILE_VERSION_2_0)
1060 /* convert pre-2.0 tapes to new tape format */
1062 if (tape->pos[i].delay > 1)
1065 tape->pos[i + 1] = tape->pos[i];
1066 tape->pos[i + 1].delay = 1;
1069 for(j=0; j<MAX_PLAYERS; j++)
1070 tape->pos[i].action[j] = MV_NO_MOVING;
1071 tape->pos[i].delay--;
1082 if (i != tape->length)
1083 chunk_size = (tape->num_participating_players + 1) * i;
1088 void LoadTapeFromFilename(char *filename)
1090 char cookie[MAX_LINE_LEN];
1091 char chunk_name[CHUNK_ID_LEN + 1];
1095 /* always start with reliable default values */
1096 setTapeInfoToDefaults();
1098 if (!(file = fopen(filename, MODE_READ)))
1101 getFileChunkBE(file, chunk_name, NULL);
1102 if (strcmp(chunk_name, "RND1") == 0)
1104 getFile32BitBE(file); /* not used */
1106 getFileChunkBE(file, chunk_name, NULL);
1107 if (strcmp(chunk_name, "TAPE") != 0)
1109 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1114 else /* check for pre-2.0 file format with cookie string */
1116 strcpy(cookie, chunk_name);
1117 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1118 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1119 cookie[strlen(cookie) - 1] = '\0';
1121 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1123 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1128 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1130 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1135 /* pre-2.0 tape files have no game version, so use file version here */
1136 tape.game_version = tape.file_version;
1139 if (tape.file_version < FILE_VERSION_1_2)
1141 /* tape files from versions before 1.2.0 without chunk structure */
1142 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1143 LoadTape_BODY(file, 2 * tape.length, &tape);
1151 int (*loader)(FILE *, int, struct TapeInfo *);
1155 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1156 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1157 { "INFO", -1, LoadTape_INFO },
1158 { "BODY", -1, LoadTape_BODY },
1162 while (getFileChunkBE(file, chunk_name, &chunk_size))
1166 while (chunk_info[i].name != NULL &&
1167 strcmp(chunk_name, chunk_info[i].name) != 0)
1170 if (chunk_info[i].name == NULL)
1172 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1173 chunk_name, filename);
1174 ReadUnusedBytesFromFile(file, chunk_size);
1176 else if (chunk_info[i].size != -1 &&
1177 chunk_info[i].size != chunk_size)
1179 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1180 chunk_size, chunk_name, filename);
1181 ReadUnusedBytesFromFile(file, chunk_size);
1185 /* call function to load this tape chunk */
1186 int chunk_size_expected =
1187 (chunk_info[i].loader)(file, chunk_size, &tape);
1189 /* the size of some chunks cannot be checked before reading other
1190 chunks first (like "HEAD" and "BODY") that contain some header
1191 information, so check them here */
1192 if (chunk_size_expected != chunk_size)
1194 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1195 chunk_size, chunk_name, filename);
1203 tape.length_seconds = GetTapeLength();
1206 void LoadTape(int level_nr)
1208 char *filename = getTapeFilename(level_nr);
1210 LoadTapeFromFilename(filename);
1213 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1215 putFileVersion(file, tape->file_version);
1216 putFileVersion(file, tape->game_version);
1219 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1222 byte store_participating_players = 0;
1224 /* set bits for participating players for compact storage */
1225 for(i=0; i<MAX_PLAYERS; i++)
1226 if (tape->player_participates[i])
1227 store_participating_players |= (1 << i);
1229 putFile32BitBE(file, tape->random_seed);
1230 putFile32BitBE(file, tape->date);
1231 putFile32BitBE(file, tape->length);
1233 fputc(store_participating_players, file);
1235 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1236 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1238 putFileVersion(file, tape->engine_version);
1241 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1243 int level_identifier_size = strlen(tape->level_identifier) + 1;
1246 putFile16BitBE(file, level_identifier_size);
1248 for(i=0; i < level_identifier_size; i++)
1249 fputc(tape->level_identifier[i], file);
1251 putFile16BitBE(file, tape->level_nr);
1254 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1258 for(i=0; i<tape->length; i++)
1260 for(j=0; j<MAX_PLAYERS; j++)
1261 if (tape->player_participates[j])
1262 fputc(tape->pos[i].action[j], file);
1264 fputc(tape->pos[i].delay, file);
1268 void SaveTape(int level_nr)
1270 char *filename = getTapeFilename(level_nr);
1272 boolean new_tape = TRUE;
1273 int num_participating_players = 0;
1274 int info_chunk_size;
1275 int body_chunk_size;
1278 InitTapeDirectory(leveldir_current->filename);
1280 /* if a tape still exists, ask to overwrite it */
1281 if (access(filename, F_OK) == 0)
1284 if (!Request("Replace old tape ?", REQ_ASK))
1288 if (!(file = fopen(filename, MODE_WRITE)))
1290 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1294 tape.file_version = FILE_VERSION_ACTUAL;
1295 tape.game_version = GAME_VERSION_ACTUAL;
1297 /* count number of participating players */
1298 for(i=0; i<MAX_PLAYERS; i++)
1299 if (tape.player_participates[i])
1300 num_participating_players++;
1302 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1303 body_chunk_size = (num_participating_players + 1) * tape.length;
1305 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1306 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1308 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1309 SaveTape_VERS(file, &tape);
1311 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1312 SaveTape_HEAD(file, &tape);
1314 putFileChunkBE(file, "INFO", info_chunk_size);
1315 SaveTape_INFO(file, &tape);
1317 putFileChunkBE(file, "BODY", body_chunk_size);
1318 SaveTape_BODY(file, &tape);
1322 SetFilePermissions(filename, PERMS_PRIVATE);
1324 tape.changed = FALSE;
1327 Request("tape saved !", REQ_CONFIRM);
1330 void DumpTape(struct TapeInfo *tape)
1334 if (TAPE_IS_EMPTY(*tape))
1336 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1340 printf_line("-", 79);
1341 printf("Tape of Level %03d (file version %06d, game version %06d)\n",
1342 tape->level_nr, tape->file_version, tape->game_version);
1343 printf("Level series identifier: '%s'\n", tape->level_identifier);
1344 printf_line("-", 79);
1346 for(i=0; i<tape->length; i++)
1348 if (i >= MAX_TAPELEN)
1351 printf("%03d: ", i);
1353 for(j=0; j<MAX_PLAYERS; j++)
1355 if (tape->player_participates[j])
1357 int action = tape->pos[i].action[j];
1359 printf("%d:%02x ", j, action);
1360 printf("[%c%c%c%c|%c%c] - ",
1361 (action & JOY_LEFT ? '<' : ' '),
1362 (action & JOY_RIGHT ? '>' : ' '),
1363 (action & JOY_UP ? '^' : ' '),
1364 (action & JOY_DOWN ? 'v' : ' '),
1365 (action & JOY_BUTTON_1 ? '1' : ' '),
1366 (action & JOY_BUTTON_2 ? '2' : ' '));
1370 printf("(%03d)\n", tape->pos[i].delay);
1373 printf_line("-", 79);
1377 /* ========================================================================= */
1378 /* score file functions */
1379 /* ========================================================================= */
1381 void LoadScore(int level_nr)
1384 char *filename = getScoreFilename(level_nr);
1385 char cookie[MAX_LINE_LEN];
1386 char line[MAX_LINE_LEN];
1390 /* always start with reliable default values */
1391 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1393 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1394 highscore[i].Score = 0;
1397 if (!(file = fopen(filename, MODE_READ)))
1400 /* check file identifier */
1401 fgets(cookie, MAX_LINE_LEN, file);
1402 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1403 cookie[strlen(cookie) - 1] = '\0';
1405 if (!checkCookieString(cookie, SCORE_COOKIE))
1407 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1412 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1414 fscanf(file, "%d", &highscore[i].Score);
1415 fgets(line, MAX_LINE_LEN, file);
1417 if (line[strlen(line) - 1] == '\n')
1418 line[strlen(line) - 1] = '\0';
1420 for (line_ptr = line; *line_ptr; line_ptr++)
1422 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1424 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1425 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1434 void SaveScore(int level_nr)
1437 char *filename = getScoreFilename(level_nr);
1440 InitScoreDirectory(leveldir_current->filename);
1442 if (!(file = fopen(filename, MODE_WRITE)))
1444 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1448 fprintf(file, "%s\n\n", SCORE_COOKIE);
1450 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1451 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1455 SetFilePermissions(filename, PERMS_PUBLIC);
1459 /* ========================================================================= */
1460 /* setup file functions */
1461 /* ========================================================================= */
1463 #define TOKEN_STR_PLAYER_PREFIX "player_"
1466 #define SETUP_TOKEN_PLAYER_NAME 0
1467 #define SETUP_TOKEN_SOUND 1
1468 #define SETUP_TOKEN_SOUND_LOOPS 2
1469 #define SETUP_TOKEN_SOUND_MUSIC 3
1470 #define SETUP_TOKEN_SOUND_SIMPLE 4
1471 #define SETUP_TOKEN_TOONS 5
1472 #define SETUP_TOKEN_SCROLL_DELAY 6
1473 #define SETUP_TOKEN_SOFT_SCROLLING 7
1474 #define SETUP_TOKEN_FADING 8
1475 #define SETUP_TOKEN_AUTORECORD 9
1476 #define SETUP_TOKEN_QUICK_DOORS 10
1477 #define SETUP_TOKEN_TEAM_MODE 11
1478 #define SETUP_TOKEN_HANDICAP 12
1479 #define SETUP_TOKEN_TIME_LIMIT 13
1480 #define SETUP_TOKEN_FULLSCREEN 14
1481 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1482 #define SETUP_TOKEN_GRAPHICS_SET 16
1483 #define SETUP_TOKEN_SOUNDS_SET 17
1484 #define SETUP_TOKEN_MUSIC_SET 18
1485 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1486 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1487 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1489 #define NUM_GLOBAL_SETUP_TOKENS 22
1492 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1493 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1494 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1495 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1496 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1497 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1498 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1499 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1500 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1502 #define NUM_EDITOR_SETUP_TOKENS 9
1504 /* shortcut setup */
1505 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1506 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1507 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1509 #define NUM_SHORTCUT_SETUP_TOKENS 3
1512 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1513 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1514 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1515 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1516 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1517 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1518 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1519 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1520 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1521 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1522 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1523 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1524 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1525 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1526 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1527 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1529 #define NUM_PLAYER_SETUP_TOKENS 16
1532 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1533 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1535 #define NUM_SYSTEM_SETUP_TOKENS 2
1538 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1540 #define NUM_OPTIONS_SETUP_TOKENS 1
1543 static struct SetupInfo si;
1544 static struct SetupEditorInfo sei;
1545 static struct SetupShortcutInfo ssi;
1546 static struct SetupInputInfo sii;
1547 static struct SetupSystemInfo syi;
1548 static struct OptionInfo soi;
1550 static struct TokenInfo global_setup_tokens[] =
1552 { TYPE_STRING, &si.player_name, "player_name" },
1553 { TYPE_SWITCH, &si.sound, "sound" },
1554 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1555 { TYPE_SWITCH, &si.sound_music, "background_music" },
1556 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1557 { TYPE_SWITCH, &si.toons, "toons" },
1558 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1559 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1560 { TYPE_SWITCH, &si.fading, "screen_fading" },
1561 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1562 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1563 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1564 { TYPE_SWITCH, &si.handicap, "handicap" },
1565 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1566 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1567 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1568 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1569 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1570 { TYPE_STRING, &si.music_set, "music_set" },
1571 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1572 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1573 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1576 static struct TokenInfo editor_setup_tokens[] =
1578 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1579 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1580 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1581 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1582 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1583 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1584 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1585 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1586 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1589 static struct TokenInfo shortcut_setup_tokens[] =
1591 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1592 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1593 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1596 static struct TokenInfo player_setup_tokens[] =
1598 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1599 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1600 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1601 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1602 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1603 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1604 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1605 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1606 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1607 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1608 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1609 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1610 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1611 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1612 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1613 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1616 static struct TokenInfo system_setup_tokens[] =
1618 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1619 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1622 static struct TokenInfo options_setup_tokens[] =
1624 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1627 static char *get_corrected_login_name(char *login_name)
1629 /* needed because player name must be a fixed length string */
1630 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1632 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1633 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1635 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1636 if (strchr(login_name_new, ' '))
1637 *strchr(login_name_new, ' ') = '\0';
1639 return login_name_new;
1642 static void setSetupInfoToDefaults(struct SetupInfo *si)
1646 si->player_name = get_corrected_login_name(getLoginName());
1649 si->sound_loops = TRUE;
1650 si->sound_music = TRUE;
1651 si->sound_simple = TRUE;
1653 si->double_buffering = TRUE;
1654 si->direct_draw = !si->double_buffering;
1655 si->scroll_delay = TRUE;
1656 si->soft_scrolling = TRUE;
1658 si->autorecord = TRUE;
1659 si->quick_doors = FALSE;
1660 si->team_mode = FALSE;
1661 si->handicap = TRUE;
1662 si->time_limit = TRUE;
1663 si->fullscreen = FALSE;
1664 si->ask_on_escape = TRUE;
1666 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1667 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1668 si->music_set = getStringCopy(MUSIC_SUBDIR);
1669 si->override_level_graphics = FALSE;
1670 si->override_level_sounds = FALSE;
1671 si->override_level_music = FALSE;
1673 si->editor.el_boulderdash = TRUE;
1674 si->editor.el_emerald_mine = TRUE;
1675 si->editor.el_more = TRUE;
1676 si->editor.el_sokoban = TRUE;
1677 si->editor.el_supaplex = TRUE;
1678 si->editor.el_diamond_caves = TRUE;
1679 si->editor.el_dx_boulderdash = TRUE;
1680 si->editor.el_chars = TRUE;
1681 si->editor.el_custom = TRUE;
1683 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1684 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1685 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1687 for (i=0; i<MAX_PLAYERS; i++)
1689 si->input[i].use_joystick = FALSE;
1690 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1691 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1692 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1693 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1694 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1695 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1696 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1697 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1698 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1699 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1700 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1701 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1702 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1703 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1704 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1707 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1708 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1710 si->options.verbose = FALSE;
1713 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
1717 if (!setup_file_hash)
1722 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1723 setSetupInfo(global_setup_tokens, i,
1724 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
1729 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1730 setSetupInfo(editor_setup_tokens, i,
1731 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
1734 /* shortcut setup */
1735 ssi = setup.shortcut;
1736 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1737 setSetupInfo(shortcut_setup_tokens, i,
1738 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
1739 setup.shortcut = ssi;
1742 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1746 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1748 sii = setup.input[pnr];
1749 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1751 char full_token[100];
1753 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1754 setSetupInfo(player_setup_tokens, i,
1755 getHashEntry(setup_file_hash, full_token));
1757 setup.input[pnr] = sii;
1762 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1763 setSetupInfo(system_setup_tokens, i,
1764 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
1768 soi = setup.options;
1769 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1770 setSetupInfo(options_setup_tokens, i,
1771 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
1772 setup.options = soi;
1777 char *filename = getSetupFilename();
1778 SetupFileHash *setup_file_hash = NULL;
1780 /* always start with reliable default values */
1781 setSetupInfoToDefaults(&setup);
1783 setup_file_hash = loadSetupFileHash(filename);
1785 if (setup_file_hash)
1787 char *player_name_new;
1789 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
1790 decodeSetupFileHash(setup_file_hash);
1792 setup.direct_draw = !setup.double_buffering;
1794 freeSetupFileHash(setup_file_hash);
1796 /* needed to work around problems with fixed length strings */
1797 player_name_new = get_corrected_login_name(setup.player_name);
1798 free(setup.player_name);
1799 setup.player_name = player_name_new;
1802 Error(ERR_WARN, "using default setup values");
1807 char *filename = getSetupFilename();
1811 InitUserDataDirectory();
1813 if (!(file = fopen(filename, MODE_WRITE)))
1815 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1819 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1820 getCookie("SETUP")));
1821 fprintf(file, "\n");
1825 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1827 /* just to make things nicer :) */
1828 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1829 i == SETUP_TOKEN_GRAPHICS_SET)
1830 fprintf(file, "\n");
1832 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1837 fprintf(file, "\n");
1838 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1839 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1841 /* shortcut setup */
1842 ssi = setup.shortcut;
1843 fprintf(file, "\n");
1844 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1845 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1848 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1852 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1853 fprintf(file, "\n");
1855 sii = setup.input[pnr];
1856 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1857 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1862 fprintf(file, "\n");
1863 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1864 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1867 soi = setup.options;
1868 fprintf(file, "\n");
1869 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1870 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1874 SetFilePermissions(filename, PERMS_PRIVATE);
1877 void LoadCustomElementDescriptions()
1879 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1880 SetupFileHash *setup_file_hash;
1883 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1885 if (element_info[i].custom_description != NULL)
1887 free(element_info[i].custom_description);
1888 element_info[i].custom_description = NULL;
1892 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1895 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1897 char *token = getStringCat2(element_info[i].token_name, ".name");
1898 char *value = getHashEntry(setup_file_hash, token);
1901 element_info[i].custom_description = getStringCopy(value);
1906 freeSetupFileHash(setup_file_hash);
1909 void LoadSpecialMenuDesignSettings()
1911 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1912 SetupFileHash *setup_file_hash;
1915 /* always start with reliable default values from default config */
1916 for (i=0; image_config_vars[i].token != NULL; i++)
1917 for (j=0; image_config[j].token != NULL; j++)
1918 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
1919 *image_config_vars[i].value =
1920 get_integer_from_string(image_config[j].value);
1922 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1925 /* special case: initialize with default values that may be overwrittem */
1926 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
1928 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
1929 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
1931 if (value_x != NULL)
1932 menu.draw_xoffset[i] = get_integer_from_string(value_x);
1933 if (value_y != NULL)
1934 menu.draw_yoffset[i] = get_integer_from_string(value_y);
1937 /* read (and overwrite with) values that may be specified in config file */
1938 for (i=0; image_config_vars[i].token != NULL; i++)
1940 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
1943 *image_config_vars[i].value = get_integer_from_string(value);
1946 freeSetupFileHash(setup_file_hash);