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.use_random_change = FALSE;
136 element_info[element].change.random = 0;
137 element_info[element].change.power = CP_NON_DESTRUCTIVE;
139 element_info[element].change.explode = FALSE;
143 element_info[element].change.content[x][y] = EL_EMPTY_SPACE;
145 /* start with no properties at all */
146 for (j=0; j < NUM_EP_BITFIELDS; j++)
147 Properties[element][j] = EP_BITMASK_DEFAULT;
150 BorderElement = EL_STEELWALL;
152 level.no_level_file = FALSE;
154 if (leveldir_current == NULL) /* only when dumping level */
157 /* try to determine better author name than 'anonymous' */
158 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
160 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
161 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
165 switch (LEVELCLASS(leveldir_current))
167 case LEVELCLASS_TUTORIAL:
168 strcpy(level.author, PROGRAM_AUTHOR_STRING);
171 case LEVELCLASS_CONTRIBUTION:
172 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
173 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
176 case LEVELCLASS_USER:
177 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
178 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
182 /* keep default value */
188 static int checkLevelElement(int element)
190 if (element >= NUM_FILE_ELEMENTS)
192 Error(ERR_WARN, "invalid level element %d", element);
193 element = EL_CHAR_QUESTION;
195 else if (element == EL_PLAYER_OBSOLETE)
196 element = EL_PLAYER_1;
197 else if (element == EL_KEY_OBSOLETE)
203 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
205 level->file_version = getFileVersion(file);
206 level->game_version = getFileVersion(file);
211 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
215 lev_fieldx = level->fieldx = fgetc(file);
216 lev_fieldy = level->fieldy = fgetc(file);
218 level->time = getFile16BitBE(file);
219 level->gems_needed = getFile16BitBE(file);
221 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
222 level->name[i] = fgetc(file);
223 level->name[MAX_LEVEL_NAME_LEN] = 0;
225 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
226 level->score[i] = fgetc(file);
228 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
229 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
232 level->yamyam_content[i][x][y] = checkLevelElement(fgetc(file));
234 level->amoeba_speed = fgetc(file);
235 level->time_magic_wall = fgetc(file);
236 level->time_wheel = fgetc(file);
237 level->amoeba_content = checkLevelElement(fgetc(file));
238 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
239 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
240 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
241 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
243 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
248 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
252 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
253 level->author[i] = fgetc(file);
254 level->author[MAX_LEVEL_NAME_LEN] = 0;
259 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
262 int chunk_size_expected = level->fieldx * level->fieldy;
264 /* Note: "chunk_size" was wrong before version 2.0 when elements are
265 stored with 16-bit encoding (and should be twice as big then).
266 Even worse, playfield data was stored 16-bit when only yamyam content
267 contained 16-bit elements and vice versa. */
269 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
270 chunk_size_expected *= 2;
272 if (chunk_size_expected != chunk_size)
274 ReadUnusedBytesFromFile(file, chunk_size);
275 return chunk_size_expected;
278 for(y=0; y<level->fieldy; y++)
279 for(x=0; x<level->fieldx; x++)
280 Feld[x][y] = Ur[x][y] =
281 checkLevelElement(level->encoding_16bit_field ?
282 getFile16BitBE(file) : fgetc(file));
286 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
290 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
291 int chunk_size_expected = header_size + content_size;
293 /* Note: "chunk_size" was wrong before version 2.0 when elements are
294 stored with 16-bit encoding (and should be twice as big then).
295 Even worse, playfield data was stored 16-bit when only yamyam content
296 contained 16-bit elements and vice versa. */
298 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
299 chunk_size_expected += content_size;
301 if (chunk_size_expected != chunk_size)
303 ReadUnusedBytesFromFile(file, chunk_size);
304 return chunk_size_expected;
308 level->num_yamyam_contents = fgetc(file);
312 /* correct invalid number of content fields -- should never happen */
313 if (level->num_yamyam_contents < 1 ||
314 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
315 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
317 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
320 level->yamyam_content[i][x][y] =
321 checkLevelElement(level->encoding_16bit_field ?
322 getFile16BitBE(file) : fgetc(file));
326 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
330 int num_contents, content_xsize, content_ysize;
331 int content_array[MAX_ELEMENT_CONTENTS][3][3];
333 element = checkLevelElement(getFile16BitBE(file));
334 num_contents = fgetc(file);
335 content_xsize = fgetc(file);
336 content_ysize = fgetc(file);
337 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
339 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
342 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
344 /* correct invalid number of content fields -- should never happen */
345 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
346 num_contents = STD_ELEMENT_CONTENTS;
348 if (element == EL_YAMYAM)
350 level->num_yamyam_contents = num_contents;
352 for(i=0; i<num_contents; i++)
355 level->yamyam_content[i][x][y] = content_array[i][x][y];
357 else if (element == EL_BD_AMOEBA)
359 level->amoeba_content = content_array[0][0][0];
363 Error(ERR_WARN, "cannot load content for element '%d'", element);
369 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
371 int num_changed_custom_elements = getFile16BitBE(file);
372 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
375 if (chunk_size_expected != chunk_size)
377 ReadUnusedBytesFromFile(file, chunk_size - 2);
378 return chunk_size_expected;
381 for (i=0; i < num_changed_custom_elements; i++)
383 int element = getFile16BitBE(file);
384 int properties = getFile32BitBE(file);
386 if (IS_CUSTOM_ELEMENT(element))
387 Properties[element][EP_BITFIELD_BASE] = properties;
389 Error(ERR_WARN, "invalid custom element number %d", element);
395 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
397 int num_changed_custom_elements = getFile16BitBE(file);
398 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
401 if (chunk_size_expected != chunk_size)
403 ReadUnusedBytesFromFile(file, chunk_size - 2);
404 return chunk_size_expected;
407 for (i=0; i < num_changed_custom_elements; i++)
409 int element = getFile16BitBE(file);
410 int custom_target_element = getFile16BitBE(file);
412 if (IS_CUSTOM_ELEMENT(element))
413 element_info[element].change.target_element = custom_target_element;
415 Error(ERR_WARN, "invalid custom element number %d", element);
421 void LoadLevelFromFilename(char *filename)
423 char cookie[MAX_LINE_LEN];
424 char chunk_name[CHUNK_ID_LEN + 1];
428 /* always start with reliable default values */
429 setLevelInfoToDefaults();
431 if (!(file = fopen(filename, MODE_READ)))
433 level.no_level_file = TRUE;
435 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
439 getFileChunkBE(file, chunk_name, NULL);
440 if (strcmp(chunk_name, "RND1") == 0)
442 getFile32BitBE(file); /* not used */
444 getFileChunkBE(file, chunk_name, NULL);
445 if (strcmp(chunk_name, "CAVE") != 0)
447 Error(ERR_WARN, "unknown format of level file '%s'", filename);
452 else /* check for pre-2.0 file format with cookie string */
454 strcpy(cookie, chunk_name);
455 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
456 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
457 cookie[strlen(cookie) - 1] = '\0';
459 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
461 Error(ERR_WARN, "unknown format of level file '%s'", filename);
466 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
468 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
473 /* pre-2.0 level files have no game version, so use file version here */
474 level.game_version = level.file_version;
477 if (level.file_version < FILE_VERSION_1_2)
479 /* level files from versions before 1.2.0 without chunk structure */
480 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
481 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
489 int (*loader)(FILE *, int, struct LevelInfo *);
493 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
494 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
495 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
496 { "BODY", -1, LoadLevel_BODY },
497 { "CONT", -1, LoadLevel_CONT },
498 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
499 { "CUS1", -1, LoadLevel_CUS1 },
500 { "CUS2", -1, LoadLevel_CUS2 },
504 while (getFileChunkBE(file, chunk_name, &chunk_size))
508 while (chunk_info[i].name != NULL &&
509 strcmp(chunk_name, chunk_info[i].name) != 0)
512 if (chunk_info[i].name == NULL)
514 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
515 chunk_name, filename);
516 ReadUnusedBytesFromFile(file, chunk_size);
518 else if (chunk_info[i].size != -1 &&
519 chunk_info[i].size != chunk_size)
521 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
522 chunk_size, chunk_name, filename);
523 ReadUnusedBytesFromFile(file, chunk_size);
527 /* call function to load this level chunk */
528 int chunk_size_expected =
529 (chunk_info[i].loader)(file, chunk_size, &level);
531 /* the size of some chunks cannot be checked before reading other
532 chunks first (like "HEAD" and "BODY") that contain some header
533 information, so check them here */
534 if (chunk_size_expected != chunk_size)
536 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
537 chunk_size, chunk_name, filename);
545 if (leveldir_current == NULL) /* only when dumping level */
548 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
549 IS_LEVELCLASS_USER(leveldir_current))
551 /* For user contributed and private levels, use the version of
552 the game engine the levels were created for.
553 Since 2.0.1, the game engine version is now directly stored
554 in the level file (chunk "VERS"), so there is no need anymore
555 to set the game version from the file version (except for old,
556 pre-2.0 levels, where the game version is still taken from the
557 file format version used to store the level -- see above). */
559 /* do some special adjustments to support older level versions */
560 if (level.file_version == FILE_VERSION_1_0)
562 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
563 Error(ERR_WARN, "using high speed movement for player");
565 /* player was faster than monsters in (pre-)1.0 levels */
566 level.double_speed = TRUE;
569 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
570 if (level.game_version == VERSION_IDENT(2,0,1))
571 level.em_slippery_gems = TRUE;
575 /* Always use the latest version of the game engine for all but
576 user contributed and private levels; this allows for actual
577 corrections in the game engine to take effect for existing,
578 converted levels (from "classic" or other existing games) to
579 make the game emulation more accurate, while (hopefully) not
580 breaking existing levels created from other players. */
582 level.game_version = GAME_VERSION_ACTUAL;
584 /* Set special EM style gems behaviour: EM style gems slip down from
585 normal, steel and growing wall. As this is a more fundamental change,
586 it seems better to set the default behaviour to "off" (as it is more
587 natural) and make it configurable in the level editor (as a property
588 of gem style elements). Already existing converted levels (neither
589 private nor contributed levels) are changed to the new behaviour. */
591 if (level.file_version < FILE_VERSION_2_0)
592 level.em_slippery_gems = TRUE;
595 /* map some elements which have changed in newer versions */
596 if (level.game_version <= VERSION_IDENT(2,2,0))
600 /* map game font elements */
601 for(y=0; y<level.fieldy; y++)
603 for(x=0; x<level.fieldx; x++)
605 int element = Ur[x][y];
607 if (element == EL_CHAR('['))
608 element = EL_CHAR_AUMLAUT;
609 else if (element == EL_CHAR('\\'))
610 element = EL_CHAR_OUMLAUT;
611 else if (element == EL_CHAR(']'))
612 element = EL_CHAR_UUMLAUT;
613 else if (element == EL_CHAR('^'))
614 element = EL_CHAR_COPYRIGHT;
616 Feld[x][y] = Ur[x][y] = element;
621 /* determine border element for this level */
625 void LoadLevel(int level_nr)
627 char *filename = getLevelFilename(level_nr);
629 LoadLevelFromFilename(filename);
630 InitElementPropertiesEngine(level.game_version);
633 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
635 putFileVersion(file, level->file_version);
636 putFileVersion(file, level->game_version);
639 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
643 fputc(level->fieldx, file);
644 fputc(level->fieldy, file);
646 putFile16BitBE(file, level->time);
647 putFile16BitBE(file, level->gems_needed);
649 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
650 fputc(level->name[i], file);
652 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
653 fputc(level->score[i], file);
655 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
658 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
659 level->yamyam_content[i][x][y]),
661 fputc(level->amoeba_speed, file);
662 fputc(level->time_magic_wall, file);
663 fputc(level->time_wheel, file);
664 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
666 fputc((level->double_speed ? 1 : 0), file);
667 fputc((level->gravity ? 1 : 0), file);
668 fputc((level->encoding_16bit_field ? 1 : 0), file);
669 fputc((level->em_slippery_gems ? 1 : 0), file);
671 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
674 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
678 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
679 fputc(level->author[i], file);
682 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
686 for(y=0; y<level->fieldy; y++)
687 for(x=0; x<level->fieldx; x++)
688 if (level->encoding_16bit_field)
689 putFile16BitBE(file, Ur[x][y]);
691 fputc(Ur[x][y], file);
695 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
699 fputc(EL_YAMYAM, file);
700 fputc(level->num_yamyam_contents, file);
704 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
707 if (level->encoding_16bit_field)
708 putFile16BitBE(file, level->yamyam_content[i][x][y]);
710 fputc(level->yamyam_content[i][x][y], file);
714 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
717 int num_contents, content_xsize, content_ysize;
718 int content_array[MAX_ELEMENT_CONTENTS][3][3];
720 if (element == EL_YAMYAM)
722 num_contents = level->num_yamyam_contents;
726 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
729 content_array[i][x][y] = level->yamyam_content[i][x][y];
731 else if (element == EL_BD_AMOEBA)
737 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
740 content_array[i][x][y] = EL_EMPTY;
741 content_array[0][0][0] = level->amoeba_content;
745 /* chunk header already written -- write empty chunk data */
746 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
748 Error(ERR_WARN, "cannot save content for element '%d'", element);
752 putFile16BitBE(file, element);
753 fputc(num_contents, file);
754 fputc(content_xsize, file);
755 fputc(content_ysize, file);
757 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
759 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
762 putFile16BitBE(file, content_array[i][x][y]);
765 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
766 int num_changed_custom_elements)
770 putFile16BitBE(file, num_changed_custom_elements);
772 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
774 int element = EL_CUSTOM_START + i;
776 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
778 if (check < num_changed_custom_elements)
780 putFile16BitBE(file, element);
781 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
788 if (check != num_changed_custom_elements) /* should not happen */
789 Error(ERR_WARN, "inconsistent number of custom element properties");
792 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
793 int num_changed_custom_elements)
797 putFile16BitBE(file, num_changed_custom_elements);
799 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
801 int element = EL_CUSTOM_START + i;
803 if (element_info[element].change.target_element != EL_EMPTY_SPACE)
805 if (check < num_changed_custom_elements)
807 putFile16BitBE(file, element);
808 putFile16BitBE(file, element_info[element].change.target_element);
815 if (check != num_changed_custom_elements) /* should not happen */
816 Error(ERR_WARN, "inconsistent number of custom target elements");
819 void SaveLevel(int level_nr)
821 char *filename = getLevelFilename(level_nr);
823 int num_changed_custom_elements1 = 0;
824 int num_changed_custom_elements2 = 0;
828 if (!(file = fopen(filename, MODE_WRITE)))
830 Error(ERR_WARN, "cannot save level file '%s'", filename);
834 level.file_version = FILE_VERSION_ACTUAL;
835 level.game_version = GAME_VERSION_ACTUAL;
837 /* check level field for 16-bit elements */
838 level.encoding_16bit_field = FALSE;
839 for(y=0; y<level.fieldy; y++)
840 for(x=0; x<level.fieldx; x++)
842 level.encoding_16bit_field = TRUE;
844 /* check yamyam content for 16-bit elements */
845 level.encoding_16bit_yamyam = FALSE;
846 for(i=0; i<level.num_yamyam_contents; i++)
849 if (level.yamyam_content[i][x][y] > 255)
850 level.encoding_16bit_yamyam = TRUE;
852 /* check amoeba content for 16-bit elements */
853 level.encoding_16bit_amoeba = FALSE;
854 if (level.amoeba_content > 255)
855 level.encoding_16bit_amoeba = TRUE;
857 /* calculate size of "BODY" chunk */
859 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
861 /* check for non-standard custom elements and calculate "CUS1" chunk size */
862 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
863 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
864 num_changed_custom_elements1++;
866 /* check for non-standard custom elements and calculate "CUS2" chunk size */
867 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
868 if (element_info[EL_CUSTOM_START + i].change.target_element != EL_EMPTY)
869 num_changed_custom_elements2++;
871 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
872 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
874 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
875 SaveLevel_VERS(file, &level);
877 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
878 SaveLevel_HEAD(file, &level);
880 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
881 SaveLevel_AUTH(file, &level);
883 putFileChunkBE(file, "BODY", body_chunk_size);
884 SaveLevel_BODY(file, &level);
886 if (level.encoding_16bit_yamyam ||
887 level.num_yamyam_contents != STD_ELEMENT_CONTENTS)
889 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
890 SaveLevel_CNT2(file, &level, EL_YAMYAM);
893 if (level.encoding_16bit_amoeba)
895 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
896 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
899 if (num_changed_custom_elements1 > 0)
901 putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements1 * 6);
902 SaveLevel_CUS1(file, &level, num_changed_custom_elements1);
905 if (num_changed_custom_elements2 > 0)
907 putFileChunkBE(file, "CUS2", 2 + num_changed_custom_elements2 * 4);
908 SaveLevel_CUS2(file, &level, num_changed_custom_elements2);
913 SetFilePermissions(filename, PERMS_PRIVATE);
916 void DumpLevel(struct LevelInfo *level)
918 printf_line("-", 79);
919 printf("Level xxx (file version %08d, game version %08d)\n",
920 level->file_version, level->game_version);
921 printf_line("-", 79);
923 printf("Level Author: '%s'\n", level->author);
924 printf("Level Title: '%s'\n", level->name);
926 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
928 printf("Level Time: %d seconds\n", level->time);
929 printf("Gems needed: %d\n", level->gems_needed);
931 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
932 printf("Time for Wheel: %d seconds\n", level->time_wheel);
933 printf("Time for Light: %d seconds\n", level->time_light);
934 printf("Time for Timegate: %d seconds\n", level->time_timegate);
936 printf("Amoeba Speed: %d\n", level->amoeba_speed);
938 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
939 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
940 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
942 printf_line("-", 79);
946 /* ========================================================================= */
947 /* tape file functions */
948 /* ========================================================================= */
950 static void setTapeInfoToDefaults()
954 /* always start with reliable default values (empty tape) */
957 /* default values (also for pre-1.2 tapes) with only the first player */
958 tape.player_participates[0] = TRUE;
959 for(i=1; i<MAX_PLAYERS; i++)
960 tape.player_participates[i] = FALSE;
962 /* at least one (default: the first) player participates in every tape */
963 tape.num_participating_players = 1;
965 tape.level_nr = level_nr;
967 tape.changed = FALSE;
969 tape.recording = FALSE;
970 tape.playing = FALSE;
971 tape.pausing = FALSE;
974 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
976 tape->file_version = getFileVersion(file);
977 tape->game_version = getFileVersion(file);
982 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
986 tape->random_seed = getFile32BitBE(file);
987 tape->date = getFile32BitBE(file);
988 tape->length = getFile32BitBE(file);
990 /* read header fields that are new since version 1.2 */
991 if (tape->file_version >= FILE_VERSION_1_2)
993 byte store_participating_players = fgetc(file);
996 /* since version 1.2, tapes store which players participate in the tape */
997 tape->num_participating_players = 0;
998 for(i=0; i<MAX_PLAYERS; i++)
1000 tape->player_participates[i] = FALSE;
1002 if (store_participating_players & (1 << i))
1004 tape->player_participates[i] = TRUE;
1005 tape->num_participating_players++;
1009 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1011 engine_version = getFileVersion(file);
1012 if (engine_version > 0)
1013 tape->engine_version = engine_version;
1019 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1021 int level_identifier_size;
1024 level_identifier_size = getFile16BitBE(file);
1026 tape->level_identifier =
1027 checked_realloc(tape->level_identifier, level_identifier_size);
1029 for(i=0; i < level_identifier_size; i++)
1030 tape->level_identifier[i] = fgetc(file);
1032 tape->level_nr = getFile16BitBE(file);
1034 chunk_size = 2 + level_identifier_size + 2;
1039 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1042 int chunk_size_expected =
1043 (tape->num_participating_players + 1) * tape->length;
1045 if (chunk_size_expected != chunk_size)
1047 ReadUnusedBytesFromFile(file, chunk_size);
1048 return chunk_size_expected;
1051 for(i=0; i<tape->length; i++)
1053 if (i >= MAX_TAPELEN)
1056 for(j=0; j<MAX_PLAYERS; j++)
1058 tape->pos[i].action[j] = MV_NO_MOVING;
1060 if (tape->player_participates[j])
1061 tape->pos[i].action[j] = fgetc(file);
1064 tape->pos[i].delay = fgetc(file);
1066 if (tape->file_version == FILE_VERSION_1_0)
1068 /* eliminate possible diagonal moves in old tapes */
1069 /* this is only for backward compatibility */
1071 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1072 byte action = tape->pos[i].action[0];
1073 int k, num_moves = 0;
1077 if (action & joy_dir[k])
1079 tape->pos[i + num_moves].action[0] = joy_dir[k];
1081 tape->pos[i + num_moves].delay = 0;
1090 tape->length += num_moves;
1093 else if (tape->file_version < FILE_VERSION_2_0)
1095 /* convert pre-2.0 tapes to new tape format */
1097 if (tape->pos[i].delay > 1)
1100 tape->pos[i + 1] = tape->pos[i];
1101 tape->pos[i + 1].delay = 1;
1104 for(j=0; j<MAX_PLAYERS; j++)
1105 tape->pos[i].action[j] = MV_NO_MOVING;
1106 tape->pos[i].delay--;
1117 if (i != tape->length)
1118 chunk_size = (tape->num_participating_players + 1) * i;
1123 void LoadTapeFromFilename(char *filename)
1125 char cookie[MAX_LINE_LEN];
1126 char chunk_name[CHUNK_ID_LEN + 1];
1130 /* always start with reliable default values */
1131 setTapeInfoToDefaults();
1133 if (!(file = fopen(filename, MODE_READ)))
1136 getFileChunkBE(file, chunk_name, NULL);
1137 if (strcmp(chunk_name, "RND1") == 0)
1139 getFile32BitBE(file); /* not used */
1141 getFileChunkBE(file, chunk_name, NULL);
1142 if (strcmp(chunk_name, "TAPE") != 0)
1144 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1149 else /* check for pre-2.0 file format with cookie string */
1151 strcpy(cookie, chunk_name);
1152 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1153 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1154 cookie[strlen(cookie) - 1] = '\0';
1156 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1158 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1163 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1165 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1170 /* pre-2.0 tape files have no game version, so use file version here */
1171 tape.game_version = tape.file_version;
1174 if (tape.file_version < FILE_VERSION_1_2)
1176 /* tape files from versions before 1.2.0 without chunk structure */
1177 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1178 LoadTape_BODY(file, 2 * tape.length, &tape);
1186 int (*loader)(FILE *, int, struct TapeInfo *);
1190 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1191 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1192 { "INFO", -1, LoadTape_INFO },
1193 { "BODY", -1, LoadTape_BODY },
1197 while (getFileChunkBE(file, chunk_name, &chunk_size))
1201 while (chunk_info[i].name != NULL &&
1202 strcmp(chunk_name, chunk_info[i].name) != 0)
1205 if (chunk_info[i].name == NULL)
1207 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1208 chunk_name, filename);
1209 ReadUnusedBytesFromFile(file, chunk_size);
1211 else if (chunk_info[i].size != -1 &&
1212 chunk_info[i].size != chunk_size)
1214 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1215 chunk_size, chunk_name, filename);
1216 ReadUnusedBytesFromFile(file, chunk_size);
1220 /* call function to load this tape chunk */
1221 int chunk_size_expected =
1222 (chunk_info[i].loader)(file, chunk_size, &tape);
1224 /* the size of some chunks cannot be checked before reading other
1225 chunks first (like "HEAD" and "BODY") that contain some header
1226 information, so check them here */
1227 if (chunk_size_expected != chunk_size)
1229 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1230 chunk_size, chunk_name, filename);
1238 tape.length_seconds = GetTapeLength();
1241 printf("tape version: %d\n", tape.game_version);
1245 void LoadTape(int level_nr)
1247 char *filename = getTapeFilename(level_nr);
1249 LoadTapeFromFilename(filename);
1252 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1254 putFileVersion(file, tape->file_version);
1255 putFileVersion(file, tape->game_version);
1258 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1261 byte store_participating_players = 0;
1263 /* set bits for participating players for compact storage */
1264 for(i=0; i<MAX_PLAYERS; i++)
1265 if (tape->player_participates[i])
1266 store_participating_players |= (1 << i);
1268 putFile32BitBE(file, tape->random_seed);
1269 putFile32BitBE(file, tape->date);
1270 putFile32BitBE(file, tape->length);
1272 fputc(store_participating_players, file);
1274 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1275 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1277 putFileVersion(file, tape->engine_version);
1280 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1282 int level_identifier_size = strlen(tape->level_identifier) + 1;
1285 putFile16BitBE(file, level_identifier_size);
1287 for(i=0; i < level_identifier_size; i++)
1288 fputc(tape->level_identifier[i], file);
1290 putFile16BitBE(file, tape->level_nr);
1293 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1297 for(i=0; i<tape->length; i++)
1299 for(j=0; j<MAX_PLAYERS; j++)
1300 if (tape->player_participates[j])
1301 fputc(tape->pos[i].action[j], file);
1303 fputc(tape->pos[i].delay, file);
1307 void SaveTape(int level_nr)
1309 char *filename = getTapeFilename(level_nr);
1311 boolean new_tape = TRUE;
1312 int num_participating_players = 0;
1313 int info_chunk_size;
1314 int body_chunk_size;
1317 InitTapeDirectory(leveldir_current->filename);
1319 /* if a tape still exists, ask to overwrite it */
1320 if (access(filename, F_OK) == 0)
1323 if (!Request("Replace old tape ?", REQ_ASK))
1327 if (!(file = fopen(filename, MODE_WRITE)))
1329 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1333 tape.file_version = FILE_VERSION_ACTUAL;
1334 tape.game_version = GAME_VERSION_ACTUAL;
1336 /* count number of participating players */
1337 for(i=0; i<MAX_PLAYERS; i++)
1338 if (tape.player_participates[i])
1339 num_participating_players++;
1341 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1342 body_chunk_size = (num_participating_players + 1) * tape.length;
1344 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1345 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1347 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1348 SaveTape_VERS(file, &tape);
1350 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1351 SaveTape_HEAD(file, &tape);
1353 putFileChunkBE(file, "INFO", info_chunk_size);
1354 SaveTape_INFO(file, &tape);
1356 putFileChunkBE(file, "BODY", body_chunk_size);
1357 SaveTape_BODY(file, &tape);
1361 SetFilePermissions(filename, PERMS_PRIVATE);
1363 tape.changed = FALSE;
1366 Request("tape saved !", REQ_CONFIRM);
1369 void DumpTape(struct TapeInfo *tape)
1373 if (TAPE_IS_EMPTY(*tape))
1375 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1379 printf_line("-", 79);
1380 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1381 tape->level_nr, tape->file_version, tape->game_version);
1382 printf("Level series identifier: '%s'\n", tape->level_identifier);
1383 printf_line("-", 79);
1385 for(i=0; i<tape->length; i++)
1387 if (i >= MAX_TAPELEN)
1390 printf("%03d: ", i);
1392 for(j=0; j<MAX_PLAYERS; j++)
1394 if (tape->player_participates[j])
1396 int action = tape->pos[i].action[j];
1398 printf("%d:%02x ", j, action);
1399 printf("[%c%c%c%c|%c%c] - ",
1400 (action & JOY_LEFT ? '<' : ' '),
1401 (action & JOY_RIGHT ? '>' : ' '),
1402 (action & JOY_UP ? '^' : ' '),
1403 (action & JOY_DOWN ? 'v' : ' '),
1404 (action & JOY_BUTTON_1 ? '1' : ' '),
1405 (action & JOY_BUTTON_2 ? '2' : ' '));
1409 printf("(%03d)\n", tape->pos[i].delay);
1412 printf_line("-", 79);
1416 /* ========================================================================= */
1417 /* score file functions */
1418 /* ========================================================================= */
1420 void LoadScore(int level_nr)
1423 char *filename = getScoreFilename(level_nr);
1424 char cookie[MAX_LINE_LEN];
1425 char line[MAX_LINE_LEN];
1429 /* always start with reliable default values */
1430 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1432 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1433 highscore[i].Score = 0;
1436 if (!(file = fopen(filename, MODE_READ)))
1439 /* check file identifier */
1440 fgets(cookie, MAX_LINE_LEN, file);
1441 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1442 cookie[strlen(cookie) - 1] = '\0';
1444 if (!checkCookieString(cookie, SCORE_COOKIE))
1446 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1451 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1453 fscanf(file, "%d", &highscore[i].Score);
1454 fgets(line, MAX_LINE_LEN, file);
1456 if (line[strlen(line) - 1] == '\n')
1457 line[strlen(line) - 1] = '\0';
1459 for (line_ptr = line; *line_ptr; line_ptr++)
1461 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1463 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1464 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1473 void SaveScore(int level_nr)
1476 char *filename = getScoreFilename(level_nr);
1479 InitScoreDirectory(leveldir_current->filename);
1481 if (!(file = fopen(filename, MODE_WRITE)))
1483 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1487 fprintf(file, "%s\n\n", SCORE_COOKIE);
1489 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1490 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1494 SetFilePermissions(filename, PERMS_PUBLIC);
1498 /* ========================================================================= */
1499 /* setup file functions */
1500 /* ========================================================================= */
1502 #define TOKEN_STR_PLAYER_PREFIX "player_"
1505 #define SETUP_TOKEN_PLAYER_NAME 0
1506 #define SETUP_TOKEN_SOUND 1
1507 #define SETUP_TOKEN_SOUND_LOOPS 2
1508 #define SETUP_TOKEN_SOUND_MUSIC 3
1509 #define SETUP_TOKEN_SOUND_SIMPLE 4
1510 #define SETUP_TOKEN_TOONS 5
1511 #define SETUP_TOKEN_SCROLL_DELAY 6
1512 #define SETUP_TOKEN_SOFT_SCROLLING 7
1513 #define SETUP_TOKEN_FADING 8
1514 #define SETUP_TOKEN_AUTORECORD 9
1515 #define SETUP_TOKEN_QUICK_DOORS 10
1516 #define SETUP_TOKEN_TEAM_MODE 11
1517 #define SETUP_TOKEN_HANDICAP 12
1518 #define SETUP_TOKEN_TIME_LIMIT 13
1519 #define SETUP_TOKEN_FULLSCREEN 14
1520 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1521 #define SETUP_TOKEN_GRAPHICS_SET 16
1522 #define SETUP_TOKEN_SOUNDS_SET 17
1523 #define SETUP_TOKEN_MUSIC_SET 18
1524 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1525 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1526 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1528 #define NUM_GLOBAL_SETUP_TOKENS 22
1531 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1532 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1533 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1534 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1535 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1536 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1537 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1538 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1539 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1541 #define NUM_EDITOR_SETUP_TOKENS 9
1543 /* shortcut setup */
1544 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1545 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1546 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1548 #define NUM_SHORTCUT_SETUP_TOKENS 3
1551 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1552 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1553 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1554 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1555 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1556 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1557 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1558 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1559 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1560 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1561 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1562 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1563 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1564 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1565 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1566 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1568 #define NUM_PLAYER_SETUP_TOKENS 16
1571 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1572 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1574 #define NUM_SYSTEM_SETUP_TOKENS 2
1577 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1579 #define NUM_OPTIONS_SETUP_TOKENS 1
1582 static struct SetupInfo si;
1583 static struct SetupEditorInfo sei;
1584 static struct SetupShortcutInfo ssi;
1585 static struct SetupInputInfo sii;
1586 static struct SetupSystemInfo syi;
1587 static struct OptionInfo soi;
1589 static struct TokenInfo global_setup_tokens[] =
1591 { TYPE_STRING, &si.player_name, "player_name" },
1592 { TYPE_SWITCH, &si.sound, "sound" },
1593 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1594 { TYPE_SWITCH, &si.sound_music, "background_music" },
1595 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1596 { TYPE_SWITCH, &si.toons, "toons" },
1597 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1598 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1599 { TYPE_SWITCH, &si.fading, "screen_fading" },
1600 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1601 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1602 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1603 { TYPE_SWITCH, &si.handicap, "handicap" },
1604 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1605 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1606 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1607 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1608 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1609 { TYPE_STRING, &si.music_set, "music_set" },
1610 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1611 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1612 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1615 static struct TokenInfo editor_setup_tokens[] =
1617 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1618 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1619 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1620 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1621 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1622 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1623 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1624 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1625 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1628 static struct TokenInfo shortcut_setup_tokens[] =
1630 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1631 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1632 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1635 static struct TokenInfo player_setup_tokens[] =
1637 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1638 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1639 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1640 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1641 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1642 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1643 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1644 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1645 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1646 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1647 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1648 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1649 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1650 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1651 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1652 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1655 static struct TokenInfo system_setup_tokens[] =
1657 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1658 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1661 static struct TokenInfo options_setup_tokens[] =
1663 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1666 static char *get_corrected_login_name(char *login_name)
1668 /* needed because player name must be a fixed length string */
1669 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1671 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1672 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1674 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1675 if (strchr(login_name_new, ' '))
1676 *strchr(login_name_new, ' ') = '\0';
1678 return login_name_new;
1681 static void setSetupInfoToDefaults(struct SetupInfo *si)
1685 si->player_name = get_corrected_login_name(getLoginName());
1688 si->sound_loops = TRUE;
1689 si->sound_music = TRUE;
1690 si->sound_simple = TRUE;
1692 si->double_buffering = TRUE;
1693 si->direct_draw = !si->double_buffering;
1694 si->scroll_delay = TRUE;
1695 si->soft_scrolling = TRUE;
1697 si->autorecord = TRUE;
1698 si->quick_doors = FALSE;
1699 si->team_mode = FALSE;
1700 si->handicap = TRUE;
1701 si->time_limit = TRUE;
1702 si->fullscreen = FALSE;
1703 si->ask_on_escape = TRUE;
1705 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1706 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1707 si->music_set = getStringCopy(MUSIC_SUBDIR);
1708 si->override_level_graphics = FALSE;
1709 si->override_level_sounds = FALSE;
1710 si->override_level_music = FALSE;
1712 si->editor.el_boulderdash = TRUE;
1713 si->editor.el_emerald_mine = TRUE;
1714 si->editor.el_more = TRUE;
1715 si->editor.el_sokoban = TRUE;
1716 si->editor.el_supaplex = TRUE;
1717 si->editor.el_diamond_caves = TRUE;
1718 si->editor.el_dx_boulderdash = TRUE;
1719 si->editor.el_chars = TRUE;
1720 si->editor.el_custom = TRUE;
1722 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1723 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1724 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1726 for (i=0; i<MAX_PLAYERS; i++)
1728 si->input[i].use_joystick = FALSE;
1729 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1730 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1731 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1732 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1733 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1734 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1735 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1736 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1737 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1738 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1739 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1740 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1741 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1742 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1743 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1746 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1747 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1749 si->options.verbose = FALSE;
1752 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
1756 if (!setup_file_hash)
1761 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1762 setSetupInfo(global_setup_tokens, i,
1763 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
1768 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1769 setSetupInfo(editor_setup_tokens, i,
1770 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
1773 /* shortcut setup */
1774 ssi = setup.shortcut;
1775 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1776 setSetupInfo(shortcut_setup_tokens, i,
1777 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
1778 setup.shortcut = ssi;
1781 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1785 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1787 sii = setup.input[pnr];
1788 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1790 char full_token[100];
1792 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1793 setSetupInfo(player_setup_tokens, i,
1794 getHashEntry(setup_file_hash, full_token));
1796 setup.input[pnr] = sii;
1801 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1802 setSetupInfo(system_setup_tokens, i,
1803 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
1807 soi = setup.options;
1808 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1809 setSetupInfo(options_setup_tokens, i,
1810 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
1811 setup.options = soi;
1816 char *filename = getSetupFilename();
1817 SetupFileHash *setup_file_hash = NULL;
1819 /* always start with reliable default values */
1820 setSetupInfoToDefaults(&setup);
1822 setup_file_hash = loadSetupFileHash(filename);
1824 if (setup_file_hash)
1826 char *player_name_new;
1828 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
1829 decodeSetupFileHash(setup_file_hash);
1831 setup.direct_draw = !setup.double_buffering;
1833 freeSetupFileHash(setup_file_hash);
1835 /* needed to work around problems with fixed length strings */
1836 player_name_new = get_corrected_login_name(setup.player_name);
1837 free(setup.player_name);
1838 setup.player_name = player_name_new;
1841 Error(ERR_WARN, "using default setup values");
1846 char *filename = getSetupFilename();
1850 InitUserDataDirectory();
1852 if (!(file = fopen(filename, MODE_WRITE)))
1854 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1858 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1859 getCookie("SETUP")));
1860 fprintf(file, "\n");
1864 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1866 /* just to make things nicer :) */
1867 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1868 i == SETUP_TOKEN_GRAPHICS_SET)
1869 fprintf(file, "\n");
1871 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1876 fprintf(file, "\n");
1877 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1878 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1880 /* shortcut setup */
1881 ssi = setup.shortcut;
1882 fprintf(file, "\n");
1883 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1884 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1887 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1891 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1892 fprintf(file, "\n");
1894 sii = setup.input[pnr];
1895 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1896 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1901 fprintf(file, "\n");
1902 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1903 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1906 soi = setup.options;
1907 fprintf(file, "\n");
1908 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1909 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1913 SetFilePermissions(filename, PERMS_PRIVATE);
1916 void LoadCustomElementDescriptions()
1918 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1919 SetupFileHash *setup_file_hash;
1922 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1924 if (element_info[i].custom_description != NULL)
1926 free(element_info[i].custom_description);
1927 element_info[i].custom_description = NULL;
1931 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1934 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1936 char *token = getStringCat2(element_info[i].token_name, ".name");
1937 char *value = getHashEntry(setup_file_hash, token);
1940 element_info[i].custom_description = getStringCopy(value);
1945 freeSetupFileHash(setup_file_hash);
1948 void LoadSpecialMenuDesignSettings()
1950 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1951 SetupFileHash *setup_file_hash;
1954 /* always start with reliable default values from default config */
1955 for (i=0; image_config_vars[i].token != NULL; i++)
1956 for (j=0; image_config[j].token != NULL; j++)
1957 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
1958 *image_config_vars[i].value =
1959 get_integer_from_string(image_config[j].value);
1961 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1964 /* special case: initialize with default values that may be overwritten */
1965 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
1967 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
1968 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
1969 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
1971 if (value_x != NULL)
1972 menu.draw_xoffset[i] = get_integer_from_string(value_x);
1973 if (value_y != NULL)
1974 menu.draw_yoffset[i] = get_integer_from_string(value_y);
1975 if (list_size != NULL)
1976 menu.list_size[i] = get_integer_from_string(list_size);
1979 /* read (and overwrite with) values that may be specified in config file */
1980 for (i=0; image_config_vars[i].token != NULL; i++)
1982 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
1985 *image_config_vars[i].value = get_integer_from_string(value);
1988 freeSetupFileHash(setup_file_hash);