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_successor[i] = EL_EMPTY_SPACE;
101 Properties[EL_CUSTOM_START + i][EP_BITFIELD_BASE] = EP_BITMASK_DEFAULT;
104 BorderElement = EL_STEELWALL;
106 level.no_level_file = FALSE;
108 if (leveldir_current == NULL) /* only when dumping level */
111 /* try to determine better author name than 'anonymous' */
112 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
114 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
115 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
119 switch (LEVELCLASS(leveldir_current))
121 case LEVELCLASS_TUTORIAL:
122 strcpy(level.author, PROGRAM_AUTHOR_STRING);
125 case LEVELCLASS_CONTRIBUTION:
126 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
127 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
130 case LEVELCLASS_USER:
131 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
132 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
136 /* keep default value */
142 static int checkLevelElement(int element)
144 if (element >= NUM_FILE_ELEMENTS)
146 Error(ERR_WARN, "invalid level element %d", element);
147 element = EL_CHAR_QUESTION;
149 else if (element == EL_PLAYER_OBSOLETE)
150 element = EL_PLAYER_1;
151 else if (element == EL_KEY_OBSOLETE)
157 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
159 level->file_version = getFileVersion(file);
160 level->game_version = getFileVersion(file);
165 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
169 lev_fieldx = level->fieldx = fgetc(file);
170 lev_fieldy = level->fieldy = fgetc(file);
172 level->time = getFile16BitBE(file);
173 level->gems_needed = getFile16BitBE(file);
175 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
176 level->name[i] = fgetc(file);
177 level->name[MAX_LEVEL_NAME_LEN] = 0;
179 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
180 level->score[i] = fgetc(file);
182 level->num_yam_contents = STD_ELEMENT_CONTENTS;
183 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
186 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
188 level->amoeba_speed = fgetc(file);
189 level->time_magic_wall = fgetc(file);
190 level->time_wheel = fgetc(file);
191 level->amoeba_content = checkLevelElement(fgetc(file));
192 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
193 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
194 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
195 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
197 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
202 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
206 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
207 level->author[i] = fgetc(file);
208 level->author[MAX_LEVEL_NAME_LEN] = 0;
213 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
216 int chunk_size_expected = level->fieldx * level->fieldy;
218 /* Note: "chunk_size" was wrong before version 2.0 when elements are
219 stored with 16-bit encoding (and should be twice as big then).
220 Even worse, playfield data was stored 16-bit when only yamyam content
221 contained 16-bit elements and vice versa. */
223 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
224 chunk_size_expected *= 2;
226 if (chunk_size_expected != chunk_size)
228 ReadUnusedBytesFromFile(file, chunk_size);
229 return chunk_size_expected;
232 for(y=0; y<level->fieldy; y++)
233 for(x=0; x<level->fieldx; x++)
234 Feld[x][y] = Ur[x][y] =
235 checkLevelElement(level->encoding_16bit_field ?
236 getFile16BitBE(file) : fgetc(file));
240 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
244 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
245 int chunk_size_expected = header_size + content_size;
247 /* Note: "chunk_size" was wrong before version 2.0 when elements are
248 stored with 16-bit encoding (and should be twice as big then).
249 Even worse, playfield data was stored 16-bit when only yamyam content
250 contained 16-bit elements and vice versa. */
252 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
253 chunk_size_expected += content_size;
255 if (chunk_size_expected != chunk_size)
257 ReadUnusedBytesFromFile(file, chunk_size);
258 return chunk_size_expected;
262 level->num_yam_contents = fgetc(file);
266 /* correct invalid number of content fields -- should never happen */
267 if (level->num_yam_contents < 1 ||
268 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
269 level->num_yam_contents = STD_ELEMENT_CONTENTS;
271 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
274 level->yam_content[i][x][y] =
275 checkLevelElement(level->encoding_16bit_field ?
276 getFile16BitBE(file) : fgetc(file));
280 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
284 int num_contents, content_xsize, content_ysize;
285 int content_array[MAX_ELEMENT_CONTENTS][3][3];
287 element = checkLevelElement(getFile16BitBE(file));
288 num_contents = fgetc(file);
289 content_xsize = fgetc(file);
290 content_ysize = fgetc(file);
291 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
293 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
296 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
298 /* correct invalid number of content fields -- should never happen */
299 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
300 num_contents = STD_ELEMENT_CONTENTS;
302 if (element == EL_YAMYAM)
304 level->num_yam_contents = num_contents;
306 for(i=0; i<num_contents; i++)
309 level->yam_content[i][x][y] = content_array[i][x][y];
311 else if (element == EL_BD_AMOEBA)
313 level->amoeba_content = content_array[0][0][0];
317 Error(ERR_WARN, "cannot load content for element '%d'", element);
323 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
325 int num_changed_custom_elements = getFile16BitBE(file);
326 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
329 if (chunk_size_expected != chunk_size)
331 ReadUnusedBytesFromFile(file, chunk_size - 2);
332 return chunk_size_expected;
335 for (i=0; i < num_changed_custom_elements; i++)
337 int element = getFile16BitBE(file);
338 int properties = getFile32BitBE(file);
340 if (IS_CUSTOM_ELEMENT(element))
341 Properties[element][EP_BITFIELD_BASE] = properties;
343 Error(ERR_WARN, "invalid custom element number %d", element);
349 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
351 int num_changed_custom_elements = getFile16BitBE(file);
352 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
355 if (chunk_size_expected != chunk_size)
357 ReadUnusedBytesFromFile(file, chunk_size - 2);
358 return chunk_size_expected;
361 for (i=0; i < num_changed_custom_elements; i++)
363 int element = getFile16BitBE(file);
364 int custom_element_successor = getFile16BitBE(file);
365 int i = element - EL_CUSTOM_START;
367 if (IS_CUSTOM_ELEMENT(element))
368 level->custom_element_successor[i] = custom_element_successor;
370 Error(ERR_WARN, "invalid custom element number %d", element);
376 void LoadLevelFromFilename(char *filename)
378 char cookie[MAX_LINE_LEN];
379 char chunk_name[CHUNK_ID_LEN + 1];
383 /* always start with reliable default values */
384 setLevelInfoToDefaults();
386 if (!(file = fopen(filename, MODE_READ)))
388 level.no_level_file = TRUE;
390 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
394 getFileChunkBE(file, chunk_name, NULL);
395 if (strcmp(chunk_name, "RND1") == 0)
397 getFile32BitBE(file); /* not used */
399 getFileChunkBE(file, chunk_name, NULL);
400 if (strcmp(chunk_name, "CAVE") != 0)
402 Error(ERR_WARN, "unknown format of level file '%s'", filename);
407 else /* check for pre-2.0 file format with cookie string */
409 strcpy(cookie, chunk_name);
410 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
411 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
412 cookie[strlen(cookie) - 1] = '\0';
414 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
416 Error(ERR_WARN, "unknown format of level file '%s'", filename);
421 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
423 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
428 /* pre-2.0 level files have no game version, so use file version here */
429 level.game_version = level.file_version;
432 if (level.file_version < FILE_VERSION_1_2)
434 /* level files from versions before 1.2.0 without chunk structure */
435 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
436 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
444 int (*loader)(FILE *, int, struct LevelInfo *);
448 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
449 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
450 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
451 { "BODY", -1, LoadLevel_BODY },
452 { "CONT", -1, LoadLevel_CONT },
453 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
454 { "CUS1", -1, LoadLevel_CUS1 },
455 { "CUS2", -1, LoadLevel_CUS2 },
459 while (getFileChunkBE(file, chunk_name, &chunk_size))
463 while (chunk_info[i].name != NULL &&
464 strcmp(chunk_name, chunk_info[i].name) != 0)
467 if (chunk_info[i].name == NULL)
469 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
470 chunk_name, filename);
471 ReadUnusedBytesFromFile(file, chunk_size);
473 else if (chunk_info[i].size != -1 &&
474 chunk_info[i].size != chunk_size)
476 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
477 chunk_size, chunk_name, filename);
478 ReadUnusedBytesFromFile(file, chunk_size);
482 /* call function to load this level chunk */
483 int chunk_size_expected =
484 (chunk_info[i].loader)(file, chunk_size, &level);
486 /* the size of some chunks cannot be checked before reading other
487 chunks first (like "HEAD" and "BODY") that contain some header
488 information, so check them here */
489 if (chunk_size_expected != chunk_size)
491 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
492 chunk_size, chunk_name, filename);
500 if (leveldir_current == NULL) /* only when dumping level */
503 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
504 IS_LEVELCLASS_USER(leveldir_current))
506 /* For user contributed and private levels, use the version of
507 the game engine the levels were created for.
508 Since 2.0.1, the game engine version is now directly stored
509 in the level file (chunk "VERS"), so there is no need anymore
510 to set the game version from the file version (except for old,
511 pre-2.0 levels, where the game version is still taken from the
512 file format version used to store the level -- see above). */
514 /* do some special adjustments to support older level versions */
515 if (level.file_version == FILE_VERSION_1_0)
517 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
518 Error(ERR_WARN, "using high speed movement for player");
520 /* player was faster than monsters in (pre-)1.0 levels */
521 level.double_speed = TRUE;
524 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
525 if (level.game_version == VERSION_IDENT(2,0,1))
526 level.em_slippery_gems = TRUE;
530 /* Always use the latest version of the game engine for all but
531 user contributed and private levels; this allows for actual
532 corrections in the game engine to take effect for existing,
533 converted levels (from "classic" or other existing games) to
534 make the game emulation more accurate, while (hopefully) not
535 breaking existing levels created from other players. */
537 level.game_version = GAME_VERSION_ACTUAL;
539 /* Set special EM style gems behaviour: EM style gems slip down from
540 normal, steel and growing wall. As this is a more fundamental change,
541 it seems better to set the default behaviour to "off" (as it is more
542 natural) and make it configurable in the level editor (as a property
543 of gem style elements). Already existing converted levels (neither
544 private nor contributed levels) are changed to the new behaviour. */
546 if (level.file_version < FILE_VERSION_2_0)
547 level.em_slippery_gems = TRUE;
550 /* map some elements which have changed in newer versions */
551 if (level.game_version <= VERSION_IDENT(2,2,0))
555 /* map game font elements */
556 for(y=0; y<level.fieldy; y++)
558 for(x=0; x<level.fieldx; x++)
560 int element = Ur[x][y];
562 if (element == EL_CHAR('['))
563 element = EL_CHAR_AUMLAUT;
564 else if (element == EL_CHAR('\\'))
565 element = EL_CHAR_OUMLAUT;
566 else if (element == EL_CHAR(']'))
567 element = EL_CHAR_UUMLAUT;
568 else if (element == EL_CHAR('^'))
569 element = EL_CHAR_COPYRIGHT;
571 Feld[x][y] = Ur[x][y] = element;
576 /* determine border element for this level */
580 void LoadLevel(int level_nr)
582 char *filename = getLevelFilename(level_nr);
584 LoadLevelFromFilename(filename);
585 InitElementPropertiesEngine(level.game_version);
588 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
590 putFileVersion(file, level->file_version);
591 putFileVersion(file, level->game_version);
594 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
598 fputc(level->fieldx, file);
599 fputc(level->fieldy, file);
601 putFile16BitBE(file, level->time);
602 putFile16BitBE(file, level->gems_needed);
604 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
605 fputc(level->name[i], file);
607 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
608 fputc(level->score[i], file);
610 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
613 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
614 level->yam_content[i][x][y]),
616 fputc(level->amoeba_speed, file);
617 fputc(level->time_magic_wall, file);
618 fputc(level->time_wheel, file);
619 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
621 fputc((level->double_speed ? 1 : 0), file);
622 fputc((level->gravity ? 1 : 0), file);
623 fputc((level->encoding_16bit_field ? 1 : 0), file);
624 fputc((level->em_slippery_gems ? 1 : 0), file);
626 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
629 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
633 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
634 fputc(level->author[i], file);
637 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
641 for(y=0; y<level->fieldy; y++)
642 for(x=0; x<level->fieldx; x++)
643 if (level->encoding_16bit_field)
644 putFile16BitBE(file, Ur[x][y]);
646 fputc(Ur[x][y], file);
650 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
654 fputc(EL_YAMYAM, file);
655 fputc(level->num_yam_contents, file);
659 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
662 if (level->encoding_16bit_field)
663 putFile16BitBE(file, level->yam_content[i][x][y]);
665 fputc(level->yam_content[i][x][y], file);
669 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
672 int num_contents, content_xsize, content_ysize;
673 int content_array[MAX_ELEMENT_CONTENTS][3][3];
675 if (element == EL_YAMYAM)
677 num_contents = level->num_yam_contents;
681 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
684 content_array[i][x][y] = level->yam_content[i][x][y];
686 else if (element == EL_BD_AMOEBA)
692 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
695 content_array[i][x][y] = EL_EMPTY;
696 content_array[0][0][0] = level->amoeba_content;
700 /* chunk header already written -- write empty chunk data */
701 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
703 Error(ERR_WARN, "cannot save content for element '%d'", element);
707 putFile16BitBE(file, element);
708 fputc(num_contents, file);
709 fputc(content_xsize, file);
710 fputc(content_ysize, file);
712 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
714 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
717 putFile16BitBE(file, content_array[i][x][y]);
720 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
721 int num_changed_custom_elements)
725 putFile16BitBE(file, num_changed_custom_elements);
727 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
729 int element = EL_CUSTOM_START + i;
731 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
733 if (check < num_changed_custom_elements)
735 putFile16BitBE(file, element);
736 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
743 if (check != num_changed_custom_elements) /* should not happen */
744 Error(ERR_WARN, "inconsistent number of custom element properties");
747 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
748 int num_changed_custom_elements)
752 putFile16BitBE(file, num_changed_custom_elements);
754 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
756 int element = EL_CUSTOM_START + i;
758 if (level->custom_element_successor[i] != EL_EMPTY_SPACE)
760 if (check < num_changed_custom_elements)
762 putFile16BitBE(file, element);
763 putFile16BitBE(file, level->custom_element_successor[i]);
770 if (check != num_changed_custom_elements) /* should not happen */
771 Error(ERR_WARN, "inconsistent number of custom element successors");
774 void SaveLevel(int level_nr)
776 char *filename = getLevelFilename(level_nr);
778 int num_changed_custom_elements1 = 0;
779 int num_changed_custom_elements2 = 0;
783 if (!(file = fopen(filename, MODE_WRITE)))
785 Error(ERR_WARN, "cannot save level file '%s'", filename);
789 level.file_version = FILE_VERSION_ACTUAL;
790 level.game_version = GAME_VERSION_ACTUAL;
792 /* check level field for 16-bit elements */
793 level.encoding_16bit_field = FALSE;
794 for(y=0; y<level.fieldy; y++)
795 for(x=0; x<level.fieldx; x++)
797 level.encoding_16bit_field = TRUE;
799 /* check yamyam content for 16-bit elements */
800 level.encoding_16bit_yamyam = FALSE;
801 for(i=0; i<level.num_yam_contents; i++)
804 if (level.yam_content[i][x][y] > 255)
805 level.encoding_16bit_yamyam = TRUE;
807 /* check amoeba content for 16-bit elements */
808 level.encoding_16bit_amoeba = FALSE;
809 if (level.amoeba_content > 255)
810 level.encoding_16bit_amoeba = TRUE;
812 /* calculate size of "BODY" chunk */
814 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
816 /* check for non-standard custom elements and calculate "CUS1" chunk size */
817 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
818 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
819 num_changed_custom_elements1++;
821 /* check for non-standard custom elements and calculate "CUS2" chunk size */
822 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
823 if (level.custom_element_successor[i] != EL_EMPTY_SPACE)
824 num_changed_custom_elements2++;
826 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
827 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
829 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
830 SaveLevel_VERS(file, &level);
832 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
833 SaveLevel_HEAD(file, &level);
835 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
836 SaveLevel_AUTH(file, &level);
838 putFileChunkBE(file, "BODY", body_chunk_size);
839 SaveLevel_BODY(file, &level);
841 if (level.encoding_16bit_yamyam ||
842 level.num_yam_contents != STD_ELEMENT_CONTENTS)
844 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
845 SaveLevel_CNT2(file, &level, EL_YAMYAM);
848 if (level.encoding_16bit_amoeba)
850 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
851 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
854 if (num_changed_custom_elements1 > 0)
856 putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements1 * 6);
857 SaveLevel_CUS1(file, &level, num_changed_custom_elements1);
860 if (num_changed_custom_elements2 > 0)
862 putFileChunkBE(file, "CUS2", 2 + num_changed_custom_elements2 * 4);
863 SaveLevel_CUS2(file, &level, num_changed_custom_elements2);
868 SetFilePermissions(filename, PERMS_PRIVATE);
871 void DumpLevel(struct LevelInfo *level)
873 printf_line("-", 79);
874 printf("Level xxx (file version %06d, game version %06d)\n",
875 level->file_version, level->game_version);
876 printf_line("-", 79);
878 printf("Level Author: '%s'\n", level->author);
879 printf("Level Title: '%s'\n", level->name);
881 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
883 printf("Level Time: %d seconds\n", level->time);
884 printf("Gems needed: %d\n", level->gems_needed);
886 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
887 printf("Time for Wheel: %d seconds\n", level->time_wheel);
888 printf("Time for Light: %d seconds\n", level->time_light);
889 printf("Time for Timegate: %d seconds\n", level->time_timegate);
891 printf("Amoeba Speed: %d\n", level->amoeba_speed);
893 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
894 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
895 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
897 printf_line("-", 79);
901 /* ========================================================================= */
902 /* tape file functions */
903 /* ========================================================================= */
905 static void setTapeInfoToDefaults()
909 /* always start with reliable default values (empty tape) */
912 /* default values (also for pre-1.2 tapes) with only the first player */
913 tape.player_participates[0] = TRUE;
914 for(i=1; i<MAX_PLAYERS; i++)
915 tape.player_participates[i] = FALSE;
917 /* at least one (default: the first) player participates in every tape */
918 tape.num_participating_players = 1;
920 tape.level_nr = level_nr;
922 tape.changed = FALSE;
924 tape.recording = FALSE;
925 tape.playing = FALSE;
926 tape.pausing = FALSE;
929 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
931 tape->file_version = getFileVersion(file);
932 tape->game_version = getFileVersion(file);
937 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
941 tape->random_seed = getFile32BitBE(file);
942 tape->date = getFile32BitBE(file);
943 tape->length = getFile32BitBE(file);
945 /* read header fields that are new since version 1.2 */
946 if (tape->file_version >= FILE_VERSION_1_2)
948 byte store_participating_players = fgetc(file);
951 /* since version 1.2, tapes store which players participate in the tape */
952 tape->num_participating_players = 0;
953 for(i=0; i<MAX_PLAYERS; i++)
955 tape->player_participates[i] = FALSE;
957 if (store_participating_players & (1 << i))
959 tape->player_participates[i] = TRUE;
960 tape->num_participating_players++;
964 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
966 engine_version = getFileVersion(file);
967 if (engine_version > 0)
968 tape->engine_version = engine_version;
974 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
976 int level_identifier_size;
979 level_identifier_size = getFile16BitBE(file);
981 tape->level_identifier =
982 checked_realloc(tape->level_identifier, level_identifier_size);
984 for(i=0; i < level_identifier_size; i++)
985 tape->level_identifier[i] = fgetc(file);
987 tape->level_nr = getFile16BitBE(file);
989 chunk_size = 2 + level_identifier_size + 2;
994 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
997 int chunk_size_expected =
998 (tape->num_participating_players + 1) * tape->length;
1000 if (chunk_size_expected != chunk_size)
1002 ReadUnusedBytesFromFile(file, chunk_size);
1003 return chunk_size_expected;
1006 for(i=0; i<tape->length; i++)
1008 if (i >= MAX_TAPELEN)
1011 for(j=0; j<MAX_PLAYERS; j++)
1013 tape->pos[i].action[j] = MV_NO_MOVING;
1015 if (tape->player_participates[j])
1016 tape->pos[i].action[j] = fgetc(file);
1019 tape->pos[i].delay = fgetc(file);
1021 if (tape->file_version == FILE_VERSION_1_0)
1023 /* eliminate possible diagonal moves in old tapes */
1024 /* this is only for backward compatibility */
1026 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1027 byte action = tape->pos[i].action[0];
1028 int k, num_moves = 0;
1032 if (action & joy_dir[k])
1034 tape->pos[i + num_moves].action[0] = joy_dir[k];
1036 tape->pos[i + num_moves].delay = 0;
1045 tape->length += num_moves;
1048 else if (tape->file_version < FILE_VERSION_2_0)
1050 /* convert pre-2.0 tapes to new tape format */
1052 if (tape->pos[i].delay > 1)
1055 tape->pos[i + 1] = tape->pos[i];
1056 tape->pos[i + 1].delay = 1;
1059 for(j=0; j<MAX_PLAYERS; j++)
1060 tape->pos[i].action[j] = MV_NO_MOVING;
1061 tape->pos[i].delay--;
1072 if (i != tape->length)
1073 chunk_size = (tape->num_participating_players + 1) * i;
1078 void LoadTapeFromFilename(char *filename)
1080 char cookie[MAX_LINE_LEN];
1081 char chunk_name[CHUNK_ID_LEN + 1];
1085 /* always start with reliable default values */
1086 setTapeInfoToDefaults();
1088 if (!(file = fopen(filename, MODE_READ)))
1091 getFileChunkBE(file, chunk_name, NULL);
1092 if (strcmp(chunk_name, "RND1") == 0)
1094 getFile32BitBE(file); /* not used */
1096 getFileChunkBE(file, chunk_name, NULL);
1097 if (strcmp(chunk_name, "TAPE") != 0)
1099 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1104 else /* check for pre-2.0 file format with cookie string */
1106 strcpy(cookie, chunk_name);
1107 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1108 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1109 cookie[strlen(cookie) - 1] = '\0';
1111 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1113 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1118 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1120 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1125 /* pre-2.0 tape files have no game version, so use file version here */
1126 tape.game_version = tape.file_version;
1129 if (tape.file_version < FILE_VERSION_1_2)
1131 /* tape files from versions before 1.2.0 without chunk structure */
1132 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1133 LoadTape_BODY(file, 2 * tape.length, &tape);
1141 int (*loader)(FILE *, int, struct TapeInfo *);
1145 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1146 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1147 { "INFO", -1, LoadTape_INFO },
1148 { "BODY", -1, LoadTape_BODY },
1152 while (getFileChunkBE(file, chunk_name, &chunk_size))
1156 while (chunk_info[i].name != NULL &&
1157 strcmp(chunk_name, chunk_info[i].name) != 0)
1160 if (chunk_info[i].name == NULL)
1162 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1163 chunk_name, filename);
1164 ReadUnusedBytesFromFile(file, chunk_size);
1166 else if (chunk_info[i].size != -1 &&
1167 chunk_info[i].size != chunk_size)
1169 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1170 chunk_size, chunk_name, filename);
1171 ReadUnusedBytesFromFile(file, chunk_size);
1175 /* call function to load this tape chunk */
1176 int chunk_size_expected =
1177 (chunk_info[i].loader)(file, chunk_size, &tape);
1179 /* the size of some chunks cannot be checked before reading other
1180 chunks first (like "HEAD" and "BODY") that contain some header
1181 information, so check them here */
1182 if (chunk_size_expected != chunk_size)
1184 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1185 chunk_size, chunk_name, filename);
1193 tape.length_seconds = GetTapeLength();
1196 void LoadTape(int level_nr)
1198 char *filename = getTapeFilename(level_nr);
1200 LoadTapeFromFilename(filename);
1203 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1205 putFileVersion(file, tape->file_version);
1206 putFileVersion(file, tape->game_version);
1209 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1212 byte store_participating_players = 0;
1214 /* set bits for participating players for compact storage */
1215 for(i=0; i<MAX_PLAYERS; i++)
1216 if (tape->player_participates[i])
1217 store_participating_players |= (1 << i);
1219 putFile32BitBE(file, tape->random_seed);
1220 putFile32BitBE(file, tape->date);
1221 putFile32BitBE(file, tape->length);
1223 fputc(store_participating_players, file);
1225 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1226 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1228 putFileVersion(file, tape->engine_version);
1231 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1233 int level_identifier_size = strlen(tape->level_identifier) + 1;
1236 putFile16BitBE(file, level_identifier_size);
1238 for(i=0; i < level_identifier_size; i++)
1239 fputc(tape->level_identifier[i], file);
1241 putFile16BitBE(file, tape->level_nr);
1244 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1248 for(i=0; i<tape->length; i++)
1250 for(j=0; j<MAX_PLAYERS; j++)
1251 if (tape->player_participates[j])
1252 fputc(tape->pos[i].action[j], file);
1254 fputc(tape->pos[i].delay, file);
1258 void SaveTape(int level_nr)
1260 char *filename = getTapeFilename(level_nr);
1262 boolean new_tape = TRUE;
1263 int num_participating_players = 0;
1264 int info_chunk_size;
1265 int body_chunk_size;
1268 InitTapeDirectory(leveldir_current->filename);
1270 /* if a tape still exists, ask to overwrite it */
1271 if (access(filename, F_OK) == 0)
1274 if (!Request("Replace old tape ?", REQ_ASK))
1278 if (!(file = fopen(filename, MODE_WRITE)))
1280 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1284 tape.file_version = FILE_VERSION_ACTUAL;
1285 tape.game_version = GAME_VERSION_ACTUAL;
1287 /* count number of participating players */
1288 for(i=0; i<MAX_PLAYERS; i++)
1289 if (tape.player_participates[i])
1290 num_participating_players++;
1292 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1293 body_chunk_size = (num_participating_players + 1) * tape.length;
1295 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1296 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1298 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1299 SaveTape_VERS(file, &tape);
1301 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1302 SaveTape_HEAD(file, &tape);
1304 putFileChunkBE(file, "INFO", info_chunk_size);
1305 SaveTape_INFO(file, &tape);
1307 putFileChunkBE(file, "BODY", body_chunk_size);
1308 SaveTape_BODY(file, &tape);
1312 SetFilePermissions(filename, PERMS_PRIVATE);
1314 tape.changed = FALSE;
1317 Request("tape saved !", REQ_CONFIRM);
1320 void DumpTape(struct TapeInfo *tape)
1324 if (TAPE_IS_EMPTY(*tape))
1326 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1330 printf_line("-", 79);
1331 printf("Tape of Level %03d (file version %06d, game version %06d)\n",
1332 tape->level_nr, tape->file_version, tape->game_version);
1333 printf("Level series identifier: '%s'\n", tape->level_identifier);
1334 printf_line("-", 79);
1336 for(i=0; i<tape->length; i++)
1338 if (i >= MAX_TAPELEN)
1341 printf("%03d: ", i);
1343 for(j=0; j<MAX_PLAYERS; j++)
1345 if (tape->player_participates[j])
1347 int action = tape->pos[i].action[j];
1349 printf("%d:%02x ", j, action);
1350 printf("[%c%c%c%c|%c%c] - ",
1351 (action & JOY_LEFT ? '<' : ' '),
1352 (action & JOY_RIGHT ? '>' : ' '),
1353 (action & JOY_UP ? '^' : ' '),
1354 (action & JOY_DOWN ? 'v' : ' '),
1355 (action & JOY_BUTTON_1 ? '1' : ' '),
1356 (action & JOY_BUTTON_2 ? '2' : ' '));
1360 printf("(%03d)\n", tape->pos[i].delay);
1363 printf_line("-", 79);
1367 /* ========================================================================= */
1368 /* score file functions */
1369 /* ========================================================================= */
1371 void LoadScore(int level_nr)
1374 char *filename = getScoreFilename(level_nr);
1375 char cookie[MAX_LINE_LEN];
1376 char line[MAX_LINE_LEN];
1380 /* always start with reliable default values */
1381 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1383 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1384 highscore[i].Score = 0;
1387 if (!(file = fopen(filename, MODE_READ)))
1390 /* check file identifier */
1391 fgets(cookie, MAX_LINE_LEN, file);
1392 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1393 cookie[strlen(cookie) - 1] = '\0';
1395 if (!checkCookieString(cookie, SCORE_COOKIE))
1397 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1402 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1404 fscanf(file, "%d", &highscore[i].Score);
1405 fgets(line, MAX_LINE_LEN, file);
1407 if (line[strlen(line) - 1] == '\n')
1408 line[strlen(line) - 1] = '\0';
1410 for (line_ptr = line; *line_ptr; line_ptr++)
1412 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1414 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1415 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1424 void SaveScore(int level_nr)
1427 char *filename = getScoreFilename(level_nr);
1430 InitScoreDirectory(leveldir_current->filename);
1432 if (!(file = fopen(filename, MODE_WRITE)))
1434 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1438 fprintf(file, "%s\n\n", SCORE_COOKIE);
1440 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1441 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1445 SetFilePermissions(filename, PERMS_PUBLIC);
1449 /* ========================================================================= */
1450 /* setup file functions */
1451 /* ========================================================================= */
1453 #define TOKEN_STR_PLAYER_PREFIX "player_"
1456 #define SETUP_TOKEN_PLAYER_NAME 0
1457 #define SETUP_TOKEN_SOUND 1
1458 #define SETUP_TOKEN_SOUND_LOOPS 2
1459 #define SETUP_TOKEN_SOUND_MUSIC 3
1460 #define SETUP_TOKEN_SOUND_SIMPLE 4
1461 #define SETUP_TOKEN_TOONS 5
1462 #define SETUP_TOKEN_SCROLL_DELAY 6
1463 #define SETUP_TOKEN_SOFT_SCROLLING 7
1464 #define SETUP_TOKEN_FADING 8
1465 #define SETUP_TOKEN_AUTORECORD 9
1466 #define SETUP_TOKEN_QUICK_DOORS 10
1467 #define SETUP_TOKEN_TEAM_MODE 11
1468 #define SETUP_TOKEN_HANDICAP 12
1469 #define SETUP_TOKEN_TIME_LIMIT 13
1470 #define SETUP_TOKEN_FULLSCREEN 14
1471 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1472 #define SETUP_TOKEN_GRAPHICS_SET 16
1473 #define SETUP_TOKEN_SOUNDS_SET 17
1474 #define SETUP_TOKEN_MUSIC_SET 18
1475 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1476 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1477 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1479 #define NUM_GLOBAL_SETUP_TOKENS 22
1482 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1483 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1484 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1485 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1486 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1487 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1488 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1489 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1490 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1492 #define NUM_EDITOR_SETUP_TOKENS 9
1494 /* shortcut setup */
1495 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1496 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1497 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1499 #define NUM_SHORTCUT_SETUP_TOKENS 3
1502 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1503 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1504 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1505 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1506 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1507 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1508 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1509 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1510 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1511 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1512 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1513 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1514 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1515 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1516 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1517 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1519 #define NUM_PLAYER_SETUP_TOKENS 16
1522 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1523 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1525 #define NUM_SYSTEM_SETUP_TOKENS 2
1528 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1530 #define NUM_OPTIONS_SETUP_TOKENS 1
1533 static struct SetupInfo si;
1534 static struct SetupEditorInfo sei;
1535 static struct SetupShortcutInfo ssi;
1536 static struct SetupInputInfo sii;
1537 static struct SetupSystemInfo syi;
1538 static struct OptionInfo soi;
1540 static struct TokenInfo global_setup_tokens[] =
1542 { TYPE_STRING, &si.player_name, "player_name" },
1543 { TYPE_SWITCH, &si.sound, "sound" },
1544 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1545 { TYPE_SWITCH, &si.sound_music, "background_music" },
1546 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1547 { TYPE_SWITCH, &si.toons, "toons" },
1548 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1549 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1550 { TYPE_SWITCH, &si.fading, "screen_fading" },
1551 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1552 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1553 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1554 { TYPE_SWITCH, &si.handicap, "handicap" },
1555 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1556 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1557 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1558 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1559 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1560 { TYPE_STRING, &si.music_set, "music_set" },
1561 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1562 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1563 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1566 static struct TokenInfo editor_setup_tokens[] =
1568 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1569 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1570 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1571 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1572 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1573 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1574 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1575 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1576 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1579 static struct TokenInfo shortcut_setup_tokens[] =
1581 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1582 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1583 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1586 static struct TokenInfo player_setup_tokens[] =
1588 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1589 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1590 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1591 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1592 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1593 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1594 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1595 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1596 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1597 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1598 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1599 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1600 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1601 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1602 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1603 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1606 static struct TokenInfo system_setup_tokens[] =
1608 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1609 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1612 static struct TokenInfo options_setup_tokens[] =
1614 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1617 static char *get_corrected_login_name(char *login_name)
1619 /* needed because player name must be a fixed length string */
1620 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1622 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1623 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1625 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1626 if (strchr(login_name_new, ' '))
1627 *strchr(login_name_new, ' ') = '\0';
1629 return login_name_new;
1632 static void setSetupInfoToDefaults(struct SetupInfo *si)
1636 si->player_name = get_corrected_login_name(getLoginName());
1639 si->sound_loops = TRUE;
1640 si->sound_music = TRUE;
1641 si->sound_simple = TRUE;
1643 si->double_buffering = TRUE;
1644 si->direct_draw = !si->double_buffering;
1645 si->scroll_delay = TRUE;
1646 si->soft_scrolling = TRUE;
1648 si->autorecord = TRUE;
1649 si->quick_doors = FALSE;
1650 si->team_mode = FALSE;
1651 si->handicap = TRUE;
1652 si->time_limit = TRUE;
1653 si->fullscreen = FALSE;
1654 si->ask_on_escape = TRUE;
1656 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1657 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1658 si->music_set = getStringCopy(MUSIC_SUBDIR);
1659 si->override_level_graphics = FALSE;
1660 si->override_level_sounds = FALSE;
1661 si->override_level_music = FALSE;
1663 si->editor.el_boulderdash = TRUE;
1664 si->editor.el_emerald_mine = TRUE;
1665 si->editor.el_more = TRUE;
1666 si->editor.el_sokoban = TRUE;
1667 si->editor.el_supaplex = TRUE;
1668 si->editor.el_diamond_caves = TRUE;
1669 si->editor.el_dx_boulderdash = TRUE;
1670 si->editor.el_chars = TRUE;
1671 si->editor.el_custom = TRUE;
1673 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1674 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1675 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1677 for (i=0; i<MAX_PLAYERS; i++)
1679 si->input[i].use_joystick = FALSE;
1680 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1681 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1682 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1683 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1684 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1685 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1686 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1687 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1688 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1689 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1690 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1691 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1692 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1693 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1694 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1697 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1698 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1700 si->options.verbose = FALSE;
1703 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1707 if (!setup_file_list)
1712 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1713 setSetupInfo(global_setup_tokens, i,
1714 getTokenValue(setup_file_list, global_setup_tokens[i].text));
1719 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1720 setSetupInfo(editor_setup_tokens, i,
1721 getTokenValue(setup_file_list,editor_setup_tokens[i].text));
1724 /* shortcut setup */
1725 ssi = setup.shortcut;
1726 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1727 setSetupInfo(shortcut_setup_tokens, i,
1728 getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
1729 setup.shortcut = ssi;
1732 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1736 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1738 sii = setup.input[pnr];
1739 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1741 char full_token[100];
1743 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1744 setSetupInfo(player_setup_tokens, i,
1745 getTokenValue(setup_file_list, full_token));
1747 setup.input[pnr] = sii;
1752 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1753 setSetupInfo(system_setup_tokens, i,
1754 getTokenValue(setup_file_list, system_setup_tokens[i].text));
1758 soi = setup.options;
1759 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1760 setSetupInfo(options_setup_tokens, i,
1761 getTokenValue(setup_file_list, options_setup_tokens[i].text));
1762 setup.options = soi;
1767 char *filename = getSetupFilename();
1768 struct SetupFileList *setup_file_list = NULL;
1770 /* always start with reliable default values */
1771 setSetupInfoToDefaults(&setup);
1773 setup_file_list = loadSetupFileList(filename);
1775 if (setup_file_list)
1777 char *player_name_new;
1779 checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
1780 decodeSetupFileList(setup_file_list);
1782 setup.direct_draw = !setup.double_buffering;
1784 freeSetupFileList(setup_file_list);
1786 /* needed to work around problems with fixed length strings */
1787 player_name_new = get_corrected_login_name(setup.player_name);
1788 free(setup.player_name);
1789 setup.player_name = player_name_new;
1792 Error(ERR_WARN, "using default setup values");
1797 char *filename = getSetupFilename();
1801 InitUserDataDirectory();
1803 if (!(file = fopen(filename, MODE_WRITE)))
1805 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1809 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1810 getCookie("SETUP")));
1811 fprintf(file, "\n");
1815 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1817 /* just to make things nicer :) */
1818 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1819 i == SETUP_TOKEN_GRAPHICS_SET)
1820 fprintf(file, "\n");
1822 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1827 fprintf(file, "\n");
1828 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1829 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1831 /* shortcut setup */
1832 ssi = setup.shortcut;
1833 fprintf(file, "\n");
1834 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1835 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1838 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1842 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1843 fprintf(file, "\n");
1845 sii = setup.input[pnr];
1846 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1847 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1852 fprintf(file, "\n");
1853 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1854 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1857 soi = setup.options;
1858 fprintf(file, "\n");
1859 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1860 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1864 SetFilePermissions(filename, PERMS_PRIVATE);
1867 void LoadCustomElementDescriptions()
1869 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1870 struct SetupFileList *setup_file_list;
1873 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1875 if (element_info[i].custom_description != NULL)
1877 free(element_info[i].custom_description);
1878 element_info[i].custom_description = NULL;
1882 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1885 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1887 char *token = getStringCat2(element_info[i].token_name, ".name");
1888 char *value = getTokenValue(setup_file_list, token);
1891 element_info[i].custom_description = getStringCopy(value);
1896 freeSetupFileList(setup_file_list);
1899 void LoadSpecialMenuDesignSettings()
1901 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1902 struct SetupFileList *setup_file_list;
1905 /* !!! CHANGE THIS !!! (redundant initialization) !!! */
1906 global.num_toons = 20;
1907 global.menu_draw_xoffset = 0;
1908 global.menu_draw_yoffset = 0;
1909 global.menu_draw_xoffset_MAIN = 0;
1910 global.menu_draw_yoffset_MAIN = 0;
1911 global.door_step_offset = 2;
1912 global.door_step_delay = 10;
1914 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1917 value = getTokenValue(setup_file_list, "global.num_toons");
1919 global.num_toons = get_integer_from_string(value);
1921 value = getTokenValue(setup_file_list, "menu.draw_xoffset");
1923 global.menu_draw_xoffset = get_integer_from_string(value);
1925 value = getTokenValue(setup_file_list, "menu.draw_yoffset");
1927 global.menu_draw_yoffset = get_integer_from_string(value);
1929 value = getTokenValue(setup_file_list, "menu.draw_xoffset.MAIN");
1931 global.menu_draw_xoffset_MAIN = get_integer_from_string(value);
1933 value = getTokenValue(setup_file_list, "menu.draw_yoffset.MAIN");
1935 global.menu_draw_yoffset_MAIN = get_integer_from_string(value);
1937 value = getTokenValue(setup_file_list, "door.step_offset");
1939 global.door_step_offset = get_integer_from_string(value);
1941 value = getTokenValue(setup_file_list, "door.step_delay");
1943 global.door_step_delay = get_integer_from_string(value);
1945 freeSetupFileList(setup_file_list);