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 level.custom_element[i].use_gfx_element = FALSE;
103 level.custom_element[i].gfx_element = EL_EMPTY_SPACE;
104 level.custom_element[i].move_direction = 0;
108 level.custom_element[i].content[x][y] = EL_EMPTY_SPACE;
110 level.custom_element[i].change.events = CE_BITMASK_DEFAULT;
111 level.custom_element[i].change.delay_fixed = 0;
112 level.custom_element[i].change.delay_random = 0;
113 level.custom_element[i].change.successor = EL_EMPTY_SPACE;
115 /* start with no properties at all */
116 for (j=0; j < NUM_EP_BITFIELDS; j++)
117 Properties[element][j] = EP_BITMASK_DEFAULT;
120 BorderElement = EL_STEELWALL;
122 level.no_level_file = FALSE;
124 if (leveldir_current == NULL) /* only when dumping level */
127 /* try to determine better author name than 'anonymous' */
128 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
130 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
131 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
135 switch (LEVELCLASS(leveldir_current))
137 case LEVELCLASS_TUTORIAL:
138 strcpy(level.author, PROGRAM_AUTHOR_STRING);
141 case LEVELCLASS_CONTRIBUTION:
142 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
143 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
146 case LEVELCLASS_USER:
147 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
148 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
152 /* keep default value */
158 static int checkLevelElement(int element)
160 if (element >= NUM_FILE_ELEMENTS)
162 Error(ERR_WARN, "invalid level element %d", element);
163 element = EL_CHAR_QUESTION;
165 else if (element == EL_PLAYER_OBSOLETE)
166 element = EL_PLAYER_1;
167 else if (element == EL_KEY_OBSOLETE)
173 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
175 level->file_version = getFileVersion(file);
176 level->game_version = getFileVersion(file);
181 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
185 lev_fieldx = level->fieldx = fgetc(file);
186 lev_fieldy = level->fieldy = fgetc(file);
188 level->time = getFile16BitBE(file);
189 level->gems_needed = getFile16BitBE(file);
191 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
192 level->name[i] = fgetc(file);
193 level->name[MAX_LEVEL_NAME_LEN] = 0;
195 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
196 level->score[i] = fgetc(file);
198 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
199 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
202 level->yamyam_content[i][x][y] = checkLevelElement(fgetc(file));
204 level->amoeba_speed = fgetc(file);
205 level->time_magic_wall = fgetc(file);
206 level->time_wheel = fgetc(file);
207 level->amoeba_content = checkLevelElement(fgetc(file));
208 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
209 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
210 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
211 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
213 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
218 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
222 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
223 level->author[i] = fgetc(file);
224 level->author[MAX_LEVEL_NAME_LEN] = 0;
229 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
232 int chunk_size_expected = level->fieldx * level->fieldy;
234 /* Note: "chunk_size" was wrong before version 2.0 when elements are
235 stored with 16-bit encoding (and should be twice as big then).
236 Even worse, playfield data was stored 16-bit when only yamyam content
237 contained 16-bit elements and vice versa. */
239 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
240 chunk_size_expected *= 2;
242 if (chunk_size_expected != chunk_size)
244 ReadUnusedBytesFromFile(file, chunk_size);
245 return chunk_size_expected;
248 for(y=0; y<level->fieldy; y++)
249 for(x=0; x<level->fieldx; x++)
250 Feld[x][y] = Ur[x][y] =
251 checkLevelElement(level->encoding_16bit_field ?
252 getFile16BitBE(file) : fgetc(file));
256 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
260 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
261 int chunk_size_expected = header_size + content_size;
263 /* Note: "chunk_size" was wrong before version 2.0 when elements are
264 stored with 16-bit encoding (and should be twice as big then).
265 Even worse, playfield data was stored 16-bit when only yamyam content
266 contained 16-bit elements and vice versa. */
268 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
269 chunk_size_expected += content_size;
271 if (chunk_size_expected != chunk_size)
273 ReadUnusedBytesFromFile(file, chunk_size);
274 return chunk_size_expected;
278 level->num_yamyam_contents = fgetc(file);
282 /* correct invalid number of content fields -- should never happen */
283 if (level->num_yamyam_contents < 1 ||
284 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
285 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
287 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
290 level->yamyam_content[i][x][y] =
291 checkLevelElement(level->encoding_16bit_field ?
292 getFile16BitBE(file) : fgetc(file));
296 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
300 int num_contents, content_xsize, content_ysize;
301 int content_array[MAX_ELEMENT_CONTENTS][3][3];
303 element = checkLevelElement(getFile16BitBE(file));
304 num_contents = fgetc(file);
305 content_xsize = fgetc(file);
306 content_ysize = fgetc(file);
307 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
309 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
312 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
314 /* correct invalid number of content fields -- should never happen */
315 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
316 num_contents = STD_ELEMENT_CONTENTS;
318 if (element == EL_YAMYAM)
320 level->num_yamyam_contents = num_contents;
322 for(i=0; i<num_contents; i++)
325 level->yamyam_content[i][x][y] = content_array[i][x][y];
327 else if (element == EL_BD_AMOEBA)
329 level->amoeba_content = content_array[0][0][0];
333 Error(ERR_WARN, "cannot load content for element '%d'", element);
339 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
341 int num_changed_custom_elements = getFile16BitBE(file);
342 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
345 if (chunk_size_expected != chunk_size)
347 ReadUnusedBytesFromFile(file, chunk_size - 2);
348 return chunk_size_expected;
351 for (i=0; i < num_changed_custom_elements; i++)
353 int element = getFile16BitBE(file);
354 int properties = getFile32BitBE(file);
356 if (IS_CUSTOM_ELEMENT(element))
357 Properties[element][EP_BITFIELD_BASE] = properties;
359 Error(ERR_WARN, "invalid custom element number %d", element);
365 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
367 int num_changed_custom_elements = getFile16BitBE(file);
368 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
371 if (chunk_size_expected != chunk_size)
373 ReadUnusedBytesFromFile(file, chunk_size - 2);
374 return chunk_size_expected;
377 for (i=0; i < num_changed_custom_elements; i++)
379 int element = getFile16BitBE(file);
380 int custom_element_successor = getFile16BitBE(file);
381 int i = element - EL_CUSTOM_START;
383 if (IS_CUSTOM_ELEMENT(element))
384 level->custom_element[i].change.successor = custom_element_successor;
386 Error(ERR_WARN, "invalid custom element number %d", element);
392 void LoadLevelFromFilename(char *filename)
394 char cookie[MAX_LINE_LEN];
395 char chunk_name[CHUNK_ID_LEN + 1];
399 /* always start with reliable default values */
400 setLevelInfoToDefaults();
402 if (!(file = fopen(filename, MODE_READ)))
404 level.no_level_file = TRUE;
406 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
410 getFileChunkBE(file, chunk_name, NULL);
411 if (strcmp(chunk_name, "RND1") == 0)
413 getFile32BitBE(file); /* not used */
415 getFileChunkBE(file, chunk_name, NULL);
416 if (strcmp(chunk_name, "CAVE") != 0)
418 Error(ERR_WARN, "unknown format of level file '%s'", filename);
423 else /* check for pre-2.0 file format with cookie string */
425 strcpy(cookie, chunk_name);
426 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
427 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
428 cookie[strlen(cookie) - 1] = '\0';
430 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
432 Error(ERR_WARN, "unknown format of level file '%s'", filename);
437 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
439 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
444 /* pre-2.0 level files have no game version, so use file version here */
445 level.game_version = level.file_version;
448 if (level.file_version < FILE_VERSION_1_2)
450 /* level files from versions before 1.2.0 without chunk structure */
451 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
452 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
460 int (*loader)(FILE *, int, struct LevelInfo *);
464 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
465 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
466 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
467 { "BODY", -1, LoadLevel_BODY },
468 { "CONT", -1, LoadLevel_CONT },
469 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
470 { "CUS1", -1, LoadLevel_CUS1 },
471 { "CUS2", -1, LoadLevel_CUS2 },
475 while (getFileChunkBE(file, chunk_name, &chunk_size))
479 while (chunk_info[i].name != NULL &&
480 strcmp(chunk_name, chunk_info[i].name) != 0)
483 if (chunk_info[i].name == NULL)
485 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
486 chunk_name, filename);
487 ReadUnusedBytesFromFile(file, chunk_size);
489 else if (chunk_info[i].size != -1 &&
490 chunk_info[i].size != chunk_size)
492 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
493 chunk_size, chunk_name, filename);
494 ReadUnusedBytesFromFile(file, chunk_size);
498 /* call function to load this level chunk */
499 int chunk_size_expected =
500 (chunk_info[i].loader)(file, chunk_size, &level);
502 /* the size of some chunks cannot be checked before reading other
503 chunks first (like "HEAD" and "BODY") that contain some header
504 information, so check them here */
505 if (chunk_size_expected != chunk_size)
507 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
508 chunk_size, chunk_name, filename);
516 if (leveldir_current == NULL) /* only when dumping level */
519 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
520 IS_LEVELCLASS_USER(leveldir_current))
522 /* For user contributed and private levels, use the version of
523 the game engine the levels were created for.
524 Since 2.0.1, the game engine version is now directly stored
525 in the level file (chunk "VERS"), so there is no need anymore
526 to set the game version from the file version (except for old,
527 pre-2.0 levels, where the game version is still taken from the
528 file format version used to store the level -- see above). */
530 /* do some special adjustments to support older level versions */
531 if (level.file_version == FILE_VERSION_1_0)
533 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
534 Error(ERR_WARN, "using high speed movement for player");
536 /* player was faster than monsters in (pre-)1.0 levels */
537 level.double_speed = TRUE;
540 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
541 if (level.game_version == VERSION_IDENT(2,0,1))
542 level.em_slippery_gems = TRUE;
546 /* Always use the latest version of the game engine for all but
547 user contributed and private levels; this allows for actual
548 corrections in the game engine to take effect for existing,
549 converted levels (from "classic" or other existing games) to
550 make the game emulation more accurate, while (hopefully) not
551 breaking existing levels created from other players. */
553 level.game_version = GAME_VERSION_ACTUAL;
555 /* Set special EM style gems behaviour: EM style gems slip down from
556 normal, steel and growing wall. As this is a more fundamental change,
557 it seems better to set the default behaviour to "off" (as it is more
558 natural) and make it configurable in the level editor (as a property
559 of gem style elements). Already existing converted levels (neither
560 private nor contributed levels) are changed to the new behaviour. */
562 if (level.file_version < FILE_VERSION_2_0)
563 level.em_slippery_gems = TRUE;
566 /* map some elements which have changed in newer versions */
567 if (level.game_version <= VERSION_IDENT(2,2,0))
571 /* map game font elements */
572 for(y=0; y<level.fieldy; y++)
574 for(x=0; x<level.fieldx; x++)
576 int element = Ur[x][y];
578 if (element == EL_CHAR('['))
579 element = EL_CHAR_AUMLAUT;
580 else if (element == EL_CHAR('\\'))
581 element = EL_CHAR_OUMLAUT;
582 else if (element == EL_CHAR(']'))
583 element = EL_CHAR_UUMLAUT;
584 else if (element == EL_CHAR('^'))
585 element = EL_CHAR_COPYRIGHT;
587 Feld[x][y] = Ur[x][y] = element;
592 /* determine border element for this level */
596 void LoadLevel(int level_nr)
598 char *filename = getLevelFilename(level_nr);
600 LoadLevelFromFilename(filename);
601 InitElementPropertiesEngine(level.game_version);
604 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
606 putFileVersion(file, level->file_version);
607 putFileVersion(file, level->game_version);
610 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
614 fputc(level->fieldx, file);
615 fputc(level->fieldy, file);
617 putFile16BitBE(file, level->time);
618 putFile16BitBE(file, level->gems_needed);
620 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
621 fputc(level->name[i], file);
623 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
624 fputc(level->score[i], file);
626 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
629 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
630 level->yamyam_content[i][x][y]),
632 fputc(level->amoeba_speed, file);
633 fputc(level->time_magic_wall, file);
634 fputc(level->time_wheel, file);
635 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
637 fputc((level->double_speed ? 1 : 0), file);
638 fputc((level->gravity ? 1 : 0), file);
639 fputc((level->encoding_16bit_field ? 1 : 0), file);
640 fputc((level->em_slippery_gems ? 1 : 0), file);
642 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
645 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
649 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
650 fputc(level->author[i], file);
653 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
657 for(y=0; y<level->fieldy; y++)
658 for(x=0; x<level->fieldx; x++)
659 if (level->encoding_16bit_field)
660 putFile16BitBE(file, Ur[x][y]);
662 fputc(Ur[x][y], file);
666 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
670 fputc(EL_YAMYAM, file);
671 fputc(level->num_yamyam_contents, file);
675 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
678 if (level->encoding_16bit_field)
679 putFile16BitBE(file, level->yamyam_content[i][x][y]);
681 fputc(level->yamyam_content[i][x][y], file);
685 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
688 int num_contents, content_xsize, content_ysize;
689 int content_array[MAX_ELEMENT_CONTENTS][3][3];
691 if (element == EL_YAMYAM)
693 num_contents = level->num_yamyam_contents;
697 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
700 content_array[i][x][y] = level->yamyam_content[i][x][y];
702 else if (element == EL_BD_AMOEBA)
708 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
711 content_array[i][x][y] = EL_EMPTY;
712 content_array[0][0][0] = level->amoeba_content;
716 /* chunk header already written -- write empty chunk data */
717 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
719 Error(ERR_WARN, "cannot save content for element '%d'", element);
723 putFile16BitBE(file, element);
724 fputc(num_contents, file);
725 fputc(content_xsize, file);
726 fputc(content_ysize, file);
728 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
730 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
733 putFile16BitBE(file, content_array[i][x][y]);
736 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
737 int num_changed_custom_elements)
741 putFile16BitBE(file, num_changed_custom_elements);
743 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
745 int element = EL_CUSTOM_START + i;
747 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
749 if (check < num_changed_custom_elements)
751 putFile16BitBE(file, element);
752 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
759 if (check != num_changed_custom_elements) /* should not happen */
760 Error(ERR_WARN, "inconsistent number of custom element properties");
763 static void SaveLevel_CUS2(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 (level->custom_element[i].change.successor != EL_EMPTY_SPACE)
776 if (check < num_changed_custom_elements)
778 putFile16BitBE(file, element);
779 putFile16BitBE(file, level->custom_element[i].change.successor);
786 if (check != num_changed_custom_elements) /* should not happen */
787 Error(ERR_WARN, "inconsistent number of custom element successors");
790 void SaveLevel(int level_nr)
792 char *filename = getLevelFilename(level_nr);
794 int num_changed_custom_elements1 = 0;
795 int num_changed_custom_elements2 = 0;
799 if (!(file = fopen(filename, MODE_WRITE)))
801 Error(ERR_WARN, "cannot save level file '%s'", filename);
805 level.file_version = FILE_VERSION_ACTUAL;
806 level.game_version = GAME_VERSION_ACTUAL;
808 /* check level field for 16-bit elements */
809 level.encoding_16bit_field = FALSE;
810 for(y=0; y<level.fieldy; y++)
811 for(x=0; x<level.fieldx; x++)
813 level.encoding_16bit_field = TRUE;
815 /* check yamyam content for 16-bit elements */
816 level.encoding_16bit_yamyam = FALSE;
817 for(i=0; i<level.num_yamyam_contents; i++)
820 if (level.yamyam_content[i][x][y] > 255)
821 level.encoding_16bit_yamyam = TRUE;
823 /* check amoeba content for 16-bit elements */
824 level.encoding_16bit_amoeba = FALSE;
825 if (level.amoeba_content > 255)
826 level.encoding_16bit_amoeba = TRUE;
828 /* calculate size of "BODY" chunk */
830 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
832 /* check for non-standard custom elements and calculate "CUS1" chunk size */
833 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
834 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
835 num_changed_custom_elements1++;
837 /* check for non-standard custom elements and calculate "CUS2" chunk size */
838 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
839 if (level.custom_element[i].change.successor != EL_EMPTY_SPACE)
840 num_changed_custom_elements2++;
842 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
843 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
845 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
846 SaveLevel_VERS(file, &level);
848 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
849 SaveLevel_HEAD(file, &level);
851 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
852 SaveLevel_AUTH(file, &level);
854 putFileChunkBE(file, "BODY", body_chunk_size);
855 SaveLevel_BODY(file, &level);
857 if (level.encoding_16bit_yamyam ||
858 level.num_yamyam_contents != STD_ELEMENT_CONTENTS)
860 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
861 SaveLevel_CNT2(file, &level, EL_YAMYAM);
864 if (level.encoding_16bit_amoeba)
866 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
867 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
870 if (num_changed_custom_elements1 > 0)
872 putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements1 * 6);
873 SaveLevel_CUS1(file, &level, num_changed_custom_elements1);
876 if (num_changed_custom_elements2 > 0)
878 putFileChunkBE(file, "CUS2", 2 + num_changed_custom_elements2 * 4);
879 SaveLevel_CUS2(file, &level, num_changed_custom_elements2);
884 SetFilePermissions(filename, PERMS_PRIVATE);
887 void DumpLevel(struct LevelInfo *level)
889 printf_line("-", 79);
890 printf("Level xxx (file version %06d, game version %06d)\n",
891 level->file_version, level->game_version);
892 printf_line("-", 79);
894 printf("Level Author: '%s'\n", level->author);
895 printf("Level Title: '%s'\n", level->name);
897 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
899 printf("Level Time: %d seconds\n", level->time);
900 printf("Gems needed: %d\n", level->gems_needed);
902 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
903 printf("Time for Wheel: %d seconds\n", level->time_wheel);
904 printf("Time for Light: %d seconds\n", level->time_light);
905 printf("Time for Timegate: %d seconds\n", level->time_timegate);
907 printf("Amoeba Speed: %d\n", level->amoeba_speed);
909 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
910 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
911 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
913 printf_line("-", 79);
917 /* ========================================================================= */
918 /* tape file functions */
919 /* ========================================================================= */
921 static void setTapeInfoToDefaults()
925 /* always start with reliable default values (empty tape) */
928 /* default values (also for pre-1.2 tapes) with only the first player */
929 tape.player_participates[0] = TRUE;
930 for(i=1; i<MAX_PLAYERS; i++)
931 tape.player_participates[i] = FALSE;
933 /* at least one (default: the first) player participates in every tape */
934 tape.num_participating_players = 1;
936 tape.level_nr = level_nr;
938 tape.changed = FALSE;
940 tape.recording = FALSE;
941 tape.playing = FALSE;
942 tape.pausing = FALSE;
945 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
947 tape->file_version = getFileVersion(file);
948 tape->game_version = getFileVersion(file);
953 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
957 tape->random_seed = getFile32BitBE(file);
958 tape->date = getFile32BitBE(file);
959 tape->length = getFile32BitBE(file);
961 /* read header fields that are new since version 1.2 */
962 if (tape->file_version >= FILE_VERSION_1_2)
964 byte store_participating_players = fgetc(file);
967 /* since version 1.2, tapes store which players participate in the tape */
968 tape->num_participating_players = 0;
969 for(i=0; i<MAX_PLAYERS; i++)
971 tape->player_participates[i] = FALSE;
973 if (store_participating_players & (1 << i))
975 tape->player_participates[i] = TRUE;
976 tape->num_participating_players++;
980 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
982 engine_version = getFileVersion(file);
983 if (engine_version > 0)
984 tape->engine_version = engine_version;
990 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
992 int level_identifier_size;
995 level_identifier_size = getFile16BitBE(file);
997 tape->level_identifier =
998 checked_realloc(tape->level_identifier, level_identifier_size);
1000 for(i=0; i < level_identifier_size; i++)
1001 tape->level_identifier[i] = fgetc(file);
1003 tape->level_nr = getFile16BitBE(file);
1005 chunk_size = 2 + level_identifier_size + 2;
1010 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1013 int chunk_size_expected =
1014 (tape->num_participating_players + 1) * tape->length;
1016 if (chunk_size_expected != chunk_size)
1018 ReadUnusedBytesFromFile(file, chunk_size);
1019 return chunk_size_expected;
1022 for(i=0; i<tape->length; i++)
1024 if (i >= MAX_TAPELEN)
1027 for(j=0; j<MAX_PLAYERS; j++)
1029 tape->pos[i].action[j] = MV_NO_MOVING;
1031 if (tape->player_participates[j])
1032 tape->pos[i].action[j] = fgetc(file);
1035 tape->pos[i].delay = fgetc(file);
1037 if (tape->file_version == FILE_VERSION_1_0)
1039 /* eliminate possible diagonal moves in old tapes */
1040 /* this is only for backward compatibility */
1042 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1043 byte action = tape->pos[i].action[0];
1044 int k, num_moves = 0;
1048 if (action & joy_dir[k])
1050 tape->pos[i + num_moves].action[0] = joy_dir[k];
1052 tape->pos[i + num_moves].delay = 0;
1061 tape->length += num_moves;
1064 else if (tape->file_version < FILE_VERSION_2_0)
1066 /* convert pre-2.0 tapes to new tape format */
1068 if (tape->pos[i].delay > 1)
1071 tape->pos[i + 1] = tape->pos[i];
1072 tape->pos[i + 1].delay = 1;
1075 for(j=0; j<MAX_PLAYERS; j++)
1076 tape->pos[i].action[j] = MV_NO_MOVING;
1077 tape->pos[i].delay--;
1088 if (i != tape->length)
1089 chunk_size = (tape->num_participating_players + 1) * i;
1094 void LoadTapeFromFilename(char *filename)
1096 char cookie[MAX_LINE_LEN];
1097 char chunk_name[CHUNK_ID_LEN + 1];
1101 /* always start with reliable default values */
1102 setTapeInfoToDefaults();
1104 if (!(file = fopen(filename, MODE_READ)))
1107 getFileChunkBE(file, chunk_name, NULL);
1108 if (strcmp(chunk_name, "RND1") == 0)
1110 getFile32BitBE(file); /* not used */
1112 getFileChunkBE(file, chunk_name, NULL);
1113 if (strcmp(chunk_name, "TAPE") != 0)
1115 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1120 else /* check for pre-2.0 file format with cookie string */
1122 strcpy(cookie, chunk_name);
1123 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1124 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1125 cookie[strlen(cookie) - 1] = '\0';
1127 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1129 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1134 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1136 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1141 /* pre-2.0 tape files have no game version, so use file version here */
1142 tape.game_version = tape.file_version;
1145 if (tape.file_version < FILE_VERSION_1_2)
1147 /* tape files from versions before 1.2.0 without chunk structure */
1148 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1149 LoadTape_BODY(file, 2 * tape.length, &tape);
1157 int (*loader)(FILE *, int, struct TapeInfo *);
1161 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1162 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1163 { "INFO", -1, LoadTape_INFO },
1164 { "BODY", -1, LoadTape_BODY },
1168 while (getFileChunkBE(file, chunk_name, &chunk_size))
1172 while (chunk_info[i].name != NULL &&
1173 strcmp(chunk_name, chunk_info[i].name) != 0)
1176 if (chunk_info[i].name == NULL)
1178 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1179 chunk_name, filename);
1180 ReadUnusedBytesFromFile(file, chunk_size);
1182 else if (chunk_info[i].size != -1 &&
1183 chunk_info[i].size != chunk_size)
1185 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1186 chunk_size, chunk_name, filename);
1187 ReadUnusedBytesFromFile(file, chunk_size);
1191 /* call function to load this tape chunk */
1192 int chunk_size_expected =
1193 (chunk_info[i].loader)(file, chunk_size, &tape);
1195 /* the size of some chunks cannot be checked before reading other
1196 chunks first (like "HEAD" and "BODY") that contain some header
1197 information, so check them here */
1198 if (chunk_size_expected != chunk_size)
1200 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1201 chunk_size, chunk_name, filename);
1209 tape.length_seconds = GetTapeLength();
1212 void LoadTape(int level_nr)
1214 char *filename = getTapeFilename(level_nr);
1216 LoadTapeFromFilename(filename);
1219 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1221 putFileVersion(file, tape->file_version);
1222 putFileVersion(file, tape->game_version);
1225 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1228 byte store_participating_players = 0;
1230 /* set bits for participating players for compact storage */
1231 for(i=0; i<MAX_PLAYERS; i++)
1232 if (tape->player_participates[i])
1233 store_participating_players |= (1 << i);
1235 putFile32BitBE(file, tape->random_seed);
1236 putFile32BitBE(file, tape->date);
1237 putFile32BitBE(file, tape->length);
1239 fputc(store_participating_players, file);
1241 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1242 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1244 putFileVersion(file, tape->engine_version);
1247 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1249 int level_identifier_size = strlen(tape->level_identifier) + 1;
1252 putFile16BitBE(file, level_identifier_size);
1254 for(i=0; i < level_identifier_size; i++)
1255 fputc(tape->level_identifier[i], file);
1257 putFile16BitBE(file, tape->level_nr);
1260 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1264 for(i=0; i<tape->length; i++)
1266 for(j=0; j<MAX_PLAYERS; j++)
1267 if (tape->player_participates[j])
1268 fputc(tape->pos[i].action[j], file);
1270 fputc(tape->pos[i].delay, file);
1274 void SaveTape(int level_nr)
1276 char *filename = getTapeFilename(level_nr);
1278 boolean new_tape = TRUE;
1279 int num_participating_players = 0;
1280 int info_chunk_size;
1281 int body_chunk_size;
1284 InitTapeDirectory(leveldir_current->filename);
1286 /* if a tape still exists, ask to overwrite it */
1287 if (access(filename, F_OK) == 0)
1290 if (!Request("Replace old tape ?", REQ_ASK))
1294 if (!(file = fopen(filename, MODE_WRITE)))
1296 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1300 tape.file_version = FILE_VERSION_ACTUAL;
1301 tape.game_version = GAME_VERSION_ACTUAL;
1303 /* count number of participating players */
1304 for(i=0; i<MAX_PLAYERS; i++)
1305 if (tape.player_participates[i])
1306 num_participating_players++;
1308 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1309 body_chunk_size = (num_participating_players + 1) * tape.length;
1311 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1312 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1314 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1315 SaveTape_VERS(file, &tape);
1317 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1318 SaveTape_HEAD(file, &tape);
1320 putFileChunkBE(file, "INFO", info_chunk_size);
1321 SaveTape_INFO(file, &tape);
1323 putFileChunkBE(file, "BODY", body_chunk_size);
1324 SaveTape_BODY(file, &tape);
1328 SetFilePermissions(filename, PERMS_PRIVATE);
1330 tape.changed = FALSE;
1333 Request("tape saved !", REQ_CONFIRM);
1336 void DumpTape(struct TapeInfo *tape)
1340 if (TAPE_IS_EMPTY(*tape))
1342 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1346 printf_line("-", 79);
1347 printf("Tape of Level %03d (file version %06d, game version %06d)\n",
1348 tape->level_nr, tape->file_version, tape->game_version);
1349 printf("Level series identifier: '%s'\n", tape->level_identifier);
1350 printf_line("-", 79);
1352 for(i=0; i<tape->length; i++)
1354 if (i >= MAX_TAPELEN)
1357 printf("%03d: ", i);
1359 for(j=0; j<MAX_PLAYERS; j++)
1361 if (tape->player_participates[j])
1363 int action = tape->pos[i].action[j];
1365 printf("%d:%02x ", j, action);
1366 printf("[%c%c%c%c|%c%c] - ",
1367 (action & JOY_LEFT ? '<' : ' '),
1368 (action & JOY_RIGHT ? '>' : ' '),
1369 (action & JOY_UP ? '^' : ' '),
1370 (action & JOY_DOWN ? 'v' : ' '),
1371 (action & JOY_BUTTON_1 ? '1' : ' '),
1372 (action & JOY_BUTTON_2 ? '2' : ' '));
1376 printf("(%03d)\n", tape->pos[i].delay);
1379 printf_line("-", 79);
1383 /* ========================================================================= */
1384 /* score file functions */
1385 /* ========================================================================= */
1387 void LoadScore(int level_nr)
1390 char *filename = getScoreFilename(level_nr);
1391 char cookie[MAX_LINE_LEN];
1392 char line[MAX_LINE_LEN];
1396 /* always start with reliable default values */
1397 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1399 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1400 highscore[i].Score = 0;
1403 if (!(file = fopen(filename, MODE_READ)))
1406 /* check file identifier */
1407 fgets(cookie, MAX_LINE_LEN, file);
1408 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1409 cookie[strlen(cookie) - 1] = '\0';
1411 if (!checkCookieString(cookie, SCORE_COOKIE))
1413 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1418 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1420 fscanf(file, "%d", &highscore[i].Score);
1421 fgets(line, MAX_LINE_LEN, file);
1423 if (line[strlen(line) - 1] == '\n')
1424 line[strlen(line) - 1] = '\0';
1426 for (line_ptr = line; *line_ptr; line_ptr++)
1428 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1430 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1431 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1440 void SaveScore(int level_nr)
1443 char *filename = getScoreFilename(level_nr);
1446 InitScoreDirectory(leveldir_current->filename);
1448 if (!(file = fopen(filename, MODE_WRITE)))
1450 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1454 fprintf(file, "%s\n\n", SCORE_COOKIE);
1456 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1457 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1461 SetFilePermissions(filename, PERMS_PUBLIC);
1465 /* ========================================================================= */
1466 /* setup file functions */
1467 /* ========================================================================= */
1469 #define TOKEN_STR_PLAYER_PREFIX "player_"
1472 #define SETUP_TOKEN_PLAYER_NAME 0
1473 #define SETUP_TOKEN_SOUND 1
1474 #define SETUP_TOKEN_SOUND_LOOPS 2
1475 #define SETUP_TOKEN_SOUND_MUSIC 3
1476 #define SETUP_TOKEN_SOUND_SIMPLE 4
1477 #define SETUP_TOKEN_TOONS 5
1478 #define SETUP_TOKEN_SCROLL_DELAY 6
1479 #define SETUP_TOKEN_SOFT_SCROLLING 7
1480 #define SETUP_TOKEN_FADING 8
1481 #define SETUP_TOKEN_AUTORECORD 9
1482 #define SETUP_TOKEN_QUICK_DOORS 10
1483 #define SETUP_TOKEN_TEAM_MODE 11
1484 #define SETUP_TOKEN_HANDICAP 12
1485 #define SETUP_TOKEN_TIME_LIMIT 13
1486 #define SETUP_TOKEN_FULLSCREEN 14
1487 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1488 #define SETUP_TOKEN_GRAPHICS_SET 16
1489 #define SETUP_TOKEN_SOUNDS_SET 17
1490 #define SETUP_TOKEN_MUSIC_SET 18
1491 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1492 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1493 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1495 #define NUM_GLOBAL_SETUP_TOKENS 22
1498 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1499 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1500 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1501 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1502 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1503 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1504 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1505 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1506 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1508 #define NUM_EDITOR_SETUP_TOKENS 9
1510 /* shortcut setup */
1511 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1512 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1513 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1515 #define NUM_SHORTCUT_SETUP_TOKENS 3
1518 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1519 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1520 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1521 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1522 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1523 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1524 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1525 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1526 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1527 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1528 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1529 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1530 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1531 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1532 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1533 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1535 #define NUM_PLAYER_SETUP_TOKENS 16
1538 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1539 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1541 #define NUM_SYSTEM_SETUP_TOKENS 2
1544 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1546 #define NUM_OPTIONS_SETUP_TOKENS 1
1549 static struct SetupInfo si;
1550 static struct SetupEditorInfo sei;
1551 static struct SetupShortcutInfo ssi;
1552 static struct SetupInputInfo sii;
1553 static struct SetupSystemInfo syi;
1554 static struct OptionInfo soi;
1556 static struct TokenInfo global_setup_tokens[] =
1558 { TYPE_STRING, &si.player_name, "player_name" },
1559 { TYPE_SWITCH, &si.sound, "sound" },
1560 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1561 { TYPE_SWITCH, &si.sound_music, "background_music" },
1562 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1563 { TYPE_SWITCH, &si.toons, "toons" },
1564 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1565 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1566 { TYPE_SWITCH, &si.fading, "screen_fading" },
1567 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1568 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1569 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1570 { TYPE_SWITCH, &si.handicap, "handicap" },
1571 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1572 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1573 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1574 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1575 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1576 { TYPE_STRING, &si.music_set, "music_set" },
1577 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1578 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1579 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1582 static struct TokenInfo editor_setup_tokens[] =
1584 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1585 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1586 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1587 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1588 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1589 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1590 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1591 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1592 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1595 static struct TokenInfo shortcut_setup_tokens[] =
1597 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1598 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1599 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1602 static struct TokenInfo player_setup_tokens[] =
1604 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1605 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1606 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1607 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1608 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1609 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1610 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1611 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1612 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1613 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1614 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1615 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1616 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1617 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1618 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1619 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1622 static struct TokenInfo system_setup_tokens[] =
1624 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1625 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1628 static struct TokenInfo options_setup_tokens[] =
1630 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1633 static char *get_corrected_login_name(char *login_name)
1635 /* needed because player name must be a fixed length string */
1636 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1638 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1639 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1641 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1642 if (strchr(login_name_new, ' '))
1643 *strchr(login_name_new, ' ') = '\0';
1645 return login_name_new;
1648 static void setSetupInfoToDefaults(struct SetupInfo *si)
1652 si->player_name = get_corrected_login_name(getLoginName());
1655 si->sound_loops = TRUE;
1656 si->sound_music = TRUE;
1657 si->sound_simple = TRUE;
1659 si->double_buffering = TRUE;
1660 si->direct_draw = !si->double_buffering;
1661 si->scroll_delay = TRUE;
1662 si->soft_scrolling = TRUE;
1664 si->autorecord = TRUE;
1665 si->quick_doors = FALSE;
1666 si->team_mode = FALSE;
1667 si->handicap = TRUE;
1668 si->time_limit = TRUE;
1669 si->fullscreen = FALSE;
1670 si->ask_on_escape = TRUE;
1672 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1673 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1674 si->music_set = getStringCopy(MUSIC_SUBDIR);
1675 si->override_level_graphics = FALSE;
1676 si->override_level_sounds = FALSE;
1677 si->override_level_music = FALSE;
1679 si->editor.el_boulderdash = TRUE;
1680 si->editor.el_emerald_mine = TRUE;
1681 si->editor.el_more = TRUE;
1682 si->editor.el_sokoban = TRUE;
1683 si->editor.el_supaplex = TRUE;
1684 si->editor.el_diamond_caves = TRUE;
1685 si->editor.el_dx_boulderdash = TRUE;
1686 si->editor.el_chars = TRUE;
1687 si->editor.el_custom = TRUE;
1689 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1690 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1691 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1693 for (i=0; i<MAX_PLAYERS; i++)
1695 si->input[i].use_joystick = FALSE;
1696 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1697 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1698 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1699 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1700 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1701 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1702 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1703 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1704 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1705 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1706 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1707 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1708 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1709 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1710 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1713 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1714 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1716 si->options.verbose = FALSE;
1719 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
1723 if (!setup_file_hash)
1728 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1729 setSetupInfo(global_setup_tokens, i,
1730 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
1735 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1736 setSetupInfo(editor_setup_tokens, i,
1737 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
1740 /* shortcut setup */
1741 ssi = setup.shortcut;
1742 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1743 setSetupInfo(shortcut_setup_tokens, i,
1744 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
1745 setup.shortcut = ssi;
1748 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1752 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1754 sii = setup.input[pnr];
1755 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1757 char full_token[100];
1759 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1760 setSetupInfo(player_setup_tokens, i,
1761 getHashEntry(setup_file_hash, full_token));
1763 setup.input[pnr] = sii;
1768 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1769 setSetupInfo(system_setup_tokens, i,
1770 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
1774 soi = setup.options;
1775 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1776 setSetupInfo(options_setup_tokens, i,
1777 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
1778 setup.options = soi;
1783 char *filename = getSetupFilename();
1784 SetupFileHash *setup_file_hash = NULL;
1786 /* always start with reliable default values */
1787 setSetupInfoToDefaults(&setup);
1789 setup_file_hash = loadSetupFileHash(filename);
1791 if (setup_file_hash)
1793 char *player_name_new;
1795 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
1796 decodeSetupFileHash(setup_file_hash);
1798 setup.direct_draw = !setup.double_buffering;
1800 freeSetupFileHash(setup_file_hash);
1802 /* needed to work around problems with fixed length strings */
1803 player_name_new = get_corrected_login_name(setup.player_name);
1804 free(setup.player_name);
1805 setup.player_name = player_name_new;
1808 Error(ERR_WARN, "using default setup values");
1813 char *filename = getSetupFilename();
1817 InitUserDataDirectory();
1819 if (!(file = fopen(filename, MODE_WRITE)))
1821 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1825 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1826 getCookie("SETUP")));
1827 fprintf(file, "\n");
1831 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1833 /* just to make things nicer :) */
1834 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1835 i == SETUP_TOKEN_GRAPHICS_SET)
1836 fprintf(file, "\n");
1838 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1843 fprintf(file, "\n");
1844 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1845 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1847 /* shortcut setup */
1848 ssi = setup.shortcut;
1849 fprintf(file, "\n");
1850 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1851 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1854 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1858 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1859 fprintf(file, "\n");
1861 sii = setup.input[pnr];
1862 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1863 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1868 fprintf(file, "\n");
1869 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1870 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1873 soi = setup.options;
1874 fprintf(file, "\n");
1875 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1876 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1880 SetFilePermissions(filename, PERMS_PRIVATE);
1883 void LoadCustomElementDescriptions()
1885 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1886 SetupFileHash *setup_file_hash;
1889 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1891 if (element_info[i].custom_description != NULL)
1893 free(element_info[i].custom_description);
1894 element_info[i].custom_description = NULL;
1898 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1901 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1903 char *token = getStringCat2(element_info[i].token_name, ".name");
1904 char *value = getHashEntry(setup_file_hash, token);
1907 element_info[i].custom_description = getStringCopy(value);
1912 freeSetupFileHash(setup_file_hash);
1915 void LoadSpecialMenuDesignSettings()
1917 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1918 SetupFileHash *setup_file_hash;
1921 /* always start with reliable default values from default config */
1922 for (i=0; image_config_vars[i].token != NULL; i++)
1923 for (j=0; image_config[j].token != NULL; j++)
1924 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
1925 *image_config_vars[i].value =
1926 get_integer_from_string(image_config[j].value);
1928 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1931 /* special case: initialize with default values that may be overwrittem */
1932 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
1934 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
1935 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
1937 if (value_x != NULL)
1938 menu.draw_xoffset[i] = get_integer_from_string(value_x);
1939 if (value_y != NULL)
1940 menu.draw_yoffset[i] = get_integer_from_string(value_y);
1943 /* read (and overwrite with) values that may be specified in config file */
1944 for (i=0; image_config_vars[i].token != NULL; i++)
1946 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
1949 *image_config_vars[i].value = get_integer_from_string(value);
1952 freeSetupFileHash(setup_file_hash);