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_yamyam_contents = STD_ELEMENT_CONTENTS;
88 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
91 level.yamyam_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 int element = EL_CUSTOM_START + i;
102 element_info[element].use_gfx_element = FALSE;
103 element_info[element].gfx_element = EL_EMPTY_SPACE;
104 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
105 element_info[element].move_direction_initial = MV_NO_MOVING;
106 element_info[element].move_stepsize = TILEX / 8;
110 element_info[element].content[x][y] = EL_EMPTY_SPACE;
112 element_info[element].change.events = CE_BITMASK_DEFAULT;
113 element_info[element].change.delay_fixed = 0;
114 element_info[element].change.delay_random = 0;
115 element_info[element].change.successor = EL_EMPTY_SPACE;
117 /* start with no properties at all */
118 for (j=0; j < NUM_EP_BITFIELDS; j++)
119 Properties[element][j] = EP_BITMASK_DEFAULT;
122 BorderElement = EL_STEELWALL;
124 level.no_level_file = FALSE;
126 if (leveldir_current == NULL) /* only when dumping level */
129 /* try to determine better author name than 'anonymous' */
130 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
132 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
133 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
137 switch (LEVELCLASS(leveldir_current))
139 case LEVELCLASS_TUTORIAL:
140 strcpy(level.author, PROGRAM_AUTHOR_STRING);
143 case LEVELCLASS_CONTRIBUTION:
144 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
145 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
148 case LEVELCLASS_USER:
149 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
150 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
154 /* keep default value */
160 static int checkLevelElement(int element)
162 if (element >= NUM_FILE_ELEMENTS)
164 Error(ERR_WARN, "invalid level element %d", element);
165 element = EL_CHAR_QUESTION;
167 else if (element == EL_PLAYER_OBSOLETE)
168 element = EL_PLAYER_1;
169 else if (element == EL_KEY_OBSOLETE)
175 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
177 level->file_version = getFileVersion(file);
178 level->game_version = getFileVersion(file);
183 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
187 lev_fieldx = level->fieldx = fgetc(file);
188 lev_fieldy = level->fieldy = fgetc(file);
190 level->time = getFile16BitBE(file);
191 level->gems_needed = getFile16BitBE(file);
193 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
194 level->name[i] = fgetc(file);
195 level->name[MAX_LEVEL_NAME_LEN] = 0;
197 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
198 level->score[i] = fgetc(file);
200 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
201 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
204 level->yamyam_content[i][x][y] = checkLevelElement(fgetc(file));
206 level->amoeba_speed = fgetc(file);
207 level->time_magic_wall = fgetc(file);
208 level->time_wheel = fgetc(file);
209 level->amoeba_content = checkLevelElement(fgetc(file));
210 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
211 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
212 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
213 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
215 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
220 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
224 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
225 level->author[i] = fgetc(file);
226 level->author[MAX_LEVEL_NAME_LEN] = 0;
231 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
234 int chunk_size_expected = level->fieldx * level->fieldy;
236 /* Note: "chunk_size" was wrong before version 2.0 when elements are
237 stored with 16-bit encoding (and should be twice as big then).
238 Even worse, playfield data was stored 16-bit when only yamyam content
239 contained 16-bit elements and vice versa. */
241 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
242 chunk_size_expected *= 2;
244 if (chunk_size_expected != chunk_size)
246 ReadUnusedBytesFromFile(file, chunk_size);
247 return chunk_size_expected;
250 for(y=0; y<level->fieldy; y++)
251 for(x=0; x<level->fieldx; x++)
252 Feld[x][y] = Ur[x][y] =
253 checkLevelElement(level->encoding_16bit_field ?
254 getFile16BitBE(file) : fgetc(file));
258 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
262 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
263 int chunk_size_expected = header_size + content_size;
265 /* Note: "chunk_size" was wrong before version 2.0 when elements are
266 stored with 16-bit encoding (and should be twice as big then).
267 Even worse, playfield data was stored 16-bit when only yamyam content
268 contained 16-bit elements and vice versa. */
270 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
271 chunk_size_expected += content_size;
273 if (chunk_size_expected != chunk_size)
275 ReadUnusedBytesFromFile(file, chunk_size);
276 return chunk_size_expected;
280 level->num_yamyam_contents = fgetc(file);
284 /* correct invalid number of content fields -- should never happen */
285 if (level->num_yamyam_contents < 1 ||
286 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
287 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
289 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
292 level->yamyam_content[i][x][y] =
293 checkLevelElement(level->encoding_16bit_field ?
294 getFile16BitBE(file) : fgetc(file));
298 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
302 int num_contents, content_xsize, content_ysize;
303 int content_array[MAX_ELEMENT_CONTENTS][3][3];
305 element = checkLevelElement(getFile16BitBE(file));
306 num_contents = fgetc(file);
307 content_xsize = fgetc(file);
308 content_ysize = fgetc(file);
309 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
311 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
314 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
316 /* correct invalid number of content fields -- should never happen */
317 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
318 num_contents = STD_ELEMENT_CONTENTS;
320 if (element == EL_YAMYAM)
322 level->num_yamyam_contents = num_contents;
324 for(i=0; i<num_contents; i++)
327 level->yamyam_content[i][x][y] = content_array[i][x][y];
329 else if (element == EL_BD_AMOEBA)
331 level->amoeba_content = content_array[0][0][0];
335 Error(ERR_WARN, "cannot load content for element '%d'", element);
341 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
343 int num_changed_custom_elements = getFile16BitBE(file);
344 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
347 if (chunk_size_expected != chunk_size)
349 ReadUnusedBytesFromFile(file, chunk_size - 2);
350 return chunk_size_expected;
353 for (i=0; i < num_changed_custom_elements; i++)
355 int element = getFile16BitBE(file);
356 int properties = getFile32BitBE(file);
358 if (IS_CUSTOM_ELEMENT(element))
359 Properties[element][EP_BITFIELD_BASE] = properties;
361 Error(ERR_WARN, "invalid custom element number %d", element);
367 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
369 int num_changed_custom_elements = getFile16BitBE(file);
370 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
373 if (chunk_size_expected != chunk_size)
375 ReadUnusedBytesFromFile(file, chunk_size - 2);
376 return chunk_size_expected;
379 for (i=0; i < num_changed_custom_elements; i++)
381 int element = getFile16BitBE(file);
382 int custom_element_successor = getFile16BitBE(file);
384 if (IS_CUSTOM_ELEMENT(element))
385 element_info[element].change.successor = custom_element_successor;
387 Error(ERR_WARN, "invalid custom element number %d", element);
393 void LoadLevelFromFilename(char *filename)
395 char cookie[MAX_LINE_LEN];
396 char chunk_name[CHUNK_ID_LEN + 1];
400 /* always start with reliable default values */
401 setLevelInfoToDefaults();
403 if (!(file = fopen(filename, MODE_READ)))
405 level.no_level_file = TRUE;
407 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
411 getFileChunkBE(file, chunk_name, NULL);
412 if (strcmp(chunk_name, "RND1") == 0)
414 getFile32BitBE(file); /* not used */
416 getFileChunkBE(file, chunk_name, NULL);
417 if (strcmp(chunk_name, "CAVE") != 0)
419 Error(ERR_WARN, "unknown format of level file '%s'", filename);
424 else /* check for pre-2.0 file format with cookie string */
426 strcpy(cookie, chunk_name);
427 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
428 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
429 cookie[strlen(cookie) - 1] = '\0';
431 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
433 Error(ERR_WARN, "unknown format of level file '%s'", filename);
438 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
440 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
445 /* pre-2.0 level files have no game version, so use file version here */
446 level.game_version = level.file_version;
449 if (level.file_version < FILE_VERSION_1_2)
451 /* level files from versions before 1.2.0 without chunk structure */
452 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
453 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
461 int (*loader)(FILE *, int, struct LevelInfo *);
465 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
466 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
467 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
468 { "BODY", -1, LoadLevel_BODY },
469 { "CONT", -1, LoadLevel_CONT },
470 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
471 { "CUS1", -1, LoadLevel_CUS1 },
472 { "CUS2", -1, LoadLevel_CUS2 },
476 while (getFileChunkBE(file, chunk_name, &chunk_size))
480 while (chunk_info[i].name != NULL &&
481 strcmp(chunk_name, chunk_info[i].name) != 0)
484 if (chunk_info[i].name == NULL)
486 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
487 chunk_name, filename);
488 ReadUnusedBytesFromFile(file, chunk_size);
490 else if (chunk_info[i].size != -1 &&
491 chunk_info[i].size != chunk_size)
493 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
494 chunk_size, chunk_name, filename);
495 ReadUnusedBytesFromFile(file, chunk_size);
499 /* call function to load this level chunk */
500 int chunk_size_expected =
501 (chunk_info[i].loader)(file, chunk_size, &level);
503 /* the size of some chunks cannot be checked before reading other
504 chunks first (like "HEAD" and "BODY") that contain some header
505 information, so check them here */
506 if (chunk_size_expected != chunk_size)
508 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
509 chunk_size, chunk_name, filename);
517 if (leveldir_current == NULL) /* only when dumping level */
520 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
521 IS_LEVELCLASS_USER(leveldir_current))
523 /* For user contributed and private levels, use the version of
524 the game engine the levels were created for.
525 Since 2.0.1, the game engine version is now directly stored
526 in the level file (chunk "VERS"), so there is no need anymore
527 to set the game version from the file version (except for old,
528 pre-2.0 levels, where the game version is still taken from the
529 file format version used to store the level -- see above). */
531 /* do some special adjustments to support older level versions */
532 if (level.file_version == FILE_VERSION_1_0)
534 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
535 Error(ERR_WARN, "using high speed movement for player");
537 /* player was faster than monsters in (pre-)1.0 levels */
538 level.double_speed = TRUE;
541 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
542 if (level.game_version == VERSION_IDENT(2,0,1))
543 level.em_slippery_gems = TRUE;
547 /* Always use the latest version of the game engine for all but
548 user contributed and private levels; this allows for actual
549 corrections in the game engine to take effect for existing,
550 converted levels (from "classic" or other existing games) to
551 make the game emulation more accurate, while (hopefully) not
552 breaking existing levels created from other players. */
554 level.game_version = GAME_VERSION_ACTUAL;
556 /* Set special EM style gems behaviour: EM style gems slip down from
557 normal, steel and growing wall. As this is a more fundamental change,
558 it seems better to set the default behaviour to "off" (as it is more
559 natural) and make it configurable in the level editor (as a property
560 of gem style elements). Already existing converted levels (neither
561 private nor contributed levels) are changed to the new behaviour. */
563 if (level.file_version < FILE_VERSION_2_0)
564 level.em_slippery_gems = TRUE;
567 /* map some elements which have changed in newer versions */
568 if (level.game_version <= VERSION_IDENT(2,2,0))
572 /* map game font elements */
573 for(y=0; y<level.fieldy; y++)
575 for(x=0; x<level.fieldx; x++)
577 int element = Ur[x][y];
579 if (element == EL_CHAR('['))
580 element = EL_CHAR_AUMLAUT;
581 else if (element == EL_CHAR('\\'))
582 element = EL_CHAR_OUMLAUT;
583 else if (element == EL_CHAR(']'))
584 element = EL_CHAR_UUMLAUT;
585 else if (element == EL_CHAR('^'))
586 element = EL_CHAR_COPYRIGHT;
588 Feld[x][y] = Ur[x][y] = element;
593 /* determine border element for this level */
597 void LoadLevel(int level_nr)
599 char *filename = getLevelFilename(level_nr);
601 LoadLevelFromFilename(filename);
602 InitElementPropertiesEngine(level.game_version);
605 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
607 putFileVersion(file, level->file_version);
608 putFileVersion(file, level->game_version);
611 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
615 fputc(level->fieldx, file);
616 fputc(level->fieldy, file);
618 putFile16BitBE(file, level->time);
619 putFile16BitBE(file, level->gems_needed);
621 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
622 fputc(level->name[i], file);
624 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
625 fputc(level->score[i], file);
627 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
630 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
631 level->yamyam_content[i][x][y]),
633 fputc(level->amoeba_speed, file);
634 fputc(level->time_magic_wall, file);
635 fputc(level->time_wheel, file);
636 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
638 fputc((level->double_speed ? 1 : 0), file);
639 fputc((level->gravity ? 1 : 0), file);
640 fputc((level->encoding_16bit_field ? 1 : 0), file);
641 fputc((level->em_slippery_gems ? 1 : 0), file);
643 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
646 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
650 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
651 fputc(level->author[i], file);
654 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
658 for(y=0; y<level->fieldy; y++)
659 for(x=0; x<level->fieldx; x++)
660 if (level->encoding_16bit_field)
661 putFile16BitBE(file, Ur[x][y]);
663 fputc(Ur[x][y], file);
667 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
671 fputc(EL_YAMYAM, file);
672 fputc(level->num_yamyam_contents, file);
676 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
679 if (level->encoding_16bit_field)
680 putFile16BitBE(file, level->yamyam_content[i][x][y]);
682 fputc(level->yamyam_content[i][x][y], file);
686 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
689 int num_contents, content_xsize, content_ysize;
690 int content_array[MAX_ELEMENT_CONTENTS][3][3];
692 if (element == EL_YAMYAM)
694 num_contents = level->num_yamyam_contents;
698 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
701 content_array[i][x][y] = level->yamyam_content[i][x][y];
703 else if (element == EL_BD_AMOEBA)
709 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
712 content_array[i][x][y] = EL_EMPTY;
713 content_array[0][0][0] = level->amoeba_content;
717 /* chunk header already written -- write empty chunk data */
718 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
720 Error(ERR_WARN, "cannot save content for element '%d'", element);
724 putFile16BitBE(file, element);
725 fputc(num_contents, file);
726 fputc(content_xsize, file);
727 fputc(content_ysize, file);
729 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
731 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
734 putFile16BitBE(file, content_array[i][x][y]);
737 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
738 int num_changed_custom_elements)
742 putFile16BitBE(file, num_changed_custom_elements);
744 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
746 int element = EL_CUSTOM_START + i;
748 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
750 if (check < num_changed_custom_elements)
752 putFile16BitBE(file, element);
753 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
760 if (check != num_changed_custom_elements) /* should not happen */
761 Error(ERR_WARN, "inconsistent number of custom element properties");
764 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
765 int num_changed_custom_elements)
769 putFile16BitBE(file, num_changed_custom_elements);
771 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
773 int element = EL_CUSTOM_START + i;
775 if (element_info[element].change.successor != EL_EMPTY_SPACE)
777 if (check < num_changed_custom_elements)
779 putFile16BitBE(file, element);
780 putFile16BitBE(file, element_info[element].change.successor);
787 if (check != num_changed_custom_elements) /* should not happen */
788 Error(ERR_WARN, "inconsistent number of custom element successors");
791 void SaveLevel(int level_nr)
793 char *filename = getLevelFilename(level_nr);
795 int num_changed_custom_elements1 = 0;
796 int num_changed_custom_elements2 = 0;
800 if (!(file = fopen(filename, MODE_WRITE)))
802 Error(ERR_WARN, "cannot save level file '%s'", filename);
806 level.file_version = FILE_VERSION_ACTUAL;
807 level.game_version = GAME_VERSION_ACTUAL;
809 /* check level field for 16-bit elements */
810 level.encoding_16bit_field = FALSE;
811 for(y=0; y<level.fieldy; y++)
812 for(x=0; x<level.fieldx; x++)
814 level.encoding_16bit_field = TRUE;
816 /* check yamyam content for 16-bit elements */
817 level.encoding_16bit_yamyam = FALSE;
818 for(i=0; i<level.num_yamyam_contents; i++)
821 if (level.yamyam_content[i][x][y] > 255)
822 level.encoding_16bit_yamyam = TRUE;
824 /* check amoeba content for 16-bit elements */
825 level.encoding_16bit_amoeba = FALSE;
826 if (level.amoeba_content > 255)
827 level.encoding_16bit_amoeba = TRUE;
829 /* calculate size of "BODY" chunk */
831 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
833 /* check for non-standard custom elements and calculate "CUS1" chunk size */
834 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
835 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
836 num_changed_custom_elements1++;
838 /* check for non-standard custom elements and calculate "CUS2" chunk size */
839 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
840 if (element_info[EL_CUSTOM_START + i].change.successor != EL_EMPTY_SPACE)
841 num_changed_custom_elements2++;
843 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
844 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
846 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
847 SaveLevel_VERS(file, &level);
849 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
850 SaveLevel_HEAD(file, &level);
852 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
853 SaveLevel_AUTH(file, &level);
855 putFileChunkBE(file, "BODY", body_chunk_size);
856 SaveLevel_BODY(file, &level);
858 if (level.encoding_16bit_yamyam ||
859 level.num_yamyam_contents != STD_ELEMENT_CONTENTS)
861 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
862 SaveLevel_CNT2(file, &level, EL_YAMYAM);
865 if (level.encoding_16bit_amoeba)
867 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
868 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
871 if (num_changed_custom_elements1 > 0)
873 putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements1 * 6);
874 SaveLevel_CUS1(file, &level, num_changed_custom_elements1);
877 if (num_changed_custom_elements2 > 0)
879 putFileChunkBE(file, "CUS2", 2 + num_changed_custom_elements2 * 4);
880 SaveLevel_CUS2(file, &level, num_changed_custom_elements2);
885 SetFilePermissions(filename, PERMS_PRIVATE);
888 void DumpLevel(struct LevelInfo *level)
890 printf_line("-", 79);
891 printf("Level xxx (file version %08d, game version %08d)\n",
892 level->file_version, level->game_version);
893 printf_line("-", 79);
895 printf("Level Author: '%s'\n", level->author);
896 printf("Level Title: '%s'\n", level->name);
898 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
900 printf("Level Time: %d seconds\n", level->time);
901 printf("Gems needed: %d\n", level->gems_needed);
903 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
904 printf("Time for Wheel: %d seconds\n", level->time_wheel);
905 printf("Time for Light: %d seconds\n", level->time_light);
906 printf("Time for Timegate: %d seconds\n", level->time_timegate);
908 printf("Amoeba Speed: %d\n", level->amoeba_speed);
910 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
911 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
912 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
914 printf_line("-", 79);
918 /* ========================================================================= */
919 /* tape file functions */
920 /* ========================================================================= */
922 static void setTapeInfoToDefaults()
926 /* always start with reliable default values (empty tape) */
929 /* default values (also for pre-1.2 tapes) with only the first player */
930 tape.player_participates[0] = TRUE;
931 for(i=1; i<MAX_PLAYERS; i++)
932 tape.player_participates[i] = FALSE;
934 /* at least one (default: the first) player participates in every tape */
935 tape.num_participating_players = 1;
937 tape.level_nr = level_nr;
939 tape.changed = FALSE;
941 tape.recording = FALSE;
942 tape.playing = FALSE;
943 tape.pausing = FALSE;
946 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
948 tape->file_version = getFileVersion(file);
949 tape->game_version = getFileVersion(file);
954 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
958 tape->random_seed = getFile32BitBE(file);
959 tape->date = getFile32BitBE(file);
960 tape->length = getFile32BitBE(file);
962 /* read header fields that are new since version 1.2 */
963 if (tape->file_version >= FILE_VERSION_1_2)
965 byte store_participating_players = fgetc(file);
968 /* since version 1.2, tapes store which players participate in the tape */
969 tape->num_participating_players = 0;
970 for(i=0; i<MAX_PLAYERS; i++)
972 tape->player_participates[i] = FALSE;
974 if (store_participating_players & (1 << i))
976 tape->player_participates[i] = TRUE;
977 tape->num_participating_players++;
981 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
983 engine_version = getFileVersion(file);
984 if (engine_version > 0)
985 tape->engine_version = engine_version;
991 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
993 int level_identifier_size;
996 level_identifier_size = getFile16BitBE(file);
998 tape->level_identifier =
999 checked_realloc(tape->level_identifier, level_identifier_size);
1001 for(i=0; i < level_identifier_size; i++)
1002 tape->level_identifier[i] = fgetc(file);
1004 tape->level_nr = getFile16BitBE(file);
1006 chunk_size = 2 + level_identifier_size + 2;
1011 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1014 int chunk_size_expected =
1015 (tape->num_participating_players + 1) * tape->length;
1017 if (chunk_size_expected != chunk_size)
1019 ReadUnusedBytesFromFile(file, chunk_size);
1020 return chunk_size_expected;
1023 for(i=0; i<tape->length; i++)
1025 if (i >= MAX_TAPELEN)
1028 for(j=0; j<MAX_PLAYERS; j++)
1030 tape->pos[i].action[j] = MV_NO_MOVING;
1032 if (tape->player_participates[j])
1033 tape->pos[i].action[j] = fgetc(file);
1036 tape->pos[i].delay = fgetc(file);
1038 if (tape->file_version == FILE_VERSION_1_0)
1040 /* eliminate possible diagonal moves in old tapes */
1041 /* this is only for backward compatibility */
1043 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1044 byte action = tape->pos[i].action[0];
1045 int k, num_moves = 0;
1049 if (action & joy_dir[k])
1051 tape->pos[i + num_moves].action[0] = joy_dir[k];
1053 tape->pos[i + num_moves].delay = 0;
1062 tape->length += num_moves;
1065 else if (tape->file_version < FILE_VERSION_2_0)
1067 /* convert pre-2.0 tapes to new tape format */
1069 if (tape->pos[i].delay > 1)
1072 tape->pos[i + 1] = tape->pos[i];
1073 tape->pos[i + 1].delay = 1;
1076 for(j=0; j<MAX_PLAYERS; j++)
1077 tape->pos[i].action[j] = MV_NO_MOVING;
1078 tape->pos[i].delay--;
1089 if (i != tape->length)
1090 chunk_size = (tape->num_participating_players + 1) * i;
1095 void LoadTapeFromFilename(char *filename)
1097 char cookie[MAX_LINE_LEN];
1098 char chunk_name[CHUNK_ID_LEN + 1];
1102 /* always start with reliable default values */
1103 setTapeInfoToDefaults();
1105 if (!(file = fopen(filename, MODE_READ)))
1108 getFileChunkBE(file, chunk_name, NULL);
1109 if (strcmp(chunk_name, "RND1") == 0)
1111 getFile32BitBE(file); /* not used */
1113 getFileChunkBE(file, chunk_name, NULL);
1114 if (strcmp(chunk_name, "TAPE") != 0)
1116 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1121 else /* check for pre-2.0 file format with cookie string */
1123 strcpy(cookie, chunk_name);
1124 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1125 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1126 cookie[strlen(cookie) - 1] = '\0';
1128 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1130 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1135 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1137 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1142 /* pre-2.0 tape files have no game version, so use file version here */
1143 tape.game_version = tape.file_version;
1146 if (tape.file_version < FILE_VERSION_1_2)
1148 /* tape files from versions before 1.2.0 without chunk structure */
1149 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1150 LoadTape_BODY(file, 2 * tape.length, &tape);
1158 int (*loader)(FILE *, int, struct TapeInfo *);
1162 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1163 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1164 { "INFO", -1, LoadTape_INFO },
1165 { "BODY", -1, LoadTape_BODY },
1169 while (getFileChunkBE(file, chunk_name, &chunk_size))
1173 while (chunk_info[i].name != NULL &&
1174 strcmp(chunk_name, chunk_info[i].name) != 0)
1177 if (chunk_info[i].name == NULL)
1179 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1180 chunk_name, filename);
1181 ReadUnusedBytesFromFile(file, chunk_size);
1183 else if (chunk_info[i].size != -1 &&
1184 chunk_info[i].size != chunk_size)
1186 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1187 chunk_size, chunk_name, filename);
1188 ReadUnusedBytesFromFile(file, chunk_size);
1192 /* call function to load this tape chunk */
1193 int chunk_size_expected =
1194 (chunk_info[i].loader)(file, chunk_size, &tape);
1196 /* the size of some chunks cannot be checked before reading other
1197 chunks first (like "HEAD" and "BODY") that contain some header
1198 information, so check them here */
1199 if (chunk_size_expected != chunk_size)
1201 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1202 chunk_size, chunk_name, filename);
1210 tape.length_seconds = GetTapeLength();
1213 void LoadTape(int level_nr)
1215 char *filename = getTapeFilename(level_nr);
1217 LoadTapeFromFilename(filename);
1220 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1222 putFileVersion(file, tape->file_version);
1223 putFileVersion(file, tape->game_version);
1226 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1229 byte store_participating_players = 0;
1231 /* set bits for participating players for compact storage */
1232 for(i=0; i<MAX_PLAYERS; i++)
1233 if (tape->player_participates[i])
1234 store_participating_players |= (1 << i);
1236 putFile32BitBE(file, tape->random_seed);
1237 putFile32BitBE(file, tape->date);
1238 putFile32BitBE(file, tape->length);
1240 fputc(store_participating_players, file);
1242 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1243 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1245 putFileVersion(file, tape->engine_version);
1248 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1250 int level_identifier_size = strlen(tape->level_identifier) + 1;
1253 putFile16BitBE(file, level_identifier_size);
1255 for(i=0; i < level_identifier_size; i++)
1256 fputc(tape->level_identifier[i], file);
1258 putFile16BitBE(file, tape->level_nr);
1261 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1265 for(i=0; i<tape->length; i++)
1267 for(j=0; j<MAX_PLAYERS; j++)
1268 if (tape->player_participates[j])
1269 fputc(tape->pos[i].action[j], file);
1271 fputc(tape->pos[i].delay, file);
1275 void SaveTape(int level_nr)
1277 char *filename = getTapeFilename(level_nr);
1279 boolean new_tape = TRUE;
1280 int num_participating_players = 0;
1281 int info_chunk_size;
1282 int body_chunk_size;
1285 InitTapeDirectory(leveldir_current->filename);
1287 /* if a tape still exists, ask to overwrite it */
1288 if (access(filename, F_OK) == 0)
1291 if (!Request("Replace old tape ?", REQ_ASK))
1295 if (!(file = fopen(filename, MODE_WRITE)))
1297 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1301 tape.file_version = FILE_VERSION_ACTUAL;
1302 tape.game_version = GAME_VERSION_ACTUAL;
1304 /* count number of participating players */
1305 for(i=0; i<MAX_PLAYERS; i++)
1306 if (tape.player_participates[i])
1307 num_participating_players++;
1309 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1310 body_chunk_size = (num_participating_players + 1) * tape.length;
1312 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1313 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1315 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1316 SaveTape_VERS(file, &tape);
1318 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1319 SaveTape_HEAD(file, &tape);
1321 putFileChunkBE(file, "INFO", info_chunk_size);
1322 SaveTape_INFO(file, &tape);
1324 putFileChunkBE(file, "BODY", body_chunk_size);
1325 SaveTape_BODY(file, &tape);
1329 SetFilePermissions(filename, PERMS_PRIVATE);
1331 tape.changed = FALSE;
1334 Request("tape saved !", REQ_CONFIRM);
1337 void DumpTape(struct TapeInfo *tape)
1341 if (TAPE_IS_EMPTY(*tape))
1343 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1347 printf_line("-", 79);
1348 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1349 tape->level_nr, tape->file_version, tape->game_version);
1350 printf("Level series identifier: '%s'\n", tape->level_identifier);
1351 printf_line("-", 79);
1353 for(i=0; i<tape->length; i++)
1355 if (i >= MAX_TAPELEN)
1358 printf("%03d: ", i);
1360 for(j=0; j<MAX_PLAYERS; j++)
1362 if (tape->player_participates[j])
1364 int action = tape->pos[i].action[j];
1366 printf("%d:%02x ", j, action);
1367 printf("[%c%c%c%c|%c%c] - ",
1368 (action & JOY_LEFT ? '<' : ' '),
1369 (action & JOY_RIGHT ? '>' : ' '),
1370 (action & JOY_UP ? '^' : ' '),
1371 (action & JOY_DOWN ? 'v' : ' '),
1372 (action & JOY_BUTTON_1 ? '1' : ' '),
1373 (action & JOY_BUTTON_2 ? '2' : ' '));
1377 printf("(%03d)\n", tape->pos[i].delay);
1380 printf_line("-", 79);
1384 /* ========================================================================= */
1385 /* score file functions */
1386 /* ========================================================================= */
1388 void LoadScore(int level_nr)
1391 char *filename = getScoreFilename(level_nr);
1392 char cookie[MAX_LINE_LEN];
1393 char line[MAX_LINE_LEN];
1397 /* always start with reliable default values */
1398 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1400 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1401 highscore[i].Score = 0;
1404 if (!(file = fopen(filename, MODE_READ)))
1407 /* check file identifier */
1408 fgets(cookie, MAX_LINE_LEN, file);
1409 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1410 cookie[strlen(cookie) - 1] = '\0';
1412 if (!checkCookieString(cookie, SCORE_COOKIE))
1414 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1419 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1421 fscanf(file, "%d", &highscore[i].Score);
1422 fgets(line, MAX_LINE_LEN, file);
1424 if (line[strlen(line) - 1] == '\n')
1425 line[strlen(line) - 1] = '\0';
1427 for (line_ptr = line; *line_ptr; line_ptr++)
1429 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1431 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1432 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1441 void SaveScore(int level_nr)
1444 char *filename = getScoreFilename(level_nr);
1447 InitScoreDirectory(leveldir_current->filename);
1449 if (!(file = fopen(filename, MODE_WRITE)))
1451 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1455 fprintf(file, "%s\n\n", SCORE_COOKIE);
1457 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1458 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1462 SetFilePermissions(filename, PERMS_PUBLIC);
1466 /* ========================================================================= */
1467 /* setup file functions */
1468 /* ========================================================================= */
1470 #define TOKEN_STR_PLAYER_PREFIX "player_"
1473 #define SETUP_TOKEN_PLAYER_NAME 0
1474 #define SETUP_TOKEN_SOUND 1
1475 #define SETUP_TOKEN_SOUND_LOOPS 2
1476 #define SETUP_TOKEN_SOUND_MUSIC 3
1477 #define SETUP_TOKEN_SOUND_SIMPLE 4
1478 #define SETUP_TOKEN_TOONS 5
1479 #define SETUP_TOKEN_SCROLL_DELAY 6
1480 #define SETUP_TOKEN_SOFT_SCROLLING 7
1481 #define SETUP_TOKEN_FADING 8
1482 #define SETUP_TOKEN_AUTORECORD 9
1483 #define SETUP_TOKEN_QUICK_DOORS 10
1484 #define SETUP_TOKEN_TEAM_MODE 11
1485 #define SETUP_TOKEN_HANDICAP 12
1486 #define SETUP_TOKEN_TIME_LIMIT 13
1487 #define SETUP_TOKEN_FULLSCREEN 14
1488 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1489 #define SETUP_TOKEN_GRAPHICS_SET 16
1490 #define SETUP_TOKEN_SOUNDS_SET 17
1491 #define SETUP_TOKEN_MUSIC_SET 18
1492 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1493 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1494 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1496 #define NUM_GLOBAL_SETUP_TOKENS 22
1499 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1500 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1501 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1502 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1503 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1504 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1505 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1506 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1507 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1509 #define NUM_EDITOR_SETUP_TOKENS 9
1511 /* shortcut setup */
1512 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1513 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1514 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1516 #define NUM_SHORTCUT_SETUP_TOKENS 3
1519 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1520 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1521 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1522 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1523 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1524 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1525 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1526 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1527 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1528 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1529 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1530 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1531 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1532 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1533 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1534 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1536 #define NUM_PLAYER_SETUP_TOKENS 16
1539 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1540 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1542 #define NUM_SYSTEM_SETUP_TOKENS 2
1545 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1547 #define NUM_OPTIONS_SETUP_TOKENS 1
1550 static struct SetupInfo si;
1551 static struct SetupEditorInfo sei;
1552 static struct SetupShortcutInfo ssi;
1553 static struct SetupInputInfo sii;
1554 static struct SetupSystemInfo syi;
1555 static struct OptionInfo soi;
1557 static struct TokenInfo global_setup_tokens[] =
1559 { TYPE_STRING, &si.player_name, "player_name" },
1560 { TYPE_SWITCH, &si.sound, "sound" },
1561 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1562 { TYPE_SWITCH, &si.sound_music, "background_music" },
1563 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1564 { TYPE_SWITCH, &si.toons, "toons" },
1565 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1566 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1567 { TYPE_SWITCH, &si.fading, "screen_fading" },
1568 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1569 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1570 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1571 { TYPE_SWITCH, &si.handicap, "handicap" },
1572 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1573 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1574 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1575 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1576 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1577 { TYPE_STRING, &si.music_set, "music_set" },
1578 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1579 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1580 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1583 static struct TokenInfo editor_setup_tokens[] =
1585 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1586 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1587 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1588 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1589 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1590 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1591 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1592 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1593 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1596 static struct TokenInfo shortcut_setup_tokens[] =
1598 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1599 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1600 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1603 static struct TokenInfo player_setup_tokens[] =
1605 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1606 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1607 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1608 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1609 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1610 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1611 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1612 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1613 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1614 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1615 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1616 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1617 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1618 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1619 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1620 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1623 static struct TokenInfo system_setup_tokens[] =
1625 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1626 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1629 static struct TokenInfo options_setup_tokens[] =
1631 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1634 static char *get_corrected_login_name(char *login_name)
1636 /* needed because player name must be a fixed length string */
1637 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1639 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1640 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1642 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1643 if (strchr(login_name_new, ' '))
1644 *strchr(login_name_new, ' ') = '\0';
1646 return login_name_new;
1649 static void setSetupInfoToDefaults(struct SetupInfo *si)
1653 si->player_name = get_corrected_login_name(getLoginName());
1656 si->sound_loops = TRUE;
1657 si->sound_music = TRUE;
1658 si->sound_simple = TRUE;
1660 si->double_buffering = TRUE;
1661 si->direct_draw = !si->double_buffering;
1662 si->scroll_delay = TRUE;
1663 si->soft_scrolling = TRUE;
1665 si->autorecord = TRUE;
1666 si->quick_doors = FALSE;
1667 si->team_mode = FALSE;
1668 si->handicap = TRUE;
1669 si->time_limit = TRUE;
1670 si->fullscreen = FALSE;
1671 si->ask_on_escape = TRUE;
1673 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1674 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1675 si->music_set = getStringCopy(MUSIC_SUBDIR);
1676 si->override_level_graphics = FALSE;
1677 si->override_level_sounds = FALSE;
1678 si->override_level_music = FALSE;
1680 si->editor.el_boulderdash = TRUE;
1681 si->editor.el_emerald_mine = TRUE;
1682 si->editor.el_more = TRUE;
1683 si->editor.el_sokoban = TRUE;
1684 si->editor.el_supaplex = TRUE;
1685 si->editor.el_diamond_caves = TRUE;
1686 si->editor.el_dx_boulderdash = TRUE;
1687 si->editor.el_chars = TRUE;
1688 si->editor.el_custom = TRUE;
1690 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1691 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1692 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1694 for (i=0; i<MAX_PLAYERS; i++)
1696 si->input[i].use_joystick = FALSE;
1697 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1698 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1699 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1700 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1701 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1702 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1703 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1704 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1705 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1706 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1707 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1708 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1709 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1710 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1711 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1714 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1715 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1717 si->options.verbose = FALSE;
1720 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
1724 if (!setup_file_hash)
1729 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1730 setSetupInfo(global_setup_tokens, i,
1731 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
1736 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1737 setSetupInfo(editor_setup_tokens, i,
1738 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
1741 /* shortcut setup */
1742 ssi = setup.shortcut;
1743 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1744 setSetupInfo(shortcut_setup_tokens, i,
1745 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
1746 setup.shortcut = ssi;
1749 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1753 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1755 sii = setup.input[pnr];
1756 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1758 char full_token[100];
1760 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1761 setSetupInfo(player_setup_tokens, i,
1762 getHashEntry(setup_file_hash, full_token));
1764 setup.input[pnr] = sii;
1769 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1770 setSetupInfo(system_setup_tokens, i,
1771 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
1775 soi = setup.options;
1776 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1777 setSetupInfo(options_setup_tokens, i,
1778 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
1779 setup.options = soi;
1784 char *filename = getSetupFilename();
1785 SetupFileHash *setup_file_hash = NULL;
1787 /* always start with reliable default values */
1788 setSetupInfoToDefaults(&setup);
1790 setup_file_hash = loadSetupFileHash(filename);
1792 if (setup_file_hash)
1794 char *player_name_new;
1796 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
1797 decodeSetupFileHash(setup_file_hash);
1799 setup.direct_draw = !setup.double_buffering;
1801 freeSetupFileHash(setup_file_hash);
1803 /* needed to work around problems with fixed length strings */
1804 player_name_new = get_corrected_login_name(setup.player_name);
1805 free(setup.player_name);
1806 setup.player_name = player_name_new;
1809 Error(ERR_WARN, "using default setup values");
1814 char *filename = getSetupFilename();
1818 InitUserDataDirectory();
1820 if (!(file = fopen(filename, MODE_WRITE)))
1822 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1826 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1827 getCookie("SETUP")));
1828 fprintf(file, "\n");
1832 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1834 /* just to make things nicer :) */
1835 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1836 i == SETUP_TOKEN_GRAPHICS_SET)
1837 fprintf(file, "\n");
1839 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1844 fprintf(file, "\n");
1845 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1846 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1848 /* shortcut setup */
1849 ssi = setup.shortcut;
1850 fprintf(file, "\n");
1851 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1852 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1855 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1859 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1860 fprintf(file, "\n");
1862 sii = setup.input[pnr];
1863 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1864 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1869 fprintf(file, "\n");
1870 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1871 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1874 soi = setup.options;
1875 fprintf(file, "\n");
1876 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1877 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1881 SetFilePermissions(filename, PERMS_PRIVATE);
1884 void LoadCustomElementDescriptions()
1886 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1887 SetupFileHash *setup_file_hash;
1890 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1892 if (element_info[i].custom_description != NULL)
1894 free(element_info[i].custom_description);
1895 element_info[i].custom_description = NULL;
1899 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1902 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1904 char *token = getStringCat2(element_info[i].token_name, ".name");
1905 char *value = getHashEntry(setup_file_hash, token);
1908 element_info[i].custom_description = getStringCopy(value);
1913 freeSetupFileHash(setup_file_hash);
1916 void LoadSpecialMenuDesignSettings()
1918 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1919 SetupFileHash *setup_file_hash;
1922 /* always start with reliable default values from default config */
1923 for (i=0; image_config_vars[i].token != NULL; i++)
1924 for (j=0; image_config[j].token != NULL; j++)
1925 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
1926 *image_config_vars[i].value =
1927 get_integer_from_string(image_config[j].value);
1929 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1932 /* special case: initialize with default values that may be overwritten */
1933 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
1935 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
1936 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
1937 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
1939 if (value_x != NULL)
1940 menu.draw_xoffset[i] = get_integer_from_string(value_x);
1941 if (value_y != NULL)
1942 menu.draw_yoffset[i] = get_integer_from_string(value_y);
1943 if (list_size != NULL)
1944 menu.list_size[i] = get_integer_from_string(list_size);
1947 /* read (and overwrite with) values that may be specified in config file */
1948 for (i=0; image_config_vars[i].token != NULL; i++)
1950 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
1953 *image_config_vars[i].value = get_integer_from_string(value);
1956 freeSetupFileHash(setup_file_hash);