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"
24 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
25 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
26 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
27 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
28 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
29 #define LEVEL_HEADER_UNUSED 14 /* unused level header bytes */
30 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
31 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
32 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
33 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
35 /* file identifier strings */
36 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
37 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
38 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
41 /* ========================================================================= */
42 /* level file functions */
43 /* ========================================================================= */
45 static void setLevelInfoToDefaults()
49 level.file_version = FILE_VERSION_ACTUAL;
50 level.game_version = GAME_VERSION_ACTUAL;
52 level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
53 level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
54 level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
56 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
57 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
59 for(x=0; x<MAX_LEV_FIELDX; x++)
60 for(y=0; y<MAX_LEV_FIELDY; y++)
61 Feld[x][y] = Ur[x][y] = EL_SAND;
64 level.gems_needed = 0;
65 level.amoeba_speed = 10;
66 level.time_magic_wall = 10;
67 level.time_wheel = 10;
68 level.time_light = 10;
69 level.time_timegate = 10;
70 level.amoeba_content = EL_DIAMOND;
71 level.double_speed = FALSE;
72 level.gravity = FALSE;
73 level.em_slippery_gems = FALSE;
75 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
77 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
78 level.author[i] = '\0';
80 strcpy(level.name, NAMELESS_LEVEL_NAME);
81 strcpy(level.author, ANONYMOUS_NAME);
83 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
86 level.num_yam_contents = STD_ELEMENT_CONTENTS;
87 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
90 level.yam_content[i][x][y] =
91 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
93 Feld[0][0] = Ur[0][0] = EL_PLAYER_1;
94 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
95 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
97 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
99 level.custom_element_successor[i] = EL_EMPTY_SPACE;
100 Properties[EL_CUSTOM_START + i][EP_BITFIELD_BASE] = EP_BITMASK_DEFAULT;
103 BorderElement = EL_STEELWALL;
105 level.no_level_file = FALSE;
107 if (leveldir_current == NULL) /* only when dumping level */
110 /* try to determine better author name than 'anonymous' */
111 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
113 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
114 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
118 switch (LEVELCLASS(leveldir_current))
120 case LEVELCLASS_TUTORIAL:
121 strcpy(level.author, PROGRAM_AUTHOR_STRING);
124 case LEVELCLASS_CONTRIBUTION:
125 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
126 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
129 case LEVELCLASS_USER:
130 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
131 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
135 /* keep default value */
141 static int checkLevelElement(int element)
143 if (element >= NUM_FILE_ELEMENTS)
145 Error(ERR_WARN, "invalid level element %d", element);
146 element = EL_CHAR_QUESTION;
148 else if (element == EL_PLAYER_OBSOLETE)
149 element = EL_PLAYER_1;
150 else if (element == EL_KEY_OBSOLETE)
156 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
158 level->file_version = getFileVersion(file);
159 level->game_version = getFileVersion(file);
164 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
168 lev_fieldx = level->fieldx = fgetc(file);
169 lev_fieldy = level->fieldy = fgetc(file);
171 level->time = getFile16BitBE(file);
172 level->gems_needed = getFile16BitBE(file);
174 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
175 level->name[i] = fgetc(file);
176 level->name[MAX_LEVEL_NAME_LEN] = 0;
178 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
179 level->score[i] = fgetc(file);
181 level->num_yam_contents = STD_ELEMENT_CONTENTS;
182 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
185 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
187 level->amoeba_speed = fgetc(file);
188 level->time_magic_wall = fgetc(file);
189 level->time_wheel = fgetc(file);
190 level->amoeba_content = checkLevelElement(fgetc(file));
191 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
192 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
193 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
194 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
196 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
201 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
205 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
206 level->author[i] = fgetc(file);
207 level->author[MAX_LEVEL_NAME_LEN] = 0;
212 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
215 int chunk_size_expected = level->fieldx * level->fieldy;
217 /* Note: "chunk_size" was wrong before version 2.0 when elements are
218 stored with 16-bit encoding (and should be twice as big then).
219 Even worse, playfield data was stored 16-bit when only yamyam content
220 contained 16-bit elements and vice versa. */
222 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
223 chunk_size_expected *= 2;
225 if (chunk_size_expected != chunk_size)
227 ReadUnusedBytesFromFile(file, chunk_size);
228 return chunk_size_expected;
231 for(y=0; y<level->fieldy; y++)
232 for(x=0; x<level->fieldx; x++)
233 Feld[x][y] = Ur[x][y] =
234 checkLevelElement(level->encoding_16bit_field ?
235 getFile16BitBE(file) : fgetc(file));
239 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
243 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
244 int chunk_size_expected = header_size + content_size;
246 /* Note: "chunk_size" was wrong before version 2.0 when elements are
247 stored with 16-bit encoding (and should be twice as big then).
248 Even worse, playfield data was stored 16-bit when only yamyam content
249 contained 16-bit elements and vice versa. */
251 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
252 chunk_size_expected += content_size;
254 if (chunk_size_expected != chunk_size)
256 ReadUnusedBytesFromFile(file, chunk_size);
257 return chunk_size_expected;
261 level->num_yam_contents = fgetc(file);
265 /* correct invalid number of content fields -- should never happen */
266 if (level->num_yam_contents < 1 ||
267 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
268 level->num_yam_contents = STD_ELEMENT_CONTENTS;
270 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
273 level->yam_content[i][x][y] =
274 checkLevelElement(level->encoding_16bit_field ?
275 getFile16BitBE(file) : fgetc(file));
279 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
283 int num_contents, content_xsize, content_ysize;
284 int content_array[MAX_ELEMENT_CONTENTS][3][3];
286 element = checkLevelElement(getFile16BitBE(file));
287 num_contents = fgetc(file);
288 content_xsize = fgetc(file);
289 content_ysize = fgetc(file);
290 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
292 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
295 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
297 /* correct invalid number of content fields -- should never happen */
298 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
299 num_contents = STD_ELEMENT_CONTENTS;
301 if (element == EL_YAMYAM)
303 level->num_yam_contents = num_contents;
305 for(i=0; i<num_contents; i++)
308 level->yam_content[i][x][y] = content_array[i][x][y];
310 else if (element == EL_BD_AMOEBA)
312 level->amoeba_content = content_array[0][0][0];
316 Error(ERR_WARN, "cannot load content for element '%d'", element);
322 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
324 int num_changed_custom_elements = getFile16BitBE(file);
325 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
328 if (chunk_size_expected != chunk_size)
330 ReadUnusedBytesFromFile(file, chunk_size - 2);
331 return chunk_size_expected;
334 for (i=0; i < num_changed_custom_elements; i++)
336 int element = getFile16BitBE(file);
337 int properties = getFile32BitBE(file);
339 if (IS_CUSTOM_ELEMENT(element))
340 Properties[element][EP_BITFIELD_BASE] = properties;
342 Error(ERR_WARN, "invalid custom element number %d", element);
348 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
350 int num_changed_custom_elements = getFile16BitBE(file);
351 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
354 if (chunk_size_expected != chunk_size)
356 ReadUnusedBytesFromFile(file, chunk_size - 2);
357 return chunk_size_expected;
360 for (i=0; i < num_changed_custom_elements; i++)
362 int element = getFile16BitBE(file);
363 int custom_element_successor = getFile16BitBE(file);
364 int i = element - EL_CUSTOM_START;
366 if (IS_CUSTOM_ELEMENT(element))
367 level->custom_element_successor[i] = custom_element_successor;
369 Error(ERR_WARN, "invalid custom element number %d", element);
375 void LoadLevelFromFilename(char *filename)
377 char cookie[MAX_LINE_LEN];
378 char chunk_name[CHUNK_ID_LEN + 1];
382 /* always start with reliable default values */
383 setLevelInfoToDefaults();
385 if (!(file = fopen(filename, MODE_READ)))
387 level.no_level_file = TRUE;
389 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
393 getFileChunkBE(file, chunk_name, NULL);
394 if (strcmp(chunk_name, "RND1") == 0)
396 getFile32BitBE(file); /* not used */
398 getFileChunkBE(file, chunk_name, NULL);
399 if (strcmp(chunk_name, "CAVE") != 0)
401 Error(ERR_WARN, "unknown format of level file '%s'", filename);
406 else /* check for pre-2.0 file format with cookie string */
408 strcpy(cookie, chunk_name);
409 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
410 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
411 cookie[strlen(cookie) - 1] = '\0';
413 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
415 Error(ERR_WARN, "unknown format of level file '%s'", filename);
420 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
422 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
427 /* pre-2.0 level files have no game version, so use file version here */
428 level.game_version = level.file_version;
431 if (level.file_version < FILE_VERSION_1_2)
433 /* level files from versions before 1.2.0 without chunk structure */
434 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
435 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
443 int (*loader)(FILE *, int, struct LevelInfo *);
447 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
448 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
449 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
450 { "BODY", -1, LoadLevel_BODY },
451 { "CONT", -1, LoadLevel_CONT },
452 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
453 { "CUS1", -1, LoadLevel_CUS1 },
454 { "CUS2", -1, LoadLevel_CUS2 },
458 while (getFileChunkBE(file, chunk_name, &chunk_size))
462 while (chunk_info[i].name != NULL &&
463 strcmp(chunk_name, chunk_info[i].name) != 0)
466 if (chunk_info[i].name == NULL)
468 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
469 chunk_name, filename);
470 ReadUnusedBytesFromFile(file, chunk_size);
472 else if (chunk_info[i].size != -1 &&
473 chunk_info[i].size != chunk_size)
475 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
476 chunk_size, chunk_name, filename);
477 ReadUnusedBytesFromFile(file, chunk_size);
481 /* call function to load this level chunk */
482 int chunk_size_expected =
483 (chunk_info[i].loader)(file, chunk_size, &level);
485 /* the size of some chunks cannot be checked before reading other
486 chunks first (like "HEAD" and "BODY") that contain some header
487 information, so check them here */
488 if (chunk_size_expected != chunk_size)
490 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
491 chunk_size, chunk_name, filename);
499 if (leveldir_current == NULL) /* only when dumping level */
502 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
503 IS_LEVELCLASS_USER(leveldir_current))
505 /* For user contributed and private levels, use the version of
506 the game engine the levels were created for.
507 Since 2.0.1, the game engine version is now directly stored
508 in the level file (chunk "VERS"), so there is no need anymore
509 to set the game version from the file version (except for old,
510 pre-2.0 levels, where the game version is still taken from the
511 file format version used to store the level -- see above). */
513 /* do some special adjustments to support older level versions */
514 if (level.file_version == FILE_VERSION_1_0)
516 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
517 Error(ERR_WARN, "using high speed movement for player");
519 /* player was faster than monsters in (pre-)1.0 levels */
520 level.double_speed = TRUE;
523 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
524 if (level.game_version == VERSION_IDENT(2,0,1))
525 level.em_slippery_gems = TRUE;
529 /* Always use the latest version of the game engine for all but
530 user contributed and private levels; this allows for actual
531 corrections in the game engine to take effect for existing,
532 converted levels (from "classic" or other existing games) to
533 make the game emulation more accurate, while (hopefully) not
534 breaking existing levels created from other players. */
536 level.game_version = GAME_VERSION_ACTUAL;
538 /* Set special EM style gems behaviour: EM style gems slip down from
539 normal, steel and growing wall. As this is a more fundamental change,
540 it seems better to set the default behaviour to "off" (as it is more
541 natural) and make it configurable in the level editor (as a property
542 of gem style elements). Already existing converted levels (neither
543 private nor contributed levels) are changed to the new behaviour. */
545 if (level.file_version < FILE_VERSION_2_0)
546 level.em_slippery_gems = TRUE;
549 /* map some elements which have changed in newer versions */
550 if (level.game_version <= VERSION_IDENT(2,2,0))
554 /* map game font elements */
555 for(y=0; y<level.fieldy; y++)
557 for(x=0; x<level.fieldx; x++)
559 int element = Ur[x][y];
561 if (element == EL_CHAR('['))
562 element = EL_CHAR_AUMLAUT;
563 else if (element == EL_CHAR('\\'))
564 element = EL_CHAR_OUMLAUT;
565 else if (element == EL_CHAR(']'))
566 element = EL_CHAR_UUMLAUT;
567 else if (element == EL_CHAR('^'))
568 element = EL_CHAR_COPYRIGHT;
570 Feld[x][y] = Ur[x][y] = element;
575 /* determine border element for this level */
579 void LoadLevel(int level_nr)
581 char *filename = getLevelFilename(level_nr);
583 LoadLevelFromFilename(filename);
586 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
588 putFileVersion(file, level->file_version);
589 putFileVersion(file, level->game_version);
592 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
596 fputc(level->fieldx, file);
597 fputc(level->fieldy, file);
599 putFile16BitBE(file, level->time);
600 putFile16BitBE(file, level->gems_needed);
602 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
603 fputc(level->name[i], file);
605 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
606 fputc(level->score[i], file);
608 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
611 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
612 level->yam_content[i][x][y]),
614 fputc(level->amoeba_speed, file);
615 fputc(level->time_magic_wall, file);
616 fputc(level->time_wheel, file);
617 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
619 fputc((level->double_speed ? 1 : 0), file);
620 fputc((level->gravity ? 1 : 0), file);
621 fputc((level->encoding_16bit_field ? 1 : 0), file);
622 fputc((level->em_slippery_gems ? 1 : 0), file);
624 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
627 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
631 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
632 fputc(level->author[i], file);
635 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
639 for(y=0; y<level->fieldy; y++)
640 for(x=0; x<level->fieldx; x++)
641 if (level->encoding_16bit_field)
642 putFile16BitBE(file, Ur[x][y]);
644 fputc(Ur[x][y], file);
648 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
652 fputc(EL_YAMYAM, file);
653 fputc(level->num_yam_contents, file);
657 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
660 if (level->encoding_16bit_field)
661 putFile16BitBE(file, level->yam_content[i][x][y]);
663 fputc(level->yam_content[i][x][y], file);
667 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
670 int num_contents, content_xsize, content_ysize;
671 int content_array[MAX_ELEMENT_CONTENTS][3][3];
673 if (element == EL_YAMYAM)
675 num_contents = level->num_yam_contents;
679 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
682 content_array[i][x][y] = level->yam_content[i][x][y];
684 else if (element == EL_BD_AMOEBA)
690 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
693 content_array[i][x][y] = EL_EMPTY;
694 content_array[0][0][0] = level->amoeba_content;
698 /* chunk header already written -- write empty chunk data */
699 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
701 Error(ERR_WARN, "cannot save content for element '%d'", element);
705 putFile16BitBE(file, element);
706 fputc(num_contents, file);
707 fputc(content_xsize, file);
708 fputc(content_ysize, file);
710 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
712 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
715 putFile16BitBE(file, content_array[i][x][y]);
718 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
719 int num_changed_custom_elements)
723 putFile16BitBE(file, num_changed_custom_elements);
725 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
727 int element = EL_CUSTOM_START + i;
729 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
731 if (check < num_changed_custom_elements)
733 putFile16BitBE(file, element);
734 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
741 if (check != num_changed_custom_elements) /* should not happen */
742 Error(ERR_WARN, "inconsistent number of custom element properties");
745 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
746 int num_changed_custom_elements)
750 putFile16BitBE(file, num_changed_custom_elements);
752 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
754 int element = EL_CUSTOM_START + i;
756 if (level->custom_element_successor[i] != EL_EMPTY_SPACE)
758 if (check < num_changed_custom_elements)
760 putFile16BitBE(file, element);
761 putFile16BitBE(file, level->custom_element_successor[i]);
768 if (check != num_changed_custom_elements) /* should not happen */
769 Error(ERR_WARN, "inconsistent number of custom element successors");
772 void SaveLevel(int level_nr)
774 char *filename = getLevelFilename(level_nr);
776 int num_changed_custom_elements1 = 0;
777 int num_changed_custom_elements2 = 0;
781 if (!(file = fopen(filename, MODE_WRITE)))
783 Error(ERR_WARN, "cannot save level file '%s'", filename);
787 level.file_version = FILE_VERSION_ACTUAL;
788 level.game_version = GAME_VERSION_ACTUAL;
790 /* check level field for 16-bit elements */
791 level.encoding_16bit_field = FALSE;
792 for(y=0; y<level.fieldy; y++)
793 for(x=0; x<level.fieldx; x++)
795 level.encoding_16bit_field = TRUE;
797 /* check yamyam content for 16-bit elements */
798 level.encoding_16bit_yamyam = FALSE;
799 for(i=0; i<level.num_yam_contents; i++)
802 if (level.yam_content[i][x][y] > 255)
803 level.encoding_16bit_yamyam = TRUE;
805 /* check amoeba content for 16-bit elements */
806 level.encoding_16bit_amoeba = FALSE;
807 if (level.amoeba_content > 255)
808 level.encoding_16bit_amoeba = TRUE;
810 /* calculate size of "BODY" chunk */
812 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
814 /* check for non-standard custom elements and calculate "CUS1" chunk size */
815 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
816 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
817 num_changed_custom_elements1++;
819 /* check for non-standard custom elements and calculate "CUS2" chunk size */
820 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
821 if (level.custom_element_successor[i] != EL_EMPTY_SPACE)
822 num_changed_custom_elements2++;
824 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
825 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
827 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
828 SaveLevel_VERS(file, &level);
830 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
831 SaveLevel_HEAD(file, &level);
833 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
834 SaveLevel_AUTH(file, &level);
836 putFileChunkBE(file, "BODY", body_chunk_size);
837 SaveLevel_BODY(file, &level);
839 if (level.encoding_16bit_yamyam ||
840 level.num_yam_contents != STD_ELEMENT_CONTENTS)
842 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
843 SaveLevel_CNT2(file, &level, EL_YAMYAM);
846 if (level.encoding_16bit_amoeba)
848 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
849 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
852 if (num_changed_custom_elements1 > 0)
854 putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements1 * 6);
855 SaveLevel_CUS1(file, &level, num_changed_custom_elements1);
858 if (num_changed_custom_elements2 > 0)
860 putFileChunkBE(file, "CUS2", 2 + num_changed_custom_elements2 * 4);
861 SaveLevel_CUS2(file, &level, num_changed_custom_elements2);
866 SetFilePermissions(filename, PERMS_PRIVATE);
869 void DumpLevel(struct LevelInfo *level)
871 printf_line("-", 79);
872 printf("Level xxx (file version %06d, game version %06d)\n",
873 level->file_version, level->game_version);
874 printf_line("-", 79);
876 printf("Level Author: '%s'\n", level->author);
877 printf("Level Title: '%s'\n", level->name);
879 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
881 printf("Level Time: %d seconds\n", level->time);
882 printf("Gems needed: %d\n", level->gems_needed);
884 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
885 printf("Time for Wheel: %d seconds\n", level->time_wheel);
886 printf("Time for Light: %d seconds\n", level->time_light);
887 printf("Time for Timegate: %d seconds\n", level->time_timegate);
889 printf("Amoeba Speed: %d\n", level->amoeba_speed);
891 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
892 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
893 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
895 printf_line("-", 79);
899 /* ========================================================================= */
900 /* tape file functions */
901 /* ========================================================================= */
903 static void setTapeInfoToDefaults()
907 /* always start with reliable default values (empty tape) */
910 /* default values (also for pre-1.2 tapes) with only the first player */
911 tape.player_participates[0] = TRUE;
912 for(i=1; i<MAX_PLAYERS; i++)
913 tape.player_participates[i] = FALSE;
915 /* at least one (default: the first) player participates in every tape */
916 tape.num_participating_players = 1;
918 tape.level_nr = level_nr;
920 tape.changed = FALSE;
922 tape.recording = FALSE;
923 tape.playing = FALSE;
924 tape.pausing = FALSE;
927 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
929 tape->file_version = getFileVersion(file);
930 tape->game_version = getFileVersion(file);
935 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
939 tape->random_seed = getFile32BitBE(file);
940 tape->date = getFile32BitBE(file);
941 tape->length = getFile32BitBE(file);
943 /* read header fields that are new since version 1.2 */
944 if (tape->file_version >= FILE_VERSION_1_2)
946 byte store_participating_players = fgetc(file);
949 /* since version 1.2, tapes store which players participate in the tape */
950 tape->num_participating_players = 0;
951 for(i=0; i<MAX_PLAYERS; i++)
953 tape->player_participates[i] = FALSE;
955 if (store_participating_players & (1 << i))
957 tape->player_participates[i] = TRUE;
958 tape->num_participating_players++;
962 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
964 engine_version = getFileVersion(file);
965 if (engine_version > 0)
966 tape->engine_version = engine_version;
972 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
974 int level_identifier_size;
977 level_identifier_size = getFile16BitBE(file);
979 tape->level_identifier =
980 checked_realloc(tape->level_identifier, level_identifier_size);
982 for(i=0; i < level_identifier_size; i++)
983 tape->level_identifier[i] = fgetc(file);
985 tape->level_nr = getFile16BitBE(file);
987 chunk_size = 2 + level_identifier_size + 2;
992 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
995 int chunk_size_expected =
996 (tape->num_participating_players + 1) * tape->length;
998 if (chunk_size_expected != chunk_size)
1000 ReadUnusedBytesFromFile(file, chunk_size);
1001 return chunk_size_expected;
1004 for(i=0; i<tape->length; i++)
1006 if (i >= MAX_TAPELEN)
1009 for(j=0; j<MAX_PLAYERS; j++)
1011 tape->pos[i].action[j] = MV_NO_MOVING;
1013 if (tape->player_participates[j])
1014 tape->pos[i].action[j] = fgetc(file);
1017 tape->pos[i].delay = fgetc(file);
1019 if (tape->file_version == FILE_VERSION_1_0)
1021 /* eliminate possible diagonal moves in old tapes */
1022 /* this is only for backward compatibility */
1024 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1025 byte action = tape->pos[i].action[0];
1026 int k, num_moves = 0;
1030 if (action & joy_dir[k])
1032 tape->pos[i + num_moves].action[0] = joy_dir[k];
1034 tape->pos[i + num_moves].delay = 0;
1043 tape->length += num_moves;
1046 else if (tape->file_version < FILE_VERSION_2_0)
1048 /* convert pre-2.0 tapes to new tape format */
1050 if (tape->pos[i].delay > 1)
1053 tape->pos[i + 1] = tape->pos[i];
1054 tape->pos[i + 1].delay = 1;
1057 for(j=0; j<MAX_PLAYERS; j++)
1058 tape->pos[i].action[j] = MV_NO_MOVING;
1059 tape->pos[i].delay--;
1070 if (i != tape->length)
1071 chunk_size = (tape->num_participating_players + 1) * i;
1076 void LoadTapeFromFilename(char *filename)
1078 char cookie[MAX_LINE_LEN];
1079 char chunk_name[CHUNK_ID_LEN + 1];
1083 /* always start with reliable default values */
1084 setTapeInfoToDefaults();
1086 if (!(file = fopen(filename, MODE_READ)))
1089 getFileChunkBE(file, chunk_name, NULL);
1090 if (strcmp(chunk_name, "RND1") == 0)
1092 getFile32BitBE(file); /* not used */
1094 getFileChunkBE(file, chunk_name, NULL);
1095 if (strcmp(chunk_name, "TAPE") != 0)
1097 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1102 else /* check for pre-2.0 file format with cookie string */
1104 strcpy(cookie, chunk_name);
1105 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1106 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1107 cookie[strlen(cookie) - 1] = '\0';
1109 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1111 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1116 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1118 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1123 /* pre-2.0 tape files have no game version, so use file version here */
1124 tape.game_version = tape.file_version;
1127 if (tape.file_version < FILE_VERSION_1_2)
1129 /* tape files from versions before 1.2.0 without chunk structure */
1130 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1131 LoadTape_BODY(file, 2 * tape.length, &tape);
1139 int (*loader)(FILE *, int, struct TapeInfo *);
1143 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1144 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1145 { "INFO", -1, LoadTape_INFO },
1146 { "BODY", -1, LoadTape_BODY },
1150 while (getFileChunkBE(file, chunk_name, &chunk_size))
1154 while (chunk_info[i].name != NULL &&
1155 strcmp(chunk_name, chunk_info[i].name) != 0)
1158 if (chunk_info[i].name == NULL)
1160 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1161 chunk_name, filename);
1162 ReadUnusedBytesFromFile(file, chunk_size);
1164 else if (chunk_info[i].size != -1 &&
1165 chunk_info[i].size != chunk_size)
1167 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1168 chunk_size, chunk_name, filename);
1169 ReadUnusedBytesFromFile(file, chunk_size);
1173 /* call function to load this tape chunk */
1174 int chunk_size_expected =
1175 (chunk_info[i].loader)(file, chunk_size, &tape);
1177 /* the size of some chunks cannot be checked before reading other
1178 chunks first (like "HEAD" and "BODY") that contain some header
1179 information, so check them here */
1180 if (chunk_size_expected != chunk_size)
1182 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1183 chunk_size, chunk_name, filename);
1191 tape.length_seconds = GetTapeLength();
1194 void LoadTape(int level_nr)
1196 char *filename = getTapeFilename(level_nr);
1198 LoadTapeFromFilename(filename);
1201 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1203 putFileVersion(file, tape->file_version);
1204 putFileVersion(file, tape->game_version);
1207 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1210 byte store_participating_players = 0;
1212 /* set bits for participating players for compact storage */
1213 for(i=0; i<MAX_PLAYERS; i++)
1214 if (tape->player_participates[i])
1215 store_participating_players |= (1 << i);
1217 putFile32BitBE(file, tape->random_seed);
1218 putFile32BitBE(file, tape->date);
1219 putFile32BitBE(file, tape->length);
1221 fputc(store_participating_players, file);
1223 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1224 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1226 putFileVersion(file, tape->engine_version);
1229 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1231 int level_identifier_size = strlen(tape->level_identifier) + 1;
1234 putFile16BitBE(file, level_identifier_size);
1236 for(i=0; i < level_identifier_size; i++)
1237 fputc(tape->level_identifier[i], file);
1239 putFile16BitBE(file, tape->level_nr);
1242 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1246 for(i=0; i<tape->length; i++)
1248 for(j=0; j<MAX_PLAYERS; j++)
1249 if (tape->player_participates[j])
1250 fputc(tape->pos[i].action[j], file);
1252 fputc(tape->pos[i].delay, file);
1256 void SaveTape(int level_nr)
1258 char *filename = getTapeFilename(level_nr);
1260 boolean new_tape = TRUE;
1261 int num_participating_players = 0;
1262 int info_chunk_size;
1263 int body_chunk_size;
1266 InitTapeDirectory(leveldir_current->filename);
1268 /* if a tape still exists, ask to overwrite it */
1269 if (access(filename, F_OK) == 0)
1272 if (!Request("Replace old tape ?", REQ_ASK))
1276 if (!(file = fopen(filename, MODE_WRITE)))
1278 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1282 tape.file_version = FILE_VERSION_ACTUAL;
1283 tape.game_version = GAME_VERSION_ACTUAL;
1285 /* count number of participating players */
1286 for(i=0; i<MAX_PLAYERS; i++)
1287 if (tape.player_participates[i])
1288 num_participating_players++;
1290 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1291 body_chunk_size = (num_participating_players + 1) * tape.length;
1293 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1294 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1296 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1297 SaveTape_VERS(file, &tape);
1299 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1300 SaveTape_HEAD(file, &tape);
1302 putFileChunkBE(file, "INFO", info_chunk_size);
1303 SaveTape_INFO(file, &tape);
1305 putFileChunkBE(file, "BODY", body_chunk_size);
1306 SaveTape_BODY(file, &tape);
1310 SetFilePermissions(filename, PERMS_PRIVATE);
1312 tape.changed = FALSE;
1315 Request("tape saved !", REQ_CONFIRM);
1318 void DumpTape(struct TapeInfo *tape)
1322 if (TAPE_IS_EMPTY(*tape))
1324 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1328 printf_line("-", 79);
1329 printf("Tape of Level %03d (file version %06d, game version %06d)\n",
1330 tape->level_nr, tape->file_version, tape->game_version);
1331 printf("Level series identifier: '%s'\n", tape->level_identifier);
1332 printf_line("-", 79);
1334 for(i=0; i<tape->length; i++)
1336 if (i >= MAX_TAPELEN)
1339 printf("%03d: ", i);
1341 for(j=0; j<MAX_PLAYERS; j++)
1343 if (tape->player_participates[j])
1345 int action = tape->pos[i].action[j];
1347 printf("%d:%02x ", j, action);
1348 printf("[%c%c%c%c|%c%c] - ",
1349 (action & JOY_LEFT ? '<' : ' '),
1350 (action & JOY_RIGHT ? '>' : ' '),
1351 (action & JOY_UP ? '^' : ' '),
1352 (action & JOY_DOWN ? 'v' : ' '),
1353 (action & JOY_BUTTON_1 ? '1' : ' '),
1354 (action & JOY_BUTTON_2 ? '2' : ' '));
1358 printf("(%03d)\n", tape->pos[i].delay);
1361 printf_line("-", 79);
1365 /* ========================================================================= */
1366 /* score file functions */
1367 /* ========================================================================= */
1369 void LoadScore(int level_nr)
1372 char *filename = getScoreFilename(level_nr);
1373 char cookie[MAX_LINE_LEN];
1374 char line[MAX_LINE_LEN];
1378 /* always start with reliable default values */
1379 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1381 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1382 highscore[i].Score = 0;
1385 if (!(file = fopen(filename, MODE_READ)))
1388 /* check file identifier */
1389 fgets(cookie, MAX_LINE_LEN, file);
1390 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1391 cookie[strlen(cookie) - 1] = '\0';
1393 if (!checkCookieString(cookie, SCORE_COOKIE))
1395 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1400 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1402 fscanf(file, "%d", &highscore[i].Score);
1403 fgets(line, MAX_LINE_LEN, file);
1405 if (line[strlen(line) - 1] == '\n')
1406 line[strlen(line) - 1] = '\0';
1408 for (line_ptr = line; *line_ptr; line_ptr++)
1410 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1412 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1413 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1422 void SaveScore(int level_nr)
1425 char *filename = getScoreFilename(level_nr);
1428 InitScoreDirectory(leveldir_current->filename);
1430 if (!(file = fopen(filename, MODE_WRITE)))
1432 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1436 fprintf(file, "%s\n\n", SCORE_COOKIE);
1438 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1439 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1443 SetFilePermissions(filename, PERMS_PUBLIC);
1447 /* ========================================================================= */
1448 /* setup file functions */
1449 /* ========================================================================= */
1451 #define TOKEN_STR_PLAYER_PREFIX "player_"
1454 #define SETUP_TOKEN_PLAYER_NAME 0
1455 #define SETUP_TOKEN_SOUND 1
1456 #define SETUP_TOKEN_SOUND_LOOPS 2
1457 #define SETUP_TOKEN_SOUND_MUSIC 3
1458 #define SETUP_TOKEN_SOUND_SIMPLE 4
1459 #define SETUP_TOKEN_TOONS 5
1460 #define SETUP_TOKEN_SCROLL_DELAY 6
1461 #define SETUP_TOKEN_SOFT_SCROLLING 7
1462 #define SETUP_TOKEN_FADING 8
1463 #define SETUP_TOKEN_AUTORECORD 9
1464 #define SETUP_TOKEN_QUICK_DOORS 10
1465 #define SETUP_TOKEN_TEAM_MODE 11
1466 #define SETUP_TOKEN_HANDICAP 12
1467 #define SETUP_TOKEN_TIME_LIMIT 13
1468 #define SETUP_TOKEN_FULLSCREEN 14
1469 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1470 #define SETUP_TOKEN_GRAPHICS_SET 16
1471 #define SETUP_TOKEN_SOUNDS_SET 17
1472 #define SETUP_TOKEN_MUSIC_SET 18
1473 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1474 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1475 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1477 #define NUM_GLOBAL_SETUP_TOKENS 22
1480 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1481 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1482 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1483 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1484 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1485 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1486 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1487 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1488 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1490 #define NUM_EDITOR_SETUP_TOKENS 9
1492 /* shortcut setup */
1493 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1494 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1495 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1497 #define NUM_SHORTCUT_SETUP_TOKENS 3
1500 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1501 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1502 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1503 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1504 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1505 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1506 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1507 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1508 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1509 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1510 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1511 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1512 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1513 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1514 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1515 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1517 #define NUM_PLAYER_SETUP_TOKENS 16
1520 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1521 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1523 #define NUM_SYSTEM_SETUP_TOKENS 2
1526 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1528 #define NUM_OPTIONS_SETUP_TOKENS 1
1531 static struct SetupInfo si;
1532 static struct SetupEditorInfo sei;
1533 static struct SetupShortcutInfo ssi;
1534 static struct SetupInputInfo sii;
1535 static struct SetupSystemInfo syi;
1536 static struct OptionInfo soi;
1538 static struct TokenInfo global_setup_tokens[] =
1540 { TYPE_STRING, &si.player_name, "player_name" },
1541 { TYPE_SWITCH, &si.sound, "sound" },
1542 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1543 { TYPE_SWITCH, &si.sound_music, "background_music" },
1544 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1545 { TYPE_SWITCH, &si.toons, "toons" },
1546 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1547 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1548 { TYPE_SWITCH, &si.fading, "screen_fading" },
1549 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1550 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1551 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1552 { TYPE_SWITCH, &si.handicap, "handicap" },
1553 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1554 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1555 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1556 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1557 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1558 { TYPE_STRING, &si.music_set, "music_set" },
1559 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1560 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1561 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1564 static struct TokenInfo editor_setup_tokens[] =
1566 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1567 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1568 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1569 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1570 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1571 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1572 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1573 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1574 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1577 static struct TokenInfo shortcut_setup_tokens[] =
1579 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1580 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1581 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1584 static struct TokenInfo player_setup_tokens[] =
1586 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1587 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1588 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1589 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1590 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1591 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1592 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1593 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1594 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1595 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1596 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1597 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1598 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1599 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1600 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1601 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1604 static struct TokenInfo system_setup_tokens[] =
1606 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1607 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1610 static struct TokenInfo options_setup_tokens[] =
1612 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1615 static char *get_corrected_login_name(char *login_name)
1617 /* needed because player name must be a fixed length string */
1618 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1620 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1621 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1623 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1624 if (strchr(login_name_new, ' '))
1625 *strchr(login_name_new, ' ') = '\0';
1627 return login_name_new;
1630 static void setSetupInfoToDefaults(struct SetupInfo *si)
1634 si->player_name = get_corrected_login_name(getLoginName());
1637 si->sound_loops = TRUE;
1638 si->sound_music = TRUE;
1639 si->sound_simple = TRUE;
1641 si->double_buffering = TRUE;
1642 si->direct_draw = !si->double_buffering;
1643 si->scroll_delay = TRUE;
1644 si->soft_scrolling = TRUE;
1646 si->autorecord = TRUE;
1647 si->quick_doors = FALSE;
1648 si->team_mode = FALSE;
1649 si->handicap = TRUE;
1650 si->time_limit = TRUE;
1651 si->fullscreen = FALSE;
1652 si->ask_on_escape = TRUE;
1654 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1655 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1656 si->music_set = getStringCopy(MUSIC_SUBDIR);
1657 si->override_level_graphics = FALSE;
1658 si->override_level_sounds = FALSE;
1659 si->override_level_music = FALSE;
1661 si->editor.el_boulderdash = TRUE;
1662 si->editor.el_emerald_mine = TRUE;
1663 si->editor.el_more = TRUE;
1664 si->editor.el_sokoban = TRUE;
1665 si->editor.el_supaplex = TRUE;
1666 si->editor.el_diamond_caves = TRUE;
1667 si->editor.el_dx_boulderdash = TRUE;
1668 si->editor.el_chars = TRUE;
1669 si->editor.el_custom = TRUE;
1671 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1672 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1673 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1675 for (i=0; i<MAX_PLAYERS; i++)
1677 si->input[i].use_joystick = FALSE;
1678 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1679 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1680 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1681 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1682 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1683 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1684 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1685 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1686 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1687 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1688 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1689 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1690 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1691 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1692 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1695 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1696 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1698 si->options.verbose = FALSE;
1701 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1705 if (!setup_file_list)
1710 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1711 setSetupInfo(global_setup_tokens, i,
1712 getTokenValue(setup_file_list, global_setup_tokens[i].text));
1717 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1718 setSetupInfo(editor_setup_tokens, i,
1719 getTokenValue(setup_file_list,editor_setup_tokens[i].text));
1722 /* shortcut setup */
1723 ssi = setup.shortcut;
1724 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1725 setSetupInfo(shortcut_setup_tokens, i,
1726 getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
1727 setup.shortcut = ssi;
1730 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1734 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1736 sii = setup.input[pnr];
1737 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1739 char full_token[100];
1741 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1742 setSetupInfo(player_setup_tokens, i,
1743 getTokenValue(setup_file_list, full_token));
1745 setup.input[pnr] = sii;
1750 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1751 setSetupInfo(system_setup_tokens, i,
1752 getTokenValue(setup_file_list, system_setup_tokens[i].text));
1756 soi = setup.options;
1757 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1758 setSetupInfo(options_setup_tokens, i,
1759 getTokenValue(setup_file_list, options_setup_tokens[i].text));
1760 setup.options = soi;
1765 char *filename = getSetupFilename();
1766 struct SetupFileList *setup_file_list = NULL;
1768 /* always start with reliable default values */
1769 setSetupInfoToDefaults(&setup);
1771 setup_file_list = loadSetupFileList(filename);
1773 if (setup_file_list)
1775 char *player_name_new;
1777 checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
1778 decodeSetupFileList(setup_file_list);
1780 setup.direct_draw = !setup.double_buffering;
1782 freeSetupFileList(setup_file_list);
1784 /* needed to work around problems with fixed length strings */
1785 player_name_new = get_corrected_login_name(setup.player_name);
1786 free(setup.player_name);
1787 setup.player_name = player_name_new;
1790 Error(ERR_WARN, "using default setup values");
1795 char *filename = getSetupFilename();
1799 InitUserDataDirectory();
1801 if (!(file = fopen(filename, MODE_WRITE)))
1803 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1807 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1808 getCookie("SETUP")));
1809 fprintf(file, "\n");
1813 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1815 /* just to make things nicer :) */
1816 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1817 i == SETUP_TOKEN_GRAPHICS_SET)
1818 fprintf(file, "\n");
1820 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1825 fprintf(file, "\n");
1826 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1827 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1829 /* shortcut setup */
1830 ssi = setup.shortcut;
1831 fprintf(file, "\n");
1832 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1833 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1836 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1840 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1841 fprintf(file, "\n");
1843 sii = setup.input[pnr];
1844 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1845 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1850 fprintf(file, "\n");
1851 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1852 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1855 soi = setup.options;
1856 fprintf(file, "\n");
1857 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1858 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1862 SetFilePermissions(filename, PERMS_PRIVATE);
1865 void LoadCustomElementDescriptions()
1867 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1868 struct SetupFileList *setup_file_list;
1871 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1873 if (element_info[i].custom_description != NULL)
1875 free(element_info[i].custom_description);
1876 element_info[i].custom_description = NULL;
1880 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1883 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1885 char *token = getStringCat2(element_info[i].token_name, ".name");
1886 char *value = getTokenValue(setup_file_list, token);
1889 element_info[i].custom_description = getStringCopy(value);
1894 freeSetupFileList(setup_file_list);
1897 void LoadSpecialMenuDesignSettings()
1899 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1900 struct SetupFileList *setup_file_list;
1903 /* !!! CHANGE THIS !!! (redundant initialization) !!! */
1904 global.num_toons = 20;
1905 global.menu_draw_xoffset = 0;
1906 global.menu_draw_yoffset = 0;
1907 global.menu_draw_xoffset_MAIN = 0;
1908 global.menu_draw_yoffset_MAIN = 0;
1909 global.door_step_offset = 2;
1910 global.door_step_delay = 10;
1912 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1915 value = getTokenValue(setup_file_list, "global.num_toons");
1917 global.num_toons = get_integer_from_string(value);
1919 value = getTokenValue(setup_file_list, "menu.draw_xoffset");
1921 global.menu_draw_xoffset = get_integer_from_string(value);
1923 value = getTokenValue(setup_file_list, "menu.draw_yoffset");
1925 global.menu_draw_yoffset = get_integer_from_string(value);
1927 value = getTokenValue(setup_file_list, "menu.draw_xoffset.MAIN");
1929 global.menu_draw_xoffset_MAIN = get_integer_from_string(value);
1931 value = getTokenValue(setup_file_list, "menu.draw_yoffset.MAIN");
1933 global.menu_draw_yoffset_MAIN = get_integer_from_string(value);
1935 value = getTokenValue(setup_file_list, "door.step_offset");
1937 global.door_step_offset = get_integer_from_string(value);
1939 value = getTokenValue(setup_file_list, "door.step_delay");
1941 global.door_step_delay = get_integer_from_string(value);
1943 freeSetupFileList(setup_file_list);