1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
17 #include "libgame/libgame.h"
25 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
26 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
27 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
28 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
29 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
30 #define LEVEL_HEADER_UNUSED 14 /* unused level header bytes */
31 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
32 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
33 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
34 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
36 /* file identifier strings */
37 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
38 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
39 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
42 /* ========================================================================= */
43 /* level file functions */
44 /* ========================================================================= */
46 static void setLevelInfoToDefaults()
50 level.file_version = FILE_VERSION_ACTUAL;
51 level.game_version = GAME_VERSION_ACTUAL;
53 level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
54 level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
55 level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
57 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
58 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
60 for(x=0; x<MAX_LEV_FIELDX; x++)
61 for(y=0; y<MAX_LEV_FIELDY; y++)
62 Feld[x][y] = Ur[x][y] = EL_SAND;
65 level.gems_needed = 0;
66 level.amoeba_speed = 10;
67 level.time_magic_wall = 10;
68 level.time_wheel = 10;
69 level.time_light = 10;
70 level.time_timegate = 10;
71 level.amoeba_content = EL_DIAMOND;
72 level.double_speed = FALSE;
73 level.gravity = FALSE;
74 level.em_slippery_gems = FALSE;
76 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
78 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
79 level.author[i] = '\0';
81 strcpy(level.name, NAMELESS_LEVEL_NAME);
82 strcpy(level.author, ANONYMOUS_NAME);
84 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
87 level.num_yamyam_contents = STD_ELEMENT_CONTENTS;
88 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
91 level.yamyam_content[i][x][y] =
92 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
94 Feld[0][0] = Ur[0][0] = EL_PLAYER_1;
95 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
96 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
98 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
100 int element = EL_CUSTOM_START + i;
102 element_info[element].use_gfx_element = FALSE;
103 element_info[element].gfx_element = EL_EMPTY_SPACE;
104 element_info[element].move_direction = 0;
108 element_info[element].content[x][y] = EL_EMPTY_SPACE;
110 element_info[element].change.events = CE_BITMASK_DEFAULT;
111 element_info[element].change.delay_fixed = 0;
112 element_info[element].change.delay_random = 0;
113 element_info[element].change.successor = EL_EMPTY_SPACE;
115 /* start with no properties at all */
116 for (j=0; j < NUM_EP_BITFIELDS; j++)
117 Properties[element][j] = EP_BITMASK_DEFAULT;
120 BorderElement = EL_STEELWALL;
122 level.no_level_file = FALSE;
124 if (leveldir_current == NULL) /* only when dumping level */
127 /* try to determine better author name than 'anonymous' */
128 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
130 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
131 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
135 switch (LEVELCLASS(leveldir_current))
137 case LEVELCLASS_TUTORIAL:
138 strcpy(level.author, PROGRAM_AUTHOR_STRING);
141 case LEVELCLASS_CONTRIBUTION:
142 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
143 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
146 case LEVELCLASS_USER:
147 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
148 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
152 /* keep default value */
158 static int checkLevelElement(int element)
160 if (element >= NUM_FILE_ELEMENTS)
162 Error(ERR_WARN, "invalid level element %d", element);
163 element = EL_CHAR_QUESTION;
165 else if (element == EL_PLAYER_OBSOLETE)
166 element = EL_PLAYER_1;
167 else if (element == EL_KEY_OBSOLETE)
173 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
175 level->file_version = getFileVersion(file);
176 level->game_version = getFileVersion(file);
181 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
185 lev_fieldx = level->fieldx = fgetc(file);
186 lev_fieldy = level->fieldy = fgetc(file);
188 level->time = getFile16BitBE(file);
189 level->gems_needed = getFile16BitBE(file);
191 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
192 level->name[i] = fgetc(file);
193 level->name[MAX_LEVEL_NAME_LEN] = 0;
195 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
196 level->score[i] = fgetc(file);
198 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
199 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
202 level->yamyam_content[i][x][y] = checkLevelElement(fgetc(file));
204 level->amoeba_speed = fgetc(file);
205 level->time_magic_wall = fgetc(file);
206 level->time_wheel = fgetc(file);
207 level->amoeba_content = checkLevelElement(fgetc(file));
208 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
209 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
210 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
211 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
213 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
218 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
222 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
223 level->author[i] = fgetc(file);
224 level->author[MAX_LEVEL_NAME_LEN] = 0;
229 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
232 int chunk_size_expected = level->fieldx * level->fieldy;
234 /* Note: "chunk_size" was wrong before version 2.0 when elements are
235 stored with 16-bit encoding (and should be twice as big then).
236 Even worse, playfield data was stored 16-bit when only yamyam content
237 contained 16-bit elements and vice versa. */
239 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
240 chunk_size_expected *= 2;
242 if (chunk_size_expected != chunk_size)
244 ReadUnusedBytesFromFile(file, chunk_size);
245 return chunk_size_expected;
248 for(y=0; y<level->fieldy; y++)
249 for(x=0; x<level->fieldx; x++)
250 Feld[x][y] = Ur[x][y] =
251 checkLevelElement(level->encoding_16bit_field ?
252 getFile16BitBE(file) : fgetc(file));
256 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
260 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
261 int chunk_size_expected = header_size + content_size;
263 /* Note: "chunk_size" was wrong before version 2.0 when elements are
264 stored with 16-bit encoding (and should be twice as big then).
265 Even worse, playfield data was stored 16-bit when only yamyam content
266 contained 16-bit elements and vice versa. */
268 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
269 chunk_size_expected += content_size;
271 if (chunk_size_expected != chunk_size)
273 ReadUnusedBytesFromFile(file, chunk_size);
274 return chunk_size_expected;
278 level->num_yamyam_contents = fgetc(file);
282 /* correct invalid number of content fields -- should never happen */
283 if (level->num_yamyam_contents < 1 ||
284 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
285 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
287 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
290 level->yamyam_content[i][x][y] =
291 checkLevelElement(level->encoding_16bit_field ?
292 getFile16BitBE(file) : fgetc(file));
296 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
300 int num_contents, content_xsize, content_ysize;
301 int content_array[MAX_ELEMENT_CONTENTS][3][3];
303 element = checkLevelElement(getFile16BitBE(file));
304 num_contents = fgetc(file);
305 content_xsize = fgetc(file);
306 content_ysize = fgetc(file);
307 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
309 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
312 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
314 /* correct invalid number of content fields -- should never happen */
315 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
316 num_contents = STD_ELEMENT_CONTENTS;
318 if (element == EL_YAMYAM)
320 level->num_yamyam_contents = num_contents;
322 for(i=0; i<num_contents; i++)
325 level->yamyam_content[i][x][y] = content_array[i][x][y];
327 else if (element == EL_BD_AMOEBA)
329 level->amoeba_content = content_array[0][0][0];
333 Error(ERR_WARN, "cannot load content for element '%d'", element);
339 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
341 int num_changed_custom_elements = getFile16BitBE(file);
342 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
345 if (chunk_size_expected != chunk_size)
347 ReadUnusedBytesFromFile(file, chunk_size - 2);
348 return chunk_size_expected;
351 for (i=0; i < num_changed_custom_elements; i++)
353 int element = getFile16BitBE(file);
354 int properties = getFile32BitBE(file);
356 if (IS_CUSTOM_ELEMENT(element))
357 Properties[element][EP_BITFIELD_BASE] = properties;
359 Error(ERR_WARN, "invalid custom element number %d", element);
365 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
367 int num_changed_custom_elements = getFile16BitBE(file);
368 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
371 if (chunk_size_expected != chunk_size)
373 ReadUnusedBytesFromFile(file, chunk_size - 2);
374 return chunk_size_expected;
377 for (i=0; i < num_changed_custom_elements; i++)
379 int element = getFile16BitBE(file);
380 int custom_element_successor = getFile16BitBE(file);
382 if (IS_CUSTOM_ELEMENT(element))
383 element_info[element].change.successor = custom_element_successor;
385 Error(ERR_WARN, "invalid custom element number %d", element);
391 void LoadLevelFromFilename(char *filename)
393 char cookie[MAX_LINE_LEN];
394 char chunk_name[CHUNK_ID_LEN + 1];
398 /* always start with reliable default values */
399 setLevelInfoToDefaults();
401 if (!(file = fopen(filename, MODE_READ)))
403 level.no_level_file = TRUE;
405 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
409 getFileChunkBE(file, chunk_name, NULL);
410 if (strcmp(chunk_name, "RND1") == 0)
412 getFile32BitBE(file); /* not used */
414 getFileChunkBE(file, chunk_name, NULL);
415 if (strcmp(chunk_name, "CAVE") != 0)
417 Error(ERR_WARN, "unknown format of level file '%s'", filename);
422 else /* check for pre-2.0 file format with cookie string */
424 strcpy(cookie, chunk_name);
425 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
426 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
427 cookie[strlen(cookie) - 1] = '\0';
429 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
431 Error(ERR_WARN, "unknown format of level file '%s'", filename);
436 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
438 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
443 /* pre-2.0 level files have no game version, so use file version here */
444 level.game_version = level.file_version;
447 if (level.file_version < FILE_VERSION_1_2)
449 /* level files from versions before 1.2.0 without chunk structure */
450 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
451 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
459 int (*loader)(FILE *, int, struct LevelInfo *);
463 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
464 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
465 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
466 { "BODY", -1, LoadLevel_BODY },
467 { "CONT", -1, LoadLevel_CONT },
468 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
469 { "CUS1", -1, LoadLevel_CUS1 },
470 { "CUS2", -1, LoadLevel_CUS2 },
474 while (getFileChunkBE(file, chunk_name, &chunk_size))
478 while (chunk_info[i].name != NULL &&
479 strcmp(chunk_name, chunk_info[i].name) != 0)
482 if (chunk_info[i].name == NULL)
484 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
485 chunk_name, filename);
486 ReadUnusedBytesFromFile(file, chunk_size);
488 else if (chunk_info[i].size != -1 &&
489 chunk_info[i].size != chunk_size)
491 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
492 chunk_size, chunk_name, filename);
493 ReadUnusedBytesFromFile(file, chunk_size);
497 /* call function to load this level chunk */
498 int chunk_size_expected =
499 (chunk_info[i].loader)(file, chunk_size, &level);
501 /* the size of some chunks cannot be checked before reading other
502 chunks first (like "HEAD" and "BODY") that contain some header
503 information, so check them here */
504 if (chunk_size_expected != chunk_size)
506 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
507 chunk_size, chunk_name, filename);
515 if (leveldir_current == NULL) /* only when dumping level */
518 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
519 IS_LEVELCLASS_USER(leveldir_current))
521 /* For user contributed and private levels, use the version of
522 the game engine the levels were created for.
523 Since 2.0.1, the game engine version is now directly stored
524 in the level file (chunk "VERS"), so there is no need anymore
525 to set the game version from the file version (except for old,
526 pre-2.0 levels, where the game version is still taken from the
527 file format version used to store the level -- see above). */
529 /* do some special adjustments to support older level versions */
530 if (level.file_version == FILE_VERSION_1_0)
532 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
533 Error(ERR_WARN, "using high speed movement for player");
535 /* player was faster than monsters in (pre-)1.0 levels */
536 level.double_speed = TRUE;
539 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
540 if (level.game_version == VERSION_IDENT(2,0,1))
541 level.em_slippery_gems = TRUE;
545 /* Always use the latest version of the game engine for all but
546 user contributed and private levels; this allows for actual
547 corrections in the game engine to take effect for existing,
548 converted levels (from "classic" or other existing games) to
549 make the game emulation more accurate, while (hopefully) not
550 breaking existing levels created from other players. */
552 level.game_version = GAME_VERSION_ACTUAL;
554 /* Set special EM style gems behaviour: EM style gems slip down from
555 normal, steel and growing wall. As this is a more fundamental change,
556 it seems better to set the default behaviour to "off" (as it is more
557 natural) and make it configurable in the level editor (as a property
558 of gem style elements). Already existing converted levels (neither
559 private nor contributed levels) are changed to the new behaviour. */
561 if (level.file_version < FILE_VERSION_2_0)
562 level.em_slippery_gems = TRUE;
565 /* map some elements which have changed in newer versions */
566 if (level.game_version <= VERSION_IDENT(2,2,0))
570 /* map game font elements */
571 for(y=0; y<level.fieldy; y++)
573 for(x=0; x<level.fieldx; x++)
575 int element = Ur[x][y];
577 if (element == EL_CHAR('['))
578 element = EL_CHAR_AUMLAUT;
579 else if (element == EL_CHAR('\\'))
580 element = EL_CHAR_OUMLAUT;
581 else if (element == EL_CHAR(']'))
582 element = EL_CHAR_UUMLAUT;
583 else if (element == EL_CHAR('^'))
584 element = EL_CHAR_COPYRIGHT;
586 Feld[x][y] = Ur[x][y] = element;
591 /* determine border element for this level */
595 void LoadLevel(int level_nr)
597 char *filename = getLevelFilename(level_nr);
599 LoadLevelFromFilename(filename);
600 InitElementPropertiesEngine(level.game_version);
603 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
605 putFileVersion(file, level->file_version);
606 putFileVersion(file, level->game_version);
609 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
613 fputc(level->fieldx, file);
614 fputc(level->fieldy, file);
616 putFile16BitBE(file, level->time);
617 putFile16BitBE(file, level->gems_needed);
619 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
620 fputc(level->name[i], file);
622 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
623 fputc(level->score[i], file);
625 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
628 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
629 level->yamyam_content[i][x][y]),
631 fputc(level->amoeba_speed, file);
632 fputc(level->time_magic_wall, file);
633 fputc(level->time_wheel, file);
634 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
636 fputc((level->double_speed ? 1 : 0), file);
637 fputc((level->gravity ? 1 : 0), file);
638 fputc((level->encoding_16bit_field ? 1 : 0), file);
639 fputc((level->em_slippery_gems ? 1 : 0), file);
641 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
644 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
648 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
649 fputc(level->author[i], file);
652 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
656 for(y=0; y<level->fieldy; y++)
657 for(x=0; x<level->fieldx; x++)
658 if (level->encoding_16bit_field)
659 putFile16BitBE(file, Ur[x][y]);
661 fputc(Ur[x][y], file);
665 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
669 fputc(EL_YAMYAM, file);
670 fputc(level->num_yamyam_contents, file);
674 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
677 if (level->encoding_16bit_field)
678 putFile16BitBE(file, level->yamyam_content[i][x][y]);
680 fputc(level->yamyam_content[i][x][y], file);
684 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
687 int num_contents, content_xsize, content_ysize;
688 int content_array[MAX_ELEMENT_CONTENTS][3][3];
690 if (element == EL_YAMYAM)
692 num_contents = level->num_yamyam_contents;
696 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
699 content_array[i][x][y] = level->yamyam_content[i][x][y];
701 else if (element == EL_BD_AMOEBA)
707 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
710 content_array[i][x][y] = EL_EMPTY;
711 content_array[0][0][0] = level->amoeba_content;
715 /* chunk header already written -- write empty chunk data */
716 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
718 Error(ERR_WARN, "cannot save content for element '%d'", element);
722 putFile16BitBE(file, element);
723 fputc(num_contents, file);
724 fputc(content_xsize, file);
725 fputc(content_ysize, file);
727 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
729 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
732 putFile16BitBE(file, content_array[i][x][y]);
735 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
736 int num_changed_custom_elements)
740 putFile16BitBE(file, num_changed_custom_elements);
742 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
744 int element = EL_CUSTOM_START + i;
746 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
748 if (check < num_changed_custom_elements)
750 putFile16BitBE(file, element);
751 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
758 if (check != num_changed_custom_elements) /* should not happen */
759 Error(ERR_WARN, "inconsistent number of custom element properties");
762 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
763 int num_changed_custom_elements)
767 putFile16BitBE(file, num_changed_custom_elements);
769 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
771 int element = EL_CUSTOM_START + i;
773 if (element_info[element].change.successor != EL_EMPTY_SPACE)
775 if (check < num_changed_custom_elements)
777 putFile16BitBE(file, element);
778 putFile16BitBE(file, element_info[element].change.successor);
785 if (check != num_changed_custom_elements) /* should not happen */
786 Error(ERR_WARN, "inconsistent number of custom element successors");
789 void SaveLevel(int level_nr)
791 char *filename = getLevelFilename(level_nr);
793 int num_changed_custom_elements1 = 0;
794 int num_changed_custom_elements2 = 0;
798 if (!(file = fopen(filename, MODE_WRITE)))
800 Error(ERR_WARN, "cannot save level file '%s'", filename);
804 level.file_version = FILE_VERSION_ACTUAL;
805 level.game_version = GAME_VERSION_ACTUAL;
807 /* check level field for 16-bit elements */
808 level.encoding_16bit_field = FALSE;
809 for(y=0; y<level.fieldy; y++)
810 for(x=0; x<level.fieldx; x++)
812 level.encoding_16bit_field = TRUE;
814 /* check yamyam content for 16-bit elements */
815 level.encoding_16bit_yamyam = FALSE;
816 for(i=0; i<level.num_yamyam_contents; i++)
819 if (level.yamyam_content[i][x][y] > 255)
820 level.encoding_16bit_yamyam = TRUE;
822 /* check amoeba content for 16-bit elements */
823 level.encoding_16bit_amoeba = FALSE;
824 if (level.amoeba_content > 255)
825 level.encoding_16bit_amoeba = TRUE;
827 /* calculate size of "BODY" chunk */
829 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
831 /* check for non-standard custom elements and calculate "CUS1" chunk size */
832 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
833 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
834 num_changed_custom_elements1++;
836 /* check for non-standard custom elements and calculate "CUS2" chunk size */
837 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
838 if (element_info[EL_CUSTOM_START + i].change.successor != EL_EMPTY_SPACE)
839 num_changed_custom_elements2++;
841 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
842 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
844 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
845 SaveLevel_VERS(file, &level);
847 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
848 SaveLevel_HEAD(file, &level);
850 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
851 SaveLevel_AUTH(file, &level);
853 putFileChunkBE(file, "BODY", body_chunk_size);
854 SaveLevel_BODY(file, &level);
856 if (level.encoding_16bit_yamyam ||
857 level.num_yamyam_contents != STD_ELEMENT_CONTENTS)
859 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
860 SaveLevel_CNT2(file, &level, EL_YAMYAM);
863 if (level.encoding_16bit_amoeba)
865 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
866 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
869 if (num_changed_custom_elements1 > 0)
871 putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements1 * 6);
872 SaveLevel_CUS1(file, &level, num_changed_custom_elements1);
875 if (num_changed_custom_elements2 > 0)
877 putFileChunkBE(file, "CUS2", 2 + num_changed_custom_elements2 * 4);
878 SaveLevel_CUS2(file, &level, num_changed_custom_elements2);
883 SetFilePermissions(filename, PERMS_PRIVATE);
886 void DumpLevel(struct LevelInfo *level)
888 printf_line("-", 79);
889 printf("Level xxx (file version %08d, game version %08d)\n",
890 level->file_version, level->game_version);
891 printf_line("-", 79);
893 printf("Level Author: '%s'\n", level->author);
894 printf("Level Title: '%s'\n", level->name);
896 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
898 printf("Level Time: %d seconds\n", level->time);
899 printf("Gems needed: %d\n", level->gems_needed);
901 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
902 printf("Time for Wheel: %d seconds\n", level->time_wheel);
903 printf("Time for Light: %d seconds\n", level->time_light);
904 printf("Time for Timegate: %d seconds\n", level->time_timegate);
906 printf("Amoeba Speed: %d\n", level->amoeba_speed);
908 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
909 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
910 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
912 printf_line("-", 79);
916 /* ========================================================================= */
917 /* tape file functions */
918 /* ========================================================================= */
920 static void setTapeInfoToDefaults()
924 /* always start with reliable default values (empty tape) */
927 /* default values (also for pre-1.2 tapes) with only the first player */
928 tape.player_participates[0] = TRUE;
929 for(i=1; i<MAX_PLAYERS; i++)
930 tape.player_participates[i] = FALSE;
932 /* at least one (default: the first) player participates in every tape */
933 tape.num_participating_players = 1;
935 tape.level_nr = level_nr;
937 tape.changed = FALSE;
939 tape.recording = FALSE;
940 tape.playing = FALSE;
941 tape.pausing = FALSE;
944 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
946 tape->file_version = getFileVersion(file);
947 tape->game_version = getFileVersion(file);
952 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
956 tape->random_seed = getFile32BitBE(file);
957 tape->date = getFile32BitBE(file);
958 tape->length = getFile32BitBE(file);
960 /* read header fields that are new since version 1.2 */
961 if (tape->file_version >= FILE_VERSION_1_2)
963 byte store_participating_players = fgetc(file);
966 /* since version 1.2, tapes store which players participate in the tape */
967 tape->num_participating_players = 0;
968 for(i=0; i<MAX_PLAYERS; i++)
970 tape->player_participates[i] = FALSE;
972 if (store_participating_players & (1 << i))
974 tape->player_participates[i] = TRUE;
975 tape->num_participating_players++;
979 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
981 engine_version = getFileVersion(file);
982 if (engine_version > 0)
983 tape->engine_version = engine_version;
989 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
991 int level_identifier_size;
994 level_identifier_size = getFile16BitBE(file);
996 tape->level_identifier =
997 checked_realloc(tape->level_identifier, level_identifier_size);
999 for(i=0; i < level_identifier_size; i++)
1000 tape->level_identifier[i] = fgetc(file);
1002 tape->level_nr = getFile16BitBE(file);
1004 chunk_size = 2 + level_identifier_size + 2;
1009 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1012 int chunk_size_expected =
1013 (tape->num_participating_players + 1) * tape->length;
1015 if (chunk_size_expected != chunk_size)
1017 ReadUnusedBytesFromFile(file, chunk_size);
1018 return chunk_size_expected;
1021 for(i=0; i<tape->length; i++)
1023 if (i >= MAX_TAPELEN)
1026 for(j=0; j<MAX_PLAYERS; j++)
1028 tape->pos[i].action[j] = MV_NO_MOVING;
1030 if (tape->player_participates[j])
1031 tape->pos[i].action[j] = fgetc(file);
1034 tape->pos[i].delay = fgetc(file);
1036 if (tape->file_version == FILE_VERSION_1_0)
1038 /* eliminate possible diagonal moves in old tapes */
1039 /* this is only for backward compatibility */
1041 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1042 byte action = tape->pos[i].action[0];
1043 int k, num_moves = 0;
1047 if (action & joy_dir[k])
1049 tape->pos[i + num_moves].action[0] = joy_dir[k];
1051 tape->pos[i + num_moves].delay = 0;
1060 tape->length += num_moves;
1063 else if (tape->file_version < FILE_VERSION_2_0)
1065 /* convert pre-2.0 tapes to new tape format */
1067 if (tape->pos[i].delay > 1)
1070 tape->pos[i + 1] = tape->pos[i];
1071 tape->pos[i + 1].delay = 1;
1074 for(j=0; j<MAX_PLAYERS; j++)
1075 tape->pos[i].action[j] = MV_NO_MOVING;
1076 tape->pos[i].delay--;
1087 if (i != tape->length)
1088 chunk_size = (tape->num_participating_players + 1) * i;
1093 void LoadTapeFromFilename(char *filename)
1095 char cookie[MAX_LINE_LEN];
1096 char chunk_name[CHUNK_ID_LEN + 1];
1100 /* always start with reliable default values */
1101 setTapeInfoToDefaults();
1103 if (!(file = fopen(filename, MODE_READ)))
1106 getFileChunkBE(file, chunk_name, NULL);
1107 if (strcmp(chunk_name, "RND1") == 0)
1109 getFile32BitBE(file); /* not used */
1111 getFileChunkBE(file, chunk_name, NULL);
1112 if (strcmp(chunk_name, "TAPE") != 0)
1114 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1119 else /* check for pre-2.0 file format with cookie string */
1121 strcpy(cookie, chunk_name);
1122 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1123 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1124 cookie[strlen(cookie) - 1] = '\0';
1126 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1128 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1133 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1135 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1140 /* pre-2.0 tape files have no game version, so use file version here */
1141 tape.game_version = tape.file_version;
1144 if (tape.file_version < FILE_VERSION_1_2)
1146 /* tape files from versions before 1.2.0 without chunk structure */
1147 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1148 LoadTape_BODY(file, 2 * tape.length, &tape);
1156 int (*loader)(FILE *, int, struct TapeInfo *);
1160 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1161 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1162 { "INFO", -1, LoadTape_INFO },
1163 { "BODY", -1, LoadTape_BODY },
1167 while (getFileChunkBE(file, chunk_name, &chunk_size))
1171 while (chunk_info[i].name != NULL &&
1172 strcmp(chunk_name, chunk_info[i].name) != 0)
1175 if (chunk_info[i].name == NULL)
1177 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1178 chunk_name, filename);
1179 ReadUnusedBytesFromFile(file, chunk_size);
1181 else if (chunk_info[i].size != -1 &&
1182 chunk_info[i].size != chunk_size)
1184 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1185 chunk_size, chunk_name, filename);
1186 ReadUnusedBytesFromFile(file, chunk_size);
1190 /* call function to load this tape chunk */
1191 int chunk_size_expected =
1192 (chunk_info[i].loader)(file, chunk_size, &tape);
1194 /* the size of some chunks cannot be checked before reading other
1195 chunks first (like "HEAD" and "BODY") that contain some header
1196 information, so check them here */
1197 if (chunk_size_expected != chunk_size)
1199 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1200 chunk_size, chunk_name, filename);
1208 tape.length_seconds = GetTapeLength();
1211 void LoadTape(int level_nr)
1213 char *filename = getTapeFilename(level_nr);
1215 LoadTapeFromFilename(filename);
1218 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1220 putFileVersion(file, tape->file_version);
1221 putFileVersion(file, tape->game_version);
1224 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1227 byte store_participating_players = 0;
1229 /* set bits for participating players for compact storage */
1230 for(i=0; i<MAX_PLAYERS; i++)
1231 if (tape->player_participates[i])
1232 store_participating_players |= (1 << i);
1234 putFile32BitBE(file, tape->random_seed);
1235 putFile32BitBE(file, tape->date);
1236 putFile32BitBE(file, tape->length);
1238 fputc(store_participating_players, file);
1240 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1241 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1243 putFileVersion(file, tape->engine_version);
1246 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1248 int level_identifier_size = strlen(tape->level_identifier) + 1;
1251 putFile16BitBE(file, level_identifier_size);
1253 for(i=0; i < level_identifier_size; i++)
1254 fputc(tape->level_identifier[i], file);
1256 putFile16BitBE(file, tape->level_nr);
1259 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1263 for(i=0; i<tape->length; i++)
1265 for(j=0; j<MAX_PLAYERS; j++)
1266 if (tape->player_participates[j])
1267 fputc(tape->pos[i].action[j], file);
1269 fputc(tape->pos[i].delay, file);
1273 void SaveTape(int level_nr)
1275 char *filename = getTapeFilename(level_nr);
1277 boolean new_tape = TRUE;
1278 int num_participating_players = 0;
1279 int info_chunk_size;
1280 int body_chunk_size;
1283 InitTapeDirectory(leveldir_current->filename);
1285 /* if a tape still exists, ask to overwrite it */
1286 if (access(filename, F_OK) == 0)
1289 if (!Request("Replace old tape ?", REQ_ASK))
1293 if (!(file = fopen(filename, MODE_WRITE)))
1295 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1299 tape.file_version = FILE_VERSION_ACTUAL;
1300 tape.game_version = GAME_VERSION_ACTUAL;
1302 /* count number of participating players */
1303 for(i=0; i<MAX_PLAYERS; i++)
1304 if (tape.player_participates[i])
1305 num_participating_players++;
1307 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1308 body_chunk_size = (num_participating_players + 1) * tape.length;
1310 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1311 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1313 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1314 SaveTape_VERS(file, &tape);
1316 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1317 SaveTape_HEAD(file, &tape);
1319 putFileChunkBE(file, "INFO", info_chunk_size);
1320 SaveTape_INFO(file, &tape);
1322 putFileChunkBE(file, "BODY", body_chunk_size);
1323 SaveTape_BODY(file, &tape);
1327 SetFilePermissions(filename, PERMS_PRIVATE);
1329 tape.changed = FALSE;
1332 Request("tape saved !", REQ_CONFIRM);
1335 void DumpTape(struct TapeInfo *tape)
1339 if (TAPE_IS_EMPTY(*tape))
1341 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1345 printf_line("-", 79);
1346 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1347 tape->level_nr, tape->file_version, tape->game_version);
1348 printf("Level series identifier: '%s'\n", tape->level_identifier);
1349 printf_line("-", 79);
1351 for(i=0; i<tape->length; i++)
1353 if (i >= MAX_TAPELEN)
1356 printf("%03d: ", i);
1358 for(j=0; j<MAX_PLAYERS; j++)
1360 if (tape->player_participates[j])
1362 int action = tape->pos[i].action[j];
1364 printf("%d:%02x ", j, action);
1365 printf("[%c%c%c%c|%c%c] - ",
1366 (action & JOY_LEFT ? '<' : ' '),
1367 (action & JOY_RIGHT ? '>' : ' '),
1368 (action & JOY_UP ? '^' : ' '),
1369 (action & JOY_DOWN ? 'v' : ' '),
1370 (action & JOY_BUTTON_1 ? '1' : ' '),
1371 (action & JOY_BUTTON_2 ? '2' : ' '));
1375 printf("(%03d)\n", tape->pos[i].delay);
1378 printf_line("-", 79);
1382 /* ========================================================================= */
1383 /* score file functions */
1384 /* ========================================================================= */
1386 void LoadScore(int level_nr)
1389 char *filename = getScoreFilename(level_nr);
1390 char cookie[MAX_LINE_LEN];
1391 char line[MAX_LINE_LEN];
1395 /* always start with reliable default values */
1396 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1398 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1399 highscore[i].Score = 0;
1402 if (!(file = fopen(filename, MODE_READ)))
1405 /* check file identifier */
1406 fgets(cookie, MAX_LINE_LEN, file);
1407 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1408 cookie[strlen(cookie) - 1] = '\0';
1410 if (!checkCookieString(cookie, SCORE_COOKIE))
1412 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1417 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1419 fscanf(file, "%d", &highscore[i].Score);
1420 fgets(line, MAX_LINE_LEN, file);
1422 if (line[strlen(line) - 1] == '\n')
1423 line[strlen(line) - 1] = '\0';
1425 for (line_ptr = line; *line_ptr; line_ptr++)
1427 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1429 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1430 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1439 void SaveScore(int level_nr)
1442 char *filename = getScoreFilename(level_nr);
1445 InitScoreDirectory(leveldir_current->filename);
1447 if (!(file = fopen(filename, MODE_WRITE)))
1449 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1453 fprintf(file, "%s\n\n", SCORE_COOKIE);
1455 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1456 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1460 SetFilePermissions(filename, PERMS_PUBLIC);
1464 /* ========================================================================= */
1465 /* setup file functions */
1466 /* ========================================================================= */
1468 #define TOKEN_STR_PLAYER_PREFIX "player_"
1471 #define SETUP_TOKEN_PLAYER_NAME 0
1472 #define SETUP_TOKEN_SOUND 1
1473 #define SETUP_TOKEN_SOUND_LOOPS 2
1474 #define SETUP_TOKEN_SOUND_MUSIC 3
1475 #define SETUP_TOKEN_SOUND_SIMPLE 4
1476 #define SETUP_TOKEN_TOONS 5
1477 #define SETUP_TOKEN_SCROLL_DELAY 6
1478 #define SETUP_TOKEN_SOFT_SCROLLING 7
1479 #define SETUP_TOKEN_FADING 8
1480 #define SETUP_TOKEN_AUTORECORD 9
1481 #define SETUP_TOKEN_QUICK_DOORS 10
1482 #define SETUP_TOKEN_TEAM_MODE 11
1483 #define SETUP_TOKEN_HANDICAP 12
1484 #define SETUP_TOKEN_TIME_LIMIT 13
1485 #define SETUP_TOKEN_FULLSCREEN 14
1486 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1487 #define SETUP_TOKEN_GRAPHICS_SET 16
1488 #define SETUP_TOKEN_SOUNDS_SET 17
1489 #define SETUP_TOKEN_MUSIC_SET 18
1490 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1491 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1492 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1494 #define NUM_GLOBAL_SETUP_TOKENS 22
1497 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1498 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1499 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1500 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1501 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1502 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1503 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1504 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1505 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1507 #define NUM_EDITOR_SETUP_TOKENS 9
1509 /* shortcut setup */
1510 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1511 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1512 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1514 #define NUM_SHORTCUT_SETUP_TOKENS 3
1517 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1518 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1519 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1520 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1521 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1522 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1523 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1524 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1525 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1526 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1527 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1528 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1529 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1530 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1531 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1532 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1534 #define NUM_PLAYER_SETUP_TOKENS 16
1537 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1538 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1540 #define NUM_SYSTEM_SETUP_TOKENS 2
1543 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1545 #define NUM_OPTIONS_SETUP_TOKENS 1
1548 static struct SetupInfo si;
1549 static struct SetupEditorInfo sei;
1550 static struct SetupShortcutInfo ssi;
1551 static struct SetupInputInfo sii;
1552 static struct SetupSystemInfo syi;
1553 static struct OptionInfo soi;
1555 static struct TokenInfo global_setup_tokens[] =
1557 { TYPE_STRING, &si.player_name, "player_name" },
1558 { TYPE_SWITCH, &si.sound, "sound" },
1559 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1560 { TYPE_SWITCH, &si.sound_music, "background_music" },
1561 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1562 { TYPE_SWITCH, &si.toons, "toons" },
1563 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1564 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1565 { TYPE_SWITCH, &si.fading, "screen_fading" },
1566 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1567 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1568 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1569 { TYPE_SWITCH, &si.handicap, "handicap" },
1570 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1571 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1572 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1573 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1574 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1575 { TYPE_STRING, &si.music_set, "music_set" },
1576 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1577 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1578 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1581 static struct TokenInfo editor_setup_tokens[] =
1583 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1584 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1585 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1586 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1587 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1588 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1589 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1590 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1591 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1594 static struct TokenInfo shortcut_setup_tokens[] =
1596 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1597 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1598 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1601 static struct TokenInfo player_setup_tokens[] =
1603 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1604 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1605 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1606 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1607 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1608 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1609 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1610 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1611 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1612 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1613 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1614 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1615 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1616 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1617 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1618 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1621 static struct TokenInfo system_setup_tokens[] =
1623 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1624 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1627 static struct TokenInfo options_setup_tokens[] =
1629 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1632 static char *get_corrected_login_name(char *login_name)
1634 /* needed because player name must be a fixed length string */
1635 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1637 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1638 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1640 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1641 if (strchr(login_name_new, ' '))
1642 *strchr(login_name_new, ' ') = '\0';
1644 return login_name_new;
1647 static void setSetupInfoToDefaults(struct SetupInfo *si)
1651 si->player_name = get_corrected_login_name(getLoginName());
1654 si->sound_loops = TRUE;
1655 si->sound_music = TRUE;
1656 si->sound_simple = TRUE;
1658 si->double_buffering = TRUE;
1659 si->direct_draw = !si->double_buffering;
1660 si->scroll_delay = TRUE;
1661 si->soft_scrolling = TRUE;
1663 si->autorecord = TRUE;
1664 si->quick_doors = FALSE;
1665 si->team_mode = FALSE;
1666 si->handicap = TRUE;
1667 si->time_limit = TRUE;
1668 si->fullscreen = FALSE;
1669 si->ask_on_escape = TRUE;
1671 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1672 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1673 si->music_set = getStringCopy(MUSIC_SUBDIR);
1674 si->override_level_graphics = FALSE;
1675 si->override_level_sounds = FALSE;
1676 si->override_level_music = FALSE;
1678 si->editor.el_boulderdash = TRUE;
1679 si->editor.el_emerald_mine = TRUE;
1680 si->editor.el_more = TRUE;
1681 si->editor.el_sokoban = TRUE;
1682 si->editor.el_supaplex = TRUE;
1683 si->editor.el_diamond_caves = TRUE;
1684 si->editor.el_dx_boulderdash = TRUE;
1685 si->editor.el_chars = TRUE;
1686 si->editor.el_custom = TRUE;
1688 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1689 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1690 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1692 for (i=0; i<MAX_PLAYERS; i++)
1694 si->input[i].use_joystick = FALSE;
1695 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1696 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1697 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1698 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1699 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1700 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1701 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1702 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1703 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1704 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1705 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1706 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1707 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1708 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1709 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1712 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1713 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1715 si->options.verbose = FALSE;
1718 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
1722 if (!setup_file_hash)
1727 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1728 setSetupInfo(global_setup_tokens, i,
1729 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
1734 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1735 setSetupInfo(editor_setup_tokens, i,
1736 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
1739 /* shortcut setup */
1740 ssi = setup.shortcut;
1741 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1742 setSetupInfo(shortcut_setup_tokens, i,
1743 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
1744 setup.shortcut = ssi;
1747 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1751 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1753 sii = setup.input[pnr];
1754 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1756 char full_token[100];
1758 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1759 setSetupInfo(player_setup_tokens, i,
1760 getHashEntry(setup_file_hash, full_token));
1762 setup.input[pnr] = sii;
1767 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1768 setSetupInfo(system_setup_tokens, i,
1769 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
1773 soi = setup.options;
1774 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1775 setSetupInfo(options_setup_tokens, i,
1776 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
1777 setup.options = soi;
1782 char *filename = getSetupFilename();
1783 SetupFileHash *setup_file_hash = NULL;
1785 /* always start with reliable default values */
1786 setSetupInfoToDefaults(&setup);
1788 setup_file_hash = loadSetupFileHash(filename);
1790 if (setup_file_hash)
1792 char *player_name_new;
1794 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
1795 decodeSetupFileHash(setup_file_hash);
1797 setup.direct_draw = !setup.double_buffering;
1799 freeSetupFileHash(setup_file_hash);
1801 /* needed to work around problems with fixed length strings */
1802 player_name_new = get_corrected_login_name(setup.player_name);
1803 free(setup.player_name);
1804 setup.player_name = player_name_new;
1807 Error(ERR_WARN, "using default setup values");
1812 char *filename = getSetupFilename();
1816 InitUserDataDirectory();
1818 if (!(file = fopen(filename, MODE_WRITE)))
1820 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1824 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1825 getCookie("SETUP")));
1826 fprintf(file, "\n");
1830 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1832 /* just to make things nicer :) */
1833 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1834 i == SETUP_TOKEN_GRAPHICS_SET)
1835 fprintf(file, "\n");
1837 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1842 fprintf(file, "\n");
1843 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1844 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1846 /* shortcut setup */
1847 ssi = setup.shortcut;
1848 fprintf(file, "\n");
1849 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1850 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1853 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1857 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1858 fprintf(file, "\n");
1860 sii = setup.input[pnr];
1861 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1862 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1867 fprintf(file, "\n");
1868 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1869 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1872 soi = setup.options;
1873 fprintf(file, "\n");
1874 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1875 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1879 SetFilePermissions(filename, PERMS_PRIVATE);
1882 void LoadCustomElementDescriptions()
1884 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1885 SetupFileHash *setup_file_hash;
1888 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1890 if (element_info[i].custom_description != NULL)
1892 free(element_info[i].custom_description);
1893 element_info[i].custom_description = NULL;
1897 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1900 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1902 char *token = getStringCat2(element_info[i].token_name, ".name");
1903 char *value = getHashEntry(setup_file_hash, token);
1906 element_info[i].custom_description = getStringCopy(value);
1911 freeSetupFileHash(setup_file_hash);
1914 void LoadSpecialMenuDesignSettings()
1916 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1917 SetupFileHash *setup_file_hash;
1920 /* always start with reliable default values from default config */
1921 for (i=0; image_config_vars[i].token != NULL; i++)
1922 for (j=0; image_config[j].token != NULL; j++)
1923 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
1924 *image_config_vars[i].value =
1925 get_integer_from_string(image_config[j].value);
1927 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1930 /* special case: initialize with default values that may be overwrittem */
1931 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
1933 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
1934 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
1936 if (value_x != NULL)
1937 menu.draw_xoffset[i] = get_integer_from_string(value_x);
1938 if (value_y != NULL)
1939 menu.draw_yoffset[i] = get_integer_from_string(value_y);
1942 /* read (and overwrite with) values that may be specified in config file */
1943 for (i=0; image_config_vars[i].token != NULL; i++)
1945 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
1948 *image_config_vars[i].value = get_integer_from_string(value);
1951 freeSetupFileHash(setup_file_hash);