1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
17 #include "libgame/libgame.h"
24 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
25 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
26 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
27 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
28 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
29 #define LEVEL_HEADER_UNUSED 14 /* unused level header bytes */
30 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
31 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
32 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
33 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
35 /* file identifier strings */
36 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
37 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
38 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
41 /* ========================================================================= */
42 /* level file functions */
43 /* ========================================================================= */
45 static void setLevelInfoToDefaults()
49 level.file_version = FILE_VERSION_ACTUAL;
50 level.game_version = GAME_VERSION_ACTUAL;
52 level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
53 level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
54 level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
56 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
57 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
59 for(x=0; x<MAX_LEV_FIELDX; x++)
60 for(y=0; y<MAX_LEV_FIELDY; y++)
61 Feld[x][y] = Ur[x][y] = EL_SAND;
64 level.gems_needed = 0;
65 level.amoeba_speed = 10;
66 level.time_magic_wall = 10;
67 level.time_wheel = 10;
68 level.time_light = 10;
69 level.time_timegate = 10;
70 level.amoeba_content = EL_DIAMOND;
71 level.double_speed = FALSE;
72 level.gravity = FALSE;
73 level.em_slippery_gems = FALSE;
75 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
77 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
78 level.author[i] = '\0';
80 strcpy(level.name, NAMELESS_LEVEL_NAME);
81 strcpy(level.author, ANONYMOUS_NAME);
83 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
86 level.num_yam_contents = STD_ELEMENT_CONTENTS;
87 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
90 level.yam_content[i][x][y] =
91 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
93 Feld[0][0] = Ur[0][0] = EL_PLAYER_1;
94 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
95 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
97 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
98 Properties1[EL_CUSTOM_START + i] = EP_BITMASK_DEFAULT;
100 BorderElement = EL_STEELWALL;
102 level.no_level_file = FALSE;
104 if (leveldir_current == NULL) /* only when dumping level */
107 /* try to determine better author name than 'anonymous' */
108 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
110 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
111 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
115 switch (LEVELCLASS(leveldir_current))
117 case LEVELCLASS_TUTORIAL:
118 strcpy(level.author, PROGRAM_AUTHOR_STRING);
121 case LEVELCLASS_CONTRIBUTION:
122 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
123 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
126 case LEVELCLASS_USER:
127 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
128 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
132 /* keep default value */
138 static int checkLevelElement(int element)
140 if (element >= NUM_FILE_ELEMENTS)
142 Error(ERR_WARN, "invalid level element %d", element);
143 element = EL_CHAR_QUESTION;
145 else if (element == EL_PLAYER_OBSOLETE)
146 element = EL_PLAYER_1;
147 else if (element == EL_KEY_OBSOLETE)
153 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
155 level->file_version = getFileVersion(file);
156 level->game_version = getFileVersion(file);
161 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
165 lev_fieldx = level->fieldx = fgetc(file);
166 lev_fieldy = level->fieldy = fgetc(file);
168 level->time = getFile16BitBE(file);
169 level->gems_needed = getFile16BitBE(file);
171 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
172 level->name[i] = fgetc(file);
173 level->name[MAX_LEVEL_NAME_LEN] = 0;
175 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
176 level->score[i] = fgetc(file);
178 level->num_yam_contents = STD_ELEMENT_CONTENTS;
179 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
182 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
184 level->amoeba_speed = fgetc(file);
185 level->time_magic_wall = fgetc(file);
186 level->time_wheel = fgetc(file);
187 level->amoeba_content = checkLevelElement(fgetc(file));
188 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
189 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
190 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
191 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
193 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
198 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
202 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
203 level->author[i] = fgetc(file);
204 level->author[MAX_LEVEL_NAME_LEN] = 0;
209 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
212 int chunk_size_expected = level->fieldx * level->fieldy;
214 /* Note: "chunk_size" was wrong before version 2.0 when elements are
215 stored with 16-bit encoding (and should be twice as big then).
216 Even worse, playfield data was stored 16-bit when only yamyam content
217 contained 16-bit elements and vice versa. */
219 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
220 chunk_size_expected *= 2;
222 if (chunk_size_expected != chunk_size)
224 ReadUnusedBytesFromFile(file, chunk_size);
225 return chunk_size_expected;
228 for(y=0; y<level->fieldy; y++)
229 for(x=0; x<level->fieldx; x++)
230 Feld[x][y] = Ur[x][y] =
231 checkLevelElement(level->encoding_16bit_field ?
232 getFile16BitBE(file) : fgetc(file));
236 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
240 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
241 int chunk_size_expected = header_size + content_size;
243 /* Note: "chunk_size" was wrong before version 2.0 when elements are
244 stored with 16-bit encoding (and should be twice as big then).
245 Even worse, playfield data was stored 16-bit when only yamyam content
246 contained 16-bit elements and vice versa. */
248 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
249 chunk_size_expected += content_size;
251 if (chunk_size_expected != chunk_size)
253 ReadUnusedBytesFromFile(file, chunk_size);
254 return chunk_size_expected;
258 level->num_yam_contents = fgetc(file);
262 /* correct invalid number of content fields -- should never happen */
263 if (level->num_yam_contents < 1 ||
264 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
265 level->num_yam_contents = STD_ELEMENT_CONTENTS;
267 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
270 level->yam_content[i][x][y] =
271 checkLevelElement(level->encoding_16bit_field ?
272 getFile16BitBE(file) : fgetc(file));
276 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
280 int num_contents, content_xsize, content_ysize;
281 int content_array[MAX_ELEMENT_CONTENTS][3][3];
283 element = checkLevelElement(getFile16BitBE(file));
284 num_contents = fgetc(file);
285 content_xsize = fgetc(file);
286 content_ysize = fgetc(file);
287 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
289 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
292 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
294 /* correct invalid number of content fields -- should never happen */
295 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
296 num_contents = STD_ELEMENT_CONTENTS;
298 if (element == EL_YAMYAM)
300 level->num_yam_contents = num_contents;
302 for(i=0; i<num_contents; i++)
305 level->yam_content[i][x][y] = content_array[i][x][y];
307 else if (element == EL_BD_AMOEBA)
309 level->amoeba_content = content_array[0][0][0];
313 Error(ERR_WARN, "cannot load content for element '%d'", element);
319 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
321 int num_changed_custom_elements = getFile16BitBE(file);
322 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
325 if (chunk_size_expected != chunk_size)
327 ReadUnusedBytesFromFile(file, chunk_size - 2);
328 return chunk_size_expected;
331 for (i=0; i < num_changed_custom_elements; i++)
333 int element = getFile16BitBE(file);
334 int properties = getFile32BitBE(file);
336 if (IS_CUSTOM_ELEMENT(element))
337 Properties1[element] = properties;
339 Error(ERR_WARN, "invalid custom element number %d", element);
345 void LoadLevelFromFilename(char *filename)
347 char cookie[MAX_LINE_LEN];
348 char chunk_name[CHUNK_ID_LEN + 1];
352 /* always start with reliable default values */
353 setLevelInfoToDefaults();
355 if (!(file = fopen(filename, MODE_READ)))
357 level.no_level_file = TRUE;
359 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
363 getFileChunkBE(file, chunk_name, NULL);
364 if (strcmp(chunk_name, "RND1") == 0)
366 getFile32BitBE(file); /* not used */
368 getFileChunkBE(file, chunk_name, NULL);
369 if (strcmp(chunk_name, "CAVE") != 0)
371 Error(ERR_WARN, "unknown format of level file '%s'", filename);
376 else /* check for pre-2.0 file format with cookie string */
378 strcpy(cookie, chunk_name);
379 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
380 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
381 cookie[strlen(cookie) - 1] = '\0';
383 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
385 Error(ERR_WARN, "unknown format of level file '%s'", filename);
390 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
392 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
397 /* pre-2.0 level files have no game version, so use file version here */
398 level.game_version = level.file_version;
401 if (level.file_version < FILE_VERSION_1_2)
403 /* level files from versions before 1.2.0 without chunk structure */
404 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
405 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
413 int (*loader)(FILE *, int, struct LevelInfo *);
417 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
418 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
419 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
420 { "BODY", -1, LoadLevel_BODY },
421 { "CONT", -1, LoadLevel_CONT },
422 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
423 { "CUS1", -1, LoadLevel_CUS1 },
427 while (getFileChunkBE(file, chunk_name, &chunk_size))
431 while (chunk_info[i].name != NULL &&
432 strcmp(chunk_name, chunk_info[i].name) != 0)
435 if (chunk_info[i].name == NULL)
437 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
438 chunk_name, filename);
439 ReadUnusedBytesFromFile(file, chunk_size);
441 else if (chunk_info[i].size != -1 &&
442 chunk_info[i].size != chunk_size)
444 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
445 chunk_size, chunk_name, filename);
446 ReadUnusedBytesFromFile(file, chunk_size);
450 /* call function to load this level chunk */
451 int chunk_size_expected =
452 (chunk_info[i].loader)(file, chunk_size, &level);
454 /* the size of some chunks cannot be checked before reading other
455 chunks first (like "HEAD" and "BODY") that contain some header
456 information, so check them here */
457 if (chunk_size_expected != chunk_size)
459 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
460 chunk_size, chunk_name, filename);
468 if (leveldir_current == NULL) /* only when dumping level */
471 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
472 IS_LEVELCLASS_USER(leveldir_current))
474 /* For user contributed and private levels, use the version of
475 the game engine the levels were created for.
476 Since 2.0.1, the game engine version is now directly stored
477 in the level file (chunk "VERS"), so there is no need anymore
478 to set the game version from the file version (except for old,
479 pre-2.0 levels, where the game version is still taken from the
480 file format version used to store the level -- see above). */
482 /* do some special adjustments to support older level versions */
483 if (level.file_version == FILE_VERSION_1_0)
485 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
486 Error(ERR_WARN, "using high speed movement for player");
488 /* player was faster than monsters in (pre-)1.0 levels */
489 level.double_speed = TRUE;
492 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
493 if (level.game_version == VERSION_IDENT(2,0,1))
494 level.em_slippery_gems = TRUE;
498 /* Always use the latest version of the game engine for all but
499 user contributed and private levels; this allows for actual
500 corrections in the game engine to take effect for existing,
501 converted levels (from "classic" or other existing games) to
502 make the game emulation more accurate, while (hopefully) not
503 breaking existing levels created from other players. */
505 level.game_version = GAME_VERSION_ACTUAL;
507 /* Set special EM style gems behaviour: EM style gems slip down from
508 normal, steel and growing wall. As this is a more fundamental change,
509 it seems better to set the default behaviour to "off" (as it is more
510 natural) and make it configurable in the level editor (as a property
511 of gem style elements). Already existing converted levels (neither
512 private nor contributed levels) are changed to the new behaviour. */
514 if (level.file_version < FILE_VERSION_2_0)
515 level.em_slippery_gems = TRUE;
518 /* determine border element for this level */
522 void LoadLevel(int level_nr)
524 char *filename = getLevelFilename(level_nr);
526 LoadLevelFromFilename(filename);
529 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
531 putFileVersion(file, level->file_version);
532 putFileVersion(file, level->game_version);
535 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
539 fputc(level->fieldx, file);
540 fputc(level->fieldy, file);
542 putFile16BitBE(file, level->time);
543 putFile16BitBE(file, level->gems_needed);
545 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
546 fputc(level->name[i], file);
548 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
549 fputc(level->score[i], file);
551 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
554 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
555 level->yam_content[i][x][y]),
557 fputc(level->amoeba_speed, file);
558 fputc(level->time_magic_wall, file);
559 fputc(level->time_wheel, file);
560 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
562 fputc((level->double_speed ? 1 : 0), file);
563 fputc((level->gravity ? 1 : 0), file);
564 fputc((level->encoding_16bit_field ? 1 : 0), file);
565 fputc((level->em_slippery_gems ? 1 : 0), file);
567 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
570 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
574 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
575 fputc(level->author[i], file);
578 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
582 for(y=0; y<level->fieldy; y++)
583 for(x=0; x<level->fieldx; x++)
584 if (level->encoding_16bit_field)
585 putFile16BitBE(file, Ur[x][y]);
587 fputc(Ur[x][y], file);
591 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
595 fputc(EL_YAMYAM, file);
596 fputc(level->num_yam_contents, file);
600 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
603 if (level->encoding_16bit_field)
604 putFile16BitBE(file, level->yam_content[i][x][y]);
606 fputc(level->yam_content[i][x][y], file);
610 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
613 int num_contents, content_xsize, content_ysize;
614 int content_array[MAX_ELEMENT_CONTENTS][3][3];
616 if (element == EL_YAMYAM)
618 num_contents = level->num_yam_contents;
622 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
625 content_array[i][x][y] = level->yam_content[i][x][y];
627 else if (element == EL_BD_AMOEBA)
633 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
636 content_array[i][x][y] = EL_EMPTY;
637 content_array[0][0][0] = level->amoeba_content;
641 /* chunk header already written -- write empty chunk data */
642 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
644 Error(ERR_WARN, "cannot save content for element '%d'", element);
648 putFile16BitBE(file, element);
649 fputc(num_contents, file);
650 fputc(content_xsize, file);
651 fputc(content_ysize, file);
653 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
655 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
658 putFile16BitBE(file, content_array[i][x][y]);
661 static void SaveLevel_CUS1(FILE *file, int num_changed_custom_elements)
665 putFile16BitBE(file, num_changed_custom_elements);
667 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
669 int element = EL_CUSTOM_START + i;
671 if (Properties1[element] != EP_BITMASK_DEFAULT)
673 if (check < num_changed_custom_elements)
675 putFile16BitBE(file, element);
676 putFile32BitBE(file, Properties1[element]);
683 if (check != num_changed_custom_elements) /* should not happen */
684 Error(ERR_WARN, "inconsistent number of custom element properties");
687 void SaveLevel(int level_nr)
689 char *filename = getLevelFilename(level_nr);
691 int num_changed_custom_elements = 0;
695 if (!(file = fopen(filename, MODE_WRITE)))
697 Error(ERR_WARN, "cannot save level file '%s'", filename);
701 level.file_version = FILE_VERSION_ACTUAL;
702 level.game_version = GAME_VERSION_ACTUAL;
704 /* check level field for 16-bit elements */
705 level.encoding_16bit_field = FALSE;
706 for(y=0; y<level.fieldy; y++)
707 for(x=0; x<level.fieldx; x++)
709 level.encoding_16bit_field = TRUE;
711 /* check yamyam content for 16-bit elements */
712 level.encoding_16bit_yamyam = FALSE;
713 for(i=0; i<level.num_yam_contents; i++)
716 if (level.yam_content[i][x][y] > 255)
717 level.encoding_16bit_yamyam = TRUE;
719 /* check amoeba content for 16-bit elements */
720 level.encoding_16bit_amoeba = FALSE;
721 if (level.amoeba_content > 255)
722 level.encoding_16bit_amoeba = TRUE;
724 /* calculate size of "BODY" chunk */
726 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
728 /* check for non-standard custom elements and calculate "CUS1" chunk size */
729 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
730 if (Properties1[EL_CUSTOM_START + i] != EP_BITMASK_DEFAULT)
731 num_changed_custom_elements++;
733 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
734 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
736 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
737 SaveLevel_VERS(file, &level);
739 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
740 SaveLevel_HEAD(file, &level);
742 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
743 SaveLevel_AUTH(file, &level);
745 putFileChunkBE(file, "BODY", body_chunk_size);
746 SaveLevel_BODY(file, &level);
748 if (level.encoding_16bit_yamyam ||
749 level.num_yam_contents != STD_ELEMENT_CONTENTS)
751 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
752 SaveLevel_CNT2(file, &level, EL_YAMYAM);
755 if (level.encoding_16bit_amoeba)
757 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
758 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
761 if (num_changed_custom_elements > 0)
763 putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements * 6);
764 SaveLevel_CUS1(file, num_changed_custom_elements);
769 SetFilePermissions(filename, PERMS_PRIVATE);
772 void DumpLevel(struct LevelInfo *level)
774 printf_line("-", 79);
775 printf("Level xxx (file version %06d, game version %06d)\n",
776 level->file_version, level->game_version);
777 printf_line("-", 79);
779 printf("Level Author: '%s'\n", level->author);
780 printf("Level Title: '%s'\n", level->name);
782 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
784 printf("Level Time: %d seconds\n", level->time);
785 printf("Gems needed: %d\n", level->gems_needed);
787 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
788 printf("Time for Wheel: %d seconds\n", level->time_wheel);
789 printf("Time for Light: %d seconds\n", level->time_light);
790 printf("Time for Timegate: %d seconds\n", level->time_timegate);
792 printf("Amoeba Speed: %d\n", level->amoeba_speed);
794 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
795 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
796 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
798 printf_line("-", 79);
802 /* ========================================================================= */
803 /* tape file functions */
804 /* ========================================================================= */
806 static void setTapeInfoToDefaults()
810 /* always start with reliable default values (empty tape) */
813 /* default values (also for pre-1.2 tapes) with only the first player */
814 tape.player_participates[0] = TRUE;
815 for(i=1; i<MAX_PLAYERS; i++)
816 tape.player_participates[i] = FALSE;
818 /* at least one (default: the first) player participates in every tape */
819 tape.num_participating_players = 1;
821 tape.level_nr = level_nr;
823 tape.changed = FALSE;
825 tape.recording = FALSE;
826 tape.playing = FALSE;
827 tape.pausing = FALSE;
830 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
832 tape->file_version = getFileVersion(file);
833 tape->game_version = getFileVersion(file);
838 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
842 tape->random_seed = getFile32BitBE(file);
843 tape->date = getFile32BitBE(file);
844 tape->length = getFile32BitBE(file);
846 /* read header fields that are new since version 1.2 */
847 if (tape->file_version >= FILE_VERSION_1_2)
849 byte store_participating_players = fgetc(file);
852 /* since version 1.2, tapes store which players participate in the tape */
853 tape->num_participating_players = 0;
854 for(i=0; i<MAX_PLAYERS; i++)
856 tape->player_participates[i] = FALSE;
858 if (store_participating_players & (1 << i))
860 tape->player_participates[i] = TRUE;
861 tape->num_participating_players++;
865 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
867 engine_version = getFileVersion(file);
868 if (engine_version > 0)
869 tape->engine_version = engine_version;
875 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
877 int level_identifier_size;
880 level_identifier_size = getFile16BitBE(file);
882 tape->level_identifier =
883 checked_realloc(tape->level_identifier, level_identifier_size);
885 for(i=0; i < level_identifier_size; i++)
886 tape->level_identifier[i] = fgetc(file);
888 tape->level_nr = getFile16BitBE(file);
890 chunk_size = 2 + level_identifier_size + 2;
895 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
898 int chunk_size_expected =
899 (tape->num_participating_players + 1) * tape->length;
901 if (chunk_size_expected != chunk_size)
903 ReadUnusedBytesFromFile(file, chunk_size);
904 return chunk_size_expected;
907 for(i=0; i<tape->length; i++)
909 if (i >= MAX_TAPELEN)
912 for(j=0; j<MAX_PLAYERS; j++)
914 tape->pos[i].action[j] = MV_NO_MOVING;
916 if (tape->player_participates[j])
917 tape->pos[i].action[j] = fgetc(file);
920 tape->pos[i].delay = fgetc(file);
922 if (tape->file_version == FILE_VERSION_1_0)
924 /* eliminate possible diagonal moves in old tapes */
925 /* this is only for backward compatibility */
927 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
928 byte action = tape->pos[i].action[0];
929 int k, num_moves = 0;
933 if (action & joy_dir[k])
935 tape->pos[i + num_moves].action[0] = joy_dir[k];
937 tape->pos[i + num_moves].delay = 0;
946 tape->length += num_moves;
949 else if (tape->file_version < FILE_VERSION_2_0)
951 /* convert pre-2.0 tapes to new tape format */
953 if (tape->pos[i].delay > 1)
956 tape->pos[i + 1] = tape->pos[i];
957 tape->pos[i + 1].delay = 1;
960 for(j=0; j<MAX_PLAYERS; j++)
961 tape->pos[i].action[j] = MV_NO_MOVING;
962 tape->pos[i].delay--;
973 if (i != tape->length)
974 chunk_size = (tape->num_participating_players + 1) * i;
979 void LoadTapeFromFilename(char *filename)
981 char cookie[MAX_LINE_LEN];
982 char chunk_name[CHUNK_ID_LEN + 1];
986 /* always start with reliable default values */
987 setTapeInfoToDefaults();
989 if (!(file = fopen(filename, MODE_READ)))
992 getFileChunkBE(file, chunk_name, NULL);
993 if (strcmp(chunk_name, "RND1") == 0)
995 getFile32BitBE(file); /* not used */
997 getFileChunkBE(file, chunk_name, NULL);
998 if (strcmp(chunk_name, "TAPE") != 0)
1000 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1005 else /* check for pre-2.0 file format with cookie string */
1007 strcpy(cookie, chunk_name);
1008 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1009 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1010 cookie[strlen(cookie) - 1] = '\0';
1012 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1014 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1019 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1021 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1026 /* pre-2.0 tape files have no game version, so use file version here */
1027 tape.game_version = tape.file_version;
1030 if (tape.file_version < FILE_VERSION_1_2)
1032 /* tape files from versions before 1.2.0 without chunk structure */
1033 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1034 LoadTape_BODY(file, 2 * tape.length, &tape);
1042 int (*loader)(FILE *, int, struct TapeInfo *);
1046 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1047 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1048 { "INFO", -1, LoadTape_INFO },
1049 { "BODY", -1, LoadTape_BODY },
1053 while (getFileChunkBE(file, chunk_name, &chunk_size))
1057 while (chunk_info[i].name != NULL &&
1058 strcmp(chunk_name, chunk_info[i].name) != 0)
1061 if (chunk_info[i].name == NULL)
1063 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1064 chunk_name, filename);
1065 ReadUnusedBytesFromFile(file, chunk_size);
1067 else if (chunk_info[i].size != -1 &&
1068 chunk_info[i].size != chunk_size)
1070 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1071 chunk_size, chunk_name, filename);
1072 ReadUnusedBytesFromFile(file, chunk_size);
1076 /* call function to load this tape chunk */
1077 int chunk_size_expected =
1078 (chunk_info[i].loader)(file, chunk_size, &tape);
1080 /* the size of some chunks cannot be checked before reading other
1081 chunks first (like "HEAD" and "BODY") that contain some header
1082 information, so check them here */
1083 if (chunk_size_expected != chunk_size)
1085 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1086 chunk_size, chunk_name, filename);
1094 tape.length_seconds = GetTapeLength();
1097 void LoadTape(int level_nr)
1099 char *filename = getTapeFilename(level_nr);
1101 LoadTapeFromFilename(filename);
1104 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1106 putFileVersion(file, tape->file_version);
1107 putFileVersion(file, tape->game_version);
1110 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1113 byte store_participating_players = 0;
1115 /* set bits for participating players for compact storage */
1116 for(i=0; i<MAX_PLAYERS; i++)
1117 if (tape->player_participates[i])
1118 store_participating_players |= (1 << i);
1120 putFile32BitBE(file, tape->random_seed);
1121 putFile32BitBE(file, tape->date);
1122 putFile32BitBE(file, tape->length);
1124 fputc(store_participating_players, file);
1126 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1127 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1129 putFileVersion(file, tape->engine_version);
1132 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1134 int level_identifier_size = strlen(tape->level_identifier) + 1;
1137 putFile16BitBE(file, level_identifier_size);
1139 for(i=0; i < level_identifier_size; i++)
1140 fputc(tape->level_identifier[i], file);
1142 putFile16BitBE(file, tape->level_nr);
1145 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1149 for(i=0; i<tape->length; i++)
1151 for(j=0; j<MAX_PLAYERS; j++)
1152 if (tape->player_participates[j])
1153 fputc(tape->pos[i].action[j], file);
1155 fputc(tape->pos[i].delay, file);
1159 void SaveTape(int level_nr)
1161 char *filename = getTapeFilename(level_nr);
1163 boolean new_tape = TRUE;
1164 int num_participating_players = 0;
1165 int info_chunk_size;
1166 int body_chunk_size;
1169 InitTapeDirectory(leveldir_current->filename);
1171 /* if a tape still exists, ask to overwrite it */
1172 if (access(filename, F_OK) == 0)
1175 if (!Request("Replace old tape ?", REQ_ASK))
1179 if (!(file = fopen(filename, MODE_WRITE)))
1181 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1185 tape.file_version = FILE_VERSION_ACTUAL;
1186 tape.game_version = GAME_VERSION_ACTUAL;
1188 /* count number of participating players */
1189 for(i=0; i<MAX_PLAYERS; i++)
1190 if (tape.player_participates[i])
1191 num_participating_players++;
1193 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1194 body_chunk_size = (num_participating_players + 1) * tape.length;
1196 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1197 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1199 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1200 SaveTape_VERS(file, &tape);
1202 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1203 SaveTape_HEAD(file, &tape);
1205 putFileChunkBE(file, "INFO", info_chunk_size);
1206 SaveTape_INFO(file, &tape);
1208 putFileChunkBE(file, "BODY", body_chunk_size);
1209 SaveTape_BODY(file, &tape);
1213 SetFilePermissions(filename, PERMS_PRIVATE);
1215 tape.changed = FALSE;
1218 Request("tape saved !", REQ_CONFIRM);
1221 void DumpTape(struct TapeInfo *tape)
1225 if (TAPE_IS_EMPTY(*tape))
1227 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1231 printf_line("-", 79);
1232 printf("Tape of Level %03d (file version %06d, game version %06d)\n",
1233 tape->level_nr, tape->file_version, tape->game_version);
1234 printf("Level series identifier: '%s'\n", tape->level_identifier);
1235 printf_line("-", 79);
1237 for(i=0; i<tape->length; i++)
1239 if (i >= MAX_TAPELEN)
1242 printf("%03d: ", i);
1244 for(j=0; j<MAX_PLAYERS; j++)
1246 if (tape->player_participates[j])
1248 int action = tape->pos[i].action[j];
1250 printf("%d:%02x ", j, action);
1251 printf("[%c%c%c%c|%c%c] - ",
1252 (action & JOY_LEFT ? '<' : ' '),
1253 (action & JOY_RIGHT ? '>' : ' '),
1254 (action & JOY_UP ? '^' : ' '),
1255 (action & JOY_DOWN ? 'v' : ' '),
1256 (action & JOY_BUTTON_1 ? '1' : ' '),
1257 (action & JOY_BUTTON_2 ? '2' : ' '));
1261 printf("(%03d)\n", tape->pos[i].delay);
1264 printf_line("-", 79);
1268 /* ========================================================================= */
1269 /* score file functions */
1270 /* ========================================================================= */
1272 void LoadScore(int level_nr)
1275 char *filename = getScoreFilename(level_nr);
1276 char cookie[MAX_LINE_LEN];
1277 char line[MAX_LINE_LEN];
1281 /* always start with reliable default values */
1282 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1284 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1285 highscore[i].Score = 0;
1288 if (!(file = fopen(filename, MODE_READ)))
1291 /* check file identifier */
1292 fgets(cookie, MAX_LINE_LEN, file);
1293 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1294 cookie[strlen(cookie) - 1] = '\0';
1296 if (!checkCookieString(cookie, SCORE_COOKIE))
1298 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1303 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1305 fscanf(file, "%d", &highscore[i].Score);
1306 fgets(line, MAX_LINE_LEN, file);
1308 if (line[strlen(line) - 1] == '\n')
1309 line[strlen(line) - 1] = '\0';
1311 for (line_ptr = line; *line_ptr; line_ptr++)
1313 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1315 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1316 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1325 void SaveScore(int level_nr)
1328 char *filename = getScoreFilename(level_nr);
1331 InitScoreDirectory(leveldir_current->filename);
1333 if (!(file = fopen(filename, MODE_WRITE)))
1335 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1339 fprintf(file, "%s\n\n", SCORE_COOKIE);
1341 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1342 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1346 SetFilePermissions(filename, PERMS_PUBLIC);
1350 /* ========================================================================= */
1351 /* setup file functions */
1352 /* ========================================================================= */
1354 #define TOKEN_STR_PLAYER_PREFIX "player_"
1357 #define SETUP_TOKEN_PLAYER_NAME 0
1358 #define SETUP_TOKEN_SOUND 1
1359 #define SETUP_TOKEN_SOUND_LOOPS 2
1360 #define SETUP_TOKEN_SOUND_MUSIC 3
1361 #define SETUP_TOKEN_SOUND_SIMPLE 4
1362 #define SETUP_TOKEN_TOONS 5
1363 #define SETUP_TOKEN_SCROLL_DELAY 6
1364 #define SETUP_TOKEN_SOFT_SCROLLING 7
1365 #define SETUP_TOKEN_FADING 8
1366 #define SETUP_TOKEN_AUTORECORD 9
1367 #define SETUP_TOKEN_QUICK_DOORS 10
1368 #define SETUP_TOKEN_TEAM_MODE 11
1369 #define SETUP_TOKEN_HANDICAP 12
1370 #define SETUP_TOKEN_TIME_LIMIT 13
1371 #define SETUP_TOKEN_FULLSCREEN 14
1372 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1373 #define SETUP_TOKEN_GRAPHICS_SET 16
1374 #define SETUP_TOKEN_SOUNDS_SET 17
1375 #define SETUP_TOKEN_MUSIC_SET 18
1376 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1377 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1378 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1380 #define NUM_GLOBAL_SETUP_TOKENS 22
1383 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1384 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1385 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1386 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1387 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1388 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1389 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1390 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1391 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1393 #define NUM_EDITOR_SETUP_TOKENS 9
1395 /* shortcut setup */
1396 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1397 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1398 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1400 #define NUM_SHORTCUT_SETUP_TOKENS 3
1403 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1404 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1405 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1406 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1407 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1408 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1409 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1410 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1411 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1412 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1413 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1414 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1415 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1416 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1417 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1418 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1420 #define NUM_PLAYER_SETUP_TOKENS 16
1423 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1424 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1426 #define NUM_SYSTEM_SETUP_TOKENS 2
1429 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1431 #define NUM_OPTIONS_SETUP_TOKENS 1
1434 static struct SetupInfo si;
1435 static struct SetupEditorInfo sei;
1436 static struct SetupShortcutInfo ssi;
1437 static struct SetupInputInfo sii;
1438 static struct SetupSystemInfo syi;
1439 static struct OptionInfo soi;
1441 static struct TokenInfo global_setup_tokens[] =
1443 { TYPE_STRING, &si.player_name, "player_name" },
1444 { TYPE_SWITCH, &si.sound, "sound" },
1445 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1446 { TYPE_SWITCH, &si.sound_music, "background_music" },
1447 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1448 { TYPE_SWITCH, &si.toons, "toons" },
1449 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1450 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1451 { TYPE_SWITCH, &si.fading, "screen_fading" },
1452 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1453 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1454 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1455 { TYPE_SWITCH, &si.handicap, "handicap" },
1456 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1457 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1458 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1459 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1460 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1461 { TYPE_STRING, &si.music_set, "music_set" },
1462 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1463 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1464 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1467 static struct TokenInfo editor_setup_tokens[] =
1469 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1470 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1471 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1472 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1473 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1474 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1475 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1476 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1477 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1480 static struct TokenInfo shortcut_setup_tokens[] =
1482 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1483 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1484 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1487 static struct TokenInfo player_setup_tokens[] =
1489 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1490 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1491 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1492 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1493 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1494 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1495 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1496 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1497 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1498 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1499 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1500 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1501 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1502 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1503 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1504 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1507 static struct TokenInfo system_setup_tokens[] =
1509 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1510 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1513 static struct TokenInfo options_setup_tokens[] =
1515 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1518 static char *get_corrected_login_name(char *login_name)
1520 /* needed because player name must be a fixed length string */
1521 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1523 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1524 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1526 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1527 if (strchr(login_name_new, ' '))
1528 *strchr(login_name_new, ' ') = '\0';
1530 return login_name_new;
1533 static void setSetupInfoToDefaults(struct SetupInfo *si)
1537 si->player_name = get_corrected_login_name(getLoginName());
1540 si->sound_loops = TRUE;
1541 si->sound_music = TRUE;
1542 si->sound_simple = TRUE;
1544 si->double_buffering = TRUE;
1545 si->direct_draw = !si->double_buffering;
1546 si->scroll_delay = TRUE;
1547 si->soft_scrolling = TRUE;
1549 si->autorecord = TRUE;
1550 si->quick_doors = FALSE;
1551 si->team_mode = FALSE;
1552 si->handicap = TRUE;
1553 si->time_limit = TRUE;
1554 si->fullscreen = FALSE;
1555 si->ask_on_escape = TRUE;
1557 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1558 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1559 si->music_set = getStringCopy(MUSIC_SUBDIR);
1560 si->override_level_graphics = FALSE;
1561 si->override_level_sounds = FALSE;
1562 si->override_level_music = FALSE;
1564 si->editor.el_boulderdash = TRUE;
1565 si->editor.el_emerald_mine = TRUE;
1566 si->editor.el_more = TRUE;
1567 si->editor.el_sokoban = TRUE;
1568 si->editor.el_supaplex = TRUE;
1569 si->editor.el_diamond_caves = TRUE;
1570 si->editor.el_dx_boulderdash = TRUE;
1571 si->editor.el_chars = TRUE;
1572 si->editor.el_custom = TRUE;
1574 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1575 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1576 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1578 for (i=0; i<MAX_PLAYERS; i++)
1580 si->input[i].use_joystick = FALSE;
1581 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1582 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1583 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1584 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1585 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1586 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1587 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1588 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1589 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1590 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1591 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1592 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1593 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1594 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1595 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1598 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1599 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1601 si->options.verbose = FALSE;
1604 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1608 if (!setup_file_list)
1613 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1614 setSetupInfo(global_setup_tokens, i,
1615 getTokenValue(setup_file_list, global_setup_tokens[i].text));
1620 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1621 setSetupInfo(editor_setup_tokens, i,
1622 getTokenValue(setup_file_list,editor_setup_tokens[i].text));
1625 /* shortcut setup */
1626 ssi = setup.shortcut;
1627 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1628 setSetupInfo(shortcut_setup_tokens, i,
1629 getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
1630 setup.shortcut = ssi;
1633 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1637 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1639 sii = setup.input[pnr];
1640 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1642 char full_token[100];
1644 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1645 setSetupInfo(player_setup_tokens, i,
1646 getTokenValue(setup_file_list, full_token));
1648 setup.input[pnr] = sii;
1653 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1654 setSetupInfo(system_setup_tokens, i,
1655 getTokenValue(setup_file_list, system_setup_tokens[i].text));
1659 soi = setup.options;
1660 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1661 setSetupInfo(options_setup_tokens, i,
1662 getTokenValue(setup_file_list, options_setup_tokens[i].text));
1663 setup.options = soi;
1668 char *filename = getSetupFilename();
1669 struct SetupFileList *setup_file_list = NULL;
1671 /* always start with reliable default values */
1672 setSetupInfoToDefaults(&setup);
1674 setup_file_list = loadSetupFileList(filename);
1676 if (setup_file_list)
1678 char *player_name_new;
1680 checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
1681 decodeSetupFileList(setup_file_list);
1683 setup.direct_draw = !setup.double_buffering;
1685 freeSetupFileList(setup_file_list);
1687 /* needed to work around problems with fixed length strings */
1688 player_name_new = get_corrected_login_name(setup.player_name);
1689 free(setup.player_name);
1690 setup.player_name = player_name_new;
1693 Error(ERR_WARN, "using default setup values");
1698 char *filename = getSetupFilename();
1702 InitUserDataDirectory();
1704 if (!(file = fopen(filename, MODE_WRITE)))
1706 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1710 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1711 getCookie("SETUP")));
1712 fprintf(file, "\n");
1716 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1718 /* just to make things nicer :) */
1719 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1720 i == SETUP_TOKEN_GRAPHICS_SET)
1721 fprintf(file, "\n");
1723 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1728 fprintf(file, "\n");
1729 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1730 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1732 /* shortcut setup */
1733 ssi = setup.shortcut;
1734 fprintf(file, "\n");
1735 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1736 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1739 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1743 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1744 fprintf(file, "\n");
1746 sii = setup.input[pnr];
1747 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1748 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1753 fprintf(file, "\n");
1754 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1755 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1758 soi = setup.options;
1759 fprintf(file, "\n");
1760 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1761 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1765 SetFilePermissions(filename, PERMS_PRIVATE);
1768 void LoadCustomElementDescriptions()
1770 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1771 struct SetupFileList *setup_file_list;
1774 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1776 if (element_info[i].custom_description != NULL)
1778 free(element_info[i].custom_description);
1779 element_info[i].custom_description = NULL;
1783 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1786 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1788 char *token = getStringCat2(element_info[i].token_name, ".name");
1789 char *value = getTokenValue(setup_file_list, token);
1792 element_info[i].custom_description = getStringCopy(value);
1797 freeSetupFileList(setup_file_list);
1800 void LoadSpecialMenuDesignSettings()
1802 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1803 struct SetupFileList *setup_file_list;
1806 /* !!! CHANGE THIS !!! (redundant initialization) !!! */
1807 global.num_toons = 20;
1808 global.menu_draw_xoffset = 0;
1809 global.menu_draw_yoffset = 0;
1810 global.menu_draw_xoffset_MAIN = 0;
1811 global.menu_draw_yoffset_MAIN = 0;
1812 global.door_step_offset = 2;
1813 global.door_step_delay = 10;
1815 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1818 value = getTokenValue(setup_file_list, "global.num_toons");
1820 global.num_toons = get_integer_from_string(value);
1822 value = getTokenValue(setup_file_list, "menu.draw_xoffset");
1824 global.menu_draw_xoffset = get_integer_from_string(value);
1826 value = getTokenValue(setup_file_list, "menu.draw_yoffset");
1828 global.menu_draw_yoffset = get_integer_from_string(value);
1830 value = getTokenValue(setup_file_list, "menu.draw_xoffset.MAIN");
1832 global.menu_draw_xoffset_MAIN = get_integer_from_string(value);
1834 value = getTokenValue(setup_file_list, "menu.draw_yoffset.MAIN");
1836 global.menu_draw_yoffset_MAIN = get_integer_from_string(value);
1838 value = getTokenValue(setup_file_list, "door.step_offset");
1840 global.door_step_offset = get_integer_from_string(value);
1842 value = getTokenValue(setup_file_list, "door.step_delay");
1844 global.door_step_delay = get_integer_from_string(value);
1846 freeSetupFileList(setup_file_list);