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].change.events = CE_BITMASK_DEFAULT;
103 level.custom_element[i].change.gfx_element = element;
104 level.custom_element[i].change.successor = EL_EMPTY_SPACE;
105 level.custom_element[i].change.delay_fixed = 0;
106 level.custom_element[i].change.delay_random = 0;
108 /* start with no properties at all */
109 for (j=0; j < NUM_EP_BITFIELDS; j++)
110 Properties[element][j] = EP_BITMASK_DEFAULT;
113 BorderElement = EL_STEELWALL;
115 level.no_level_file = FALSE;
117 if (leveldir_current == NULL) /* only when dumping level */
120 /* try to determine better author name than 'anonymous' */
121 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
123 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
124 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
128 switch (LEVELCLASS(leveldir_current))
130 case LEVELCLASS_TUTORIAL:
131 strcpy(level.author, PROGRAM_AUTHOR_STRING);
134 case LEVELCLASS_CONTRIBUTION:
135 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
136 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
139 case LEVELCLASS_USER:
140 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
141 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
145 /* keep default value */
151 static int checkLevelElement(int element)
153 if (element >= NUM_FILE_ELEMENTS)
155 Error(ERR_WARN, "invalid level element %d", element);
156 element = EL_CHAR_QUESTION;
158 else if (element == EL_PLAYER_OBSOLETE)
159 element = EL_PLAYER_1;
160 else if (element == EL_KEY_OBSOLETE)
166 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
168 level->file_version = getFileVersion(file);
169 level->game_version = getFileVersion(file);
174 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
178 lev_fieldx = level->fieldx = fgetc(file);
179 lev_fieldy = level->fieldy = fgetc(file);
181 level->time = getFile16BitBE(file);
182 level->gems_needed = getFile16BitBE(file);
184 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
185 level->name[i] = fgetc(file);
186 level->name[MAX_LEVEL_NAME_LEN] = 0;
188 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
189 level->score[i] = fgetc(file);
191 level->num_yam_contents = STD_ELEMENT_CONTENTS;
192 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
195 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
197 level->amoeba_speed = fgetc(file);
198 level->time_magic_wall = fgetc(file);
199 level->time_wheel = fgetc(file);
200 level->amoeba_content = checkLevelElement(fgetc(file));
201 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
202 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
203 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
204 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
206 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
211 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
215 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
216 level->author[i] = fgetc(file);
217 level->author[MAX_LEVEL_NAME_LEN] = 0;
222 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
225 int chunk_size_expected = level->fieldx * level->fieldy;
227 /* Note: "chunk_size" was wrong before version 2.0 when elements are
228 stored with 16-bit encoding (and should be twice as big then).
229 Even worse, playfield data was stored 16-bit when only yamyam content
230 contained 16-bit elements and vice versa. */
232 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
233 chunk_size_expected *= 2;
235 if (chunk_size_expected != chunk_size)
237 ReadUnusedBytesFromFile(file, chunk_size);
238 return chunk_size_expected;
241 for(y=0; y<level->fieldy; y++)
242 for(x=0; x<level->fieldx; x++)
243 Feld[x][y] = Ur[x][y] =
244 checkLevelElement(level->encoding_16bit_field ?
245 getFile16BitBE(file) : fgetc(file));
249 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
253 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
254 int chunk_size_expected = header_size + content_size;
256 /* Note: "chunk_size" was wrong before version 2.0 when elements are
257 stored with 16-bit encoding (and should be twice as big then).
258 Even worse, playfield data was stored 16-bit when only yamyam content
259 contained 16-bit elements and vice versa. */
261 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
262 chunk_size_expected += content_size;
264 if (chunk_size_expected != chunk_size)
266 ReadUnusedBytesFromFile(file, chunk_size);
267 return chunk_size_expected;
271 level->num_yam_contents = fgetc(file);
275 /* correct invalid number of content fields -- should never happen */
276 if (level->num_yam_contents < 1 ||
277 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
278 level->num_yam_contents = STD_ELEMENT_CONTENTS;
280 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
283 level->yam_content[i][x][y] =
284 checkLevelElement(level->encoding_16bit_field ?
285 getFile16BitBE(file) : fgetc(file));
289 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
293 int num_contents, content_xsize, content_ysize;
294 int content_array[MAX_ELEMENT_CONTENTS][3][3];
296 element = checkLevelElement(getFile16BitBE(file));
297 num_contents = fgetc(file);
298 content_xsize = fgetc(file);
299 content_ysize = fgetc(file);
300 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
302 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
305 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
307 /* correct invalid number of content fields -- should never happen */
308 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
309 num_contents = STD_ELEMENT_CONTENTS;
311 if (element == EL_YAMYAM)
313 level->num_yam_contents = num_contents;
315 for(i=0; i<num_contents; i++)
318 level->yam_content[i][x][y] = content_array[i][x][y];
320 else if (element == EL_BD_AMOEBA)
322 level->amoeba_content = content_array[0][0][0];
326 Error(ERR_WARN, "cannot load content for element '%d'", element);
332 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
334 int num_changed_custom_elements = getFile16BitBE(file);
335 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
338 if (chunk_size_expected != chunk_size)
340 ReadUnusedBytesFromFile(file, chunk_size - 2);
341 return chunk_size_expected;
344 for (i=0; i < num_changed_custom_elements; i++)
346 int element = getFile16BitBE(file);
347 int properties = getFile32BitBE(file);
349 if (IS_CUSTOM_ELEMENT(element))
350 Properties[element][EP_BITFIELD_BASE] = properties;
352 Error(ERR_WARN, "invalid custom element number %d", element);
358 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
360 int num_changed_custom_elements = getFile16BitBE(file);
361 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
364 if (chunk_size_expected != chunk_size)
366 ReadUnusedBytesFromFile(file, chunk_size - 2);
367 return chunk_size_expected;
370 for (i=0; i < num_changed_custom_elements; i++)
372 int element = getFile16BitBE(file);
373 int custom_element_successor = getFile16BitBE(file);
374 int i = element - EL_CUSTOM_START;
376 if (IS_CUSTOM_ELEMENT(element))
377 level->custom_element[i].change.successor = custom_element_successor;
379 Error(ERR_WARN, "invalid custom element number %d", element);
385 void LoadLevelFromFilename(char *filename)
387 char cookie[MAX_LINE_LEN];
388 char chunk_name[CHUNK_ID_LEN + 1];
392 /* always start with reliable default values */
393 setLevelInfoToDefaults();
395 if (!(file = fopen(filename, MODE_READ)))
397 level.no_level_file = TRUE;
399 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
403 getFileChunkBE(file, chunk_name, NULL);
404 if (strcmp(chunk_name, "RND1") == 0)
406 getFile32BitBE(file); /* not used */
408 getFileChunkBE(file, chunk_name, NULL);
409 if (strcmp(chunk_name, "CAVE") != 0)
411 Error(ERR_WARN, "unknown format of level file '%s'", filename);
416 else /* check for pre-2.0 file format with cookie string */
418 strcpy(cookie, chunk_name);
419 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
420 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
421 cookie[strlen(cookie) - 1] = '\0';
423 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
425 Error(ERR_WARN, "unknown format of level file '%s'", filename);
430 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
432 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
437 /* pre-2.0 level files have no game version, so use file version here */
438 level.game_version = level.file_version;
441 if (level.file_version < FILE_VERSION_1_2)
443 /* level files from versions before 1.2.0 without chunk structure */
444 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
445 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
453 int (*loader)(FILE *, int, struct LevelInfo *);
457 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
458 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
459 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
460 { "BODY", -1, LoadLevel_BODY },
461 { "CONT", -1, LoadLevel_CONT },
462 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
463 { "CUS1", -1, LoadLevel_CUS1 },
464 { "CUS2", -1, LoadLevel_CUS2 },
468 while (getFileChunkBE(file, chunk_name, &chunk_size))
472 while (chunk_info[i].name != NULL &&
473 strcmp(chunk_name, chunk_info[i].name) != 0)
476 if (chunk_info[i].name == NULL)
478 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
479 chunk_name, filename);
480 ReadUnusedBytesFromFile(file, chunk_size);
482 else if (chunk_info[i].size != -1 &&
483 chunk_info[i].size != chunk_size)
485 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
486 chunk_size, chunk_name, filename);
487 ReadUnusedBytesFromFile(file, chunk_size);
491 /* call function to load this level chunk */
492 int chunk_size_expected =
493 (chunk_info[i].loader)(file, chunk_size, &level);
495 /* the size of some chunks cannot be checked before reading other
496 chunks first (like "HEAD" and "BODY") that contain some header
497 information, so check them here */
498 if (chunk_size_expected != chunk_size)
500 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
501 chunk_size, chunk_name, filename);
509 if (leveldir_current == NULL) /* only when dumping level */
512 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
513 IS_LEVELCLASS_USER(leveldir_current))
515 /* For user contributed and private levels, use the version of
516 the game engine the levels were created for.
517 Since 2.0.1, the game engine version is now directly stored
518 in the level file (chunk "VERS"), so there is no need anymore
519 to set the game version from the file version (except for old,
520 pre-2.0 levels, where the game version is still taken from the
521 file format version used to store the level -- see above). */
523 /* do some special adjustments to support older level versions */
524 if (level.file_version == FILE_VERSION_1_0)
526 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
527 Error(ERR_WARN, "using high speed movement for player");
529 /* player was faster than monsters in (pre-)1.0 levels */
530 level.double_speed = TRUE;
533 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
534 if (level.game_version == VERSION_IDENT(2,0,1))
535 level.em_slippery_gems = TRUE;
539 /* Always use the latest version of the game engine for all but
540 user contributed and private levels; this allows for actual
541 corrections in the game engine to take effect for existing,
542 converted levels (from "classic" or other existing games) to
543 make the game emulation more accurate, while (hopefully) not
544 breaking existing levels created from other players. */
546 level.game_version = GAME_VERSION_ACTUAL;
548 /* Set special EM style gems behaviour: EM style gems slip down from
549 normal, steel and growing wall. As this is a more fundamental change,
550 it seems better to set the default behaviour to "off" (as it is more
551 natural) and make it configurable in the level editor (as a property
552 of gem style elements). Already existing converted levels (neither
553 private nor contributed levels) are changed to the new behaviour. */
555 if (level.file_version < FILE_VERSION_2_0)
556 level.em_slippery_gems = TRUE;
559 /* map some elements which have changed in newer versions */
560 if (level.game_version <= VERSION_IDENT(2,2,0))
564 /* map game font elements */
565 for(y=0; y<level.fieldy; y++)
567 for(x=0; x<level.fieldx; x++)
569 int element = Ur[x][y];
571 if (element == EL_CHAR('['))
572 element = EL_CHAR_AUMLAUT;
573 else if (element == EL_CHAR('\\'))
574 element = EL_CHAR_OUMLAUT;
575 else if (element == EL_CHAR(']'))
576 element = EL_CHAR_UUMLAUT;
577 else if (element == EL_CHAR('^'))
578 element = EL_CHAR_COPYRIGHT;
580 Feld[x][y] = Ur[x][y] = element;
585 /* determine border element for this level */
589 void LoadLevel(int level_nr)
591 char *filename = getLevelFilename(level_nr);
593 LoadLevelFromFilename(filename);
594 InitElementPropertiesEngine(level.game_version);
597 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
599 putFileVersion(file, level->file_version);
600 putFileVersion(file, level->game_version);
603 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
607 fputc(level->fieldx, file);
608 fputc(level->fieldy, file);
610 putFile16BitBE(file, level->time);
611 putFile16BitBE(file, level->gems_needed);
613 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
614 fputc(level->name[i], file);
616 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
617 fputc(level->score[i], file);
619 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
622 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
623 level->yam_content[i][x][y]),
625 fputc(level->amoeba_speed, file);
626 fputc(level->time_magic_wall, file);
627 fputc(level->time_wheel, file);
628 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
630 fputc((level->double_speed ? 1 : 0), file);
631 fputc((level->gravity ? 1 : 0), file);
632 fputc((level->encoding_16bit_field ? 1 : 0), file);
633 fputc((level->em_slippery_gems ? 1 : 0), file);
635 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
638 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
642 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
643 fputc(level->author[i], file);
646 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
650 for(y=0; y<level->fieldy; y++)
651 for(x=0; x<level->fieldx; x++)
652 if (level->encoding_16bit_field)
653 putFile16BitBE(file, Ur[x][y]);
655 fputc(Ur[x][y], file);
659 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
663 fputc(EL_YAMYAM, file);
664 fputc(level->num_yam_contents, file);
668 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
671 if (level->encoding_16bit_field)
672 putFile16BitBE(file, level->yam_content[i][x][y]);
674 fputc(level->yam_content[i][x][y], file);
678 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
681 int num_contents, content_xsize, content_ysize;
682 int content_array[MAX_ELEMENT_CONTENTS][3][3];
684 if (element == EL_YAMYAM)
686 num_contents = level->num_yam_contents;
690 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
693 content_array[i][x][y] = level->yam_content[i][x][y];
695 else if (element == EL_BD_AMOEBA)
701 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
704 content_array[i][x][y] = EL_EMPTY;
705 content_array[0][0][0] = level->amoeba_content;
709 /* chunk header already written -- write empty chunk data */
710 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
712 Error(ERR_WARN, "cannot save content for element '%d'", element);
716 putFile16BitBE(file, element);
717 fputc(num_contents, file);
718 fputc(content_xsize, file);
719 fputc(content_ysize, file);
721 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
723 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
726 putFile16BitBE(file, content_array[i][x][y]);
729 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
730 int num_changed_custom_elements)
734 putFile16BitBE(file, num_changed_custom_elements);
736 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
738 int element = EL_CUSTOM_START + i;
740 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
742 if (check < num_changed_custom_elements)
744 putFile16BitBE(file, element);
745 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
752 if (check != num_changed_custom_elements) /* should not happen */
753 Error(ERR_WARN, "inconsistent number of custom element properties");
756 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
757 int num_changed_custom_elements)
761 putFile16BitBE(file, num_changed_custom_elements);
763 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
765 int element = EL_CUSTOM_START + i;
767 if (level->custom_element[i].change.successor != EL_EMPTY_SPACE)
769 if (check < num_changed_custom_elements)
771 putFile16BitBE(file, element);
772 putFile16BitBE(file, level->custom_element[i].change.successor);
779 if (check != num_changed_custom_elements) /* should not happen */
780 Error(ERR_WARN, "inconsistent number of custom element successors");
783 void SaveLevel(int level_nr)
785 char *filename = getLevelFilename(level_nr);
787 int num_changed_custom_elements1 = 0;
788 int num_changed_custom_elements2 = 0;
792 if (!(file = fopen(filename, MODE_WRITE)))
794 Error(ERR_WARN, "cannot save level file '%s'", filename);
798 level.file_version = FILE_VERSION_ACTUAL;
799 level.game_version = GAME_VERSION_ACTUAL;
801 /* check level field for 16-bit elements */
802 level.encoding_16bit_field = FALSE;
803 for(y=0; y<level.fieldy; y++)
804 for(x=0; x<level.fieldx; x++)
806 level.encoding_16bit_field = TRUE;
808 /* check yamyam content for 16-bit elements */
809 level.encoding_16bit_yamyam = FALSE;
810 for(i=0; i<level.num_yam_contents; i++)
813 if (level.yam_content[i][x][y] > 255)
814 level.encoding_16bit_yamyam = TRUE;
816 /* check amoeba content for 16-bit elements */
817 level.encoding_16bit_amoeba = FALSE;
818 if (level.amoeba_content > 255)
819 level.encoding_16bit_amoeba = TRUE;
821 /* calculate size of "BODY" chunk */
823 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
825 /* check for non-standard custom elements and calculate "CUS1" chunk size */
826 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
827 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
828 num_changed_custom_elements1++;
830 /* check for non-standard custom elements and calculate "CUS2" chunk size */
831 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
832 if (level.custom_element[i].change.successor != EL_EMPTY_SPACE)
833 num_changed_custom_elements2++;
835 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
836 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
838 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
839 SaveLevel_VERS(file, &level);
841 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
842 SaveLevel_HEAD(file, &level);
844 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
845 SaveLevel_AUTH(file, &level);
847 putFileChunkBE(file, "BODY", body_chunk_size);
848 SaveLevel_BODY(file, &level);
850 if (level.encoding_16bit_yamyam ||
851 level.num_yam_contents != STD_ELEMENT_CONTENTS)
853 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
854 SaveLevel_CNT2(file, &level, EL_YAMYAM);
857 if (level.encoding_16bit_amoeba)
859 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
860 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
863 if (num_changed_custom_elements1 > 0)
865 putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements1 * 6);
866 SaveLevel_CUS1(file, &level, num_changed_custom_elements1);
869 if (num_changed_custom_elements2 > 0)
871 putFileChunkBE(file, "CUS2", 2 + num_changed_custom_elements2 * 4);
872 SaveLevel_CUS2(file, &level, num_changed_custom_elements2);
877 SetFilePermissions(filename, PERMS_PRIVATE);
880 void DumpLevel(struct LevelInfo *level)
882 printf_line("-", 79);
883 printf("Level xxx (file version %06d, game version %06d)\n",
884 level->file_version, level->game_version);
885 printf_line("-", 79);
887 printf("Level Author: '%s'\n", level->author);
888 printf("Level Title: '%s'\n", level->name);
890 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
892 printf("Level Time: %d seconds\n", level->time);
893 printf("Gems needed: %d\n", level->gems_needed);
895 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
896 printf("Time for Wheel: %d seconds\n", level->time_wheel);
897 printf("Time for Light: %d seconds\n", level->time_light);
898 printf("Time for Timegate: %d seconds\n", level->time_timegate);
900 printf("Amoeba Speed: %d\n", level->amoeba_speed);
902 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
903 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
904 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
906 printf_line("-", 79);
910 /* ========================================================================= */
911 /* tape file functions */
912 /* ========================================================================= */
914 static void setTapeInfoToDefaults()
918 /* always start with reliable default values (empty tape) */
921 /* default values (also for pre-1.2 tapes) with only the first player */
922 tape.player_participates[0] = TRUE;
923 for(i=1; i<MAX_PLAYERS; i++)
924 tape.player_participates[i] = FALSE;
926 /* at least one (default: the first) player participates in every tape */
927 tape.num_participating_players = 1;
929 tape.level_nr = level_nr;
931 tape.changed = FALSE;
933 tape.recording = FALSE;
934 tape.playing = FALSE;
935 tape.pausing = FALSE;
938 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
940 tape->file_version = getFileVersion(file);
941 tape->game_version = getFileVersion(file);
946 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
950 tape->random_seed = getFile32BitBE(file);
951 tape->date = getFile32BitBE(file);
952 tape->length = getFile32BitBE(file);
954 /* read header fields that are new since version 1.2 */
955 if (tape->file_version >= FILE_VERSION_1_2)
957 byte store_participating_players = fgetc(file);
960 /* since version 1.2, tapes store which players participate in the tape */
961 tape->num_participating_players = 0;
962 for(i=0; i<MAX_PLAYERS; i++)
964 tape->player_participates[i] = FALSE;
966 if (store_participating_players & (1 << i))
968 tape->player_participates[i] = TRUE;
969 tape->num_participating_players++;
973 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
975 engine_version = getFileVersion(file);
976 if (engine_version > 0)
977 tape->engine_version = engine_version;
983 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
985 int level_identifier_size;
988 level_identifier_size = getFile16BitBE(file);
990 tape->level_identifier =
991 checked_realloc(tape->level_identifier, level_identifier_size);
993 for(i=0; i < level_identifier_size; i++)
994 tape->level_identifier[i] = fgetc(file);
996 tape->level_nr = getFile16BitBE(file);
998 chunk_size = 2 + level_identifier_size + 2;
1003 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1006 int chunk_size_expected =
1007 (tape->num_participating_players + 1) * tape->length;
1009 if (chunk_size_expected != chunk_size)
1011 ReadUnusedBytesFromFile(file, chunk_size);
1012 return chunk_size_expected;
1015 for(i=0; i<tape->length; i++)
1017 if (i >= MAX_TAPELEN)
1020 for(j=0; j<MAX_PLAYERS; j++)
1022 tape->pos[i].action[j] = MV_NO_MOVING;
1024 if (tape->player_participates[j])
1025 tape->pos[i].action[j] = fgetc(file);
1028 tape->pos[i].delay = fgetc(file);
1030 if (tape->file_version == FILE_VERSION_1_0)
1032 /* eliminate possible diagonal moves in old tapes */
1033 /* this is only for backward compatibility */
1035 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1036 byte action = tape->pos[i].action[0];
1037 int k, num_moves = 0;
1041 if (action & joy_dir[k])
1043 tape->pos[i + num_moves].action[0] = joy_dir[k];
1045 tape->pos[i + num_moves].delay = 0;
1054 tape->length += num_moves;
1057 else if (tape->file_version < FILE_VERSION_2_0)
1059 /* convert pre-2.0 tapes to new tape format */
1061 if (tape->pos[i].delay > 1)
1064 tape->pos[i + 1] = tape->pos[i];
1065 tape->pos[i + 1].delay = 1;
1068 for(j=0; j<MAX_PLAYERS; j++)
1069 tape->pos[i].action[j] = MV_NO_MOVING;
1070 tape->pos[i].delay--;
1081 if (i != tape->length)
1082 chunk_size = (tape->num_participating_players + 1) * i;
1087 void LoadTapeFromFilename(char *filename)
1089 char cookie[MAX_LINE_LEN];
1090 char chunk_name[CHUNK_ID_LEN + 1];
1094 /* always start with reliable default values */
1095 setTapeInfoToDefaults();
1097 if (!(file = fopen(filename, MODE_READ)))
1100 getFileChunkBE(file, chunk_name, NULL);
1101 if (strcmp(chunk_name, "RND1") == 0)
1103 getFile32BitBE(file); /* not used */
1105 getFileChunkBE(file, chunk_name, NULL);
1106 if (strcmp(chunk_name, "TAPE") != 0)
1108 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1113 else /* check for pre-2.0 file format with cookie string */
1115 strcpy(cookie, chunk_name);
1116 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1117 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1118 cookie[strlen(cookie) - 1] = '\0';
1120 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1122 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1127 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1129 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1134 /* pre-2.0 tape files have no game version, so use file version here */
1135 tape.game_version = tape.file_version;
1138 if (tape.file_version < FILE_VERSION_1_2)
1140 /* tape files from versions before 1.2.0 without chunk structure */
1141 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1142 LoadTape_BODY(file, 2 * tape.length, &tape);
1150 int (*loader)(FILE *, int, struct TapeInfo *);
1154 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1155 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1156 { "INFO", -1, LoadTape_INFO },
1157 { "BODY", -1, LoadTape_BODY },
1161 while (getFileChunkBE(file, chunk_name, &chunk_size))
1165 while (chunk_info[i].name != NULL &&
1166 strcmp(chunk_name, chunk_info[i].name) != 0)
1169 if (chunk_info[i].name == NULL)
1171 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1172 chunk_name, filename);
1173 ReadUnusedBytesFromFile(file, chunk_size);
1175 else if (chunk_info[i].size != -1 &&
1176 chunk_info[i].size != chunk_size)
1178 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1179 chunk_size, chunk_name, filename);
1180 ReadUnusedBytesFromFile(file, chunk_size);
1184 /* call function to load this tape chunk */
1185 int chunk_size_expected =
1186 (chunk_info[i].loader)(file, chunk_size, &tape);
1188 /* the size of some chunks cannot be checked before reading other
1189 chunks first (like "HEAD" and "BODY") that contain some header
1190 information, so check them here */
1191 if (chunk_size_expected != chunk_size)
1193 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1194 chunk_size, chunk_name, filename);
1202 tape.length_seconds = GetTapeLength();
1205 void LoadTape(int level_nr)
1207 char *filename = getTapeFilename(level_nr);
1209 LoadTapeFromFilename(filename);
1212 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1214 putFileVersion(file, tape->file_version);
1215 putFileVersion(file, tape->game_version);
1218 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1221 byte store_participating_players = 0;
1223 /* set bits for participating players for compact storage */
1224 for(i=0; i<MAX_PLAYERS; i++)
1225 if (tape->player_participates[i])
1226 store_participating_players |= (1 << i);
1228 putFile32BitBE(file, tape->random_seed);
1229 putFile32BitBE(file, tape->date);
1230 putFile32BitBE(file, tape->length);
1232 fputc(store_participating_players, file);
1234 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1235 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1237 putFileVersion(file, tape->engine_version);
1240 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1242 int level_identifier_size = strlen(tape->level_identifier) + 1;
1245 putFile16BitBE(file, level_identifier_size);
1247 for(i=0; i < level_identifier_size; i++)
1248 fputc(tape->level_identifier[i], file);
1250 putFile16BitBE(file, tape->level_nr);
1253 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1257 for(i=0; i<tape->length; i++)
1259 for(j=0; j<MAX_PLAYERS; j++)
1260 if (tape->player_participates[j])
1261 fputc(tape->pos[i].action[j], file);
1263 fputc(tape->pos[i].delay, file);
1267 void SaveTape(int level_nr)
1269 char *filename = getTapeFilename(level_nr);
1271 boolean new_tape = TRUE;
1272 int num_participating_players = 0;
1273 int info_chunk_size;
1274 int body_chunk_size;
1277 InitTapeDirectory(leveldir_current->filename);
1279 /* if a tape still exists, ask to overwrite it */
1280 if (access(filename, F_OK) == 0)
1283 if (!Request("Replace old tape ?", REQ_ASK))
1287 if (!(file = fopen(filename, MODE_WRITE)))
1289 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1293 tape.file_version = FILE_VERSION_ACTUAL;
1294 tape.game_version = GAME_VERSION_ACTUAL;
1296 /* count number of participating players */
1297 for(i=0; i<MAX_PLAYERS; i++)
1298 if (tape.player_participates[i])
1299 num_participating_players++;
1301 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1302 body_chunk_size = (num_participating_players + 1) * tape.length;
1304 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1305 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1307 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1308 SaveTape_VERS(file, &tape);
1310 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1311 SaveTape_HEAD(file, &tape);
1313 putFileChunkBE(file, "INFO", info_chunk_size);
1314 SaveTape_INFO(file, &tape);
1316 putFileChunkBE(file, "BODY", body_chunk_size);
1317 SaveTape_BODY(file, &tape);
1321 SetFilePermissions(filename, PERMS_PRIVATE);
1323 tape.changed = FALSE;
1326 Request("tape saved !", REQ_CONFIRM);
1329 void DumpTape(struct TapeInfo *tape)
1333 if (TAPE_IS_EMPTY(*tape))
1335 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1339 printf_line("-", 79);
1340 printf("Tape of Level %03d (file version %06d, game version %06d)\n",
1341 tape->level_nr, tape->file_version, tape->game_version);
1342 printf("Level series identifier: '%s'\n", tape->level_identifier);
1343 printf_line("-", 79);
1345 for(i=0; i<tape->length; i++)
1347 if (i >= MAX_TAPELEN)
1350 printf("%03d: ", i);
1352 for(j=0; j<MAX_PLAYERS; j++)
1354 if (tape->player_participates[j])
1356 int action = tape->pos[i].action[j];
1358 printf("%d:%02x ", j, action);
1359 printf("[%c%c%c%c|%c%c] - ",
1360 (action & JOY_LEFT ? '<' : ' '),
1361 (action & JOY_RIGHT ? '>' : ' '),
1362 (action & JOY_UP ? '^' : ' '),
1363 (action & JOY_DOWN ? 'v' : ' '),
1364 (action & JOY_BUTTON_1 ? '1' : ' '),
1365 (action & JOY_BUTTON_2 ? '2' : ' '));
1369 printf("(%03d)\n", tape->pos[i].delay);
1372 printf_line("-", 79);
1376 /* ========================================================================= */
1377 /* score file functions */
1378 /* ========================================================================= */
1380 void LoadScore(int level_nr)
1383 char *filename = getScoreFilename(level_nr);
1384 char cookie[MAX_LINE_LEN];
1385 char line[MAX_LINE_LEN];
1389 /* always start with reliable default values */
1390 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1392 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1393 highscore[i].Score = 0;
1396 if (!(file = fopen(filename, MODE_READ)))
1399 /* check file identifier */
1400 fgets(cookie, MAX_LINE_LEN, file);
1401 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1402 cookie[strlen(cookie) - 1] = '\0';
1404 if (!checkCookieString(cookie, SCORE_COOKIE))
1406 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1411 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1413 fscanf(file, "%d", &highscore[i].Score);
1414 fgets(line, MAX_LINE_LEN, file);
1416 if (line[strlen(line) - 1] == '\n')
1417 line[strlen(line) - 1] = '\0';
1419 for (line_ptr = line; *line_ptr; line_ptr++)
1421 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1423 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1424 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1433 void SaveScore(int level_nr)
1436 char *filename = getScoreFilename(level_nr);
1439 InitScoreDirectory(leveldir_current->filename);
1441 if (!(file = fopen(filename, MODE_WRITE)))
1443 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1447 fprintf(file, "%s\n\n", SCORE_COOKIE);
1449 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1450 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1454 SetFilePermissions(filename, PERMS_PUBLIC);
1458 /* ========================================================================= */
1459 /* setup file functions */
1460 /* ========================================================================= */
1462 #define TOKEN_STR_PLAYER_PREFIX "player_"
1465 #define SETUP_TOKEN_PLAYER_NAME 0
1466 #define SETUP_TOKEN_SOUND 1
1467 #define SETUP_TOKEN_SOUND_LOOPS 2
1468 #define SETUP_TOKEN_SOUND_MUSIC 3
1469 #define SETUP_TOKEN_SOUND_SIMPLE 4
1470 #define SETUP_TOKEN_TOONS 5
1471 #define SETUP_TOKEN_SCROLL_DELAY 6
1472 #define SETUP_TOKEN_SOFT_SCROLLING 7
1473 #define SETUP_TOKEN_FADING 8
1474 #define SETUP_TOKEN_AUTORECORD 9
1475 #define SETUP_TOKEN_QUICK_DOORS 10
1476 #define SETUP_TOKEN_TEAM_MODE 11
1477 #define SETUP_TOKEN_HANDICAP 12
1478 #define SETUP_TOKEN_TIME_LIMIT 13
1479 #define SETUP_TOKEN_FULLSCREEN 14
1480 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1481 #define SETUP_TOKEN_GRAPHICS_SET 16
1482 #define SETUP_TOKEN_SOUNDS_SET 17
1483 #define SETUP_TOKEN_MUSIC_SET 18
1484 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1485 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1486 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1488 #define NUM_GLOBAL_SETUP_TOKENS 22
1491 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1492 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1493 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1494 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1495 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1496 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1497 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1498 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1499 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1501 #define NUM_EDITOR_SETUP_TOKENS 9
1503 /* shortcut setup */
1504 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1505 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1506 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1508 #define NUM_SHORTCUT_SETUP_TOKENS 3
1511 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1512 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1513 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1514 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1515 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1516 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1517 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1518 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1519 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1520 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1521 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1522 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1523 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1524 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1525 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1526 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1528 #define NUM_PLAYER_SETUP_TOKENS 16
1531 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1532 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1534 #define NUM_SYSTEM_SETUP_TOKENS 2
1537 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1539 #define NUM_OPTIONS_SETUP_TOKENS 1
1542 static struct SetupInfo si;
1543 static struct SetupEditorInfo sei;
1544 static struct SetupShortcutInfo ssi;
1545 static struct SetupInputInfo sii;
1546 static struct SetupSystemInfo syi;
1547 static struct OptionInfo soi;
1549 static struct TokenInfo global_setup_tokens[] =
1551 { TYPE_STRING, &si.player_name, "player_name" },
1552 { TYPE_SWITCH, &si.sound, "sound" },
1553 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1554 { TYPE_SWITCH, &si.sound_music, "background_music" },
1555 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1556 { TYPE_SWITCH, &si.toons, "toons" },
1557 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1558 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1559 { TYPE_SWITCH, &si.fading, "screen_fading" },
1560 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1561 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1562 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1563 { TYPE_SWITCH, &si.handicap, "handicap" },
1564 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1565 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1566 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1567 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1568 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1569 { TYPE_STRING, &si.music_set, "music_set" },
1570 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1571 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1572 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1575 static struct TokenInfo editor_setup_tokens[] =
1577 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1578 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1579 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1580 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1581 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1582 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1583 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1584 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1585 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1588 static struct TokenInfo shortcut_setup_tokens[] =
1590 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1591 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1592 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1595 static struct TokenInfo player_setup_tokens[] =
1597 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1598 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1599 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1600 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1601 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1602 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1603 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1604 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1605 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1606 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1607 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1608 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1609 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1610 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1611 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1612 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1615 static struct TokenInfo system_setup_tokens[] =
1617 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1618 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1621 static struct TokenInfo options_setup_tokens[] =
1623 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1626 static char *get_corrected_login_name(char *login_name)
1628 /* needed because player name must be a fixed length string */
1629 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1631 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1632 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1634 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1635 if (strchr(login_name_new, ' '))
1636 *strchr(login_name_new, ' ') = '\0';
1638 return login_name_new;
1641 static void setSetupInfoToDefaults(struct SetupInfo *si)
1645 si->player_name = get_corrected_login_name(getLoginName());
1648 si->sound_loops = TRUE;
1649 si->sound_music = TRUE;
1650 si->sound_simple = TRUE;
1652 si->double_buffering = TRUE;
1653 si->direct_draw = !si->double_buffering;
1654 si->scroll_delay = TRUE;
1655 si->soft_scrolling = TRUE;
1657 si->autorecord = TRUE;
1658 si->quick_doors = FALSE;
1659 si->team_mode = FALSE;
1660 si->handicap = TRUE;
1661 si->time_limit = TRUE;
1662 si->fullscreen = FALSE;
1663 si->ask_on_escape = TRUE;
1665 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1666 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1667 si->music_set = getStringCopy(MUSIC_SUBDIR);
1668 si->override_level_graphics = FALSE;
1669 si->override_level_sounds = FALSE;
1670 si->override_level_music = FALSE;
1672 si->editor.el_boulderdash = TRUE;
1673 si->editor.el_emerald_mine = TRUE;
1674 si->editor.el_more = TRUE;
1675 si->editor.el_sokoban = TRUE;
1676 si->editor.el_supaplex = TRUE;
1677 si->editor.el_diamond_caves = TRUE;
1678 si->editor.el_dx_boulderdash = TRUE;
1679 si->editor.el_chars = TRUE;
1680 si->editor.el_custom = TRUE;
1682 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1683 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1684 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1686 for (i=0; i<MAX_PLAYERS; i++)
1688 si->input[i].use_joystick = FALSE;
1689 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1690 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1691 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1692 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1693 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1694 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1695 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1696 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1697 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1698 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1699 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1700 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1701 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1702 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1703 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1706 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1707 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1709 si->options.verbose = FALSE;
1712 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
1716 if (!setup_file_hash)
1721 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1722 setSetupInfo(global_setup_tokens, i,
1723 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
1728 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1729 setSetupInfo(editor_setup_tokens, i,
1730 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
1733 /* shortcut setup */
1734 ssi = setup.shortcut;
1735 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1736 setSetupInfo(shortcut_setup_tokens, i,
1737 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
1738 setup.shortcut = ssi;
1741 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1745 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1747 sii = setup.input[pnr];
1748 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1750 char full_token[100];
1752 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1753 setSetupInfo(player_setup_tokens, i,
1754 getHashEntry(setup_file_hash, full_token));
1756 setup.input[pnr] = sii;
1761 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1762 setSetupInfo(system_setup_tokens, i,
1763 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
1767 soi = setup.options;
1768 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1769 setSetupInfo(options_setup_tokens, i,
1770 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
1771 setup.options = soi;
1776 char *filename = getSetupFilename();
1777 SetupFileHash *setup_file_hash = NULL;
1779 /* always start with reliable default values */
1780 setSetupInfoToDefaults(&setup);
1782 setup_file_hash = loadSetupFileHash(filename);
1784 if (setup_file_hash)
1786 char *player_name_new;
1788 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
1789 decodeSetupFileHash(setup_file_hash);
1791 setup.direct_draw = !setup.double_buffering;
1793 freeSetupFileHash(setup_file_hash);
1795 /* needed to work around problems with fixed length strings */
1796 player_name_new = get_corrected_login_name(setup.player_name);
1797 free(setup.player_name);
1798 setup.player_name = player_name_new;
1801 Error(ERR_WARN, "using default setup values");
1806 char *filename = getSetupFilename();
1810 InitUserDataDirectory();
1812 if (!(file = fopen(filename, MODE_WRITE)))
1814 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1818 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1819 getCookie("SETUP")));
1820 fprintf(file, "\n");
1824 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1826 /* just to make things nicer :) */
1827 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1828 i == SETUP_TOKEN_GRAPHICS_SET)
1829 fprintf(file, "\n");
1831 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1836 fprintf(file, "\n");
1837 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1838 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1840 /* shortcut setup */
1841 ssi = setup.shortcut;
1842 fprintf(file, "\n");
1843 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1844 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1847 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1851 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1852 fprintf(file, "\n");
1854 sii = setup.input[pnr];
1855 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1856 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1861 fprintf(file, "\n");
1862 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1863 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1866 soi = setup.options;
1867 fprintf(file, "\n");
1868 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1869 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1873 SetFilePermissions(filename, PERMS_PRIVATE);
1876 void LoadCustomElementDescriptions()
1878 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1879 SetupFileHash *setup_file_hash;
1882 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1884 if (element_info[i].custom_description != NULL)
1886 free(element_info[i].custom_description);
1887 element_info[i].custom_description = NULL;
1891 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1894 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1896 char *token = getStringCat2(element_info[i].token_name, ".name");
1897 char *value = getHashEntry(setup_file_hash, token);
1900 element_info[i].custom_description = getStringCopy(value);
1905 freeSetupFileHash(setup_file_hash);
1908 void LoadSpecialMenuDesignSettings()
1910 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1911 SetupFileHash *setup_file_hash;
1914 /* always start with reliable default values from default config */
1915 for (i=0; image_config_vars[i].token != NULL; i++)
1916 for (j=0; image_config[j].token != NULL; j++)
1917 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
1918 *image_config_vars[i].value =
1919 get_integer_from_string(image_config[j].value);
1921 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1924 /* special case: initialize with default values that may be overwrittem */
1925 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
1927 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
1928 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
1930 if (value_x != NULL)
1931 menu.draw_xoffset[i] = get_integer_from_string(value_x);
1932 if (value_y != NULL)
1933 menu.draw_yoffset[i] = get_integer_from_string(value_y);
1936 /* read (and overwrite with) values that may be specified in config file */
1937 for (i=0; image_config_vars[i].token != NULL; i++)
1939 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
1942 *image_config_vars[i].value = get_integer_from_string(value);
1945 freeSetupFileHash(setup_file_hash);