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;
102 /* start with no properties at all */
104 for (j=0; j < NUM_EP_BITFIELDS; j++)
105 Properties[EL_CUSTOM_START + i][j] = EP_BITMASK_DEFAULT;
107 Properties[EL_CUSTOM_START + i][EP_BITFIELD_BASE] = EP_BITMASK_DEFAULT;
111 BorderElement = EL_STEELWALL;
113 level.no_level_file = FALSE;
115 if (leveldir_current == NULL) /* only when dumping level */
118 /* try to determine better author name than 'anonymous' */
119 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
121 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
122 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
126 switch (LEVELCLASS(leveldir_current))
128 case LEVELCLASS_TUTORIAL:
129 strcpy(level.author, PROGRAM_AUTHOR_STRING);
132 case LEVELCLASS_CONTRIBUTION:
133 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
134 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
137 case LEVELCLASS_USER:
138 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
139 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
143 /* keep default value */
149 static int checkLevelElement(int element)
151 if (element >= NUM_FILE_ELEMENTS)
153 Error(ERR_WARN, "invalid level element %d", element);
154 element = EL_CHAR_QUESTION;
156 else if (element == EL_PLAYER_OBSOLETE)
157 element = EL_PLAYER_1;
158 else if (element == EL_KEY_OBSOLETE)
164 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
166 level->file_version = getFileVersion(file);
167 level->game_version = getFileVersion(file);
172 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
176 lev_fieldx = level->fieldx = fgetc(file);
177 lev_fieldy = level->fieldy = fgetc(file);
179 level->time = getFile16BitBE(file);
180 level->gems_needed = getFile16BitBE(file);
182 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
183 level->name[i] = fgetc(file);
184 level->name[MAX_LEVEL_NAME_LEN] = 0;
186 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
187 level->score[i] = fgetc(file);
189 level->num_yam_contents = STD_ELEMENT_CONTENTS;
190 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
193 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
195 level->amoeba_speed = fgetc(file);
196 level->time_magic_wall = fgetc(file);
197 level->time_wheel = fgetc(file);
198 level->amoeba_content = checkLevelElement(fgetc(file));
199 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
200 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
201 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
202 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
204 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
209 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
213 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
214 level->author[i] = fgetc(file);
215 level->author[MAX_LEVEL_NAME_LEN] = 0;
220 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
223 int chunk_size_expected = level->fieldx * level->fieldy;
225 /* Note: "chunk_size" was wrong before version 2.0 when elements are
226 stored with 16-bit encoding (and should be twice as big then).
227 Even worse, playfield data was stored 16-bit when only yamyam content
228 contained 16-bit elements and vice versa. */
230 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
231 chunk_size_expected *= 2;
233 if (chunk_size_expected != chunk_size)
235 ReadUnusedBytesFromFile(file, chunk_size);
236 return chunk_size_expected;
239 for(y=0; y<level->fieldy; y++)
240 for(x=0; x<level->fieldx; x++)
241 Feld[x][y] = Ur[x][y] =
242 checkLevelElement(level->encoding_16bit_field ?
243 getFile16BitBE(file) : fgetc(file));
247 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
251 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
252 int chunk_size_expected = header_size + content_size;
254 /* Note: "chunk_size" was wrong before version 2.0 when elements are
255 stored with 16-bit encoding (and should be twice as big then).
256 Even worse, playfield data was stored 16-bit when only yamyam content
257 contained 16-bit elements and vice versa. */
259 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
260 chunk_size_expected += content_size;
262 if (chunk_size_expected != chunk_size)
264 ReadUnusedBytesFromFile(file, chunk_size);
265 return chunk_size_expected;
269 level->num_yam_contents = fgetc(file);
273 /* correct invalid number of content fields -- should never happen */
274 if (level->num_yam_contents < 1 ||
275 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
276 level->num_yam_contents = STD_ELEMENT_CONTENTS;
278 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
281 level->yam_content[i][x][y] =
282 checkLevelElement(level->encoding_16bit_field ?
283 getFile16BitBE(file) : fgetc(file));
287 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
291 int num_contents, content_xsize, content_ysize;
292 int content_array[MAX_ELEMENT_CONTENTS][3][3];
294 element = checkLevelElement(getFile16BitBE(file));
295 num_contents = fgetc(file);
296 content_xsize = fgetc(file);
297 content_ysize = fgetc(file);
298 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
300 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
303 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
305 /* correct invalid number of content fields -- should never happen */
306 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
307 num_contents = STD_ELEMENT_CONTENTS;
309 if (element == EL_YAMYAM)
311 level->num_yam_contents = num_contents;
313 for(i=0; i<num_contents; i++)
316 level->yam_content[i][x][y] = content_array[i][x][y];
318 else if (element == EL_BD_AMOEBA)
320 level->amoeba_content = content_array[0][0][0];
324 Error(ERR_WARN, "cannot load content for element '%d'", element);
330 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
332 int num_changed_custom_elements = getFile16BitBE(file);
333 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
336 if (chunk_size_expected != chunk_size)
338 ReadUnusedBytesFromFile(file, chunk_size - 2);
339 return chunk_size_expected;
342 for (i=0; i < num_changed_custom_elements; i++)
344 int element = getFile16BitBE(file);
345 int properties = getFile32BitBE(file);
347 if (IS_CUSTOM_ELEMENT(element))
348 Properties[element][EP_BITFIELD_BASE] = properties;
350 Error(ERR_WARN, "invalid custom element number %d", element);
356 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
358 int num_changed_custom_elements = getFile16BitBE(file);
359 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
362 if (chunk_size_expected != chunk_size)
364 ReadUnusedBytesFromFile(file, chunk_size - 2);
365 return chunk_size_expected;
368 for (i=0; i < num_changed_custom_elements; i++)
370 int element = getFile16BitBE(file);
371 int custom_element_successor = getFile16BitBE(file);
372 int i = element - EL_CUSTOM_START;
374 if (IS_CUSTOM_ELEMENT(element))
375 level->custom_element_successor[i] = custom_element_successor;
377 Error(ERR_WARN, "invalid custom element number %d", element);
383 void LoadLevelFromFilename(char *filename)
385 char cookie[MAX_LINE_LEN];
386 char chunk_name[CHUNK_ID_LEN + 1];
390 /* always start with reliable default values */
391 setLevelInfoToDefaults();
393 if (!(file = fopen(filename, MODE_READ)))
395 level.no_level_file = TRUE;
397 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
401 getFileChunkBE(file, chunk_name, NULL);
402 if (strcmp(chunk_name, "RND1") == 0)
404 getFile32BitBE(file); /* not used */
406 getFileChunkBE(file, chunk_name, NULL);
407 if (strcmp(chunk_name, "CAVE") != 0)
409 Error(ERR_WARN, "unknown format of level file '%s'", filename);
414 else /* check for pre-2.0 file format with cookie string */
416 strcpy(cookie, chunk_name);
417 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
418 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
419 cookie[strlen(cookie) - 1] = '\0';
421 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
423 Error(ERR_WARN, "unknown format of level file '%s'", filename);
428 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
430 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
435 /* pre-2.0 level files have no game version, so use file version here */
436 level.game_version = level.file_version;
439 if (level.file_version < FILE_VERSION_1_2)
441 /* level files from versions before 1.2.0 without chunk structure */
442 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
443 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
451 int (*loader)(FILE *, int, struct LevelInfo *);
455 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
456 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
457 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
458 { "BODY", -1, LoadLevel_BODY },
459 { "CONT", -1, LoadLevel_CONT },
460 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
461 { "CUS1", -1, LoadLevel_CUS1 },
462 { "CUS2", -1, LoadLevel_CUS2 },
466 while (getFileChunkBE(file, chunk_name, &chunk_size))
470 while (chunk_info[i].name != NULL &&
471 strcmp(chunk_name, chunk_info[i].name) != 0)
474 if (chunk_info[i].name == NULL)
476 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
477 chunk_name, filename);
478 ReadUnusedBytesFromFile(file, chunk_size);
480 else if (chunk_info[i].size != -1 &&
481 chunk_info[i].size != chunk_size)
483 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
484 chunk_size, chunk_name, filename);
485 ReadUnusedBytesFromFile(file, chunk_size);
489 /* call function to load this level chunk */
490 int chunk_size_expected =
491 (chunk_info[i].loader)(file, chunk_size, &level);
493 /* the size of some chunks cannot be checked before reading other
494 chunks first (like "HEAD" and "BODY") that contain some header
495 information, so check them here */
496 if (chunk_size_expected != chunk_size)
498 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
499 chunk_size, chunk_name, filename);
507 if (leveldir_current == NULL) /* only when dumping level */
510 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
511 IS_LEVELCLASS_USER(leveldir_current))
513 /* For user contributed and private levels, use the version of
514 the game engine the levels were created for.
515 Since 2.0.1, the game engine version is now directly stored
516 in the level file (chunk "VERS"), so there is no need anymore
517 to set the game version from the file version (except for old,
518 pre-2.0 levels, where the game version is still taken from the
519 file format version used to store the level -- see above). */
521 /* do some special adjustments to support older level versions */
522 if (level.file_version == FILE_VERSION_1_0)
524 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
525 Error(ERR_WARN, "using high speed movement for player");
527 /* player was faster than monsters in (pre-)1.0 levels */
528 level.double_speed = TRUE;
531 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
532 if (level.game_version == VERSION_IDENT(2,0,1))
533 level.em_slippery_gems = TRUE;
537 /* Always use the latest version of the game engine for all but
538 user contributed and private levels; this allows for actual
539 corrections in the game engine to take effect for existing,
540 converted levels (from "classic" or other existing games) to
541 make the game emulation more accurate, while (hopefully) not
542 breaking existing levels created from other players. */
544 level.game_version = GAME_VERSION_ACTUAL;
546 /* Set special EM style gems behaviour: EM style gems slip down from
547 normal, steel and growing wall. As this is a more fundamental change,
548 it seems better to set the default behaviour to "off" (as it is more
549 natural) and make it configurable in the level editor (as a property
550 of gem style elements). Already existing converted levels (neither
551 private nor contributed levels) are changed to the new behaviour. */
553 if (level.file_version < FILE_VERSION_2_0)
554 level.em_slippery_gems = TRUE;
557 /* map some elements which have changed in newer versions */
558 if (level.game_version <= VERSION_IDENT(2,2,0))
562 /* map game font elements */
563 for(y=0; y<level.fieldy; y++)
565 for(x=0; x<level.fieldx; x++)
567 int element = Ur[x][y];
569 if (element == EL_CHAR('['))
570 element = EL_CHAR_AUMLAUT;
571 else if (element == EL_CHAR('\\'))
572 element = EL_CHAR_OUMLAUT;
573 else if (element == EL_CHAR(']'))
574 element = EL_CHAR_UUMLAUT;
575 else if (element == EL_CHAR('^'))
576 element = EL_CHAR_COPYRIGHT;
578 Feld[x][y] = Ur[x][y] = element;
583 /* determine border element for this level */
587 void LoadLevel(int level_nr)
589 char *filename = getLevelFilename(level_nr);
591 LoadLevelFromFilename(filename);
592 InitElementPropertiesEngine(level.game_version);
595 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
597 putFileVersion(file, level->file_version);
598 putFileVersion(file, level->game_version);
601 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
605 fputc(level->fieldx, file);
606 fputc(level->fieldy, file);
608 putFile16BitBE(file, level->time);
609 putFile16BitBE(file, level->gems_needed);
611 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
612 fputc(level->name[i], file);
614 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
615 fputc(level->score[i], file);
617 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
620 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
621 level->yam_content[i][x][y]),
623 fputc(level->amoeba_speed, file);
624 fputc(level->time_magic_wall, file);
625 fputc(level->time_wheel, file);
626 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
628 fputc((level->double_speed ? 1 : 0), file);
629 fputc((level->gravity ? 1 : 0), file);
630 fputc((level->encoding_16bit_field ? 1 : 0), file);
631 fputc((level->em_slippery_gems ? 1 : 0), file);
633 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
636 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
640 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
641 fputc(level->author[i], file);
644 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
648 for(y=0; y<level->fieldy; y++)
649 for(x=0; x<level->fieldx; x++)
650 if (level->encoding_16bit_field)
651 putFile16BitBE(file, Ur[x][y]);
653 fputc(Ur[x][y], file);
657 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
661 fputc(EL_YAMYAM, file);
662 fputc(level->num_yam_contents, file);
666 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
669 if (level->encoding_16bit_field)
670 putFile16BitBE(file, level->yam_content[i][x][y]);
672 fputc(level->yam_content[i][x][y], file);
676 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
679 int num_contents, content_xsize, content_ysize;
680 int content_array[MAX_ELEMENT_CONTENTS][3][3];
682 if (element == EL_YAMYAM)
684 num_contents = level->num_yam_contents;
688 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
691 content_array[i][x][y] = level->yam_content[i][x][y];
693 else if (element == EL_BD_AMOEBA)
699 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
702 content_array[i][x][y] = EL_EMPTY;
703 content_array[0][0][0] = level->amoeba_content;
707 /* chunk header already written -- write empty chunk data */
708 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
710 Error(ERR_WARN, "cannot save content for element '%d'", element);
714 putFile16BitBE(file, element);
715 fputc(num_contents, file);
716 fputc(content_xsize, file);
717 fputc(content_ysize, file);
719 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
721 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
724 putFile16BitBE(file, content_array[i][x][y]);
727 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
728 int num_changed_custom_elements)
732 putFile16BitBE(file, num_changed_custom_elements);
734 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
736 int element = EL_CUSTOM_START + i;
738 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
740 if (check < num_changed_custom_elements)
742 putFile16BitBE(file, element);
743 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
750 if (check != num_changed_custom_elements) /* should not happen */
751 Error(ERR_WARN, "inconsistent number of custom element properties");
754 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
755 int num_changed_custom_elements)
759 putFile16BitBE(file, num_changed_custom_elements);
761 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
763 int element = EL_CUSTOM_START + i;
765 if (level->custom_element_successor[i] != EL_EMPTY_SPACE)
767 if (check < num_changed_custom_elements)
769 putFile16BitBE(file, element);
770 putFile16BitBE(file, level->custom_element_successor[i]);
777 if (check != num_changed_custom_elements) /* should not happen */
778 Error(ERR_WARN, "inconsistent number of custom element successors");
781 void SaveLevel(int level_nr)
783 char *filename = getLevelFilename(level_nr);
785 int num_changed_custom_elements1 = 0;
786 int num_changed_custom_elements2 = 0;
790 if (!(file = fopen(filename, MODE_WRITE)))
792 Error(ERR_WARN, "cannot save level file '%s'", filename);
796 level.file_version = FILE_VERSION_ACTUAL;
797 level.game_version = GAME_VERSION_ACTUAL;
799 /* check level field for 16-bit elements */
800 level.encoding_16bit_field = FALSE;
801 for(y=0; y<level.fieldy; y++)
802 for(x=0; x<level.fieldx; x++)
804 level.encoding_16bit_field = TRUE;
806 /* check yamyam content for 16-bit elements */
807 level.encoding_16bit_yamyam = FALSE;
808 for(i=0; i<level.num_yam_contents; i++)
811 if (level.yam_content[i][x][y] > 255)
812 level.encoding_16bit_yamyam = TRUE;
814 /* check amoeba content for 16-bit elements */
815 level.encoding_16bit_amoeba = FALSE;
816 if (level.amoeba_content > 255)
817 level.encoding_16bit_amoeba = TRUE;
819 /* calculate size of "BODY" chunk */
821 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
823 /* check for non-standard custom elements and calculate "CUS1" chunk size */
824 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
825 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
826 num_changed_custom_elements1++;
828 /* check for non-standard custom elements and calculate "CUS2" chunk size */
829 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
830 if (level.custom_element_successor[i] != EL_EMPTY_SPACE)
831 num_changed_custom_elements2++;
833 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
834 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
836 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
837 SaveLevel_VERS(file, &level);
839 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
840 SaveLevel_HEAD(file, &level);
842 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
843 SaveLevel_AUTH(file, &level);
845 putFileChunkBE(file, "BODY", body_chunk_size);
846 SaveLevel_BODY(file, &level);
848 if (level.encoding_16bit_yamyam ||
849 level.num_yam_contents != STD_ELEMENT_CONTENTS)
851 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
852 SaveLevel_CNT2(file, &level, EL_YAMYAM);
855 if (level.encoding_16bit_amoeba)
857 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
858 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
861 if (num_changed_custom_elements1 > 0)
863 putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements1 * 6);
864 SaveLevel_CUS1(file, &level, num_changed_custom_elements1);
867 if (num_changed_custom_elements2 > 0)
869 putFileChunkBE(file, "CUS2", 2 + num_changed_custom_elements2 * 4);
870 SaveLevel_CUS2(file, &level, num_changed_custom_elements2);
875 SetFilePermissions(filename, PERMS_PRIVATE);
878 void DumpLevel(struct LevelInfo *level)
880 printf_line("-", 79);
881 printf("Level xxx (file version %06d, game version %06d)\n",
882 level->file_version, level->game_version);
883 printf_line("-", 79);
885 printf("Level Author: '%s'\n", level->author);
886 printf("Level Title: '%s'\n", level->name);
888 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
890 printf("Level Time: %d seconds\n", level->time);
891 printf("Gems needed: %d\n", level->gems_needed);
893 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
894 printf("Time for Wheel: %d seconds\n", level->time_wheel);
895 printf("Time for Light: %d seconds\n", level->time_light);
896 printf("Time for Timegate: %d seconds\n", level->time_timegate);
898 printf("Amoeba Speed: %d\n", level->amoeba_speed);
900 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
901 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
902 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
904 printf_line("-", 79);
908 /* ========================================================================= */
909 /* tape file functions */
910 /* ========================================================================= */
912 static void setTapeInfoToDefaults()
916 /* always start with reliable default values (empty tape) */
919 /* default values (also for pre-1.2 tapes) with only the first player */
920 tape.player_participates[0] = TRUE;
921 for(i=1; i<MAX_PLAYERS; i++)
922 tape.player_participates[i] = FALSE;
924 /* at least one (default: the first) player participates in every tape */
925 tape.num_participating_players = 1;
927 tape.level_nr = level_nr;
929 tape.changed = FALSE;
931 tape.recording = FALSE;
932 tape.playing = FALSE;
933 tape.pausing = FALSE;
936 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
938 tape->file_version = getFileVersion(file);
939 tape->game_version = getFileVersion(file);
944 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
948 tape->random_seed = getFile32BitBE(file);
949 tape->date = getFile32BitBE(file);
950 tape->length = getFile32BitBE(file);
952 /* read header fields that are new since version 1.2 */
953 if (tape->file_version >= FILE_VERSION_1_2)
955 byte store_participating_players = fgetc(file);
958 /* since version 1.2, tapes store which players participate in the tape */
959 tape->num_participating_players = 0;
960 for(i=0; i<MAX_PLAYERS; i++)
962 tape->player_participates[i] = FALSE;
964 if (store_participating_players & (1 << i))
966 tape->player_participates[i] = TRUE;
967 tape->num_participating_players++;
971 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
973 engine_version = getFileVersion(file);
974 if (engine_version > 0)
975 tape->engine_version = engine_version;
981 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
983 int level_identifier_size;
986 level_identifier_size = getFile16BitBE(file);
988 tape->level_identifier =
989 checked_realloc(tape->level_identifier, level_identifier_size);
991 for(i=0; i < level_identifier_size; i++)
992 tape->level_identifier[i] = fgetc(file);
994 tape->level_nr = getFile16BitBE(file);
996 chunk_size = 2 + level_identifier_size + 2;
1001 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1004 int chunk_size_expected =
1005 (tape->num_participating_players + 1) * tape->length;
1007 if (chunk_size_expected != chunk_size)
1009 ReadUnusedBytesFromFile(file, chunk_size);
1010 return chunk_size_expected;
1013 for(i=0; i<tape->length; i++)
1015 if (i >= MAX_TAPELEN)
1018 for(j=0; j<MAX_PLAYERS; j++)
1020 tape->pos[i].action[j] = MV_NO_MOVING;
1022 if (tape->player_participates[j])
1023 tape->pos[i].action[j] = fgetc(file);
1026 tape->pos[i].delay = fgetc(file);
1028 if (tape->file_version == FILE_VERSION_1_0)
1030 /* eliminate possible diagonal moves in old tapes */
1031 /* this is only for backward compatibility */
1033 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1034 byte action = tape->pos[i].action[0];
1035 int k, num_moves = 0;
1039 if (action & joy_dir[k])
1041 tape->pos[i + num_moves].action[0] = joy_dir[k];
1043 tape->pos[i + num_moves].delay = 0;
1052 tape->length += num_moves;
1055 else if (tape->file_version < FILE_VERSION_2_0)
1057 /* convert pre-2.0 tapes to new tape format */
1059 if (tape->pos[i].delay > 1)
1062 tape->pos[i + 1] = tape->pos[i];
1063 tape->pos[i + 1].delay = 1;
1066 for(j=0; j<MAX_PLAYERS; j++)
1067 tape->pos[i].action[j] = MV_NO_MOVING;
1068 tape->pos[i].delay--;
1079 if (i != tape->length)
1080 chunk_size = (tape->num_participating_players + 1) * i;
1085 void LoadTapeFromFilename(char *filename)
1087 char cookie[MAX_LINE_LEN];
1088 char chunk_name[CHUNK_ID_LEN + 1];
1092 /* always start with reliable default values */
1093 setTapeInfoToDefaults();
1095 if (!(file = fopen(filename, MODE_READ)))
1098 getFileChunkBE(file, chunk_name, NULL);
1099 if (strcmp(chunk_name, "RND1") == 0)
1101 getFile32BitBE(file); /* not used */
1103 getFileChunkBE(file, chunk_name, NULL);
1104 if (strcmp(chunk_name, "TAPE") != 0)
1106 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1111 else /* check for pre-2.0 file format with cookie string */
1113 strcpy(cookie, chunk_name);
1114 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1115 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1116 cookie[strlen(cookie) - 1] = '\0';
1118 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1120 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1125 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1127 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1132 /* pre-2.0 tape files have no game version, so use file version here */
1133 tape.game_version = tape.file_version;
1136 if (tape.file_version < FILE_VERSION_1_2)
1138 /* tape files from versions before 1.2.0 without chunk structure */
1139 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1140 LoadTape_BODY(file, 2 * tape.length, &tape);
1148 int (*loader)(FILE *, int, struct TapeInfo *);
1152 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1153 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1154 { "INFO", -1, LoadTape_INFO },
1155 { "BODY", -1, LoadTape_BODY },
1159 while (getFileChunkBE(file, chunk_name, &chunk_size))
1163 while (chunk_info[i].name != NULL &&
1164 strcmp(chunk_name, chunk_info[i].name) != 0)
1167 if (chunk_info[i].name == NULL)
1169 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1170 chunk_name, filename);
1171 ReadUnusedBytesFromFile(file, chunk_size);
1173 else if (chunk_info[i].size != -1 &&
1174 chunk_info[i].size != chunk_size)
1176 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1177 chunk_size, chunk_name, filename);
1178 ReadUnusedBytesFromFile(file, chunk_size);
1182 /* call function to load this tape chunk */
1183 int chunk_size_expected =
1184 (chunk_info[i].loader)(file, chunk_size, &tape);
1186 /* the size of some chunks cannot be checked before reading other
1187 chunks first (like "HEAD" and "BODY") that contain some header
1188 information, so check them here */
1189 if (chunk_size_expected != chunk_size)
1191 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1192 chunk_size, chunk_name, filename);
1200 tape.length_seconds = GetTapeLength();
1203 void LoadTape(int level_nr)
1205 char *filename = getTapeFilename(level_nr);
1207 LoadTapeFromFilename(filename);
1210 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1212 putFileVersion(file, tape->file_version);
1213 putFileVersion(file, tape->game_version);
1216 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1219 byte store_participating_players = 0;
1221 /* set bits for participating players for compact storage */
1222 for(i=0; i<MAX_PLAYERS; i++)
1223 if (tape->player_participates[i])
1224 store_participating_players |= (1 << i);
1226 putFile32BitBE(file, tape->random_seed);
1227 putFile32BitBE(file, tape->date);
1228 putFile32BitBE(file, tape->length);
1230 fputc(store_participating_players, file);
1232 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1233 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1235 putFileVersion(file, tape->engine_version);
1238 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1240 int level_identifier_size = strlen(tape->level_identifier) + 1;
1243 putFile16BitBE(file, level_identifier_size);
1245 for(i=0; i < level_identifier_size; i++)
1246 fputc(tape->level_identifier[i], file);
1248 putFile16BitBE(file, tape->level_nr);
1251 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1255 for(i=0; i<tape->length; i++)
1257 for(j=0; j<MAX_PLAYERS; j++)
1258 if (tape->player_participates[j])
1259 fputc(tape->pos[i].action[j], file);
1261 fputc(tape->pos[i].delay, file);
1265 void SaveTape(int level_nr)
1267 char *filename = getTapeFilename(level_nr);
1269 boolean new_tape = TRUE;
1270 int num_participating_players = 0;
1271 int info_chunk_size;
1272 int body_chunk_size;
1275 InitTapeDirectory(leveldir_current->filename);
1277 /* if a tape still exists, ask to overwrite it */
1278 if (access(filename, F_OK) == 0)
1281 if (!Request("Replace old tape ?", REQ_ASK))
1285 if (!(file = fopen(filename, MODE_WRITE)))
1287 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1291 tape.file_version = FILE_VERSION_ACTUAL;
1292 tape.game_version = GAME_VERSION_ACTUAL;
1294 /* count number of participating players */
1295 for(i=0; i<MAX_PLAYERS; i++)
1296 if (tape.player_participates[i])
1297 num_participating_players++;
1299 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1300 body_chunk_size = (num_participating_players + 1) * tape.length;
1302 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1303 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1305 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1306 SaveTape_VERS(file, &tape);
1308 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1309 SaveTape_HEAD(file, &tape);
1311 putFileChunkBE(file, "INFO", info_chunk_size);
1312 SaveTape_INFO(file, &tape);
1314 putFileChunkBE(file, "BODY", body_chunk_size);
1315 SaveTape_BODY(file, &tape);
1319 SetFilePermissions(filename, PERMS_PRIVATE);
1321 tape.changed = FALSE;
1324 Request("tape saved !", REQ_CONFIRM);
1327 void DumpTape(struct TapeInfo *tape)
1331 if (TAPE_IS_EMPTY(*tape))
1333 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1337 printf_line("-", 79);
1338 printf("Tape of Level %03d (file version %06d, game version %06d)\n",
1339 tape->level_nr, tape->file_version, tape->game_version);
1340 printf("Level series identifier: '%s'\n", tape->level_identifier);
1341 printf_line("-", 79);
1343 for(i=0; i<tape->length; i++)
1345 if (i >= MAX_TAPELEN)
1348 printf("%03d: ", i);
1350 for(j=0; j<MAX_PLAYERS; j++)
1352 if (tape->player_participates[j])
1354 int action = tape->pos[i].action[j];
1356 printf("%d:%02x ", j, action);
1357 printf("[%c%c%c%c|%c%c] - ",
1358 (action & JOY_LEFT ? '<' : ' '),
1359 (action & JOY_RIGHT ? '>' : ' '),
1360 (action & JOY_UP ? '^' : ' '),
1361 (action & JOY_DOWN ? 'v' : ' '),
1362 (action & JOY_BUTTON_1 ? '1' : ' '),
1363 (action & JOY_BUTTON_2 ? '2' : ' '));
1367 printf("(%03d)\n", tape->pos[i].delay);
1370 printf_line("-", 79);
1374 /* ========================================================================= */
1375 /* score file functions */
1376 /* ========================================================================= */
1378 void LoadScore(int level_nr)
1381 char *filename = getScoreFilename(level_nr);
1382 char cookie[MAX_LINE_LEN];
1383 char line[MAX_LINE_LEN];
1387 /* always start with reliable default values */
1388 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1390 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1391 highscore[i].Score = 0;
1394 if (!(file = fopen(filename, MODE_READ)))
1397 /* check file identifier */
1398 fgets(cookie, MAX_LINE_LEN, file);
1399 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1400 cookie[strlen(cookie) - 1] = '\0';
1402 if (!checkCookieString(cookie, SCORE_COOKIE))
1404 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1409 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1411 fscanf(file, "%d", &highscore[i].Score);
1412 fgets(line, MAX_LINE_LEN, file);
1414 if (line[strlen(line) - 1] == '\n')
1415 line[strlen(line) - 1] = '\0';
1417 for (line_ptr = line; *line_ptr; line_ptr++)
1419 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1421 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1422 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1431 void SaveScore(int level_nr)
1434 char *filename = getScoreFilename(level_nr);
1437 InitScoreDirectory(leveldir_current->filename);
1439 if (!(file = fopen(filename, MODE_WRITE)))
1441 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1445 fprintf(file, "%s\n\n", SCORE_COOKIE);
1447 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1448 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1452 SetFilePermissions(filename, PERMS_PUBLIC);
1456 /* ========================================================================= */
1457 /* setup file functions */
1458 /* ========================================================================= */
1460 #define TOKEN_STR_PLAYER_PREFIX "player_"
1463 #define SETUP_TOKEN_PLAYER_NAME 0
1464 #define SETUP_TOKEN_SOUND 1
1465 #define SETUP_TOKEN_SOUND_LOOPS 2
1466 #define SETUP_TOKEN_SOUND_MUSIC 3
1467 #define SETUP_TOKEN_SOUND_SIMPLE 4
1468 #define SETUP_TOKEN_TOONS 5
1469 #define SETUP_TOKEN_SCROLL_DELAY 6
1470 #define SETUP_TOKEN_SOFT_SCROLLING 7
1471 #define SETUP_TOKEN_FADING 8
1472 #define SETUP_TOKEN_AUTORECORD 9
1473 #define SETUP_TOKEN_QUICK_DOORS 10
1474 #define SETUP_TOKEN_TEAM_MODE 11
1475 #define SETUP_TOKEN_HANDICAP 12
1476 #define SETUP_TOKEN_TIME_LIMIT 13
1477 #define SETUP_TOKEN_FULLSCREEN 14
1478 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1479 #define SETUP_TOKEN_GRAPHICS_SET 16
1480 #define SETUP_TOKEN_SOUNDS_SET 17
1481 #define SETUP_TOKEN_MUSIC_SET 18
1482 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1483 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1484 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1486 #define NUM_GLOBAL_SETUP_TOKENS 22
1489 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1490 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1491 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1492 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1493 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1494 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1495 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1496 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1497 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1499 #define NUM_EDITOR_SETUP_TOKENS 9
1501 /* shortcut setup */
1502 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1503 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1504 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1506 #define NUM_SHORTCUT_SETUP_TOKENS 3
1509 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1510 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1511 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1512 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1513 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1514 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1515 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1516 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1517 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1518 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1519 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1520 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1521 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1522 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1523 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1524 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1526 #define NUM_PLAYER_SETUP_TOKENS 16
1529 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1530 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1532 #define NUM_SYSTEM_SETUP_TOKENS 2
1535 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1537 #define NUM_OPTIONS_SETUP_TOKENS 1
1540 static struct SetupInfo si;
1541 static struct SetupEditorInfo sei;
1542 static struct SetupShortcutInfo ssi;
1543 static struct SetupInputInfo sii;
1544 static struct SetupSystemInfo syi;
1545 static struct OptionInfo soi;
1547 static struct TokenInfo global_setup_tokens[] =
1549 { TYPE_STRING, &si.player_name, "player_name" },
1550 { TYPE_SWITCH, &si.sound, "sound" },
1551 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1552 { TYPE_SWITCH, &si.sound_music, "background_music" },
1553 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1554 { TYPE_SWITCH, &si.toons, "toons" },
1555 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1556 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1557 { TYPE_SWITCH, &si.fading, "screen_fading" },
1558 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1559 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1560 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1561 { TYPE_SWITCH, &si.handicap, "handicap" },
1562 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1563 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1564 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1565 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1566 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1567 { TYPE_STRING, &si.music_set, "music_set" },
1568 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1569 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1570 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1573 static struct TokenInfo editor_setup_tokens[] =
1575 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1576 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1577 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1578 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1579 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1580 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1581 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1582 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1583 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1586 static struct TokenInfo shortcut_setup_tokens[] =
1588 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1589 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1590 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1593 static struct TokenInfo player_setup_tokens[] =
1595 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1596 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1597 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1598 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1599 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1600 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1601 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1602 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1603 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1604 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1605 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1606 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1607 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1608 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1609 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1610 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1613 static struct TokenInfo system_setup_tokens[] =
1615 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1616 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1619 static struct TokenInfo options_setup_tokens[] =
1621 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1624 static char *get_corrected_login_name(char *login_name)
1626 /* needed because player name must be a fixed length string */
1627 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1629 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1630 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1632 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1633 if (strchr(login_name_new, ' '))
1634 *strchr(login_name_new, ' ') = '\0';
1636 return login_name_new;
1639 static void setSetupInfoToDefaults(struct SetupInfo *si)
1643 si->player_name = get_corrected_login_name(getLoginName());
1646 si->sound_loops = TRUE;
1647 si->sound_music = TRUE;
1648 si->sound_simple = TRUE;
1650 si->double_buffering = TRUE;
1651 si->direct_draw = !si->double_buffering;
1652 si->scroll_delay = TRUE;
1653 si->soft_scrolling = TRUE;
1655 si->autorecord = TRUE;
1656 si->quick_doors = FALSE;
1657 si->team_mode = FALSE;
1658 si->handicap = TRUE;
1659 si->time_limit = TRUE;
1660 si->fullscreen = FALSE;
1661 si->ask_on_escape = TRUE;
1663 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1664 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1665 si->music_set = getStringCopy(MUSIC_SUBDIR);
1666 si->override_level_graphics = FALSE;
1667 si->override_level_sounds = FALSE;
1668 si->override_level_music = FALSE;
1670 si->editor.el_boulderdash = TRUE;
1671 si->editor.el_emerald_mine = TRUE;
1672 si->editor.el_more = TRUE;
1673 si->editor.el_sokoban = TRUE;
1674 si->editor.el_supaplex = TRUE;
1675 si->editor.el_diamond_caves = TRUE;
1676 si->editor.el_dx_boulderdash = TRUE;
1677 si->editor.el_chars = TRUE;
1678 si->editor.el_custom = TRUE;
1680 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1681 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1682 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1684 for (i=0; i<MAX_PLAYERS; i++)
1686 si->input[i].use_joystick = FALSE;
1687 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1688 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1689 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1690 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1691 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1692 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1693 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1694 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1695 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1696 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1697 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1698 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1699 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1700 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1701 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1704 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1705 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1707 si->options.verbose = FALSE;
1710 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
1714 if (!setup_file_hash)
1719 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1720 setSetupInfo(global_setup_tokens, i,
1721 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
1726 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1727 setSetupInfo(editor_setup_tokens, i,
1728 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
1731 /* shortcut setup */
1732 ssi = setup.shortcut;
1733 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1734 setSetupInfo(shortcut_setup_tokens, i,
1735 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
1736 setup.shortcut = ssi;
1739 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1743 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1745 sii = setup.input[pnr];
1746 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1748 char full_token[100];
1750 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1751 setSetupInfo(player_setup_tokens, i,
1752 getHashEntry(setup_file_hash, full_token));
1754 setup.input[pnr] = sii;
1759 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1760 setSetupInfo(system_setup_tokens, i,
1761 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
1765 soi = setup.options;
1766 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1767 setSetupInfo(options_setup_tokens, i,
1768 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
1769 setup.options = soi;
1774 char *filename = getSetupFilename();
1775 SetupFileHash *setup_file_hash = NULL;
1777 /* always start with reliable default values */
1778 setSetupInfoToDefaults(&setup);
1780 setup_file_hash = loadSetupFileHash(filename);
1782 if (setup_file_hash)
1784 char *player_name_new;
1786 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
1787 decodeSetupFileHash(setup_file_hash);
1789 setup.direct_draw = !setup.double_buffering;
1791 freeSetupFileHash(setup_file_hash);
1793 /* needed to work around problems with fixed length strings */
1794 player_name_new = get_corrected_login_name(setup.player_name);
1795 free(setup.player_name);
1796 setup.player_name = player_name_new;
1799 Error(ERR_WARN, "using default setup values");
1804 char *filename = getSetupFilename();
1808 InitUserDataDirectory();
1810 if (!(file = fopen(filename, MODE_WRITE)))
1812 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1816 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1817 getCookie("SETUP")));
1818 fprintf(file, "\n");
1822 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1824 /* just to make things nicer :) */
1825 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1826 i == SETUP_TOKEN_GRAPHICS_SET)
1827 fprintf(file, "\n");
1829 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1834 fprintf(file, "\n");
1835 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1836 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1838 /* shortcut setup */
1839 ssi = setup.shortcut;
1840 fprintf(file, "\n");
1841 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1842 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1845 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1849 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1850 fprintf(file, "\n");
1852 sii = setup.input[pnr];
1853 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1854 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1859 fprintf(file, "\n");
1860 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1861 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1864 soi = setup.options;
1865 fprintf(file, "\n");
1866 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1867 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1871 SetFilePermissions(filename, PERMS_PRIVATE);
1874 void LoadCustomElementDescriptions()
1876 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1877 SetupFileHash *setup_file_hash;
1880 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1882 if (element_info[i].custom_description != NULL)
1884 free(element_info[i].custom_description);
1885 element_info[i].custom_description = NULL;
1889 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1892 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1894 char *token = getStringCat2(element_info[i].token_name, ".name");
1895 char *value = getHashEntry(setup_file_hash, token);
1898 element_info[i].custom_description = getStringCopy(value);
1903 freeSetupFileHash(setup_file_hash);
1906 void LoadSpecialMenuDesignSettings()
1908 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1909 SetupFileHash *setup_file_hash;
1912 /* always start with reliable default values from default config */
1913 for (i=0; image_config_vars[i].token != NULL; i++)
1914 for (j=0; image_config[j].token != NULL; j++)
1915 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
1916 *image_config_vars[i].value =
1917 get_integer_from_string(image_config[j].value);
1919 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1922 /* special case: initialize with default values that may be overwrittem */
1923 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
1925 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
1926 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
1928 if (value_x != NULL)
1929 menu.draw_xoffset[i] = get_integer_from_string(value_x);
1930 if (value_y != NULL)
1931 menu.draw_yoffset[i] = get_integer_from_string(value_y);
1934 /* read (and overwrite with) values that may be specified in config file */
1935 for (i=0; image_config_vars[i].token != NULL; i++)
1937 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
1940 *image_config_vars[i].value = get_integer_from_string(value);
1943 freeSetupFileHash(setup_file_hash);