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 /* map some elements which have changed in newer versions */
519 if (level.game_version <= VERSION_IDENT(2,2,0))
523 /* map game font elements */
524 for(y=0; y<level.fieldy; y++)
526 for(x=0; x<level.fieldx; x++)
528 int element = Ur[x][y];
530 if (element == EL_CHAR('['))
531 element = EL_CHAR_AUMLAUT;
532 else if (element == EL_CHAR('\\'))
533 element = EL_CHAR_OUMLAUT;
534 else if (element == EL_CHAR(']'))
535 element = EL_CHAR_UUMLAUT;
536 else if (element == EL_CHAR('^'))
537 element = EL_CHAR_COPYRIGHT;
539 Feld[x][y] = Ur[x][y] = element;
544 /* determine border element for this level */
548 void LoadLevel(int level_nr)
550 char *filename = getLevelFilename(level_nr);
552 LoadLevelFromFilename(filename);
555 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
557 putFileVersion(file, level->file_version);
558 putFileVersion(file, level->game_version);
561 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
565 fputc(level->fieldx, file);
566 fputc(level->fieldy, file);
568 putFile16BitBE(file, level->time);
569 putFile16BitBE(file, level->gems_needed);
571 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
572 fputc(level->name[i], file);
574 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
575 fputc(level->score[i], file);
577 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
580 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
581 level->yam_content[i][x][y]),
583 fputc(level->amoeba_speed, file);
584 fputc(level->time_magic_wall, file);
585 fputc(level->time_wheel, file);
586 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
588 fputc((level->double_speed ? 1 : 0), file);
589 fputc((level->gravity ? 1 : 0), file);
590 fputc((level->encoding_16bit_field ? 1 : 0), file);
591 fputc((level->em_slippery_gems ? 1 : 0), file);
593 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
596 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
600 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
601 fputc(level->author[i], file);
604 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
608 for(y=0; y<level->fieldy; y++)
609 for(x=0; x<level->fieldx; x++)
610 if (level->encoding_16bit_field)
611 putFile16BitBE(file, Ur[x][y]);
613 fputc(Ur[x][y], file);
617 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
621 fputc(EL_YAMYAM, file);
622 fputc(level->num_yam_contents, file);
626 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
629 if (level->encoding_16bit_field)
630 putFile16BitBE(file, level->yam_content[i][x][y]);
632 fputc(level->yam_content[i][x][y], file);
636 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
639 int num_contents, content_xsize, content_ysize;
640 int content_array[MAX_ELEMENT_CONTENTS][3][3];
642 if (element == EL_YAMYAM)
644 num_contents = level->num_yam_contents;
648 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
651 content_array[i][x][y] = level->yam_content[i][x][y];
653 else if (element == EL_BD_AMOEBA)
659 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
662 content_array[i][x][y] = EL_EMPTY;
663 content_array[0][0][0] = level->amoeba_content;
667 /* chunk header already written -- write empty chunk data */
668 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
670 Error(ERR_WARN, "cannot save content for element '%d'", element);
674 putFile16BitBE(file, element);
675 fputc(num_contents, file);
676 fputc(content_xsize, file);
677 fputc(content_ysize, file);
679 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
681 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
684 putFile16BitBE(file, content_array[i][x][y]);
687 static void SaveLevel_CUS1(FILE *file, int num_changed_custom_elements)
691 putFile16BitBE(file, num_changed_custom_elements);
693 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
695 int element = EL_CUSTOM_START + i;
697 if (Properties1[element] != EP_BITMASK_DEFAULT)
699 if (check < num_changed_custom_elements)
701 putFile16BitBE(file, element);
702 putFile32BitBE(file, Properties1[element]);
709 if (check != num_changed_custom_elements) /* should not happen */
710 Error(ERR_WARN, "inconsistent number of custom element properties");
713 void SaveLevel(int level_nr)
715 char *filename = getLevelFilename(level_nr);
717 int num_changed_custom_elements = 0;
721 if (!(file = fopen(filename, MODE_WRITE)))
723 Error(ERR_WARN, "cannot save level file '%s'", filename);
727 level.file_version = FILE_VERSION_ACTUAL;
728 level.game_version = GAME_VERSION_ACTUAL;
730 /* check level field for 16-bit elements */
731 level.encoding_16bit_field = FALSE;
732 for(y=0; y<level.fieldy; y++)
733 for(x=0; x<level.fieldx; x++)
735 level.encoding_16bit_field = TRUE;
737 /* check yamyam content for 16-bit elements */
738 level.encoding_16bit_yamyam = FALSE;
739 for(i=0; i<level.num_yam_contents; i++)
742 if (level.yam_content[i][x][y] > 255)
743 level.encoding_16bit_yamyam = TRUE;
745 /* check amoeba content for 16-bit elements */
746 level.encoding_16bit_amoeba = FALSE;
747 if (level.amoeba_content > 255)
748 level.encoding_16bit_amoeba = TRUE;
750 /* calculate size of "BODY" chunk */
752 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
754 /* check for non-standard custom elements and calculate "CUS1" chunk size */
755 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
756 if (Properties1[EL_CUSTOM_START + i] != EP_BITMASK_DEFAULT)
757 num_changed_custom_elements++;
759 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
760 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
762 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
763 SaveLevel_VERS(file, &level);
765 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
766 SaveLevel_HEAD(file, &level);
768 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
769 SaveLevel_AUTH(file, &level);
771 putFileChunkBE(file, "BODY", body_chunk_size);
772 SaveLevel_BODY(file, &level);
774 if (level.encoding_16bit_yamyam ||
775 level.num_yam_contents != STD_ELEMENT_CONTENTS)
777 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
778 SaveLevel_CNT2(file, &level, EL_YAMYAM);
781 if (level.encoding_16bit_amoeba)
783 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
784 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
787 if (num_changed_custom_elements > 0)
789 putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements * 6);
790 SaveLevel_CUS1(file, num_changed_custom_elements);
795 SetFilePermissions(filename, PERMS_PRIVATE);
798 void DumpLevel(struct LevelInfo *level)
800 printf_line("-", 79);
801 printf("Level xxx (file version %06d, game version %06d)\n",
802 level->file_version, level->game_version);
803 printf_line("-", 79);
805 printf("Level Author: '%s'\n", level->author);
806 printf("Level Title: '%s'\n", level->name);
808 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
810 printf("Level Time: %d seconds\n", level->time);
811 printf("Gems needed: %d\n", level->gems_needed);
813 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
814 printf("Time for Wheel: %d seconds\n", level->time_wheel);
815 printf("Time for Light: %d seconds\n", level->time_light);
816 printf("Time for Timegate: %d seconds\n", level->time_timegate);
818 printf("Amoeba Speed: %d\n", level->amoeba_speed);
820 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
821 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
822 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
824 printf_line("-", 79);
828 /* ========================================================================= */
829 /* tape file functions */
830 /* ========================================================================= */
832 static void setTapeInfoToDefaults()
836 /* always start with reliable default values (empty tape) */
839 /* default values (also for pre-1.2 tapes) with only the first player */
840 tape.player_participates[0] = TRUE;
841 for(i=1; i<MAX_PLAYERS; i++)
842 tape.player_participates[i] = FALSE;
844 /* at least one (default: the first) player participates in every tape */
845 tape.num_participating_players = 1;
847 tape.level_nr = level_nr;
849 tape.changed = FALSE;
851 tape.recording = FALSE;
852 tape.playing = FALSE;
853 tape.pausing = FALSE;
856 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
858 tape->file_version = getFileVersion(file);
859 tape->game_version = getFileVersion(file);
864 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
868 tape->random_seed = getFile32BitBE(file);
869 tape->date = getFile32BitBE(file);
870 tape->length = getFile32BitBE(file);
872 /* read header fields that are new since version 1.2 */
873 if (tape->file_version >= FILE_VERSION_1_2)
875 byte store_participating_players = fgetc(file);
878 /* since version 1.2, tapes store which players participate in the tape */
879 tape->num_participating_players = 0;
880 for(i=0; i<MAX_PLAYERS; i++)
882 tape->player_participates[i] = FALSE;
884 if (store_participating_players & (1 << i))
886 tape->player_participates[i] = TRUE;
887 tape->num_participating_players++;
891 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
893 engine_version = getFileVersion(file);
894 if (engine_version > 0)
895 tape->engine_version = engine_version;
901 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
903 int level_identifier_size;
906 level_identifier_size = getFile16BitBE(file);
908 tape->level_identifier =
909 checked_realloc(tape->level_identifier, level_identifier_size);
911 for(i=0; i < level_identifier_size; i++)
912 tape->level_identifier[i] = fgetc(file);
914 tape->level_nr = getFile16BitBE(file);
916 chunk_size = 2 + level_identifier_size + 2;
921 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
924 int chunk_size_expected =
925 (tape->num_participating_players + 1) * tape->length;
927 if (chunk_size_expected != chunk_size)
929 ReadUnusedBytesFromFile(file, chunk_size);
930 return chunk_size_expected;
933 for(i=0; i<tape->length; i++)
935 if (i >= MAX_TAPELEN)
938 for(j=0; j<MAX_PLAYERS; j++)
940 tape->pos[i].action[j] = MV_NO_MOVING;
942 if (tape->player_participates[j])
943 tape->pos[i].action[j] = fgetc(file);
946 tape->pos[i].delay = fgetc(file);
948 if (tape->file_version == FILE_VERSION_1_0)
950 /* eliminate possible diagonal moves in old tapes */
951 /* this is only for backward compatibility */
953 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
954 byte action = tape->pos[i].action[0];
955 int k, num_moves = 0;
959 if (action & joy_dir[k])
961 tape->pos[i + num_moves].action[0] = joy_dir[k];
963 tape->pos[i + num_moves].delay = 0;
972 tape->length += num_moves;
975 else if (tape->file_version < FILE_VERSION_2_0)
977 /* convert pre-2.0 tapes to new tape format */
979 if (tape->pos[i].delay > 1)
982 tape->pos[i + 1] = tape->pos[i];
983 tape->pos[i + 1].delay = 1;
986 for(j=0; j<MAX_PLAYERS; j++)
987 tape->pos[i].action[j] = MV_NO_MOVING;
988 tape->pos[i].delay--;
999 if (i != tape->length)
1000 chunk_size = (tape->num_participating_players + 1) * i;
1005 void LoadTapeFromFilename(char *filename)
1007 char cookie[MAX_LINE_LEN];
1008 char chunk_name[CHUNK_ID_LEN + 1];
1012 /* always start with reliable default values */
1013 setTapeInfoToDefaults();
1015 if (!(file = fopen(filename, MODE_READ)))
1018 getFileChunkBE(file, chunk_name, NULL);
1019 if (strcmp(chunk_name, "RND1") == 0)
1021 getFile32BitBE(file); /* not used */
1023 getFileChunkBE(file, chunk_name, NULL);
1024 if (strcmp(chunk_name, "TAPE") != 0)
1026 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1031 else /* check for pre-2.0 file format with cookie string */
1033 strcpy(cookie, chunk_name);
1034 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1035 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1036 cookie[strlen(cookie) - 1] = '\0';
1038 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1040 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1045 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1047 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1052 /* pre-2.0 tape files have no game version, so use file version here */
1053 tape.game_version = tape.file_version;
1056 if (tape.file_version < FILE_VERSION_1_2)
1058 /* tape files from versions before 1.2.0 without chunk structure */
1059 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1060 LoadTape_BODY(file, 2 * tape.length, &tape);
1068 int (*loader)(FILE *, int, struct TapeInfo *);
1072 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1073 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1074 { "INFO", -1, LoadTape_INFO },
1075 { "BODY", -1, LoadTape_BODY },
1079 while (getFileChunkBE(file, chunk_name, &chunk_size))
1083 while (chunk_info[i].name != NULL &&
1084 strcmp(chunk_name, chunk_info[i].name) != 0)
1087 if (chunk_info[i].name == NULL)
1089 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1090 chunk_name, filename);
1091 ReadUnusedBytesFromFile(file, chunk_size);
1093 else if (chunk_info[i].size != -1 &&
1094 chunk_info[i].size != chunk_size)
1096 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1097 chunk_size, chunk_name, filename);
1098 ReadUnusedBytesFromFile(file, chunk_size);
1102 /* call function to load this tape chunk */
1103 int chunk_size_expected =
1104 (chunk_info[i].loader)(file, chunk_size, &tape);
1106 /* the size of some chunks cannot be checked before reading other
1107 chunks first (like "HEAD" and "BODY") that contain some header
1108 information, so check them here */
1109 if (chunk_size_expected != chunk_size)
1111 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1112 chunk_size, chunk_name, filename);
1120 tape.length_seconds = GetTapeLength();
1123 void LoadTape(int level_nr)
1125 char *filename = getTapeFilename(level_nr);
1127 LoadTapeFromFilename(filename);
1130 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1132 putFileVersion(file, tape->file_version);
1133 putFileVersion(file, tape->game_version);
1136 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1139 byte store_participating_players = 0;
1141 /* set bits for participating players for compact storage */
1142 for(i=0; i<MAX_PLAYERS; i++)
1143 if (tape->player_participates[i])
1144 store_participating_players |= (1 << i);
1146 putFile32BitBE(file, tape->random_seed);
1147 putFile32BitBE(file, tape->date);
1148 putFile32BitBE(file, tape->length);
1150 fputc(store_participating_players, file);
1152 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1153 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1155 putFileVersion(file, tape->engine_version);
1158 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1160 int level_identifier_size = strlen(tape->level_identifier) + 1;
1163 putFile16BitBE(file, level_identifier_size);
1165 for(i=0; i < level_identifier_size; i++)
1166 fputc(tape->level_identifier[i], file);
1168 putFile16BitBE(file, tape->level_nr);
1171 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1175 for(i=0; i<tape->length; i++)
1177 for(j=0; j<MAX_PLAYERS; j++)
1178 if (tape->player_participates[j])
1179 fputc(tape->pos[i].action[j], file);
1181 fputc(tape->pos[i].delay, file);
1185 void SaveTape(int level_nr)
1187 char *filename = getTapeFilename(level_nr);
1189 boolean new_tape = TRUE;
1190 int num_participating_players = 0;
1191 int info_chunk_size;
1192 int body_chunk_size;
1195 InitTapeDirectory(leveldir_current->filename);
1197 /* if a tape still exists, ask to overwrite it */
1198 if (access(filename, F_OK) == 0)
1201 if (!Request("Replace old tape ?", REQ_ASK))
1205 if (!(file = fopen(filename, MODE_WRITE)))
1207 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1211 tape.file_version = FILE_VERSION_ACTUAL;
1212 tape.game_version = GAME_VERSION_ACTUAL;
1214 /* count number of participating players */
1215 for(i=0; i<MAX_PLAYERS; i++)
1216 if (tape.player_participates[i])
1217 num_participating_players++;
1219 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1220 body_chunk_size = (num_participating_players + 1) * tape.length;
1222 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1223 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1225 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1226 SaveTape_VERS(file, &tape);
1228 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1229 SaveTape_HEAD(file, &tape);
1231 putFileChunkBE(file, "INFO", info_chunk_size);
1232 SaveTape_INFO(file, &tape);
1234 putFileChunkBE(file, "BODY", body_chunk_size);
1235 SaveTape_BODY(file, &tape);
1239 SetFilePermissions(filename, PERMS_PRIVATE);
1241 tape.changed = FALSE;
1244 Request("tape saved !", REQ_CONFIRM);
1247 void DumpTape(struct TapeInfo *tape)
1251 if (TAPE_IS_EMPTY(*tape))
1253 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1257 printf_line("-", 79);
1258 printf("Tape of Level %03d (file version %06d, game version %06d)\n",
1259 tape->level_nr, tape->file_version, tape->game_version);
1260 printf("Level series identifier: '%s'\n", tape->level_identifier);
1261 printf_line("-", 79);
1263 for(i=0; i<tape->length; i++)
1265 if (i >= MAX_TAPELEN)
1268 printf("%03d: ", i);
1270 for(j=0; j<MAX_PLAYERS; j++)
1272 if (tape->player_participates[j])
1274 int action = tape->pos[i].action[j];
1276 printf("%d:%02x ", j, action);
1277 printf("[%c%c%c%c|%c%c] - ",
1278 (action & JOY_LEFT ? '<' : ' '),
1279 (action & JOY_RIGHT ? '>' : ' '),
1280 (action & JOY_UP ? '^' : ' '),
1281 (action & JOY_DOWN ? 'v' : ' '),
1282 (action & JOY_BUTTON_1 ? '1' : ' '),
1283 (action & JOY_BUTTON_2 ? '2' : ' '));
1287 printf("(%03d)\n", tape->pos[i].delay);
1290 printf_line("-", 79);
1294 /* ========================================================================= */
1295 /* score file functions */
1296 /* ========================================================================= */
1298 void LoadScore(int level_nr)
1301 char *filename = getScoreFilename(level_nr);
1302 char cookie[MAX_LINE_LEN];
1303 char line[MAX_LINE_LEN];
1307 /* always start with reliable default values */
1308 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1310 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1311 highscore[i].Score = 0;
1314 if (!(file = fopen(filename, MODE_READ)))
1317 /* check file identifier */
1318 fgets(cookie, MAX_LINE_LEN, file);
1319 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1320 cookie[strlen(cookie) - 1] = '\0';
1322 if (!checkCookieString(cookie, SCORE_COOKIE))
1324 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1329 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1331 fscanf(file, "%d", &highscore[i].Score);
1332 fgets(line, MAX_LINE_LEN, file);
1334 if (line[strlen(line) - 1] == '\n')
1335 line[strlen(line) - 1] = '\0';
1337 for (line_ptr = line; *line_ptr; line_ptr++)
1339 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1341 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1342 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1351 void SaveScore(int level_nr)
1354 char *filename = getScoreFilename(level_nr);
1357 InitScoreDirectory(leveldir_current->filename);
1359 if (!(file = fopen(filename, MODE_WRITE)))
1361 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1365 fprintf(file, "%s\n\n", SCORE_COOKIE);
1367 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1368 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1372 SetFilePermissions(filename, PERMS_PUBLIC);
1376 /* ========================================================================= */
1377 /* setup file functions */
1378 /* ========================================================================= */
1380 #define TOKEN_STR_PLAYER_PREFIX "player_"
1383 #define SETUP_TOKEN_PLAYER_NAME 0
1384 #define SETUP_TOKEN_SOUND 1
1385 #define SETUP_TOKEN_SOUND_LOOPS 2
1386 #define SETUP_TOKEN_SOUND_MUSIC 3
1387 #define SETUP_TOKEN_SOUND_SIMPLE 4
1388 #define SETUP_TOKEN_TOONS 5
1389 #define SETUP_TOKEN_SCROLL_DELAY 6
1390 #define SETUP_TOKEN_SOFT_SCROLLING 7
1391 #define SETUP_TOKEN_FADING 8
1392 #define SETUP_TOKEN_AUTORECORD 9
1393 #define SETUP_TOKEN_QUICK_DOORS 10
1394 #define SETUP_TOKEN_TEAM_MODE 11
1395 #define SETUP_TOKEN_HANDICAP 12
1396 #define SETUP_TOKEN_TIME_LIMIT 13
1397 #define SETUP_TOKEN_FULLSCREEN 14
1398 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1399 #define SETUP_TOKEN_GRAPHICS_SET 16
1400 #define SETUP_TOKEN_SOUNDS_SET 17
1401 #define SETUP_TOKEN_MUSIC_SET 18
1402 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1403 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1404 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1406 #define NUM_GLOBAL_SETUP_TOKENS 22
1409 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1410 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1411 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1412 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1413 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1414 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1415 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1416 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1417 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1419 #define NUM_EDITOR_SETUP_TOKENS 9
1421 /* shortcut setup */
1422 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1423 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1424 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1426 #define NUM_SHORTCUT_SETUP_TOKENS 3
1429 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1430 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1431 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1432 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1433 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1434 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1435 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1436 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1437 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1438 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1439 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1440 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1441 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1442 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1443 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1444 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1446 #define NUM_PLAYER_SETUP_TOKENS 16
1449 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1450 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1452 #define NUM_SYSTEM_SETUP_TOKENS 2
1455 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1457 #define NUM_OPTIONS_SETUP_TOKENS 1
1460 static struct SetupInfo si;
1461 static struct SetupEditorInfo sei;
1462 static struct SetupShortcutInfo ssi;
1463 static struct SetupInputInfo sii;
1464 static struct SetupSystemInfo syi;
1465 static struct OptionInfo soi;
1467 static struct TokenInfo global_setup_tokens[] =
1469 { TYPE_STRING, &si.player_name, "player_name" },
1470 { TYPE_SWITCH, &si.sound, "sound" },
1471 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1472 { TYPE_SWITCH, &si.sound_music, "background_music" },
1473 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1474 { TYPE_SWITCH, &si.toons, "toons" },
1475 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1476 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1477 { TYPE_SWITCH, &si.fading, "screen_fading" },
1478 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1479 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1480 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1481 { TYPE_SWITCH, &si.handicap, "handicap" },
1482 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1483 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1484 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1485 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1486 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1487 { TYPE_STRING, &si.music_set, "music_set" },
1488 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1489 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1490 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1493 static struct TokenInfo editor_setup_tokens[] =
1495 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1496 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1497 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1498 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1499 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1500 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1501 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1502 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1503 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1506 static struct TokenInfo shortcut_setup_tokens[] =
1508 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1509 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1510 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1513 static struct TokenInfo player_setup_tokens[] =
1515 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1516 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1517 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1518 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1519 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1520 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1521 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1522 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1523 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1524 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1525 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1526 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1527 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1528 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1529 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1530 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1533 static struct TokenInfo system_setup_tokens[] =
1535 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1536 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1539 static struct TokenInfo options_setup_tokens[] =
1541 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1544 static char *get_corrected_login_name(char *login_name)
1546 /* needed because player name must be a fixed length string */
1547 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1549 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1550 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1552 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1553 if (strchr(login_name_new, ' '))
1554 *strchr(login_name_new, ' ') = '\0';
1556 return login_name_new;
1559 static void setSetupInfoToDefaults(struct SetupInfo *si)
1563 si->player_name = get_corrected_login_name(getLoginName());
1566 si->sound_loops = TRUE;
1567 si->sound_music = TRUE;
1568 si->sound_simple = TRUE;
1570 si->double_buffering = TRUE;
1571 si->direct_draw = !si->double_buffering;
1572 si->scroll_delay = TRUE;
1573 si->soft_scrolling = TRUE;
1575 si->autorecord = TRUE;
1576 si->quick_doors = FALSE;
1577 si->team_mode = FALSE;
1578 si->handicap = TRUE;
1579 si->time_limit = TRUE;
1580 si->fullscreen = FALSE;
1581 si->ask_on_escape = TRUE;
1583 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1584 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1585 si->music_set = getStringCopy(MUSIC_SUBDIR);
1586 si->override_level_graphics = FALSE;
1587 si->override_level_sounds = FALSE;
1588 si->override_level_music = FALSE;
1590 si->editor.el_boulderdash = TRUE;
1591 si->editor.el_emerald_mine = TRUE;
1592 si->editor.el_more = TRUE;
1593 si->editor.el_sokoban = TRUE;
1594 si->editor.el_supaplex = TRUE;
1595 si->editor.el_diamond_caves = TRUE;
1596 si->editor.el_dx_boulderdash = TRUE;
1597 si->editor.el_chars = TRUE;
1598 si->editor.el_custom = TRUE;
1600 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1601 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1602 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1604 for (i=0; i<MAX_PLAYERS; i++)
1606 si->input[i].use_joystick = FALSE;
1607 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1608 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1609 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1610 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1611 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1612 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1613 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1614 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1615 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1616 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1617 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1618 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1619 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1620 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1621 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1624 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1625 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1627 si->options.verbose = FALSE;
1630 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1634 if (!setup_file_list)
1639 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1640 setSetupInfo(global_setup_tokens, i,
1641 getTokenValue(setup_file_list, global_setup_tokens[i].text));
1646 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1647 setSetupInfo(editor_setup_tokens, i,
1648 getTokenValue(setup_file_list,editor_setup_tokens[i].text));
1651 /* shortcut setup */
1652 ssi = setup.shortcut;
1653 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1654 setSetupInfo(shortcut_setup_tokens, i,
1655 getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
1656 setup.shortcut = ssi;
1659 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1663 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1665 sii = setup.input[pnr];
1666 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1668 char full_token[100];
1670 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1671 setSetupInfo(player_setup_tokens, i,
1672 getTokenValue(setup_file_list, full_token));
1674 setup.input[pnr] = sii;
1679 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1680 setSetupInfo(system_setup_tokens, i,
1681 getTokenValue(setup_file_list, system_setup_tokens[i].text));
1685 soi = setup.options;
1686 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1687 setSetupInfo(options_setup_tokens, i,
1688 getTokenValue(setup_file_list, options_setup_tokens[i].text));
1689 setup.options = soi;
1694 char *filename = getSetupFilename();
1695 struct SetupFileList *setup_file_list = NULL;
1697 /* always start with reliable default values */
1698 setSetupInfoToDefaults(&setup);
1700 setup_file_list = loadSetupFileList(filename);
1702 if (setup_file_list)
1704 char *player_name_new;
1706 checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
1707 decodeSetupFileList(setup_file_list);
1709 setup.direct_draw = !setup.double_buffering;
1711 freeSetupFileList(setup_file_list);
1713 /* needed to work around problems with fixed length strings */
1714 player_name_new = get_corrected_login_name(setup.player_name);
1715 free(setup.player_name);
1716 setup.player_name = player_name_new;
1719 Error(ERR_WARN, "using default setup values");
1724 char *filename = getSetupFilename();
1728 InitUserDataDirectory();
1730 if (!(file = fopen(filename, MODE_WRITE)))
1732 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1736 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1737 getCookie("SETUP")));
1738 fprintf(file, "\n");
1742 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1744 /* just to make things nicer :) */
1745 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1746 i == SETUP_TOKEN_GRAPHICS_SET)
1747 fprintf(file, "\n");
1749 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1754 fprintf(file, "\n");
1755 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1756 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1758 /* shortcut setup */
1759 ssi = setup.shortcut;
1760 fprintf(file, "\n");
1761 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1762 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1765 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1769 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1770 fprintf(file, "\n");
1772 sii = setup.input[pnr];
1773 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1774 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1779 fprintf(file, "\n");
1780 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1781 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1784 soi = setup.options;
1785 fprintf(file, "\n");
1786 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1787 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1791 SetFilePermissions(filename, PERMS_PRIVATE);
1794 void LoadCustomElementDescriptions()
1796 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1797 struct SetupFileList *setup_file_list;
1800 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1802 if (element_info[i].custom_description != NULL)
1804 free(element_info[i].custom_description);
1805 element_info[i].custom_description = NULL;
1809 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1812 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1814 char *token = getStringCat2(element_info[i].token_name, ".name");
1815 char *value = getTokenValue(setup_file_list, token);
1818 element_info[i].custom_description = getStringCopy(value);
1823 freeSetupFileList(setup_file_list);
1826 void LoadSpecialMenuDesignSettings()
1828 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1829 struct SetupFileList *setup_file_list;
1832 /* !!! CHANGE THIS !!! (redundant initialization) !!! */
1833 global.num_toons = 20;
1834 global.menu_draw_xoffset = 0;
1835 global.menu_draw_yoffset = 0;
1836 global.menu_draw_xoffset_MAIN = 0;
1837 global.menu_draw_yoffset_MAIN = 0;
1838 global.door_step_offset = 2;
1839 global.door_step_delay = 10;
1841 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1844 value = getTokenValue(setup_file_list, "global.num_toons");
1846 global.num_toons = get_integer_from_string(value);
1848 value = getTokenValue(setup_file_list, "menu.draw_xoffset");
1850 global.menu_draw_xoffset = get_integer_from_string(value);
1852 value = getTokenValue(setup_file_list, "menu.draw_yoffset");
1854 global.menu_draw_yoffset = get_integer_from_string(value);
1856 value = getTokenValue(setup_file_list, "menu.draw_xoffset.MAIN");
1858 global.menu_draw_xoffset_MAIN = get_integer_from_string(value);
1860 value = getTokenValue(setup_file_list, "menu.draw_yoffset.MAIN");
1862 global.menu_draw_yoffset_MAIN = get_integer_from_string(value);
1864 value = getTokenValue(setup_file_list, "door.step_offset");
1866 global.door_step_offset = get_integer_from_string(value);
1868 value = getTokenValue(setup_file_list, "door.step_delay");
1870 global.door_step_delay = get_integer_from_string(value);
1872 freeSetupFileList(setup_file_list);