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_gfx_element = FALSE;
103 element_info[element].gfx_element = EL_EMPTY_SPACE;
105 element_info[element].push_delay_fixed = 2; /* special default */
106 element_info[element].push_delay_random = 8; /* special default */
107 element_info[element].move_delay_fixed = 0;
108 element_info[element].move_delay_random = 0;
110 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
111 element_info[element].move_direction_initial = MV_NO_MOVING;
112 element_info[element].move_stepsize = TILEX / 8;
116 element_info[element].content[x][y] = EL_EMPTY_SPACE;
118 element_info[element].change.events = CE_BITMASK_DEFAULT;
119 element_info[element].change.delay_fixed = 0;
120 element_info[element].change.delay_random = 0;
121 element_info[element].change.successor = EL_EMPTY_SPACE;
123 /* start with no properties at all */
124 for (j=0; j < NUM_EP_BITFIELDS; j++)
125 Properties[element][j] = EP_BITMASK_DEFAULT;
128 BorderElement = EL_STEELWALL;
130 level.no_level_file = FALSE;
132 if (leveldir_current == NULL) /* only when dumping level */
135 /* try to determine better author name than 'anonymous' */
136 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
138 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
139 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
143 switch (LEVELCLASS(leveldir_current))
145 case LEVELCLASS_TUTORIAL:
146 strcpy(level.author, PROGRAM_AUTHOR_STRING);
149 case LEVELCLASS_CONTRIBUTION:
150 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
151 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
154 case LEVELCLASS_USER:
155 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
156 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
160 /* keep default value */
166 static int checkLevelElement(int element)
168 if (element >= NUM_FILE_ELEMENTS)
170 Error(ERR_WARN, "invalid level element %d", element);
171 element = EL_CHAR_QUESTION;
173 else if (element == EL_PLAYER_OBSOLETE)
174 element = EL_PLAYER_1;
175 else if (element == EL_KEY_OBSOLETE)
181 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
183 level->file_version = getFileVersion(file);
184 level->game_version = getFileVersion(file);
189 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
193 lev_fieldx = level->fieldx = fgetc(file);
194 lev_fieldy = level->fieldy = fgetc(file);
196 level->time = getFile16BitBE(file);
197 level->gems_needed = getFile16BitBE(file);
199 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
200 level->name[i] = fgetc(file);
201 level->name[MAX_LEVEL_NAME_LEN] = 0;
203 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
204 level->score[i] = fgetc(file);
206 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
207 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
210 level->yamyam_content[i][x][y] = checkLevelElement(fgetc(file));
212 level->amoeba_speed = fgetc(file);
213 level->time_magic_wall = fgetc(file);
214 level->time_wheel = fgetc(file);
215 level->amoeba_content = checkLevelElement(fgetc(file));
216 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
217 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
218 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
219 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
221 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
226 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
230 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
231 level->author[i] = fgetc(file);
232 level->author[MAX_LEVEL_NAME_LEN] = 0;
237 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
240 int chunk_size_expected = level->fieldx * level->fieldy;
242 /* Note: "chunk_size" was wrong before version 2.0 when elements are
243 stored with 16-bit encoding (and should be twice as big then).
244 Even worse, playfield data was stored 16-bit when only yamyam content
245 contained 16-bit elements and vice versa. */
247 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
248 chunk_size_expected *= 2;
250 if (chunk_size_expected != chunk_size)
252 ReadUnusedBytesFromFile(file, chunk_size);
253 return chunk_size_expected;
256 for(y=0; y<level->fieldy; y++)
257 for(x=0; x<level->fieldx; x++)
258 Feld[x][y] = Ur[x][y] =
259 checkLevelElement(level->encoding_16bit_field ?
260 getFile16BitBE(file) : fgetc(file));
264 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
268 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
269 int chunk_size_expected = header_size + content_size;
271 /* Note: "chunk_size" was wrong before version 2.0 when elements are
272 stored with 16-bit encoding (and should be twice as big then).
273 Even worse, playfield data was stored 16-bit when only yamyam content
274 contained 16-bit elements and vice versa. */
276 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
277 chunk_size_expected += content_size;
279 if (chunk_size_expected != chunk_size)
281 ReadUnusedBytesFromFile(file, chunk_size);
282 return chunk_size_expected;
286 level->num_yamyam_contents = fgetc(file);
290 /* correct invalid number of content fields -- should never happen */
291 if (level->num_yamyam_contents < 1 ||
292 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
293 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
295 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
298 level->yamyam_content[i][x][y] =
299 checkLevelElement(level->encoding_16bit_field ?
300 getFile16BitBE(file) : fgetc(file));
304 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
308 int num_contents, content_xsize, content_ysize;
309 int content_array[MAX_ELEMENT_CONTENTS][3][3];
311 element = checkLevelElement(getFile16BitBE(file));
312 num_contents = fgetc(file);
313 content_xsize = fgetc(file);
314 content_ysize = fgetc(file);
315 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
317 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
320 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
322 /* correct invalid number of content fields -- should never happen */
323 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
324 num_contents = STD_ELEMENT_CONTENTS;
326 if (element == EL_YAMYAM)
328 level->num_yamyam_contents = num_contents;
330 for(i=0; i<num_contents; i++)
333 level->yamyam_content[i][x][y] = content_array[i][x][y];
335 else if (element == EL_BD_AMOEBA)
337 level->amoeba_content = content_array[0][0][0];
341 Error(ERR_WARN, "cannot load content for element '%d'", element);
347 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
349 int num_changed_custom_elements = getFile16BitBE(file);
350 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
353 if (chunk_size_expected != chunk_size)
355 ReadUnusedBytesFromFile(file, chunk_size - 2);
356 return chunk_size_expected;
359 for (i=0; i < num_changed_custom_elements; i++)
361 int element = getFile16BitBE(file);
362 int properties = getFile32BitBE(file);
364 if (IS_CUSTOM_ELEMENT(element))
365 Properties[element][EP_BITFIELD_BASE] = properties;
367 Error(ERR_WARN, "invalid custom element number %d", element);
373 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
375 int num_changed_custom_elements = getFile16BitBE(file);
376 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
379 if (chunk_size_expected != chunk_size)
381 ReadUnusedBytesFromFile(file, chunk_size - 2);
382 return chunk_size_expected;
385 for (i=0; i < num_changed_custom_elements; i++)
387 int element = getFile16BitBE(file);
388 int custom_element_successor = getFile16BitBE(file);
390 if (IS_CUSTOM_ELEMENT(element))
391 element_info[element].change.successor = custom_element_successor;
393 Error(ERR_WARN, "invalid custom element number %d", element);
399 void LoadLevelFromFilename(char *filename)
401 char cookie[MAX_LINE_LEN];
402 char chunk_name[CHUNK_ID_LEN + 1];
406 /* always start with reliable default values */
407 setLevelInfoToDefaults();
409 if (!(file = fopen(filename, MODE_READ)))
411 level.no_level_file = TRUE;
413 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
417 getFileChunkBE(file, chunk_name, NULL);
418 if (strcmp(chunk_name, "RND1") == 0)
420 getFile32BitBE(file); /* not used */
422 getFileChunkBE(file, chunk_name, NULL);
423 if (strcmp(chunk_name, "CAVE") != 0)
425 Error(ERR_WARN, "unknown format of level file '%s'", filename);
430 else /* check for pre-2.0 file format with cookie string */
432 strcpy(cookie, chunk_name);
433 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
434 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
435 cookie[strlen(cookie) - 1] = '\0';
437 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
439 Error(ERR_WARN, "unknown format of level file '%s'", filename);
444 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
446 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
451 /* pre-2.0 level files have no game version, so use file version here */
452 level.game_version = level.file_version;
455 if (level.file_version < FILE_VERSION_1_2)
457 /* level files from versions before 1.2.0 without chunk structure */
458 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
459 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
467 int (*loader)(FILE *, int, struct LevelInfo *);
471 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
472 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
473 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
474 { "BODY", -1, LoadLevel_BODY },
475 { "CONT", -1, LoadLevel_CONT },
476 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
477 { "CUS1", -1, LoadLevel_CUS1 },
478 { "CUS2", -1, LoadLevel_CUS2 },
482 while (getFileChunkBE(file, chunk_name, &chunk_size))
486 while (chunk_info[i].name != NULL &&
487 strcmp(chunk_name, chunk_info[i].name) != 0)
490 if (chunk_info[i].name == NULL)
492 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
493 chunk_name, filename);
494 ReadUnusedBytesFromFile(file, chunk_size);
496 else if (chunk_info[i].size != -1 &&
497 chunk_info[i].size != chunk_size)
499 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
500 chunk_size, chunk_name, filename);
501 ReadUnusedBytesFromFile(file, chunk_size);
505 /* call function to load this level chunk */
506 int chunk_size_expected =
507 (chunk_info[i].loader)(file, chunk_size, &level);
509 /* the size of some chunks cannot be checked before reading other
510 chunks first (like "HEAD" and "BODY") that contain some header
511 information, so check them here */
512 if (chunk_size_expected != chunk_size)
514 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
515 chunk_size, chunk_name, filename);
523 if (leveldir_current == NULL) /* only when dumping level */
526 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
527 IS_LEVELCLASS_USER(leveldir_current))
529 /* For user contributed and private levels, use the version of
530 the game engine the levels were created for.
531 Since 2.0.1, the game engine version is now directly stored
532 in the level file (chunk "VERS"), so there is no need anymore
533 to set the game version from the file version (except for old,
534 pre-2.0 levels, where the game version is still taken from the
535 file format version used to store the level -- see above). */
537 /* do some special adjustments to support older level versions */
538 if (level.file_version == FILE_VERSION_1_0)
540 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
541 Error(ERR_WARN, "using high speed movement for player");
543 /* player was faster than monsters in (pre-)1.0 levels */
544 level.double_speed = TRUE;
547 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
548 if (level.game_version == VERSION_IDENT(2,0,1))
549 level.em_slippery_gems = TRUE;
553 /* Always use the latest version of the game engine for all but
554 user contributed and private levels; this allows for actual
555 corrections in the game engine to take effect for existing,
556 converted levels (from "classic" or other existing games) to
557 make the game emulation more accurate, while (hopefully) not
558 breaking existing levels created from other players. */
560 level.game_version = GAME_VERSION_ACTUAL;
562 /* Set special EM style gems behaviour: EM style gems slip down from
563 normal, steel and growing wall. As this is a more fundamental change,
564 it seems better to set the default behaviour to "off" (as it is more
565 natural) and make it configurable in the level editor (as a property
566 of gem style elements). Already existing converted levels (neither
567 private nor contributed levels) are changed to the new behaviour. */
569 if (level.file_version < FILE_VERSION_2_0)
570 level.em_slippery_gems = TRUE;
573 /* map some elements which have changed in newer versions */
574 if (level.game_version <= VERSION_IDENT(2,2,0))
578 /* map game font elements */
579 for(y=0; y<level.fieldy; y++)
581 for(x=0; x<level.fieldx; x++)
583 int element = Ur[x][y];
585 if (element == EL_CHAR('['))
586 element = EL_CHAR_AUMLAUT;
587 else if (element == EL_CHAR('\\'))
588 element = EL_CHAR_OUMLAUT;
589 else if (element == EL_CHAR(']'))
590 element = EL_CHAR_UUMLAUT;
591 else if (element == EL_CHAR('^'))
592 element = EL_CHAR_COPYRIGHT;
594 Feld[x][y] = Ur[x][y] = element;
599 /* determine border element for this level */
603 void LoadLevel(int level_nr)
605 char *filename = getLevelFilename(level_nr);
607 LoadLevelFromFilename(filename);
608 InitElementPropertiesEngine(level.game_version);
611 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
613 putFileVersion(file, level->file_version);
614 putFileVersion(file, level->game_version);
617 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
621 fputc(level->fieldx, file);
622 fputc(level->fieldy, file);
624 putFile16BitBE(file, level->time);
625 putFile16BitBE(file, level->gems_needed);
627 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
628 fputc(level->name[i], file);
630 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
631 fputc(level->score[i], file);
633 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
636 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
637 level->yamyam_content[i][x][y]),
639 fputc(level->amoeba_speed, file);
640 fputc(level->time_magic_wall, file);
641 fputc(level->time_wheel, file);
642 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
644 fputc((level->double_speed ? 1 : 0), file);
645 fputc((level->gravity ? 1 : 0), file);
646 fputc((level->encoding_16bit_field ? 1 : 0), file);
647 fputc((level->em_slippery_gems ? 1 : 0), file);
649 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
652 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
656 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
657 fputc(level->author[i], file);
660 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
664 for(y=0; y<level->fieldy; y++)
665 for(x=0; x<level->fieldx; x++)
666 if (level->encoding_16bit_field)
667 putFile16BitBE(file, Ur[x][y]);
669 fputc(Ur[x][y], file);
673 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
677 fputc(EL_YAMYAM, file);
678 fputc(level->num_yamyam_contents, file);
682 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
685 if (level->encoding_16bit_field)
686 putFile16BitBE(file, level->yamyam_content[i][x][y]);
688 fputc(level->yamyam_content[i][x][y], file);
692 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
695 int num_contents, content_xsize, content_ysize;
696 int content_array[MAX_ELEMENT_CONTENTS][3][3];
698 if (element == EL_YAMYAM)
700 num_contents = level->num_yamyam_contents;
704 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
707 content_array[i][x][y] = level->yamyam_content[i][x][y];
709 else if (element == EL_BD_AMOEBA)
715 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
718 content_array[i][x][y] = EL_EMPTY;
719 content_array[0][0][0] = level->amoeba_content;
723 /* chunk header already written -- write empty chunk data */
724 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
726 Error(ERR_WARN, "cannot save content for element '%d'", element);
730 putFile16BitBE(file, element);
731 fputc(num_contents, file);
732 fputc(content_xsize, file);
733 fputc(content_ysize, file);
735 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
737 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
740 putFile16BitBE(file, content_array[i][x][y]);
743 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
744 int num_changed_custom_elements)
748 putFile16BitBE(file, num_changed_custom_elements);
750 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
752 int element = EL_CUSTOM_START + i;
754 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
756 if (check < num_changed_custom_elements)
758 putFile16BitBE(file, element);
759 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
766 if (check != num_changed_custom_elements) /* should not happen */
767 Error(ERR_WARN, "inconsistent number of custom element properties");
770 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
771 int num_changed_custom_elements)
775 putFile16BitBE(file, num_changed_custom_elements);
777 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
779 int element = EL_CUSTOM_START + i;
781 if (element_info[element].change.successor != EL_EMPTY_SPACE)
783 if (check < num_changed_custom_elements)
785 putFile16BitBE(file, element);
786 putFile16BitBE(file, element_info[element].change.successor);
793 if (check != num_changed_custom_elements) /* should not happen */
794 Error(ERR_WARN, "inconsistent number of custom element successors");
797 void SaveLevel(int level_nr)
799 char *filename = getLevelFilename(level_nr);
801 int num_changed_custom_elements1 = 0;
802 int num_changed_custom_elements2 = 0;
806 if (!(file = fopen(filename, MODE_WRITE)))
808 Error(ERR_WARN, "cannot save level file '%s'", filename);
812 level.file_version = FILE_VERSION_ACTUAL;
813 level.game_version = GAME_VERSION_ACTUAL;
815 /* check level field for 16-bit elements */
816 level.encoding_16bit_field = FALSE;
817 for(y=0; y<level.fieldy; y++)
818 for(x=0; x<level.fieldx; x++)
820 level.encoding_16bit_field = TRUE;
822 /* check yamyam content for 16-bit elements */
823 level.encoding_16bit_yamyam = FALSE;
824 for(i=0; i<level.num_yamyam_contents; i++)
827 if (level.yamyam_content[i][x][y] > 255)
828 level.encoding_16bit_yamyam = TRUE;
830 /* check amoeba content for 16-bit elements */
831 level.encoding_16bit_amoeba = FALSE;
832 if (level.amoeba_content > 255)
833 level.encoding_16bit_amoeba = TRUE;
835 /* calculate size of "BODY" chunk */
837 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
839 /* check for non-standard custom elements and calculate "CUS1" chunk size */
840 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
841 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
842 num_changed_custom_elements1++;
844 /* check for non-standard custom elements and calculate "CUS2" chunk size */
845 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
846 if (element_info[EL_CUSTOM_START + i].change.successor != EL_EMPTY_SPACE)
847 num_changed_custom_elements2++;
849 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
850 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
852 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
853 SaveLevel_VERS(file, &level);
855 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
856 SaveLevel_HEAD(file, &level);
858 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
859 SaveLevel_AUTH(file, &level);
861 putFileChunkBE(file, "BODY", body_chunk_size);
862 SaveLevel_BODY(file, &level);
864 if (level.encoding_16bit_yamyam ||
865 level.num_yamyam_contents != STD_ELEMENT_CONTENTS)
867 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
868 SaveLevel_CNT2(file, &level, EL_YAMYAM);
871 if (level.encoding_16bit_amoeba)
873 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
874 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
877 if (num_changed_custom_elements1 > 0)
879 putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements1 * 6);
880 SaveLevel_CUS1(file, &level, num_changed_custom_elements1);
883 if (num_changed_custom_elements2 > 0)
885 putFileChunkBE(file, "CUS2", 2 + num_changed_custom_elements2 * 4);
886 SaveLevel_CUS2(file, &level, num_changed_custom_elements2);
891 SetFilePermissions(filename, PERMS_PRIVATE);
894 void DumpLevel(struct LevelInfo *level)
896 printf_line("-", 79);
897 printf("Level xxx (file version %08d, game version %08d)\n",
898 level->file_version, level->game_version);
899 printf_line("-", 79);
901 printf("Level Author: '%s'\n", level->author);
902 printf("Level Title: '%s'\n", level->name);
904 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
906 printf("Level Time: %d seconds\n", level->time);
907 printf("Gems needed: %d\n", level->gems_needed);
909 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
910 printf("Time for Wheel: %d seconds\n", level->time_wheel);
911 printf("Time for Light: %d seconds\n", level->time_light);
912 printf("Time for Timegate: %d seconds\n", level->time_timegate);
914 printf("Amoeba Speed: %d\n", level->amoeba_speed);
916 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
917 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
918 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
920 printf_line("-", 79);
924 /* ========================================================================= */
925 /* tape file functions */
926 /* ========================================================================= */
928 static void setTapeInfoToDefaults()
932 /* always start with reliable default values (empty tape) */
935 /* default values (also for pre-1.2 tapes) with only the first player */
936 tape.player_participates[0] = TRUE;
937 for(i=1; i<MAX_PLAYERS; i++)
938 tape.player_participates[i] = FALSE;
940 /* at least one (default: the first) player participates in every tape */
941 tape.num_participating_players = 1;
943 tape.level_nr = level_nr;
945 tape.changed = FALSE;
947 tape.recording = FALSE;
948 tape.playing = FALSE;
949 tape.pausing = FALSE;
952 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
954 tape->file_version = getFileVersion(file);
955 tape->game_version = getFileVersion(file);
960 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
964 tape->random_seed = getFile32BitBE(file);
965 tape->date = getFile32BitBE(file);
966 tape->length = getFile32BitBE(file);
968 /* read header fields that are new since version 1.2 */
969 if (tape->file_version >= FILE_VERSION_1_2)
971 byte store_participating_players = fgetc(file);
974 /* since version 1.2, tapes store which players participate in the tape */
975 tape->num_participating_players = 0;
976 for(i=0; i<MAX_PLAYERS; i++)
978 tape->player_participates[i] = FALSE;
980 if (store_participating_players & (1 << i))
982 tape->player_participates[i] = TRUE;
983 tape->num_participating_players++;
987 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
989 engine_version = getFileVersion(file);
990 if (engine_version > 0)
991 tape->engine_version = engine_version;
997 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
999 int level_identifier_size;
1002 level_identifier_size = getFile16BitBE(file);
1004 tape->level_identifier =
1005 checked_realloc(tape->level_identifier, level_identifier_size);
1007 for(i=0; i < level_identifier_size; i++)
1008 tape->level_identifier[i] = fgetc(file);
1010 tape->level_nr = getFile16BitBE(file);
1012 chunk_size = 2 + level_identifier_size + 2;
1017 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1020 int chunk_size_expected =
1021 (tape->num_participating_players + 1) * tape->length;
1023 if (chunk_size_expected != chunk_size)
1025 ReadUnusedBytesFromFile(file, chunk_size);
1026 return chunk_size_expected;
1029 for(i=0; i<tape->length; i++)
1031 if (i >= MAX_TAPELEN)
1034 for(j=0; j<MAX_PLAYERS; j++)
1036 tape->pos[i].action[j] = MV_NO_MOVING;
1038 if (tape->player_participates[j])
1039 tape->pos[i].action[j] = fgetc(file);
1042 tape->pos[i].delay = fgetc(file);
1044 if (tape->file_version == FILE_VERSION_1_0)
1046 /* eliminate possible diagonal moves in old tapes */
1047 /* this is only for backward compatibility */
1049 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1050 byte action = tape->pos[i].action[0];
1051 int k, num_moves = 0;
1055 if (action & joy_dir[k])
1057 tape->pos[i + num_moves].action[0] = joy_dir[k];
1059 tape->pos[i + num_moves].delay = 0;
1068 tape->length += num_moves;
1071 else if (tape->file_version < FILE_VERSION_2_0)
1073 /* convert pre-2.0 tapes to new tape format */
1075 if (tape->pos[i].delay > 1)
1078 tape->pos[i + 1] = tape->pos[i];
1079 tape->pos[i + 1].delay = 1;
1082 for(j=0; j<MAX_PLAYERS; j++)
1083 tape->pos[i].action[j] = MV_NO_MOVING;
1084 tape->pos[i].delay--;
1095 if (i != tape->length)
1096 chunk_size = (tape->num_participating_players + 1) * i;
1101 void LoadTapeFromFilename(char *filename)
1103 char cookie[MAX_LINE_LEN];
1104 char chunk_name[CHUNK_ID_LEN + 1];
1108 /* always start with reliable default values */
1109 setTapeInfoToDefaults();
1111 if (!(file = fopen(filename, MODE_READ)))
1114 getFileChunkBE(file, chunk_name, NULL);
1115 if (strcmp(chunk_name, "RND1") == 0)
1117 getFile32BitBE(file); /* not used */
1119 getFileChunkBE(file, chunk_name, NULL);
1120 if (strcmp(chunk_name, "TAPE") != 0)
1122 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1127 else /* check for pre-2.0 file format with cookie string */
1129 strcpy(cookie, chunk_name);
1130 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1131 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1132 cookie[strlen(cookie) - 1] = '\0';
1134 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1136 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1141 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1143 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1148 /* pre-2.0 tape files have no game version, so use file version here */
1149 tape.game_version = tape.file_version;
1152 if (tape.file_version < FILE_VERSION_1_2)
1154 /* tape files from versions before 1.2.0 without chunk structure */
1155 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1156 LoadTape_BODY(file, 2 * tape.length, &tape);
1164 int (*loader)(FILE *, int, struct TapeInfo *);
1168 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1169 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1170 { "INFO", -1, LoadTape_INFO },
1171 { "BODY", -1, LoadTape_BODY },
1175 while (getFileChunkBE(file, chunk_name, &chunk_size))
1179 while (chunk_info[i].name != NULL &&
1180 strcmp(chunk_name, chunk_info[i].name) != 0)
1183 if (chunk_info[i].name == NULL)
1185 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1186 chunk_name, filename);
1187 ReadUnusedBytesFromFile(file, chunk_size);
1189 else if (chunk_info[i].size != -1 &&
1190 chunk_info[i].size != chunk_size)
1192 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1193 chunk_size, chunk_name, filename);
1194 ReadUnusedBytesFromFile(file, chunk_size);
1198 /* call function to load this tape chunk */
1199 int chunk_size_expected =
1200 (chunk_info[i].loader)(file, chunk_size, &tape);
1202 /* the size of some chunks cannot be checked before reading other
1203 chunks first (like "HEAD" and "BODY") that contain some header
1204 information, so check them here */
1205 if (chunk_size_expected != chunk_size)
1207 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1208 chunk_size, chunk_name, filename);
1216 tape.length_seconds = GetTapeLength();
1219 printf("tape version: %d\n", tape.game_version);
1223 void LoadTape(int level_nr)
1225 char *filename = getTapeFilename(level_nr);
1227 LoadTapeFromFilename(filename);
1230 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1232 putFileVersion(file, tape->file_version);
1233 putFileVersion(file, tape->game_version);
1236 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1239 byte store_participating_players = 0;
1241 /* set bits for participating players for compact storage */
1242 for(i=0; i<MAX_PLAYERS; i++)
1243 if (tape->player_participates[i])
1244 store_participating_players |= (1 << i);
1246 putFile32BitBE(file, tape->random_seed);
1247 putFile32BitBE(file, tape->date);
1248 putFile32BitBE(file, tape->length);
1250 fputc(store_participating_players, file);
1252 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1253 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1255 putFileVersion(file, tape->engine_version);
1258 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1260 int level_identifier_size = strlen(tape->level_identifier) + 1;
1263 putFile16BitBE(file, level_identifier_size);
1265 for(i=0; i < level_identifier_size; i++)
1266 fputc(tape->level_identifier[i], file);
1268 putFile16BitBE(file, tape->level_nr);
1271 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1275 for(i=0; i<tape->length; i++)
1277 for(j=0; j<MAX_PLAYERS; j++)
1278 if (tape->player_participates[j])
1279 fputc(tape->pos[i].action[j], file);
1281 fputc(tape->pos[i].delay, file);
1285 void SaveTape(int level_nr)
1287 char *filename = getTapeFilename(level_nr);
1289 boolean new_tape = TRUE;
1290 int num_participating_players = 0;
1291 int info_chunk_size;
1292 int body_chunk_size;
1295 InitTapeDirectory(leveldir_current->filename);
1297 /* if a tape still exists, ask to overwrite it */
1298 if (access(filename, F_OK) == 0)
1301 if (!Request("Replace old tape ?", REQ_ASK))
1305 if (!(file = fopen(filename, MODE_WRITE)))
1307 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1311 tape.file_version = FILE_VERSION_ACTUAL;
1312 tape.game_version = GAME_VERSION_ACTUAL;
1314 /* count number of participating players */
1315 for(i=0; i<MAX_PLAYERS; i++)
1316 if (tape.player_participates[i])
1317 num_participating_players++;
1319 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1320 body_chunk_size = (num_participating_players + 1) * tape.length;
1322 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1323 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1325 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1326 SaveTape_VERS(file, &tape);
1328 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1329 SaveTape_HEAD(file, &tape);
1331 putFileChunkBE(file, "INFO", info_chunk_size);
1332 SaveTape_INFO(file, &tape);
1334 putFileChunkBE(file, "BODY", body_chunk_size);
1335 SaveTape_BODY(file, &tape);
1339 SetFilePermissions(filename, PERMS_PRIVATE);
1341 tape.changed = FALSE;
1344 Request("tape saved !", REQ_CONFIRM);
1347 void DumpTape(struct TapeInfo *tape)
1351 if (TAPE_IS_EMPTY(*tape))
1353 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1357 printf_line("-", 79);
1358 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1359 tape->level_nr, tape->file_version, tape->game_version);
1360 printf("Level series identifier: '%s'\n", tape->level_identifier);
1361 printf_line("-", 79);
1363 for(i=0; i<tape->length; i++)
1365 if (i >= MAX_TAPELEN)
1368 printf("%03d: ", i);
1370 for(j=0; j<MAX_PLAYERS; j++)
1372 if (tape->player_participates[j])
1374 int action = tape->pos[i].action[j];
1376 printf("%d:%02x ", j, action);
1377 printf("[%c%c%c%c|%c%c] - ",
1378 (action & JOY_LEFT ? '<' : ' '),
1379 (action & JOY_RIGHT ? '>' : ' '),
1380 (action & JOY_UP ? '^' : ' '),
1381 (action & JOY_DOWN ? 'v' : ' '),
1382 (action & JOY_BUTTON_1 ? '1' : ' '),
1383 (action & JOY_BUTTON_2 ? '2' : ' '));
1387 printf("(%03d)\n", tape->pos[i].delay);
1390 printf_line("-", 79);
1394 /* ========================================================================= */
1395 /* score file functions */
1396 /* ========================================================================= */
1398 void LoadScore(int level_nr)
1401 char *filename = getScoreFilename(level_nr);
1402 char cookie[MAX_LINE_LEN];
1403 char line[MAX_LINE_LEN];
1407 /* always start with reliable default values */
1408 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1410 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1411 highscore[i].Score = 0;
1414 if (!(file = fopen(filename, MODE_READ)))
1417 /* check file identifier */
1418 fgets(cookie, MAX_LINE_LEN, file);
1419 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1420 cookie[strlen(cookie) - 1] = '\0';
1422 if (!checkCookieString(cookie, SCORE_COOKIE))
1424 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1429 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1431 fscanf(file, "%d", &highscore[i].Score);
1432 fgets(line, MAX_LINE_LEN, file);
1434 if (line[strlen(line) - 1] == '\n')
1435 line[strlen(line) - 1] = '\0';
1437 for (line_ptr = line; *line_ptr; line_ptr++)
1439 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1441 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1442 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1451 void SaveScore(int level_nr)
1454 char *filename = getScoreFilename(level_nr);
1457 InitScoreDirectory(leveldir_current->filename);
1459 if (!(file = fopen(filename, MODE_WRITE)))
1461 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1465 fprintf(file, "%s\n\n", SCORE_COOKIE);
1467 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1468 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1472 SetFilePermissions(filename, PERMS_PUBLIC);
1476 /* ========================================================================= */
1477 /* setup file functions */
1478 /* ========================================================================= */
1480 #define TOKEN_STR_PLAYER_PREFIX "player_"
1483 #define SETUP_TOKEN_PLAYER_NAME 0
1484 #define SETUP_TOKEN_SOUND 1
1485 #define SETUP_TOKEN_SOUND_LOOPS 2
1486 #define SETUP_TOKEN_SOUND_MUSIC 3
1487 #define SETUP_TOKEN_SOUND_SIMPLE 4
1488 #define SETUP_TOKEN_TOONS 5
1489 #define SETUP_TOKEN_SCROLL_DELAY 6
1490 #define SETUP_TOKEN_SOFT_SCROLLING 7
1491 #define SETUP_TOKEN_FADING 8
1492 #define SETUP_TOKEN_AUTORECORD 9
1493 #define SETUP_TOKEN_QUICK_DOORS 10
1494 #define SETUP_TOKEN_TEAM_MODE 11
1495 #define SETUP_TOKEN_HANDICAP 12
1496 #define SETUP_TOKEN_TIME_LIMIT 13
1497 #define SETUP_TOKEN_FULLSCREEN 14
1498 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1499 #define SETUP_TOKEN_GRAPHICS_SET 16
1500 #define SETUP_TOKEN_SOUNDS_SET 17
1501 #define SETUP_TOKEN_MUSIC_SET 18
1502 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1503 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1504 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1506 #define NUM_GLOBAL_SETUP_TOKENS 22
1509 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1510 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1511 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1512 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1513 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1514 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1515 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1516 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1517 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1519 #define NUM_EDITOR_SETUP_TOKENS 9
1521 /* shortcut setup */
1522 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1523 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1524 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1526 #define NUM_SHORTCUT_SETUP_TOKENS 3
1529 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1530 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1531 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1532 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1533 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1534 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1535 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1536 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1537 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1538 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1539 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1540 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1541 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1542 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1543 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1544 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1546 #define NUM_PLAYER_SETUP_TOKENS 16
1549 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1550 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1552 #define NUM_SYSTEM_SETUP_TOKENS 2
1555 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1557 #define NUM_OPTIONS_SETUP_TOKENS 1
1560 static struct SetupInfo si;
1561 static struct SetupEditorInfo sei;
1562 static struct SetupShortcutInfo ssi;
1563 static struct SetupInputInfo sii;
1564 static struct SetupSystemInfo syi;
1565 static struct OptionInfo soi;
1567 static struct TokenInfo global_setup_tokens[] =
1569 { TYPE_STRING, &si.player_name, "player_name" },
1570 { TYPE_SWITCH, &si.sound, "sound" },
1571 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1572 { TYPE_SWITCH, &si.sound_music, "background_music" },
1573 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1574 { TYPE_SWITCH, &si.toons, "toons" },
1575 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1576 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1577 { TYPE_SWITCH, &si.fading, "screen_fading" },
1578 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1579 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1580 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1581 { TYPE_SWITCH, &si.handicap, "handicap" },
1582 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1583 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1584 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1585 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1586 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1587 { TYPE_STRING, &si.music_set, "music_set" },
1588 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1589 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1590 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1593 static struct TokenInfo editor_setup_tokens[] =
1595 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1596 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1597 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1598 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1599 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1600 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1601 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1602 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1603 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1606 static struct TokenInfo shortcut_setup_tokens[] =
1608 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1609 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1610 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1613 static struct TokenInfo player_setup_tokens[] =
1615 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1616 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1617 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1618 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1619 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1620 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1621 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1622 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1623 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1624 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1625 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1626 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1627 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1628 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1629 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1630 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1633 static struct TokenInfo system_setup_tokens[] =
1635 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1636 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1639 static struct TokenInfo options_setup_tokens[] =
1641 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1644 static char *get_corrected_login_name(char *login_name)
1646 /* needed because player name must be a fixed length string */
1647 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1649 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1650 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1652 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1653 if (strchr(login_name_new, ' '))
1654 *strchr(login_name_new, ' ') = '\0';
1656 return login_name_new;
1659 static void setSetupInfoToDefaults(struct SetupInfo *si)
1663 si->player_name = get_corrected_login_name(getLoginName());
1666 si->sound_loops = TRUE;
1667 si->sound_music = TRUE;
1668 si->sound_simple = TRUE;
1670 si->double_buffering = TRUE;
1671 si->direct_draw = !si->double_buffering;
1672 si->scroll_delay = TRUE;
1673 si->soft_scrolling = TRUE;
1675 si->autorecord = TRUE;
1676 si->quick_doors = FALSE;
1677 si->team_mode = FALSE;
1678 si->handicap = TRUE;
1679 si->time_limit = TRUE;
1680 si->fullscreen = FALSE;
1681 si->ask_on_escape = TRUE;
1683 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1684 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1685 si->music_set = getStringCopy(MUSIC_SUBDIR);
1686 si->override_level_graphics = FALSE;
1687 si->override_level_sounds = FALSE;
1688 si->override_level_music = FALSE;
1690 si->editor.el_boulderdash = TRUE;
1691 si->editor.el_emerald_mine = TRUE;
1692 si->editor.el_more = TRUE;
1693 si->editor.el_sokoban = TRUE;
1694 si->editor.el_supaplex = TRUE;
1695 si->editor.el_diamond_caves = TRUE;
1696 si->editor.el_dx_boulderdash = TRUE;
1697 si->editor.el_chars = TRUE;
1698 si->editor.el_custom = TRUE;
1700 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1701 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1702 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1704 for (i=0; i<MAX_PLAYERS; i++)
1706 si->input[i].use_joystick = FALSE;
1707 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1708 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1709 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1710 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1711 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1712 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1713 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1714 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1715 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1716 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1717 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1718 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1719 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1720 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1721 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1724 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1725 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1727 si->options.verbose = FALSE;
1730 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
1734 if (!setup_file_hash)
1739 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1740 setSetupInfo(global_setup_tokens, i,
1741 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
1746 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1747 setSetupInfo(editor_setup_tokens, i,
1748 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
1751 /* shortcut setup */
1752 ssi = setup.shortcut;
1753 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1754 setSetupInfo(shortcut_setup_tokens, i,
1755 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
1756 setup.shortcut = ssi;
1759 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1763 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1765 sii = setup.input[pnr];
1766 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1768 char full_token[100];
1770 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1771 setSetupInfo(player_setup_tokens, i,
1772 getHashEntry(setup_file_hash, full_token));
1774 setup.input[pnr] = sii;
1779 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1780 setSetupInfo(system_setup_tokens, i,
1781 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
1785 soi = setup.options;
1786 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1787 setSetupInfo(options_setup_tokens, i,
1788 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
1789 setup.options = soi;
1794 char *filename = getSetupFilename();
1795 SetupFileHash *setup_file_hash = NULL;
1797 /* always start with reliable default values */
1798 setSetupInfoToDefaults(&setup);
1800 setup_file_hash = loadSetupFileHash(filename);
1802 if (setup_file_hash)
1804 char *player_name_new;
1806 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
1807 decodeSetupFileHash(setup_file_hash);
1809 setup.direct_draw = !setup.double_buffering;
1811 freeSetupFileHash(setup_file_hash);
1813 /* needed to work around problems with fixed length strings */
1814 player_name_new = get_corrected_login_name(setup.player_name);
1815 free(setup.player_name);
1816 setup.player_name = player_name_new;
1819 Error(ERR_WARN, "using default setup values");
1824 char *filename = getSetupFilename();
1828 InitUserDataDirectory();
1830 if (!(file = fopen(filename, MODE_WRITE)))
1832 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1836 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1837 getCookie("SETUP")));
1838 fprintf(file, "\n");
1842 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1844 /* just to make things nicer :) */
1845 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1846 i == SETUP_TOKEN_GRAPHICS_SET)
1847 fprintf(file, "\n");
1849 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1854 fprintf(file, "\n");
1855 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1856 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1858 /* shortcut setup */
1859 ssi = setup.shortcut;
1860 fprintf(file, "\n");
1861 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1862 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1865 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1869 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1870 fprintf(file, "\n");
1872 sii = setup.input[pnr];
1873 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1874 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1879 fprintf(file, "\n");
1880 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1881 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1884 soi = setup.options;
1885 fprintf(file, "\n");
1886 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1887 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1891 SetFilePermissions(filename, PERMS_PRIVATE);
1894 void LoadCustomElementDescriptions()
1896 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1897 SetupFileHash *setup_file_hash;
1900 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1902 if (element_info[i].custom_description != NULL)
1904 free(element_info[i].custom_description);
1905 element_info[i].custom_description = NULL;
1909 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1912 for (i=0; i<NUM_FILE_ELEMENTS; i++)
1914 char *token = getStringCat2(element_info[i].token_name, ".name");
1915 char *value = getHashEntry(setup_file_hash, token);
1918 element_info[i].custom_description = getStringCopy(value);
1923 freeSetupFileHash(setup_file_hash);
1926 void LoadSpecialMenuDesignSettings()
1928 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1929 SetupFileHash *setup_file_hash;
1932 /* always start with reliable default values from default config */
1933 for (i=0; image_config_vars[i].token != NULL; i++)
1934 for (j=0; image_config[j].token != NULL; j++)
1935 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
1936 *image_config_vars[i].value =
1937 get_integer_from_string(image_config[j].value);
1939 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
1942 /* special case: initialize with default values that may be overwritten */
1943 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
1945 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
1946 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
1947 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
1949 if (value_x != NULL)
1950 menu.draw_xoffset[i] = get_integer_from_string(value_x);
1951 if (value_y != NULL)
1952 menu.draw_yoffset[i] = get_integer_from_string(value_y);
1953 if (list_size != NULL)
1954 menu.list_size[i] = get_integer_from_string(list_size);
1957 /* read (and overwrite with) values that may be specified in config file */
1958 for (i=0; image_config_vars[i].token != NULL; i++)
1960 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
1963 *image_config_vars[i].value = get_integer_from_string(value);
1966 freeSetupFileHash(setup_file_hash);