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 int element = EL_CUSTOM_START + i;
102 level.custom_element[i].use_gfx_element = FALSE;
103 level.custom_element[i].gfx_element = EL_EMPTY_SPACE;
105 level.custom_element[i].change.events = CE_BITMASK_DEFAULT;
106 level.custom_element[i].change.delay_fixed = 0;
107 level.custom_element[i].change.delay_random = 0;
108 level.custom_element[i].change.successor = EL_EMPTY_SPACE;
110 /* start with no properties at all */
111 for (j=0; j < NUM_EP_BITFIELDS; j++)
112 Properties[element][j] = EP_BITMASK_DEFAULT;
115 BorderElement = EL_STEELWALL;
117 level.no_level_file = FALSE;
119 if (leveldir_current == NULL) /* only when dumping level */
122 /* try to determine better author name than 'anonymous' */
123 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
125 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
126 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
130 switch (LEVELCLASS(leveldir_current))
132 case LEVELCLASS_TUTORIAL:
133 strcpy(level.author, PROGRAM_AUTHOR_STRING);
136 case LEVELCLASS_CONTRIBUTION:
137 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
138 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
141 case LEVELCLASS_USER:
142 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
143 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
147 /* keep default value */
153 static int checkLevelElement(int element)
155 if (element >= NUM_FILE_ELEMENTS)
157 Error(ERR_WARN, "invalid level element %d", element);
158 element = EL_CHAR_QUESTION;
160 else if (element == EL_PLAYER_OBSOLETE)
161 element = EL_PLAYER_1;
162 else if (element == EL_KEY_OBSOLETE)
168 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
170 level->file_version = getFileVersion(file);
171 level->game_version = getFileVersion(file);
176 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
180 lev_fieldx = level->fieldx = fgetc(file);
181 lev_fieldy = level->fieldy = fgetc(file);
183 level->time = getFile16BitBE(file);
184 level->gems_needed = getFile16BitBE(file);
186 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
187 level->name[i] = fgetc(file);
188 level->name[MAX_LEVEL_NAME_LEN] = 0;
190 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
191 level->score[i] = fgetc(file);
193 level->num_yam_contents = STD_ELEMENT_CONTENTS;
194 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
197 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
199 level->amoeba_speed = fgetc(file);
200 level->time_magic_wall = fgetc(file);
201 level->time_wheel = fgetc(file);
202 level->amoeba_content = checkLevelElement(fgetc(file));
203 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
204 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
205 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
206 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
208 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
213 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
217 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
218 level->author[i] = fgetc(file);
219 level->author[MAX_LEVEL_NAME_LEN] = 0;
224 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
227 int chunk_size_expected = level->fieldx * level->fieldy;
229 /* Note: "chunk_size" was wrong before version 2.0 when elements are
230 stored with 16-bit encoding (and should be twice as big then).
231 Even worse, playfield data was stored 16-bit when only yamyam content
232 contained 16-bit elements and vice versa. */
234 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
235 chunk_size_expected *= 2;
237 if (chunk_size_expected != chunk_size)
239 ReadUnusedBytesFromFile(file, chunk_size);
240 return chunk_size_expected;
243 for(y=0; y<level->fieldy; y++)
244 for(x=0; x<level->fieldx; x++)
245 Feld[x][y] = Ur[x][y] =
246 checkLevelElement(level->encoding_16bit_field ?
247 getFile16BitBE(file) : fgetc(file));
251 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
255 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
256 int chunk_size_expected = header_size + content_size;
258 /* Note: "chunk_size" was wrong before version 2.0 when elements are
259 stored with 16-bit encoding (and should be twice as big then).
260 Even worse, playfield data was stored 16-bit when only yamyam content
261 contained 16-bit elements and vice versa. */
263 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
264 chunk_size_expected += content_size;
266 if (chunk_size_expected != chunk_size)
268 ReadUnusedBytesFromFile(file, chunk_size);
269 return chunk_size_expected;
273 level->num_yam_contents = fgetc(file);
277 /* correct invalid number of content fields -- should never happen */
278 if (level->num_yam_contents < 1 ||
279 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
280 level->num_yam_contents = STD_ELEMENT_CONTENTS;
282 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
285 level->yam_content[i][x][y] =
286 checkLevelElement(level->encoding_16bit_field ?
287 getFile16BitBE(file) : fgetc(file));
291 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
295 int num_contents, content_xsize, content_ysize;
296 int content_array[MAX_ELEMENT_CONTENTS][3][3];
298 element = checkLevelElement(getFile16BitBE(file));
299 num_contents = fgetc(file);
300 content_xsize = fgetc(file);
301 content_ysize = fgetc(file);
302 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
304 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
307 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
309 /* correct invalid number of content fields -- should never happen */
310 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
311 num_contents = STD_ELEMENT_CONTENTS;
313 if (element == EL_YAMYAM)
315 level->num_yam_contents = num_contents;
317 for(i=0; i<num_contents; i++)
320 level->yam_content[i][x][y] = content_array[i][x][y];
322 else if (element == EL_BD_AMOEBA)
324 level->amoeba_content = content_array[0][0][0];
328 Error(ERR_WARN, "cannot load content for element '%d'", element);
334 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
336 int num_changed_custom_elements = getFile16BitBE(file);
337 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
340 if (chunk_size_expected != chunk_size)
342 ReadUnusedBytesFromFile(file, chunk_size - 2);
343 return chunk_size_expected;
346 for (i=0; i < num_changed_custom_elements; i++)
348 int element = getFile16BitBE(file);
349 int properties = getFile32BitBE(file);
351 if (IS_CUSTOM_ELEMENT(element))
352 Properties[element][EP_BITFIELD_BASE] = properties;
354 Error(ERR_WARN, "invalid custom element number %d", element);
360 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
362 int num_changed_custom_elements = getFile16BitBE(file);
363 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
366 if (chunk_size_expected != chunk_size)
368 ReadUnusedBytesFromFile(file, chunk_size - 2);
369 return chunk_size_expected;
372 for (i=0; i < num_changed_custom_elements; i++)
374 int element = getFile16BitBE(file);
375 int custom_element_successor = getFile16BitBE(file);
376 int i = element - EL_CUSTOM_START;
378 if (IS_CUSTOM_ELEMENT(element))
379 level->custom_element[i].change.successor = custom_element_successor;
381 Error(ERR_WARN, "invalid custom element number %d", element);
387 void LoadLevelFromFilename(char *filename)
389 char cookie[MAX_LINE_LEN];
390 char chunk_name[CHUNK_ID_LEN + 1];
394 /* always start with reliable default values */
395 setLevelInfoToDefaults();
397 if (!(file = fopen(filename, MODE_READ)))
399 level.no_level_file = TRUE;
401 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
405 getFileChunkBE(file, chunk_name, NULL);
406 if (strcmp(chunk_name, "RND1") == 0)
408 getFile32BitBE(file); /* not used */
410 getFileChunkBE(file, chunk_name, NULL);
411 if (strcmp(chunk_name, "CAVE") != 0)
413 Error(ERR_WARN, "unknown format of level file '%s'", filename);
418 else /* check for pre-2.0 file format with cookie string */
420 strcpy(cookie, chunk_name);
421 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
422 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
423 cookie[strlen(cookie) - 1] = '\0';
425 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
427 Error(ERR_WARN, "unknown format of level file '%s'", filename);
432 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
434 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
439 /* pre-2.0 level files have no game version, so use file version here */
440 level.game_version = level.file_version;
443 if (level.file_version < FILE_VERSION_1_2)
445 /* level files from versions before 1.2.0 without chunk structure */
446 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
447 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
455 int (*loader)(FILE *, int, struct LevelInfo *);
459 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
460 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
461 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
462 { "BODY", -1, LoadLevel_BODY },
463 { "CONT", -1, LoadLevel_CONT },
464 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
465 { "CUS1", -1, LoadLevel_CUS1 },
466 { "CUS2", -1, LoadLevel_CUS2 },
470 while (getFileChunkBE(file, chunk_name, &chunk_size))
474 while (chunk_info[i].name != NULL &&
475 strcmp(chunk_name, chunk_info[i].name) != 0)
478 if (chunk_info[i].name == NULL)
480 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
481 chunk_name, filename);
482 ReadUnusedBytesFromFile(file, chunk_size);
484 else if (chunk_info[i].size != -1 &&
485 chunk_info[i].size != chunk_size)
487 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
488 chunk_size, chunk_name, filename);
489 ReadUnusedBytesFromFile(file, chunk_size);
493 /* call function to load this level chunk */
494 int chunk_size_expected =
495 (chunk_info[i].loader)(file, chunk_size, &level);
497 /* the size of some chunks cannot be checked before reading other
498 chunks first (like "HEAD" and "BODY") that contain some header
499 information, so check them here */
500 if (chunk_size_expected != chunk_size)
502 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
503 chunk_size, chunk_name, filename);
511 if (leveldir_current == NULL) /* only when dumping level */
514 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
515 IS_LEVELCLASS_USER(leveldir_current))
517 /* For user contributed and private levels, use the version of
518 the game engine the levels were created for.
519 Since 2.0.1, the game engine version is now directly stored
520 in the level file (chunk "VERS"), so there is no need anymore
521 to set the game version from the file version (except for old,
522 pre-2.0 levels, where the game version is still taken from the
523 file format version used to store the level -- see above). */
525 /* do some special adjustments to support older level versions */
526 if (level.file_version == FILE_VERSION_1_0)
528 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
529 Error(ERR_WARN, "using high speed movement for player");
531 /* player was faster than monsters in (pre-)1.0 levels */
532 level.double_speed = TRUE;
535 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
536 if (level.game_version == VERSION_IDENT(2,0,1))
537 level.em_slippery_gems = TRUE;
541 /* Always use the latest version of the game engine for all but
542 user contributed and private levels; this allows for actual
543 corrections in the game engine to take effect for existing,
544 converted levels (from "classic" or other existing games) to
545 make the game emulation more accurate, while (hopefully) not
546 breaking existing levels created from other players. */
548 level.game_version = GAME_VERSION_ACTUAL;
550 /* Set special EM style gems behaviour: EM style gems slip down from
551 normal, steel and growing wall. As this is a more fundamental change,
552 it seems better to set the default behaviour to "off" (as it is more
553 natural) and make it configurable in the level editor (as a property
554 of gem style elements). Already existing converted levels (neither
555 private nor contributed levels) are changed to the new behaviour. */
557 if (level.file_version < FILE_VERSION_2_0)
558 level.em_slippery_gems = TRUE;
561 /* map some elements which have changed in newer versions */
562 if (level.game_version <= VERSION_IDENT(2,2,0))
566 /* map game font elements */
567 for(y=0; y<level.fieldy; y++)
569 for(x=0; x<level.fieldx; x++)
571 int element = Ur[x][y];
573 if (element == EL_CHAR('['))
574 element = EL_CHAR_AUMLAUT;
575 else if (element == EL_CHAR('\\'))
576 element = EL_CHAR_OUMLAUT;
577 else if (element == EL_CHAR(']'))
578 element = EL_CHAR_UUMLAUT;
579 else if (element == EL_CHAR('^'))
580 element = EL_CHAR_COPYRIGHT;
582 Feld[x][y] = Ur[x][y] = element;
587 /* determine border element for this level */
591 void LoadLevel(int level_nr)
593 char *filename = getLevelFilename(level_nr);
595 LoadLevelFromFilename(filename);
596 InitElementPropertiesEngine(level.game_version);
599 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
601 putFileVersion(file, level->file_version);
602 putFileVersion(file, level->game_version);
605 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
609 fputc(level->fieldx, file);
610 fputc(level->fieldy, file);
612 putFile16BitBE(file, level->time);
613 putFile16BitBE(file, level->gems_needed);
615 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
616 fputc(level->name[i], file);
618 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
619 fputc(level->score[i], file);
621 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
624 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
625 level->yam_content[i][x][y]),
627 fputc(level->amoeba_speed, file);
628 fputc(level->time_magic_wall, file);
629 fputc(level->time_wheel, file);
630 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
632 fputc((level->double_speed ? 1 : 0), file);
633 fputc((level->gravity ? 1 : 0), file);
634 fputc((level->encoding_16bit_field ? 1 : 0), file);
635 fputc((level->em_slippery_gems ? 1 : 0), file);
637 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
640 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
644 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
645 fputc(level->author[i], file);
648 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
652 for(y=0; y<level->fieldy; y++)
653 for(x=0; x<level->fieldx; x++)
654 if (level->encoding_16bit_field)
655 putFile16BitBE(file, Ur[x][y]);
657 fputc(Ur[x][y], file);
661 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
665 fputc(EL_YAMYAM, file);
666 fputc(level->num_yam_contents, file);
670 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
673 if (level->encoding_16bit_field)
674 putFile16BitBE(file, level->yam_content[i][x][y]);
676 fputc(level->yam_content[i][x][y], file);
680 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
683 int num_contents, content_xsize, content_ysize;
684 int content_array[MAX_ELEMENT_CONTENTS][3][3];
686 if (element == EL_YAMYAM)
688 num_contents = level->num_yam_contents;
692 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
695 content_array[i][x][y] = level->yam_content[i][x][y];
697 else if (element == EL_BD_AMOEBA)
703 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
706 content_array[i][x][y] = EL_EMPTY;
707 content_array[0][0][0] = level->amoeba_content;
711 /* chunk header already written -- write empty chunk data */
712 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
714 Error(ERR_WARN, "cannot save content for element '%d'", element);
718 putFile16BitBE(file, element);
719 fputc(num_contents, file);
720 fputc(content_xsize, file);
721 fputc(content_ysize, file);
723 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
725 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
728 putFile16BitBE(file, content_array[i][x][y]);
731 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
732 int num_changed_custom_elements)
736 putFile16BitBE(file, num_changed_custom_elements);
738 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
740 int element = EL_CUSTOM_START + i;
742 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
744 if (check < num_changed_custom_elements)
746 putFile16BitBE(file, element);
747 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
754 if (check != num_changed_custom_elements) /* should not happen */
755 Error(ERR_WARN, "inconsistent number of custom element properties");
758 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
759 int num_changed_custom_elements)
763 putFile16BitBE(file, num_changed_custom_elements);
765 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
767 int element = EL_CUSTOM_START + i;
769 if (level->custom_element[i].change.successor != EL_EMPTY_SPACE)
771 if (check < num_changed_custom_elements)
773 putFile16BitBE(file, element);
774 putFile16BitBE(file, level->custom_element[i].change.successor);
781 if (check != num_changed_custom_elements) /* should not happen */
782 Error(ERR_WARN, "inconsistent number of custom element successors");
785 void SaveLevel(int level_nr)
787 char *filename = getLevelFilename(level_nr);
789 int num_changed_custom_elements1 = 0;
790 int num_changed_custom_elements2 = 0;
794 if (!(file = fopen(filename, MODE_WRITE)))
796 Error(ERR_WARN, "cannot save level file '%s'", filename);
800 level.file_version = FILE_VERSION_ACTUAL;
801 level.game_version = GAME_VERSION_ACTUAL;
803 /* check level field for 16-bit elements */
804 level.encoding_16bit_field = FALSE;
805 for(y=0; y<level.fieldy; y++)
806 for(x=0; x<level.fieldx; x++)
808 level.encoding_16bit_field = TRUE;
810 /* check yamyam content for 16-bit elements */
811 level.encoding_16bit_yamyam = FALSE;
812 for(i=0; i<level.num_yam_contents; i++)
815 if (level.yam_content[i][x][y] > 255)
816 level.encoding_16bit_yamyam = TRUE;
818 /* check amoeba content for 16-bit elements */
819 level.encoding_16bit_amoeba = FALSE;
820 if (level.amoeba_content > 255)
821 level.encoding_16bit_amoeba = TRUE;
823 /* calculate size of "BODY" chunk */
825 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
827 /* check for non-standard custom elements and calculate "CUS1" chunk size */
828 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
829 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
830 num_changed_custom_elements1++;
832 /* check for non-standard custom elements and calculate "CUS2" chunk size */
833 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
834 if (level.custom_element[i].change.successor != EL_EMPTY_SPACE)
835 num_changed_custom_elements2++;
837 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
838 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
840 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
841 SaveLevel_VERS(file, &level);
843 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
844 SaveLevel_HEAD(file, &level);
846 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
847 SaveLevel_AUTH(file, &level);
849 putFileChunkBE(file, "BODY", body_chunk_size);
850 SaveLevel_BODY(file, &level);
852 if (level.encoding_16bit_yamyam ||
853 level.num_yam_contents != STD_ELEMENT_CONTENTS)
855 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
856 SaveLevel_CNT2(file, &level, EL_YAMYAM);
859 if (level.encoding_16bit_amoeba)
861 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
862 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
865 if (num_changed_custom_elements1 > 0)
867 putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements1 * 6);
868 SaveLevel_CUS1(file, &level, num_changed_custom_elements1);
871 if (num_changed_custom_elements2 > 0)
873 putFileChunkBE(file, "CUS2", 2 + num_changed_custom_elements2 * 4);
874 SaveLevel_CUS2(file, &level, num_changed_custom_elements2);
879 SetFilePermissions(filename, PERMS_PRIVATE);
882 void DumpLevel(struct LevelInfo *level)
884 printf_line("-", 79);
885 printf("Level xxx (file version %06d, game version %06d)\n",
886 level->file_version, level->game_version);
887 printf_line("-", 79);
889 printf("Level Author: '%s'\n", level->author);
890 printf("Level Title: '%s'\n", level->name);
892 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
894 printf("Level Time: %d seconds\n", level->time);
895 printf("Gems needed: %d\n", level->gems_needed);
897 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
898 printf("Time for Wheel: %d seconds\n", level->time_wheel);
899 printf("Time for Light: %d seconds\n", level->time_light);
900 printf("Time for Timegate: %d seconds\n", level->time_timegate);
902 printf("Amoeba Speed: %d\n", level->amoeba_speed);
904 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
905 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
906 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
908 printf_line("-", 79);
912 /* ========================================================================= */
913 /* tape file functions */
914 /* ========================================================================= */
916 static void setTapeInfoToDefaults()
920 /* always start with reliable default values (empty tape) */
923 /* default values (also for pre-1.2 tapes) with only the first player */
924 tape.player_participates[0] = TRUE;
925 for(i=1; i<MAX_PLAYERS; i++)
926 tape.player_participates[i] = FALSE;
928 /* at least one (default: the first) player participates in every tape */
929 tape.num_participating_players = 1;
931 tape.level_nr = level_nr;
933 tape.changed = FALSE;
935 tape.recording = FALSE;
936 tape.playing = FALSE;
937 tape.pausing = FALSE;
940 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
942 tape->file_version = getFileVersion(file);
943 tape->game_version = getFileVersion(file);
948 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
952 tape->random_seed = getFile32BitBE(file);
953 tape->date = getFile32BitBE(file);
954 tape->length = getFile32BitBE(file);
956 /* read header fields that are new since version 1.2 */
957 if (tape->file_version >= FILE_VERSION_1_2)
959 byte store_participating_players = fgetc(file);
962 /* since version 1.2, tapes store which players participate in the tape */
963 tape->num_participating_players = 0;
964 for(i=0; i<MAX_PLAYERS; i++)
966 tape->player_participates[i] = FALSE;
968 if (store_participating_players & (1 << i))
970 tape->player_participates[i] = TRUE;
971 tape->num_participating_players++;
975 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
977 engine_version = getFileVersion(file);
978 if (engine_version > 0)
979 tape->engine_version = engine_version;
985 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
987 int level_identifier_size;
990 level_identifier_size = getFile16BitBE(file);
992 tape->level_identifier =
993 checked_realloc(tape->level_identifier, level_identifier_size);
995 for(i=0; i < level_identifier_size; i++)
996 tape->level_identifier[i] = fgetc(file);
998 tape->level_nr = getFile16BitBE(file);
1000 chunk_size = 2 + level_identifier_size + 2;
1005 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1008 int chunk_size_expected =
1009 (tape->num_participating_players + 1) * tape->length;
1011 if (chunk_size_expected != chunk_size)
1013 ReadUnusedBytesFromFile(file, chunk_size);
1014 return chunk_size_expected;
1017 for(i=0; i<tape->length; i++)
1019 if (i >= MAX_TAPELEN)
1022 for(j=0; j<MAX_PLAYERS; j++)
1024 tape->pos[i].action[j] = MV_NO_MOVING;
1026 if (tape->player_participates[j])
1027 tape->pos[i].action[j] = fgetc(file);
1030 tape->pos[i].delay = fgetc(file);
1032 if (tape->file_version == FILE_VERSION_1_0)
1034 /* eliminate possible diagonal moves in old tapes */
1035 /* this is only for backward compatibility */
1037 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1038 byte action = tape->pos[i].action[0];
1039 int k, num_moves = 0;
1043 if (action & joy_dir[k])
1045 tape->pos[i + num_moves].action[0] = joy_dir[k];
1047 tape->pos[i + num_moves].delay = 0;
1056 tape->length += num_moves;
1059 else if (tape->file_version < FILE_VERSION_2_0)
1061 /* convert pre-2.0 tapes to new tape format */
1063 if (tape->pos[i].delay > 1)
1066 tape->pos[i + 1] = tape->pos[i];
1067 tape->pos[i + 1].delay = 1;
1070 for(j=0; j<MAX_PLAYERS; j++)
1071 tape->pos[i].action[j] = MV_NO_MOVING;
1072 tape->pos[i].delay--;
1083 if (i != tape->length)
1084 chunk_size = (tape->num_participating_players + 1) * i;
1089 void LoadTapeFromFilename(char *filename)
1091 char cookie[MAX_LINE_LEN];
1092 char chunk_name[CHUNK_ID_LEN + 1];
1096 /* always start with reliable default values */
1097 setTapeInfoToDefaults();
1099 if (!(file = fopen(filename, MODE_READ)))
1102 getFileChunkBE(file, chunk_name, NULL);
1103 if (strcmp(chunk_name, "RND1") == 0)
1105 getFile32BitBE(file); /* not used */
1107 getFileChunkBE(file, chunk_name, NULL);
1108 if (strcmp(chunk_name, "TAPE") != 0)
1110 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1115 else /* check for pre-2.0 file format with cookie string */
1117 strcpy(cookie, chunk_name);
1118 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1119 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1120 cookie[strlen(cookie) - 1] = '\0';
1122 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1124 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1129 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1131 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1136 /* pre-2.0 tape files have no game version, so use file version here */
1137 tape.game_version = tape.file_version;
1140 if (tape.file_version < FILE_VERSION_1_2)
1142 /* tape files from versions before 1.2.0 without chunk structure */
1143 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1144 LoadTape_BODY(file, 2 * tape.length, &tape);
1152 int (*loader)(FILE *, int, struct TapeInfo *);
1156 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1157 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1158 { "INFO", -1, LoadTape_INFO },
1159 { "BODY", -1, LoadTape_BODY },
1163 while (getFileChunkBE(file, chunk_name, &chunk_size))
1167 while (chunk_info[i].name != NULL &&
1168 strcmp(chunk_name, chunk_info[i].name) != 0)
1171 if (chunk_info[i].name == NULL)
1173 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1174 chunk_name, filename);
1175 ReadUnusedBytesFromFile(file, chunk_size);
1177 else if (chunk_info[i].size != -1 &&
1178 chunk_info[i].size != chunk_size)
1180 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1181 chunk_size, chunk_name, filename);
1182 ReadUnusedBytesFromFile(file, chunk_size);
1186 /* call function to load this tape chunk */
1187 int chunk_size_expected =
1188 (chunk_info[i].loader)(file, chunk_size, &tape);
1190 /* the size of some chunks cannot be checked before reading other
1191 chunks first (like "HEAD" and "BODY") that contain some header
1192 information, so check them here */
1193 if (chunk_size_expected != chunk_size)
1195 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1196 chunk_size, chunk_name, filename);
1204 tape.length_seconds = GetTapeLength();
1207 void LoadTape(int level_nr)
1209 char *filename = getTapeFilename(level_nr);
1211 LoadTapeFromFilename(filename);
1214 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1216 putFileVersion(file, tape->file_version);
1217 putFileVersion(file, tape->game_version);
1220 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1223 byte store_participating_players = 0;
1225 /* set bits for participating players for compact storage */
1226 for(i=0; i<MAX_PLAYERS; i++)
1227 if (tape->player_participates[i])
1228 store_participating_players |= (1 << i);
1230 putFile32BitBE(file, tape->random_seed);
1231 putFile32BitBE(file, tape->date);
1232 putFile32BitBE(file, tape->length);
1234 fputc(store_participating_players, file);
1236 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1237 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1239 putFileVersion(file, tape->engine_version);
1242 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1244 int level_identifier_size = strlen(tape->level_identifier) + 1;
1247 putFile16BitBE(file, level_identifier_size);
1249 for(i=0; i < level_identifier_size; i++)
1250 fputc(tape->level_identifier[i], file);
1252 putFile16BitBE(file, tape->level_nr);
1255 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1259 for(i=0; i<tape->length; i++)
1261 for(j=0; j<MAX_PLAYERS; j++)
1262 if (tape->player_participates[j])
1263 fputc(tape->pos[i].action[j], file);
1265 fputc(tape->pos[i].delay, file);
1269 void SaveTape(int level_nr)
1271 char *filename = getTapeFilename(level_nr);
1273 boolean new_tape = TRUE;
1274 int num_participating_players = 0;
1275 int info_chunk_size;
1276 int body_chunk_size;
1279 InitTapeDirectory(leveldir_current->filename);
1281 /* if a tape still exists, ask to overwrite it */
1282 if (access(filename, F_OK) == 0)
1285 if (!Request("Replace old tape ?", REQ_ASK))
1289 if (!(file = fopen(filename, MODE_WRITE)))
1291 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1295 tape.file_version = FILE_VERSION_ACTUAL;
1296 tape.game_version = GAME_VERSION_ACTUAL;
1298 /* count number of participating players */
1299 for(i=0; i<MAX_PLAYERS; i++)
1300 if (tape.player_participates[i])
1301 num_participating_players++;
1303 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1304 body_chunk_size = (num_participating_players + 1) * tape.length;
1306 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1307 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1309 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1310 SaveTape_VERS(file, &tape);
1312 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1313 SaveTape_HEAD(file, &tape);
1315 putFileChunkBE(file, "INFO", info_chunk_size);
1316 SaveTape_INFO(file, &tape);
1318 putFileChunkBE(file, "BODY", body_chunk_size);
1319 SaveTape_BODY(file, &tape);
1323 SetFilePermissions(filename, PERMS_PRIVATE);
1325 tape.changed = FALSE;
1328 Request("tape saved !", REQ_CONFIRM);
1331 void DumpTape(struct TapeInfo *tape)
1335 if (TAPE_IS_EMPTY(*tape))
1337 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1341 printf_line("-", 79);
1342 printf("Tape of Level %03d (file version %06d, game version %06d)\n",
1343 tape->level_nr, tape->file_version, tape->game_version);
1344 printf("Level series identifier: '%s'\n", tape->level_identifier);
1345 printf_line("-", 79);
1347 for(i=0; i<tape->length; i++)
1349 if (i >= MAX_TAPELEN)
1352 printf("%03d: ", i);
1354 for(j=0; j<MAX_PLAYERS; j++)
1356 if (tape->player_participates[j])
1358 int action = tape->pos[i].action[j];
1360 printf("%d:%02x ", j, action);
1361 printf("[%c%c%c%c|%c%c] - ",
1362 (action & JOY_LEFT ? '<' : ' '),
1363 (action & JOY_RIGHT ? '>' : ' '),
1364 (action & JOY_UP ? '^' : ' '),
1365 (action & JOY_DOWN ? 'v' : ' '),
1366 (action & JOY_BUTTON_1 ? '1' : ' '),
1367 (action & JOY_BUTTON_2 ? '2' : ' '));
1371 printf("(%03d)\n", tape->pos[i].delay);
1374 printf_line("-", 79);
1378 /* ========================================================================= */
1379 /* score file functions */
1380 /* ========================================================================= */
1382 void LoadScore(int level_nr)
1385 char *filename = getScoreFilename(level_nr);
1386 char cookie[MAX_LINE_LEN];
1387 char line[MAX_LINE_LEN];
1391 /* always start with reliable default values */
1392 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1394 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1395 highscore[i].Score = 0;
1398 if (!(file = fopen(filename, MODE_READ)))
1401 /* check file identifier */
1402 fgets(cookie, MAX_LINE_LEN, file);
1403 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1404 cookie[strlen(cookie) - 1] = '\0';
1406 if (!checkCookieString(cookie, SCORE_COOKIE))
1408 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1413 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1415 fscanf(file, "%d", &highscore[i].Score);
1416 fgets(line, MAX_LINE_LEN, file);
1418 if (line[strlen(line) - 1] == '\n')
1419 line[strlen(line) - 1] = '\0';
1421 for (line_ptr = line; *line_ptr; line_ptr++)
1423 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1425 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1426 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1435 void SaveScore(int level_nr)
1438 char *filename = getScoreFilename(level_nr);
1441 InitScoreDirectory(leveldir_current->filename);
1443 if (!(file = fopen(filename, MODE_WRITE)))
1445 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1449 fprintf(file, "%s\n\n", SCORE_COOKIE);
1451 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1452 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1456 SetFilePermissions(filename, PERMS_PUBLIC);
1460 /* ========================================================================= */
1461 /* setup file functions */
1462 /* ========================================================================= */
1464 #define TOKEN_STR_PLAYER_PREFIX "player_"
1467 #define SETUP_TOKEN_PLAYER_NAME 0
1468 #define SETUP_TOKEN_SOUND 1
1469 #define SETUP_TOKEN_SOUND_LOOPS 2
1470 #define SETUP_TOKEN_SOUND_MUSIC 3
1471 #define SETUP_TOKEN_SOUND_SIMPLE 4
1472 #define SETUP_TOKEN_TOONS 5
1473 #define SETUP_TOKEN_SCROLL_DELAY 6
1474 #define SETUP_TOKEN_SOFT_SCROLLING 7
1475 #define SETUP_TOKEN_FADING 8
1476 #define SETUP_TOKEN_AUTORECORD 9
1477 #define SETUP_TOKEN_QUICK_DOORS 10
1478 #define SETUP_TOKEN_TEAM_MODE 11
1479 #define SETUP_TOKEN_HANDICAP 12
1480 #define SETUP_TOKEN_TIME_LIMIT 13
1481 #define SETUP_TOKEN_FULLSCREEN 14
1482 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1483 #define SETUP_TOKEN_GRAPHICS_SET 16
1484 #define SETUP_TOKEN_SOUNDS_SET 17
1485 #define SETUP_TOKEN_MUSIC_SET 18
1486 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1487 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1488 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1490 #define NUM_GLOBAL_SETUP_TOKENS 22
1493 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1494 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1495 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1496 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1497 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1498 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1499 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1500 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1501 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1503 #define NUM_EDITOR_SETUP_TOKENS 9
1505 /* shortcut setup */
1506 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1507 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1508 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1510 #define NUM_SHORTCUT_SETUP_TOKENS 3
1513 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1514 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1515 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1516 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1517 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1518 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1519 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1520 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1521 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1522 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1523 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1524 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1525 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1526 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1527 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1528 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1530 #define NUM_PLAYER_SETUP_TOKENS 16
1533 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1534 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1536 #define NUM_SYSTEM_SETUP_TOKENS 2
1539 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1541 #define NUM_OPTIONS_SETUP_TOKENS 1
1544 static struct SetupInfo si;
1545 static struct SetupEditorInfo sei;
1546 static struct SetupShortcutInfo ssi;
1547 static struct SetupInputInfo sii;
1548 static struct SetupSystemInfo syi;
1549 static struct OptionInfo soi;
1551 static struct TokenInfo global_setup_tokens[] =
1553 { TYPE_STRING, &si.player_name, "player_name" },
1554 { TYPE_SWITCH, &si.sound, "sound" },
1555 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1556 { TYPE_SWITCH, &si.sound_music, "background_music" },
1557 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1558 { TYPE_SWITCH, &si.toons, "toons" },
1559 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1560 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1561 { TYPE_SWITCH, &si.fading, "screen_fading" },
1562 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1563 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1564 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1565 { TYPE_SWITCH, &si.handicap, "handicap" },
1566 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1567 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1568 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1569 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1570 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1571 { TYPE_STRING, &si.music_set, "music_set" },
1572 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1573 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1574 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1577 static struct TokenInfo editor_setup_tokens[] =
1579 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1580 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1581 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1582 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1583 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1584 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1585 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1586 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1587 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1590 static struct TokenInfo shortcut_setup_tokens[] =
1592 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1593 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1594 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1597 static struct TokenInfo player_setup_tokens[] =
1599 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1600 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1601 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1602 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1603 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1604 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1605 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1606 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1607 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1608 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1609 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1610 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1611 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1612 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1613 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1614 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1617 static struct TokenInfo system_setup_tokens[] =
1619 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1620 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1623 static struct TokenInfo options_setup_tokens[] =
1625 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1628 static char *get_corrected_login_name(char *login_name)
1630 /* needed because player name must be a fixed length string */
1631 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1633 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1634 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1636 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1637 if (strchr(login_name_new, ' '))
1638 *strchr(login_name_new, ' ') = '\0';
1640 return login_name_new;
1643 static void setSetupInfoToDefaults(struct SetupInfo *si)
1647 si->player_name = get_corrected_login_name(getLoginName());
1650 si->sound_loops = TRUE;
1651 si->sound_music = TRUE;
1652 si->sound_simple = TRUE;
1654 si->double_buffering = TRUE;
1655 si->direct_draw = !si->double_buffering;
1656 si->scroll_delay = TRUE;
1657 si->soft_scrolling = TRUE;
1659 si->autorecord = TRUE;
1660 si->quick_doors = FALSE;
1661 si->team_mode = FALSE;
1662 si->handicap = TRUE;
1663 si->time_limit = TRUE;
1664 si->fullscreen = FALSE;
1665 si->ask_on_escape = TRUE;
1667 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1668 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1669 si->music_set = getStringCopy(MUSIC_SUBDIR);
1670 si->override_level_graphics = FALSE;
1671 si->override_level_sounds = FALSE;
1672 si->override_level_music = FALSE;
1674 si->editor.el_boulderdash = TRUE;
1675 si->editor.el_emerald_mine = TRUE;
1676 si->editor.el_more = TRUE;
1677 si->editor.el_sokoban = TRUE;
1678 si->editor.el_supaplex = TRUE;
1679 si->editor.el_diamond_caves = TRUE;
1680 si->editor.el_dx_boulderdash = TRUE;
1681 si->editor.el_chars = TRUE;
1682 si->editor.el_custom = TRUE;
1684 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1685 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1686 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1688 for (i=0; i<MAX_PLAYERS; i++)
1690 si->input[i].use_joystick = FALSE;
1691 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1692 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1693 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1694 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1695 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1696 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1697 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1698 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1699 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1700 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1701 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1702 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1703 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1704 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1705 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1708 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1709 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1711 si->options.verbose = FALSE;
1714 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
1718 if (!setup_file_hash)
1723 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1724 setSetupInfo(global_setup_tokens, i,
1725 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
1730 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1731 setSetupInfo(editor_setup_tokens, i,
1732 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
1735 /* shortcut setup */
1736 ssi = setup.shortcut;
1737 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1738 setSetupInfo(shortcut_setup_tokens, i,
1739 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
1740 setup.shortcut = ssi;
1743 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1747 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1749 sii = setup.input[pnr];
1750 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1752 char full_token[100];
1754 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1755 setSetupInfo(player_setup_tokens, i,
1756 getHashEntry(setup_file_hash, full_token));
1758 setup.input[pnr] = sii;
1763 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1764 setSetupInfo(system_setup_tokens, i,
1765 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
1769 soi = setup.options;
1770 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1771 setSetupInfo(options_setup_tokens, i,
1772 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
1773 setup.options = soi;
1778 char *filename = getSetupFilename();
1779 SetupFileHash *setup_file_hash = NULL;
1781 /* always start with reliable default values */
1782 setSetupInfoToDefaults(&setup);
1784 setup_file_hash = loadSetupFileHash(filename);
1786 if (setup_file_hash)
1788 char *player_name_new;
1790 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
1791 decodeSetupFileHash(setup_file_hash);
1793 setup.direct_draw = !setup.double_buffering;
1795 freeSetupFileHash(setup_file_hash);
1797 /* needed to work around problems with fixed length strings */
1798 player_name_new = get_corrected_login_name(setup.player_name);
1799 free(setup.player_name);
1800 setup.player_name = player_name_new;
1803 Error(ERR_WARN, "using default setup values");
1808 char *filename = getSetupFilename();
1812 InitUserDataDirectory();
1814 if (!(file = fopen(filename, MODE_WRITE)))
1816 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1820 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1821 getCookie("SETUP")));
1822 fprintf(file, "\n");
1826 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1828 /* just to make things nicer :) */
1829 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1830 i == SETUP_TOKEN_GRAPHICS_SET)
1831 fprintf(file, "\n");
1833 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1838 fprintf(file, "\n");
1839 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1840 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1842 /* shortcut setup */
1843 ssi = setup.shortcut;
1844 fprintf(file, "\n");
1845 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1846 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1849 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1853 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1854 fprintf(file, "\n");
1856 sii = setup.input[pnr];
1857 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1858 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1863 fprintf(file, "\n");
1864 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1865 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1868 soi = setup.options;
1869 fprintf(file, "\n");
1870 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1871 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1875 SetFilePermissions(filename, PERMS_PRIVATE);
1878 void LoadCustomElementDescriptions()
1880 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1881 SetupFileHash *setup_file_hash;
1884 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1886 if (element_info[i].custom_description != NULL)
1888 free(element_info[i].custom_description);
1889 element_info[i].custom_description = NULL;
1893 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1896 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1898 char *token = getStringCat2(element_info[i].token_name, ".name");
1899 char *value = getHashEntry(setup_file_hash, token);
1902 element_info[i].custom_description = getStringCopy(value);
1907 freeSetupFileHash(setup_file_hash);
1910 void LoadSpecialMenuDesignSettings()
1912 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1913 SetupFileHash *setup_file_hash;
1916 /* always start with reliable default values from default config */
1917 for (i=0; image_config_vars[i].token != NULL; i++)
1918 for (j=0; image_config[j].token != NULL; j++)
1919 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
1920 *image_config_vars[i].value =
1921 get_integer_from_string(image_config[j].value);
1923 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1926 /* special case: initialize with default values that may be overwrittem */
1927 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
1929 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
1930 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
1932 if (value_x != NULL)
1933 menu.draw_xoffset[i] = get_integer_from_string(value_x);
1934 if (value_y != NULL)
1935 menu.draw_yoffset[i] = get_integer_from_string(value_y);
1938 /* read (and overwrite with) values that may be specified in config file */
1939 for (i=0; image_config_vars[i].token != NULL; i++)
1941 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
1944 *image_config_vars[i].value = get_integer_from_string(value);
1947 freeSetupFileHash(setup_file_hash);