1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
17 #include "libgame/libgame.h"
25 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
26 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
27 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
28 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
29 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
30 #define LEVEL_HEADER_UNUSED 14 /* unused level header bytes */
31 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
32 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
33 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
34 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
36 /* file identifier strings */
37 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
38 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
39 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
42 /* ========================================================================= */
43 /* level file functions */
44 /* ========================================================================= */
46 static void setLevelInfoToDefaults()
50 level.file_version = FILE_VERSION_ACTUAL;
51 level.game_version = GAME_VERSION_ACTUAL;
53 level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
54 level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
55 level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
57 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
58 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
60 for(x=0; x<MAX_LEV_FIELDX; x++)
61 for(y=0; y<MAX_LEV_FIELDY; y++)
62 Feld[x][y] = Ur[x][y] = EL_SAND;
65 level.gems_needed = 0;
66 level.amoeba_speed = 10;
67 level.time_magic_wall = 10;
68 level.time_wheel = 10;
69 level.time_light = 10;
70 level.time_timegate = 10;
71 level.amoeba_content = EL_DIAMOND;
72 level.double_speed = FALSE;
73 level.gravity = FALSE;
74 level.em_slippery_gems = FALSE;
76 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
78 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
79 level.author[i] = '\0';
81 strcpy(level.name, NAMELESS_LEVEL_NAME);
82 strcpy(level.author, ANONYMOUS_NAME);
84 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
87 level.num_yamyam_contents = STD_ELEMENT_CONTENTS;
88 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
91 level.yamyam_content[i][x][y] =
92 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
94 Feld[0][0] = Ur[0][0] = EL_PLAYER_1;
95 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
96 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
98 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
100 int element = EL_CUSTOM_START + i;
102 element_info[element].use_template = FALSE;
104 element_info[element].use_gfx_element = FALSE;
105 element_info[element].gfx_element = EL_EMPTY_SPACE;
107 element_info[element].score = 0;
108 element_info[element].gem_count = 0;
110 element_info[element].push_delay_fixed = 2; /* special default */
111 element_info[element].push_delay_random = 8; /* special default */
112 element_info[element].move_delay_fixed = 0;
113 element_info[element].move_delay_random = 0;
115 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
116 element_info[element].move_direction_initial = MV_NO_MOVING;
117 element_info[element].move_stepsize = TILEX / 8;
121 element_info[element].content[x][y] = EL_EMPTY_SPACE;
123 element_info[element].change.events = CE_BITMASK_DEFAULT;
125 element_info[element].change.delay_fixed = 0;
126 element_info[element].change.delay_random = 0;
127 element_info[element].change.delay_frames = -1; /* use default */
129 element_info[element].change.trigger = EL_EMPTY_SPACE;
131 element_info[element].change.target_element = EL_EMPTY_SPACE;
133 element_info[element].change.use_content = FALSE;
134 element_info[element].change.only_complete = FALSE;
135 element_info[element].change.power = CP_NON_DESTRUCTIVE;
137 element_info[element].change.explode = FALSE;
141 element_info[element].change.content[x][y] = EL_EMPTY_SPACE;
143 /* start with no properties at all */
144 for (j=0; j < NUM_EP_BITFIELDS; j++)
145 Properties[element][j] = EP_BITMASK_DEFAULT;
148 BorderElement = EL_STEELWALL;
150 level.no_level_file = FALSE;
152 if (leveldir_current == NULL) /* only when dumping level */
155 /* try to determine better author name than 'anonymous' */
156 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
158 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
159 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
163 switch (LEVELCLASS(leveldir_current))
165 case LEVELCLASS_TUTORIAL:
166 strcpy(level.author, PROGRAM_AUTHOR_STRING);
169 case LEVELCLASS_CONTRIBUTION:
170 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
171 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
174 case LEVELCLASS_USER:
175 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
176 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
180 /* keep default value */
186 static int checkLevelElement(int element)
188 if (element >= NUM_FILE_ELEMENTS)
190 Error(ERR_WARN, "invalid level element %d", element);
191 element = EL_CHAR_QUESTION;
193 else if (element == EL_PLAYER_OBSOLETE)
194 element = EL_PLAYER_1;
195 else if (element == EL_KEY_OBSOLETE)
201 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
203 level->file_version = getFileVersion(file);
204 level->game_version = getFileVersion(file);
209 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
213 lev_fieldx = level->fieldx = fgetc(file);
214 lev_fieldy = level->fieldy = fgetc(file);
216 level->time = getFile16BitBE(file);
217 level->gems_needed = getFile16BitBE(file);
219 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
220 level->name[i] = fgetc(file);
221 level->name[MAX_LEVEL_NAME_LEN] = 0;
223 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
224 level->score[i] = fgetc(file);
226 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
227 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
230 level->yamyam_content[i][x][y] = checkLevelElement(fgetc(file));
232 level->amoeba_speed = fgetc(file);
233 level->time_magic_wall = fgetc(file);
234 level->time_wheel = fgetc(file);
235 level->amoeba_content = checkLevelElement(fgetc(file));
236 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
237 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
238 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
239 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
241 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
246 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
250 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
251 level->author[i] = fgetc(file);
252 level->author[MAX_LEVEL_NAME_LEN] = 0;
257 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
260 int chunk_size_expected = level->fieldx * level->fieldy;
262 /* Note: "chunk_size" was wrong before version 2.0 when elements are
263 stored with 16-bit encoding (and should be twice as big then).
264 Even worse, playfield data was stored 16-bit when only yamyam content
265 contained 16-bit elements and vice versa. */
267 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
268 chunk_size_expected *= 2;
270 if (chunk_size_expected != chunk_size)
272 ReadUnusedBytesFromFile(file, chunk_size);
273 return chunk_size_expected;
276 for(y=0; y<level->fieldy; y++)
277 for(x=0; x<level->fieldx; x++)
278 Feld[x][y] = Ur[x][y] =
279 checkLevelElement(level->encoding_16bit_field ?
280 getFile16BitBE(file) : fgetc(file));
284 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
288 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
289 int chunk_size_expected = header_size + content_size;
291 /* Note: "chunk_size" was wrong before version 2.0 when elements are
292 stored with 16-bit encoding (and should be twice as big then).
293 Even worse, playfield data was stored 16-bit when only yamyam content
294 contained 16-bit elements and vice versa. */
296 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
297 chunk_size_expected += content_size;
299 if (chunk_size_expected != chunk_size)
301 ReadUnusedBytesFromFile(file, chunk_size);
302 return chunk_size_expected;
306 level->num_yamyam_contents = fgetc(file);
310 /* correct invalid number of content fields -- should never happen */
311 if (level->num_yamyam_contents < 1 ||
312 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
313 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
315 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
318 level->yamyam_content[i][x][y] =
319 checkLevelElement(level->encoding_16bit_field ?
320 getFile16BitBE(file) : fgetc(file));
324 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
328 int num_contents, content_xsize, content_ysize;
329 int content_array[MAX_ELEMENT_CONTENTS][3][3];
331 element = checkLevelElement(getFile16BitBE(file));
332 num_contents = fgetc(file);
333 content_xsize = fgetc(file);
334 content_ysize = fgetc(file);
335 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
337 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
340 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
342 /* correct invalid number of content fields -- should never happen */
343 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
344 num_contents = STD_ELEMENT_CONTENTS;
346 if (element == EL_YAMYAM)
348 level->num_yamyam_contents = num_contents;
350 for(i=0; i<num_contents; i++)
353 level->yamyam_content[i][x][y] = content_array[i][x][y];
355 else if (element == EL_BD_AMOEBA)
357 level->amoeba_content = content_array[0][0][0];
361 Error(ERR_WARN, "cannot load content for element '%d'", element);
367 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
369 int num_changed_custom_elements = getFile16BitBE(file);
370 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
373 if (chunk_size_expected != chunk_size)
375 ReadUnusedBytesFromFile(file, chunk_size - 2);
376 return chunk_size_expected;
379 for (i=0; i < num_changed_custom_elements; i++)
381 int element = getFile16BitBE(file);
382 int properties = getFile32BitBE(file);
384 if (IS_CUSTOM_ELEMENT(element))
385 Properties[element][EP_BITFIELD_BASE] = properties;
387 Error(ERR_WARN, "invalid custom element number %d", element);
393 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
395 int num_changed_custom_elements = getFile16BitBE(file);
396 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
399 if (chunk_size_expected != chunk_size)
401 ReadUnusedBytesFromFile(file, chunk_size - 2);
402 return chunk_size_expected;
405 for (i=0; i < num_changed_custom_elements; i++)
407 int element = getFile16BitBE(file);
408 int custom_target_element = getFile16BitBE(file);
410 if (IS_CUSTOM_ELEMENT(element))
411 element_info[element].change.target_element = custom_target_element;
413 Error(ERR_WARN, "invalid custom element number %d", element);
419 void LoadLevelFromFilename(char *filename)
421 char cookie[MAX_LINE_LEN];
422 char chunk_name[CHUNK_ID_LEN + 1];
426 /* always start with reliable default values */
427 setLevelInfoToDefaults();
429 if (!(file = fopen(filename, MODE_READ)))
431 level.no_level_file = TRUE;
433 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
437 getFileChunkBE(file, chunk_name, NULL);
438 if (strcmp(chunk_name, "RND1") == 0)
440 getFile32BitBE(file); /* not used */
442 getFileChunkBE(file, chunk_name, NULL);
443 if (strcmp(chunk_name, "CAVE") != 0)
445 Error(ERR_WARN, "unknown format of level file '%s'", filename);
450 else /* check for pre-2.0 file format with cookie string */
452 strcpy(cookie, chunk_name);
453 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
454 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
455 cookie[strlen(cookie) - 1] = '\0';
457 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
459 Error(ERR_WARN, "unknown format of level file '%s'", filename);
464 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
466 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
471 /* pre-2.0 level files have no game version, so use file version here */
472 level.game_version = level.file_version;
475 if (level.file_version < FILE_VERSION_1_2)
477 /* level files from versions before 1.2.0 without chunk structure */
478 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
479 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
487 int (*loader)(FILE *, int, struct LevelInfo *);
491 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
492 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
493 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
494 { "BODY", -1, LoadLevel_BODY },
495 { "CONT", -1, LoadLevel_CONT },
496 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
497 { "CUS1", -1, LoadLevel_CUS1 },
498 { "CUS2", -1, LoadLevel_CUS2 },
502 while (getFileChunkBE(file, chunk_name, &chunk_size))
506 while (chunk_info[i].name != NULL &&
507 strcmp(chunk_name, chunk_info[i].name) != 0)
510 if (chunk_info[i].name == NULL)
512 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
513 chunk_name, filename);
514 ReadUnusedBytesFromFile(file, chunk_size);
516 else if (chunk_info[i].size != -1 &&
517 chunk_info[i].size != chunk_size)
519 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
520 chunk_size, chunk_name, filename);
521 ReadUnusedBytesFromFile(file, chunk_size);
525 /* call function to load this level chunk */
526 int chunk_size_expected =
527 (chunk_info[i].loader)(file, chunk_size, &level);
529 /* the size of some chunks cannot be checked before reading other
530 chunks first (like "HEAD" and "BODY") that contain some header
531 information, so check them here */
532 if (chunk_size_expected != chunk_size)
534 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
535 chunk_size, chunk_name, filename);
543 if (leveldir_current == NULL) /* only when dumping level */
546 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
547 IS_LEVELCLASS_USER(leveldir_current))
549 /* For user contributed and private levels, use the version of
550 the game engine the levels were created for.
551 Since 2.0.1, the game engine version is now directly stored
552 in the level file (chunk "VERS"), so there is no need anymore
553 to set the game version from the file version (except for old,
554 pre-2.0 levels, where the game version is still taken from the
555 file format version used to store the level -- see above). */
557 /* do some special adjustments to support older level versions */
558 if (level.file_version == FILE_VERSION_1_0)
560 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
561 Error(ERR_WARN, "using high speed movement for player");
563 /* player was faster than monsters in (pre-)1.0 levels */
564 level.double_speed = TRUE;
567 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
568 if (level.game_version == VERSION_IDENT(2,0,1))
569 level.em_slippery_gems = TRUE;
573 /* Always use the latest version of the game engine for all but
574 user contributed and private levels; this allows for actual
575 corrections in the game engine to take effect for existing,
576 converted levels (from "classic" or other existing games) to
577 make the game emulation more accurate, while (hopefully) not
578 breaking existing levels created from other players. */
580 level.game_version = GAME_VERSION_ACTUAL;
582 /* Set special EM style gems behaviour: EM style gems slip down from
583 normal, steel and growing wall. As this is a more fundamental change,
584 it seems better to set the default behaviour to "off" (as it is more
585 natural) and make it configurable in the level editor (as a property
586 of gem style elements). Already existing converted levels (neither
587 private nor contributed levels) are changed to the new behaviour. */
589 if (level.file_version < FILE_VERSION_2_0)
590 level.em_slippery_gems = TRUE;
593 /* map some elements which have changed in newer versions */
594 if (level.game_version <= VERSION_IDENT(2,2,0))
598 /* map game font elements */
599 for(y=0; y<level.fieldy; y++)
601 for(x=0; x<level.fieldx; x++)
603 int element = Ur[x][y];
605 if (element == EL_CHAR('['))
606 element = EL_CHAR_AUMLAUT;
607 else if (element == EL_CHAR('\\'))
608 element = EL_CHAR_OUMLAUT;
609 else if (element == EL_CHAR(']'))
610 element = EL_CHAR_UUMLAUT;
611 else if (element == EL_CHAR('^'))
612 element = EL_CHAR_COPYRIGHT;
614 Feld[x][y] = Ur[x][y] = element;
619 /* determine border element for this level */
623 void LoadLevel(int level_nr)
625 char *filename = getLevelFilename(level_nr);
627 LoadLevelFromFilename(filename);
628 InitElementPropertiesEngine(level.game_version);
631 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
633 putFileVersion(file, level->file_version);
634 putFileVersion(file, level->game_version);
637 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
641 fputc(level->fieldx, file);
642 fputc(level->fieldy, file);
644 putFile16BitBE(file, level->time);
645 putFile16BitBE(file, level->gems_needed);
647 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
648 fputc(level->name[i], file);
650 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
651 fputc(level->score[i], file);
653 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
656 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
657 level->yamyam_content[i][x][y]),
659 fputc(level->amoeba_speed, file);
660 fputc(level->time_magic_wall, file);
661 fputc(level->time_wheel, file);
662 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
664 fputc((level->double_speed ? 1 : 0), file);
665 fputc((level->gravity ? 1 : 0), file);
666 fputc((level->encoding_16bit_field ? 1 : 0), file);
667 fputc((level->em_slippery_gems ? 1 : 0), file);
669 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
672 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
676 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
677 fputc(level->author[i], file);
680 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
684 for(y=0; y<level->fieldy; y++)
685 for(x=0; x<level->fieldx; x++)
686 if (level->encoding_16bit_field)
687 putFile16BitBE(file, Ur[x][y]);
689 fputc(Ur[x][y], file);
693 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
697 fputc(EL_YAMYAM, file);
698 fputc(level->num_yamyam_contents, file);
702 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
705 if (level->encoding_16bit_field)
706 putFile16BitBE(file, level->yamyam_content[i][x][y]);
708 fputc(level->yamyam_content[i][x][y], file);
712 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
715 int num_contents, content_xsize, content_ysize;
716 int content_array[MAX_ELEMENT_CONTENTS][3][3];
718 if (element == EL_YAMYAM)
720 num_contents = level->num_yamyam_contents;
724 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
727 content_array[i][x][y] = level->yamyam_content[i][x][y];
729 else if (element == EL_BD_AMOEBA)
735 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
738 content_array[i][x][y] = EL_EMPTY;
739 content_array[0][0][0] = level->amoeba_content;
743 /* chunk header already written -- write empty chunk data */
744 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
746 Error(ERR_WARN, "cannot save content for element '%d'", element);
750 putFile16BitBE(file, element);
751 fputc(num_contents, file);
752 fputc(content_xsize, file);
753 fputc(content_ysize, file);
755 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
757 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
760 putFile16BitBE(file, content_array[i][x][y]);
763 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
764 int num_changed_custom_elements)
768 putFile16BitBE(file, num_changed_custom_elements);
770 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
772 int element = EL_CUSTOM_START + i;
774 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
776 if (check < num_changed_custom_elements)
778 putFile16BitBE(file, element);
779 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
786 if (check != num_changed_custom_elements) /* should not happen */
787 Error(ERR_WARN, "inconsistent number of custom element properties");
790 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
791 int num_changed_custom_elements)
795 putFile16BitBE(file, num_changed_custom_elements);
797 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
799 int element = EL_CUSTOM_START + i;
801 if (element_info[element].change.target_element != EL_EMPTY_SPACE)
803 if (check < num_changed_custom_elements)
805 putFile16BitBE(file, element);
806 putFile16BitBE(file, element_info[element].change.target_element);
813 if (check != num_changed_custom_elements) /* should not happen */
814 Error(ERR_WARN, "inconsistent number of custom target elements");
817 void SaveLevel(int level_nr)
819 char *filename = getLevelFilename(level_nr);
821 int num_changed_custom_elements1 = 0;
822 int num_changed_custom_elements2 = 0;
826 if (!(file = fopen(filename, MODE_WRITE)))
828 Error(ERR_WARN, "cannot save level file '%s'", filename);
832 level.file_version = FILE_VERSION_ACTUAL;
833 level.game_version = GAME_VERSION_ACTUAL;
835 /* check level field for 16-bit elements */
836 level.encoding_16bit_field = FALSE;
837 for(y=0; y<level.fieldy; y++)
838 for(x=0; x<level.fieldx; x++)
840 level.encoding_16bit_field = TRUE;
842 /* check yamyam content for 16-bit elements */
843 level.encoding_16bit_yamyam = FALSE;
844 for(i=0; i<level.num_yamyam_contents; i++)
847 if (level.yamyam_content[i][x][y] > 255)
848 level.encoding_16bit_yamyam = TRUE;
850 /* check amoeba content for 16-bit elements */
851 level.encoding_16bit_amoeba = FALSE;
852 if (level.amoeba_content > 255)
853 level.encoding_16bit_amoeba = TRUE;
855 /* calculate size of "BODY" chunk */
857 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
859 /* check for non-standard custom elements and calculate "CUS1" chunk size */
860 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
861 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
862 num_changed_custom_elements1++;
864 /* check for non-standard custom elements and calculate "CUS2" chunk size */
865 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
866 if (element_info[EL_CUSTOM_START + i].change.target_element != EL_EMPTY)
867 num_changed_custom_elements2++;
869 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
870 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
872 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
873 SaveLevel_VERS(file, &level);
875 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
876 SaveLevel_HEAD(file, &level);
878 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
879 SaveLevel_AUTH(file, &level);
881 putFileChunkBE(file, "BODY", body_chunk_size);
882 SaveLevel_BODY(file, &level);
884 if (level.encoding_16bit_yamyam ||
885 level.num_yamyam_contents != STD_ELEMENT_CONTENTS)
887 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
888 SaveLevel_CNT2(file, &level, EL_YAMYAM);
891 if (level.encoding_16bit_amoeba)
893 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
894 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
897 if (num_changed_custom_elements1 > 0)
899 putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements1 * 6);
900 SaveLevel_CUS1(file, &level, num_changed_custom_elements1);
903 if (num_changed_custom_elements2 > 0)
905 putFileChunkBE(file, "CUS2", 2 + num_changed_custom_elements2 * 4);
906 SaveLevel_CUS2(file, &level, num_changed_custom_elements2);
911 SetFilePermissions(filename, PERMS_PRIVATE);
914 void DumpLevel(struct LevelInfo *level)
916 printf_line("-", 79);
917 printf("Level xxx (file version %08d, game version %08d)\n",
918 level->file_version, level->game_version);
919 printf_line("-", 79);
921 printf("Level Author: '%s'\n", level->author);
922 printf("Level Title: '%s'\n", level->name);
924 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
926 printf("Level Time: %d seconds\n", level->time);
927 printf("Gems needed: %d\n", level->gems_needed);
929 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
930 printf("Time for Wheel: %d seconds\n", level->time_wheel);
931 printf("Time for Light: %d seconds\n", level->time_light);
932 printf("Time for Timegate: %d seconds\n", level->time_timegate);
934 printf("Amoeba Speed: %d\n", level->amoeba_speed);
936 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
937 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
938 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
940 printf_line("-", 79);
944 /* ========================================================================= */
945 /* tape file functions */
946 /* ========================================================================= */
948 static void setTapeInfoToDefaults()
952 /* always start with reliable default values (empty tape) */
955 /* default values (also for pre-1.2 tapes) with only the first player */
956 tape.player_participates[0] = TRUE;
957 for(i=1; i<MAX_PLAYERS; i++)
958 tape.player_participates[i] = FALSE;
960 /* at least one (default: the first) player participates in every tape */
961 tape.num_participating_players = 1;
963 tape.level_nr = level_nr;
965 tape.changed = FALSE;
967 tape.recording = FALSE;
968 tape.playing = FALSE;
969 tape.pausing = FALSE;
972 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
974 tape->file_version = getFileVersion(file);
975 tape->game_version = getFileVersion(file);
980 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
984 tape->random_seed = getFile32BitBE(file);
985 tape->date = getFile32BitBE(file);
986 tape->length = getFile32BitBE(file);
988 /* read header fields that are new since version 1.2 */
989 if (tape->file_version >= FILE_VERSION_1_2)
991 byte store_participating_players = fgetc(file);
994 /* since version 1.2, tapes store which players participate in the tape */
995 tape->num_participating_players = 0;
996 for(i=0; i<MAX_PLAYERS; i++)
998 tape->player_participates[i] = FALSE;
1000 if (store_participating_players & (1 << i))
1002 tape->player_participates[i] = TRUE;
1003 tape->num_participating_players++;
1007 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1009 engine_version = getFileVersion(file);
1010 if (engine_version > 0)
1011 tape->engine_version = engine_version;
1017 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1019 int level_identifier_size;
1022 level_identifier_size = getFile16BitBE(file);
1024 tape->level_identifier =
1025 checked_realloc(tape->level_identifier, level_identifier_size);
1027 for(i=0; i < level_identifier_size; i++)
1028 tape->level_identifier[i] = fgetc(file);
1030 tape->level_nr = getFile16BitBE(file);
1032 chunk_size = 2 + level_identifier_size + 2;
1037 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1040 int chunk_size_expected =
1041 (tape->num_participating_players + 1) * tape->length;
1043 if (chunk_size_expected != chunk_size)
1045 ReadUnusedBytesFromFile(file, chunk_size);
1046 return chunk_size_expected;
1049 for(i=0; i<tape->length; i++)
1051 if (i >= MAX_TAPELEN)
1054 for(j=0; j<MAX_PLAYERS; j++)
1056 tape->pos[i].action[j] = MV_NO_MOVING;
1058 if (tape->player_participates[j])
1059 tape->pos[i].action[j] = fgetc(file);
1062 tape->pos[i].delay = fgetc(file);
1064 if (tape->file_version == FILE_VERSION_1_0)
1066 /* eliminate possible diagonal moves in old tapes */
1067 /* this is only for backward compatibility */
1069 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1070 byte action = tape->pos[i].action[0];
1071 int k, num_moves = 0;
1075 if (action & joy_dir[k])
1077 tape->pos[i + num_moves].action[0] = joy_dir[k];
1079 tape->pos[i + num_moves].delay = 0;
1088 tape->length += num_moves;
1091 else if (tape->file_version < FILE_VERSION_2_0)
1093 /* convert pre-2.0 tapes to new tape format */
1095 if (tape->pos[i].delay > 1)
1098 tape->pos[i + 1] = tape->pos[i];
1099 tape->pos[i + 1].delay = 1;
1102 for(j=0; j<MAX_PLAYERS; j++)
1103 tape->pos[i].action[j] = MV_NO_MOVING;
1104 tape->pos[i].delay--;
1115 if (i != tape->length)
1116 chunk_size = (tape->num_participating_players + 1) * i;
1121 void LoadTapeFromFilename(char *filename)
1123 char cookie[MAX_LINE_LEN];
1124 char chunk_name[CHUNK_ID_LEN + 1];
1128 /* always start with reliable default values */
1129 setTapeInfoToDefaults();
1131 if (!(file = fopen(filename, MODE_READ)))
1134 getFileChunkBE(file, chunk_name, NULL);
1135 if (strcmp(chunk_name, "RND1") == 0)
1137 getFile32BitBE(file); /* not used */
1139 getFileChunkBE(file, chunk_name, NULL);
1140 if (strcmp(chunk_name, "TAPE") != 0)
1142 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1147 else /* check for pre-2.0 file format with cookie string */
1149 strcpy(cookie, chunk_name);
1150 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1151 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1152 cookie[strlen(cookie) - 1] = '\0';
1154 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1156 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1161 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1163 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1168 /* pre-2.0 tape files have no game version, so use file version here */
1169 tape.game_version = tape.file_version;
1172 if (tape.file_version < FILE_VERSION_1_2)
1174 /* tape files from versions before 1.2.0 without chunk structure */
1175 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1176 LoadTape_BODY(file, 2 * tape.length, &tape);
1184 int (*loader)(FILE *, int, struct TapeInfo *);
1188 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1189 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1190 { "INFO", -1, LoadTape_INFO },
1191 { "BODY", -1, LoadTape_BODY },
1195 while (getFileChunkBE(file, chunk_name, &chunk_size))
1199 while (chunk_info[i].name != NULL &&
1200 strcmp(chunk_name, chunk_info[i].name) != 0)
1203 if (chunk_info[i].name == NULL)
1205 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1206 chunk_name, filename);
1207 ReadUnusedBytesFromFile(file, chunk_size);
1209 else if (chunk_info[i].size != -1 &&
1210 chunk_info[i].size != chunk_size)
1212 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1213 chunk_size, chunk_name, filename);
1214 ReadUnusedBytesFromFile(file, chunk_size);
1218 /* call function to load this tape chunk */
1219 int chunk_size_expected =
1220 (chunk_info[i].loader)(file, chunk_size, &tape);
1222 /* the size of some chunks cannot be checked before reading other
1223 chunks first (like "HEAD" and "BODY") that contain some header
1224 information, so check them here */
1225 if (chunk_size_expected != chunk_size)
1227 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1228 chunk_size, chunk_name, filename);
1236 tape.length_seconds = GetTapeLength();
1239 printf("tape version: %d\n", tape.game_version);
1243 void LoadTape(int level_nr)
1245 char *filename = getTapeFilename(level_nr);
1247 LoadTapeFromFilename(filename);
1250 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1252 putFileVersion(file, tape->file_version);
1253 putFileVersion(file, tape->game_version);
1256 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1259 byte store_participating_players = 0;
1261 /* set bits for participating players for compact storage */
1262 for(i=0; i<MAX_PLAYERS; i++)
1263 if (tape->player_participates[i])
1264 store_participating_players |= (1 << i);
1266 putFile32BitBE(file, tape->random_seed);
1267 putFile32BitBE(file, tape->date);
1268 putFile32BitBE(file, tape->length);
1270 fputc(store_participating_players, file);
1272 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1273 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1275 putFileVersion(file, tape->engine_version);
1278 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1280 int level_identifier_size = strlen(tape->level_identifier) + 1;
1283 putFile16BitBE(file, level_identifier_size);
1285 for(i=0; i < level_identifier_size; i++)
1286 fputc(tape->level_identifier[i], file);
1288 putFile16BitBE(file, tape->level_nr);
1291 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1295 for(i=0; i<tape->length; i++)
1297 for(j=0; j<MAX_PLAYERS; j++)
1298 if (tape->player_participates[j])
1299 fputc(tape->pos[i].action[j], file);
1301 fputc(tape->pos[i].delay, file);
1305 void SaveTape(int level_nr)
1307 char *filename = getTapeFilename(level_nr);
1309 boolean new_tape = TRUE;
1310 int num_participating_players = 0;
1311 int info_chunk_size;
1312 int body_chunk_size;
1315 InitTapeDirectory(leveldir_current->filename);
1317 /* if a tape still exists, ask to overwrite it */
1318 if (access(filename, F_OK) == 0)
1321 if (!Request("Replace old tape ?", REQ_ASK))
1325 if (!(file = fopen(filename, MODE_WRITE)))
1327 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1331 tape.file_version = FILE_VERSION_ACTUAL;
1332 tape.game_version = GAME_VERSION_ACTUAL;
1334 /* count number of participating players */
1335 for(i=0; i<MAX_PLAYERS; i++)
1336 if (tape.player_participates[i])
1337 num_participating_players++;
1339 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1340 body_chunk_size = (num_participating_players + 1) * tape.length;
1342 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1343 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1345 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1346 SaveTape_VERS(file, &tape);
1348 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1349 SaveTape_HEAD(file, &tape);
1351 putFileChunkBE(file, "INFO", info_chunk_size);
1352 SaveTape_INFO(file, &tape);
1354 putFileChunkBE(file, "BODY", body_chunk_size);
1355 SaveTape_BODY(file, &tape);
1359 SetFilePermissions(filename, PERMS_PRIVATE);
1361 tape.changed = FALSE;
1364 Request("tape saved !", REQ_CONFIRM);
1367 void DumpTape(struct TapeInfo *tape)
1371 if (TAPE_IS_EMPTY(*tape))
1373 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1377 printf_line("-", 79);
1378 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1379 tape->level_nr, tape->file_version, tape->game_version);
1380 printf("Level series identifier: '%s'\n", tape->level_identifier);
1381 printf_line("-", 79);
1383 for(i=0; i<tape->length; i++)
1385 if (i >= MAX_TAPELEN)
1388 printf("%03d: ", i);
1390 for(j=0; j<MAX_PLAYERS; j++)
1392 if (tape->player_participates[j])
1394 int action = tape->pos[i].action[j];
1396 printf("%d:%02x ", j, action);
1397 printf("[%c%c%c%c|%c%c] - ",
1398 (action & JOY_LEFT ? '<' : ' '),
1399 (action & JOY_RIGHT ? '>' : ' '),
1400 (action & JOY_UP ? '^' : ' '),
1401 (action & JOY_DOWN ? 'v' : ' '),
1402 (action & JOY_BUTTON_1 ? '1' : ' '),
1403 (action & JOY_BUTTON_2 ? '2' : ' '));
1407 printf("(%03d)\n", tape->pos[i].delay);
1410 printf_line("-", 79);
1414 /* ========================================================================= */
1415 /* score file functions */
1416 /* ========================================================================= */
1418 void LoadScore(int level_nr)
1421 char *filename = getScoreFilename(level_nr);
1422 char cookie[MAX_LINE_LEN];
1423 char line[MAX_LINE_LEN];
1427 /* always start with reliable default values */
1428 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1430 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1431 highscore[i].Score = 0;
1434 if (!(file = fopen(filename, MODE_READ)))
1437 /* check file identifier */
1438 fgets(cookie, MAX_LINE_LEN, file);
1439 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1440 cookie[strlen(cookie) - 1] = '\0';
1442 if (!checkCookieString(cookie, SCORE_COOKIE))
1444 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1449 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1451 fscanf(file, "%d", &highscore[i].Score);
1452 fgets(line, MAX_LINE_LEN, file);
1454 if (line[strlen(line) - 1] == '\n')
1455 line[strlen(line) - 1] = '\0';
1457 for (line_ptr = line; *line_ptr; line_ptr++)
1459 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1461 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1462 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1471 void SaveScore(int level_nr)
1474 char *filename = getScoreFilename(level_nr);
1477 InitScoreDirectory(leveldir_current->filename);
1479 if (!(file = fopen(filename, MODE_WRITE)))
1481 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1485 fprintf(file, "%s\n\n", SCORE_COOKIE);
1487 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1488 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1492 SetFilePermissions(filename, PERMS_PUBLIC);
1496 /* ========================================================================= */
1497 /* setup file functions */
1498 /* ========================================================================= */
1500 #define TOKEN_STR_PLAYER_PREFIX "player_"
1503 #define SETUP_TOKEN_PLAYER_NAME 0
1504 #define SETUP_TOKEN_SOUND 1
1505 #define SETUP_TOKEN_SOUND_LOOPS 2
1506 #define SETUP_TOKEN_SOUND_MUSIC 3
1507 #define SETUP_TOKEN_SOUND_SIMPLE 4
1508 #define SETUP_TOKEN_TOONS 5
1509 #define SETUP_TOKEN_SCROLL_DELAY 6
1510 #define SETUP_TOKEN_SOFT_SCROLLING 7
1511 #define SETUP_TOKEN_FADING 8
1512 #define SETUP_TOKEN_AUTORECORD 9
1513 #define SETUP_TOKEN_QUICK_DOORS 10
1514 #define SETUP_TOKEN_TEAM_MODE 11
1515 #define SETUP_TOKEN_HANDICAP 12
1516 #define SETUP_TOKEN_TIME_LIMIT 13
1517 #define SETUP_TOKEN_FULLSCREEN 14
1518 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1519 #define SETUP_TOKEN_GRAPHICS_SET 16
1520 #define SETUP_TOKEN_SOUNDS_SET 17
1521 #define SETUP_TOKEN_MUSIC_SET 18
1522 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1523 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1524 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1526 #define NUM_GLOBAL_SETUP_TOKENS 22
1529 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1530 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1531 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1532 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1533 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1534 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1535 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1536 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1537 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1539 #define NUM_EDITOR_SETUP_TOKENS 9
1541 /* shortcut setup */
1542 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1543 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1544 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1546 #define NUM_SHORTCUT_SETUP_TOKENS 3
1549 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1550 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1551 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1552 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1553 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1554 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1555 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1556 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1557 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1558 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1559 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1560 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1561 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1562 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1563 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1564 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1566 #define NUM_PLAYER_SETUP_TOKENS 16
1569 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1570 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1572 #define NUM_SYSTEM_SETUP_TOKENS 2
1575 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1577 #define NUM_OPTIONS_SETUP_TOKENS 1
1580 static struct SetupInfo si;
1581 static struct SetupEditorInfo sei;
1582 static struct SetupShortcutInfo ssi;
1583 static struct SetupInputInfo sii;
1584 static struct SetupSystemInfo syi;
1585 static struct OptionInfo soi;
1587 static struct TokenInfo global_setup_tokens[] =
1589 { TYPE_STRING, &si.player_name, "player_name" },
1590 { TYPE_SWITCH, &si.sound, "sound" },
1591 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1592 { TYPE_SWITCH, &si.sound_music, "background_music" },
1593 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1594 { TYPE_SWITCH, &si.toons, "toons" },
1595 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1596 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1597 { TYPE_SWITCH, &si.fading, "screen_fading" },
1598 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1599 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1600 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1601 { TYPE_SWITCH, &si.handicap, "handicap" },
1602 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1603 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1604 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1605 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1606 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1607 { TYPE_STRING, &si.music_set, "music_set" },
1608 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1609 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1610 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1613 static struct TokenInfo editor_setup_tokens[] =
1615 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1616 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1617 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1618 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1619 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1620 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1621 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1622 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1623 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1626 static struct TokenInfo shortcut_setup_tokens[] =
1628 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1629 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1630 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1633 static struct TokenInfo player_setup_tokens[] =
1635 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1636 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1637 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1638 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1639 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1640 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1641 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1642 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1643 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1644 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1645 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1646 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1647 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1648 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1649 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1650 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1653 static struct TokenInfo system_setup_tokens[] =
1655 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1656 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1659 static struct TokenInfo options_setup_tokens[] =
1661 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1664 static char *get_corrected_login_name(char *login_name)
1666 /* needed because player name must be a fixed length string */
1667 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1669 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1670 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1672 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1673 if (strchr(login_name_new, ' '))
1674 *strchr(login_name_new, ' ') = '\0';
1676 return login_name_new;
1679 static void setSetupInfoToDefaults(struct SetupInfo *si)
1683 si->player_name = get_corrected_login_name(getLoginName());
1686 si->sound_loops = TRUE;
1687 si->sound_music = TRUE;
1688 si->sound_simple = TRUE;
1690 si->double_buffering = TRUE;
1691 si->direct_draw = !si->double_buffering;
1692 si->scroll_delay = TRUE;
1693 si->soft_scrolling = TRUE;
1695 si->autorecord = TRUE;
1696 si->quick_doors = FALSE;
1697 si->team_mode = FALSE;
1698 si->handicap = TRUE;
1699 si->time_limit = TRUE;
1700 si->fullscreen = FALSE;
1701 si->ask_on_escape = TRUE;
1703 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1704 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1705 si->music_set = getStringCopy(MUSIC_SUBDIR);
1706 si->override_level_graphics = FALSE;
1707 si->override_level_sounds = FALSE;
1708 si->override_level_music = FALSE;
1710 si->editor.el_boulderdash = TRUE;
1711 si->editor.el_emerald_mine = TRUE;
1712 si->editor.el_more = TRUE;
1713 si->editor.el_sokoban = TRUE;
1714 si->editor.el_supaplex = TRUE;
1715 si->editor.el_diamond_caves = TRUE;
1716 si->editor.el_dx_boulderdash = TRUE;
1717 si->editor.el_chars = TRUE;
1718 si->editor.el_custom = TRUE;
1720 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1721 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1722 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1724 for (i=0; i<MAX_PLAYERS; i++)
1726 si->input[i].use_joystick = FALSE;
1727 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1728 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1729 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1730 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1731 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1732 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1733 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1734 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1735 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1736 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1737 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1738 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1739 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1740 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1741 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1744 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1745 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1747 si->options.verbose = FALSE;
1750 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
1754 if (!setup_file_hash)
1759 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1760 setSetupInfo(global_setup_tokens, i,
1761 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
1766 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1767 setSetupInfo(editor_setup_tokens, i,
1768 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
1771 /* shortcut setup */
1772 ssi = setup.shortcut;
1773 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1774 setSetupInfo(shortcut_setup_tokens, i,
1775 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
1776 setup.shortcut = ssi;
1779 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1783 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1785 sii = setup.input[pnr];
1786 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1788 char full_token[100];
1790 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1791 setSetupInfo(player_setup_tokens, i,
1792 getHashEntry(setup_file_hash, full_token));
1794 setup.input[pnr] = sii;
1799 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1800 setSetupInfo(system_setup_tokens, i,
1801 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
1805 soi = setup.options;
1806 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1807 setSetupInfo(options_setup_tokens, i,
1808 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
1809 setup.options = soi;
1814 char *filename = getSetupFilename();
1815 SetupFileHash *setup_file_hash = NULL;
1817 /* always start with reliable default values */
1818 setSetupInfoToDefaults(&setup);
1820 setup_file_hash = loadSetupFileHash(filename);
1822 if (setup_file_hash)
1824 char *player_name_new;
1826 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
1827 decodeSetupFileHash(setup_file_hash);
1829 setup.direct_draw = !setup.double_buffering;
1831 freeSetupFileHash(setup_file_hash);
1833 /* needed to work around problems with fixed length strings */
1834 player_name_new = get_corrected_login_name(setup.player_name);
1835 free(setup.player_name);
1836 setup.player_name = player_name_new;
1839 Error(ERR_WARN, "using default setup values");
1844 char *filename = getSetupFilename();
1848 InitUserDataDirectory();
1850 if (!(file = fopen(filename, MODE_WRITE)))
1852 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1856 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1857 getCookie("SETUP")));
1858 fprintf(file, "\n");
1862 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1864 /* just to make things nicer :) */
1865 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1866 i == SETUP_TOKEN_GRAPHICS_SET)
1867 fprintf(file, "\n");
1869 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1874 fprintf(file, "\n");
1875 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1876 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1878 /* shortcut setup */
1879 ssi = setup.shortcut;
1880 fprintf(file, "\n");
1881 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1882 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1885 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1889 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1890 fprintf(file, "\n");
1892 sii = setup.input[pnr];
1893 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1894 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1899 fprintf(file, "\n");
1900 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1901 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1904 soi = setup.options;
1905 fprintf(file, "\n");
1906 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1907 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1911 SetFilePermissions(filename, PERMS_PRIVATE);
1914 void LoadCustomElementDescriptions()
1916 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1917 SetupFileHash *setup_file_hash;
1920 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1922 if (element_info[i].custom_description != NULL)
1924 free(element_info[i].custom_description);
1925 element_info[i].custom_description = NULL;
1929 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1932 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1934 char *token = getStringCat2(element_info[i].token_name, ".name");
1935 char *value = getHashEntry(setup_file_hash, token);
1938 element_info[i].custom_description = getStringCopy(value);
1943 freeSetupFileHash(setup_file_hash);
1946 void LoadSpecialMenuDesignSettings()
1948 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1949 SetupFileHash *setup_file_hash;
1952 /* always start with reliable default values from default config */
1953 for (i=0; image_config_vars[i].token != NULL; i++)
1954 for (j=0; image_config[j].token != NULL; j++)
1955 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
1956 *image_config_vars[i].value =
1957 get_integer_from_string(image_config[j].value);
1959 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1962 /* special case: initialize with default values that may be overwritten */
1963 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
1965 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
1966 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
1967 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
1969 if (value_x != NULL)
1970 menu.draw_xoffset[i] = get_integer_from_string(value_x);
1971 if (value_y != NULL)
1972 menu.draw_yoffset[i] = get_integer_from_string(value_y);
1973 if (list_size != NULL)
1974 menu.list_size[i] = get_integer_from_string(list_size);
1977 /* read (and overwrite with) values that may be specified in config file */
1978 for (i=0; image_config_vars[i].token != NULL; i++)
1980 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
1983 *image_config_vars[i].value = get_integer_from_string(value);
1986 freeSetupFileHash(setup_file_hash);