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 13 /* 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 LEVEL_CPART_CUS3_SIZE 102 /* size of CUS3 chunk part */
34 #define LEVEL_CPART_CUS3_UNUSED 16 /* unused CUS3 bytes / part */
35 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
36 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
38 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + x * LEVEL_CPART_CUS3_SIZE)
40 /* file identifier strings */
41 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
42 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
43 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
46 /* ========================================================================= */
47 /* level file functions */
48 /* ========================================================================= */
50 static void setLevelInfoToDefaults()
54 level.file_version = FILE_VERSION_ACTUAL;
55 level.game_version = GAME_VERSION_ACTUAL;
57 level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
58 level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
59 level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
61 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
62 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
64 for(x=0; x<MAX_LEV_FIELDX; x++)
65 for(y=0; y<MAX_LEV_FIELDY; y++)
66 Feld[x][y] = Ur[x][y] = EL_SAND;
69 level.gems_needed = 0;
70 level.amoeba_speed = 10;
71 level.time_magic_wall = 10;
72 level.time_wheel = 10;
73 level.time_light = 10;
74 level.time_timegate = 10;
75 level.amoeba_content = EL_DIAMOND;
76 level.double_speed = FALSE;
77 level.gravity = FALSE;
78 level.em_slippery_gems = FALSE;
80 level.use_custom_template = FALSE;
82 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
84 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
85 level.author[i] = '\0';
87 strcpy(level.name, NAMELESS_LEVEL_NAME);
88 strcpy(level.author, ANONYMOUS_NAME);
90 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
93 level.num_yamyam_contents = STD_ELEMENT_CONTENTS;
94 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
97 level.yamyam_content[i][x][y] =
98 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
100 Feld[0][0] = Ur[0][0] = EL_PLAYER_1;
101 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
102 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
104 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
106 int element = EL_CUSTOM_START + i;
108 element_info[element].use_gfx_element = FALSE;
109 element_info[element].gfx_element = EL_EMPTY_SPACE;
111 element_info[element].score = 0;
112 element_info[element].gem_count = 0;
114 element_info[element].push_delay_fixed = 2; /* special default */
115 element_info[element].push_delay_random = 8; /* special default */
116 element_info[element].move_delay_fixed = 0;
117 element_info[element].move_delay_random = 0;
119 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
120 element_info[element].move_direction_initial = MV_NO_MOVING;
121 element_info[element].move_stepsize = TILEX / 8;
125 element_info[element].content[x][y] = EL_EMPTY_SPACE;
127 element_info[element].change.events = CE_BITMASK_DEFAULT;
128 element_info[element].change.target_element = EL_EMPTY_SPACE;
130 element_info[element].change.delay_fixed = 0;
131 element_info[element].change.delay_random = 0;
132 element_info[element].change.delay_frames = -1; /* use default */
134 element_info[element].change.trigger_element = EL_EMPTY_SPACE;
136 element_info[element].change.explode = FALSE;
137 element_info[element].change.use_content = FALSE;
138 element_info[element].change.only_complete = FALSE;
139 element_info[element].change.use_random_change = FALSE;
140 element_info[element].change.random = 0;
141 element_info[element].change.power = CP_NON_DESTRUCTIVE;
145 element_info[element].change.content[x][y] = EL_EMPTY_SPACE;
147 /* start with no properties at all */
148 for (j=0; j < NUM_EP_BITFIELDS; j++)
149 Properties[element][j] = EP_BITMASK_DEFAULT;
152 BorderElement = EL_STEELWALL;
154 level.no_level_file = FALSE;
156 if (leveldir_current == NULL) /* only when dumping level */
159 /* try to determine better author name than 'anonymous' */
160 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
162 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
163 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
167 switch (LEVELCLASS(leveldir_current))
169 case LEVELCLASS_TUTORIAL:
170 strcpy(level.author, PROGRAM_AUTHOR_STRING);
173 case LEVELCLASS_CONTRIBUTION:
174 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
175 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
178 case LEVELCLASS_USER:
179 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
180 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
184 /* keep default value */
190 static int checkLevelElement(int element)
192 if (element >= NUM_FILE_ELEMENTS)
194 Error(ERR_WARN, "invalid level element %d", element);
195 element = EL_CHAR_QUESTION;
197 else if (element == EL_PLAYER_OBSOLETE)
198 element = EL_PLAYER_1;
199 else if (element == EL_KEY_OBSOLETE)
205 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
207 level->file_version = getFileVersion(file);
208 level->game_version = getFileVersion(file);
213 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
217 lev_fieldx = level->fieldx = fgetc(file);
218 lev_fieldy = level->fieldy = fgetc(file);
220 level->time = getFile16BitBE(file);
221 level->gems_needed = getFile16BitBE(file);
223 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
224 level->name[i] = fgetc(file);
225 level->name[MAX_LEVEL_NAME_LEN] = 0;
227 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
228 level->score[i] = fgetc(file);
230 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
231 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
234 level->yamyam_content[i][x][y] = checkLevelElement(fgetc(file));
236 level->amoeba_speed = fgetc(file);
237 level->time_magic_wall = fgetc(file);
238 level->time_wheel = fgetc(file);
239 level->amoeba_content = checkLevelElement(fgetc(file));
240 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
241 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
242 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
243 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
245 level->use_custom_template = (fgetc(file) == 1 ? TRUE : FALSE);
247 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
252 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
256 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
257 level->author[i] = fgetc(file);
258 level->author[MAX_LEVEL_NAME_LEN] = 0;
263 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
266 int chunk_size_expected = level->fieldx * level->fieldy;
268 /* Note: "chunk_size" was wrong before version 2.0 when elements are
269 stored with 16-bit encoding (and should be twice as big then).
270 Even worse, playfield data was stored 16-bit when only yamyam content
271 contained 16-bit elements and vice versa. */
273 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
274 chunk_size_expected *= 2;
276 if (chunk_size_expected != chunk_size)
278 ReadUnusedBytesFromFile(file, chunk_size);
279 return chunk_size_expected;
282 for(y=0; y<level->fieldy; y++)
283 for(x=0; x<level->fieldx; x++)
284 Feld[x][y] = Ur[x][y] =
285 checkLevelElement(level->encoding_16bit_field ?
286 getFile16BitBE(file) : fgetc(file));
290 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
294 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
295 int chunk_size_expected = header_size + content_size;
297 /* Note: "chunk_size" was wrong before version 2.0 when elements are
298 stored with 16-bit encoding (and should be twice as big then).
299 Even worse, playfield data was stored 16-bit when only yamyam content
300 contained 16-bit elements and vice versa. */
302 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
303 chunk_size_expected += content_size;
305 if (chunk_size_expected != chunk_size)
307 ReadUnusedBytesFromFile(file, chunk_size);
308 return chunk_size_expected;
312 level->num_yamyam_contents = fgetc(file);
316 /* correct invalid number of content fields -- should never happen */
317 if (level->num_yamyam_contents < 1 ||
318 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
319 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
321 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
324 level->yamyam_content[i][x][y] =
325 checkLevelElement(level->encoding_16bit_field ?
326 getFile16BitBE(file) : fgetc(file));
330 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
334 int num_contents, content_xsize, content_ysize;
335 int content_array[MAX_ELEMENT_CONTENTS][3][3];
337 element = checkLevelElement(getFile16BitBE(file));
338 num_contents = fgetc(file);
339 content_xsize = fgetc(file);
340 content_ysize = fgetc(file);
341 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
343 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
346 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
348 /* correct invalid number of content fields -- should never happen */
349 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
350 num_contents = STD_ELEMENT_CONTENTS;
352 if (element == EL_YAMYAM)
354 level->num_yamyam_contents = num_contents;
356 for(i=0; i<num_contents; i++)
359 level->yamyam_content[i][x][y] = content_array[i][x][y];
361 else if (element == EL_BD_AMOEBA)
363 level->amoeba_content = content_array[0][0][0];
367 Error(ERR_WARN, "cannot load content for element '%d'", element);
373 static int LoadLevel_CUS1(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 * 6;
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 properties = getFile32BitBE(file);
390 if (IS_CUSTOM_ELEMENT(element))
391 Properties[element][EP_BITFIELD_BASE] = properties;
393 Error(ERR_WARN, "invalid custom element number %d", element);
399 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
401 int num_changed_custom_elements = getFile16BitBE(file);
402 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
405 if (chunk_size_expected != chunk_size)
407 ReadUnusedBytesFromFile(file, chunk_size - 2);
408 return chunk_size_expected;
411 for (i=0; i < num_changed_custom_elements; i++)
413 int element = getFile16BitBE(file);
414 int custom_target_element = getFile16BitBE(file);
416 if (IS_CUSTOM_ELEMENT(element))
417 element_info[element].change.target_element = custom_target_element;
419 Error(ERR_WARN, "invalid custom element number %d", element);
425 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
427 int num_changed_custom_elements = getFile16BitBE(file);
428 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
431 if (chunk_size_expected != chunk_size)
433 ReadUnusedBytesFromFile(file, chunk_size - 2);
434 return chunk_size_expected;
437 for (i=0; i < num_changed_custom_elements; i++)
439 int element = getFile16BitBE(file);
441 if (!IS_CUSTOM_ELEMENT(element))
443 Error(ERR_WARN, "invalid custom element number %d", element);
445 element = EL_DEFAULT; /* dummy element used for artwork config */
448 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
450 /* some free bytes for future properties and padding */
451 ReadUnusedBytesFromFile(file, 7);
453 element_info[element].use_gfx_element = getFile8Bit(file);
454 element_info[element].gfx_element =
455 checkLevelElement(getFile16BitBE(file));
457 element_info[element].score = getFile8Bit(file);
458 element_info[element].gem_count = getFile8Bit(file);
460 element_info[element].push_delay_fixed = getFile16BitBE(file);
461 element_info[element].push_delay_random = getFile16BitBE(file);
462 element_info[element].move_delay_fixed = getFile16BitBE(file);
463 element_info[element].move_delay_random = getFile16BitBE(file);
465 element_info[element].move_pattern = getFile16BitBE(file);
466 element_info[element].move_direction_initial = getFile8Bit(file);
467 element_info[element].move_stepsize = getFile8Bit(file);
471 element_info[element].content[x][y] =
472 checkLevelElement(getFile16BitBE(file));
474 element_info[element].change.events = getFile32BitBE(file);
476 element_info[element].change.target_element =
477 checkLevelElement(getFile16BitBE(file));
479 element_info[element].change.delay_fixed = getFile16BitBE(file);
480 element_info[element].change.delay_random = getFile16BitBE(file);
481 element_info[element].change.delay_frames = getFile16BitBE(file);
483 element_info[element].change.trigger_element =
484 checkLevelElement(getFile16BitBE(file));
486 element_info[element].change.explode = getFile8Bit(file);
487 element_info[element].change.use_content = getFile8Bit(file);
488 element_info[element].change.only_complete = getFile8Bit(file);
489 element_info[element].change.use_random_change = getFile8Bit(file);
491 element_info[element].change.random = getFile8Bit(file);
492 element_info[element].change.power = getFile8Bit(file);
496 element_info[element].change.content[x][y] =
497 checkLevelElement(getFile16BitBE(file));
499 /* some free bytes for future properties and padding */
500 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
506 void LoadLevelFromFilename(char *filename)
508 char cookie[MAX_LINE_LEN];
509 char chunk_name[CHUNK_ID_LEN + 1];
513 /* always start with reliable default values */
514 setLevelInfoToDefaults();
516 if (!(file = fopen(filename, MODE_READ)))
518 level.no_level_file = TRUE;
520 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
524 getFileChunkBE(file, chunk_name, NULL);
525 if (strcmp(chunk_name, "RND1") == 0)
527 getFile32BitBE(file); /* not used */
529 getFileChunkBE(file, chunk_name, NULL);
530 if (strcmp(chunk_name, "CAVE") != 0)
532 Error(ERR_WARN, "unknown format of level file '%s'", filename);
537 else /* check for pre-2.0 file format with cookie string */
539 strcpy(cookie, chunk_name);
540 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
541 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
542 cookie[strlen(cookie) - 1] = '\0';
544 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
546 Error(ERR_WARN, "unknown format of level file '%s'", filename);
551 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
553 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
558 /* pre-2.0 level files have no game version, so use file version here */
559 level.game_version = level.file_version;
562 if (level.file_version < FILE_VERSION_1_2)
564 /* level files from versions before 1.2.0 without chunk structure */
565 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
566 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
574 int (*loader)(FILE *, int, struct LevelInfo *);
578 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
579 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
580 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
581 { "BODY", -1, LoadLevel_BODY },
582 { "CONT", -1, LoadLevel_CONT },
583 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
584 { "CUS1", -1, LoadLevel_CUS1 },
585 { "CUS2", -1, LoadLevel_CUS2 },
586 { "CUS3", -1, LoadLevel_CUS3 },
590 while (getFileChunkBE(file, chunk_name, &chunk_size))
594 while (chunk_info[i].name != NULL &&
595 strcmp(chunk_name, chunk_info[i].name) != 0)
598 if (chunk_info[i].name == NULL)
600 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
601 chunk_name, filename);
602 ReadUnusedBytesFromFile(file, chunk_size);
604 else if (chunk_info[i].size != -1 &&
605 chunk_info[i].size != chunk_size)
607 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
608 chunk_size, chunk_name, filename);
609 ReadUnusedBytesFromFile(file, chunk_size);
613 /* call function to load this level chunk */
614 int chunk_size_expected =
615 (chunk_info[i].loader)(file, chunk_size, &level);
617 /* the size of some chunks cannot be checked before reading other
618 chunks first (like "HEAD" and "BODY") that contain some header
619 information, so check them here */
620 if (chunk_size_expected != chunk_size)
622 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
623 chunk_size, chunk_name, filename);
631 if (leveldir_current == NULL) /* only when dumping level */
634 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
635 IS_LEVELCLASS_USER(leveldir_current))
637 /* For user contributed and private levels, use the version of
638 the game engine the levels were created for.
639 Since 2.0.1, the game engine version is now directly stored
640 in the level file (chunk "VERS"), so there is no need anymore
641 to set the game version from the file version (except for old,
642 pre-2.0 levels, where the game version is still taken from the
643 file format version used to store the level -- see above). */
645 /* do some special adjustments to support older level versions */
646 if (level.file_version == FILE_VERSION_1_0)
648 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
649 Error(ERR_WARN, "using high speed movement for player");
651 /* player was faster than monsters in (pre-)1.0 levels */
652 level.double_speed = TRUE;
655 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
656 if (level.game_version == VERSION_IDENT(2,0,1))
657 level.em_slippery_gems = TRUE;
661 /* Always use the latest version of the game engine for all but
662 user contributed and private levels; this allows for actual
663 corrections in the game engine to take effect for existing,
664 converted levels (from "classic" or other existing games) to
665 make the game emulation more accurate, while (hopefully) not
666 breaking existing levels created from other players. */
668 level.game_version = GAME_VERSION_ACTUAL;
670 /* Set special EM style gems behaviour: EM style gems slip down from
671 normal, steel and growing wall. As this is a more fundamental change,
672 it seems better to set the default behaviour to "off" (as it is more
673 natural) and make it configurable in the level editor (as a property
674 of gem style elements). Already existing converted levels (neither
675 private nor contributed levels) are changed to the new behaviour. */
677 if (level.file_version < FILE_VERSION_2_0)
678 level.em_slippery_gems = TRUE;
681 /* map some elements which have changed in newer versions */
682 if (level.game_version <= VERSION_IDENT(2,2,0))
686 /* map game font elements */
687 for(y=0; y<level.fieldy; y++)
689 for(x=0; x<level.fieldx; x++)
691 int element = Ur[x][y];
693 if (element == EL_CHAR('['))
694 element = EL_CHAR_AUMLAUT;
695 else if (element == EL_CHAR('\\'))
696 element = EL_CHAR_OUMLAUT;
697 else if (element == EL_CHAR(']'))
698 element = EL_CHAR_UUMLAUT;
699 else if (element == EL_CHAR('^'))
700 element = EL_CHAR_COPYRIGHT;
702 Feld[x][y] = Ur[x][y] = element;
707 /* determine border element for this level */
711 void LoadLevel(int level_nr)
713 char *filename = getLevelFilename(level_nr);
715 LoadLevelFromFilename(filename);
716 InitElementPropertiesEngine(level.game_version);
719 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
721 putFileVersion(file, level->file_version);
722 putFileVersion(file, level->game_version);
725 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
729 fputc(level->fieldx, file);
730 fputc(level->fieldy, file);
732 putFile16BitBE(file, level->time);
733 putFile16BitBE(file, level->gems_needed);
735 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
736 fputc(level->name[i], file);
738 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
739 fputc(level->score[i], file);
741 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
744 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
745 level->yamyam_content[i][x][y]),
747 fputc(level->amoeba_speed, file);
748 fputc(level->time_magic_wall, file);
749 fputc(level->time_wheel, file);
750 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
752 fputc((level->double_speed ? 1 : 0), file);
753 fputc((level->gravity ? 1 : 0), file);
754 fputc((level->encoding_16bit_field ? 1 : 0), file);
755 fputc((level->em_slippery_gems ? 1 : 0), file);
757 fputc((level->use_custom_template ? 1 : 0), file);
759 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
762 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
766 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
767 fputc(level->author[i], file);
770 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
774 for(y=0; y<level->fieldy; y++)
775 for(x=0; x<level->fieldx; x++)
776 if (level->encoding_16bit_field)
777 putFile16BitBE(file, Ur[x][y]);
779 fputc(Ur[x][y], file);
783 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
787 fputc(EL_YAMYAM, file);
788 fputc(level->num_yamyam_contents, file);
792 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
795 if (level->encoding_16bit_field)
796 putFile16BitBE(file, level->yamyam_content[i][x][y]);
798 fputc(level->yamyam_content[i][x][y], file);
802 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
805 int num_contents, content_xsize, content_ysize;
806 int content_array[MAX_ELEMENT_CONTENTS][3][3];
808 if (element == EL_YAMYAM)
810 num_contents = level->num_yamyam_contents;
814 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
817 content_array[i][x][y] = level->yamyam_content[i][x][y];
819 else if (element == EL_BD_AMOEBA)
825 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
828 content_array[i][x][y] = EL_EMPTY;
829 content_array[0][0][0] = level->amoeba_content;
833 /* chunk header already written -- write empty chunk data */
834 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
836 Error(ERR_WARN, "cannot save content for element '%d'", element);
840 putFile16BitBE(file, element);
841 fputc(num_contents, file);
842 fputc(content_xsize, file);
843 fputc(content_ysize, file);
845 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
847 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
850 putFile16BitBE(file, content_array[i][x][y]);
854 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
855 int num_changed_custom_elements)
859 putFile16BitBE(file, num_changed_custom_elements);
861 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
863 int element = EL_CUSTOM_START + i;
865 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
867 if (check < num_changed_custom_elements)
869 putFile16BitBE(file, element);
870 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
877 if (check != num_changed_custom_elements) /* should not happen */
878 Error(ERR_WARN, "inconsistent number of custom element properties");
883 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
884 int num_changed_custom_elements)
888 putFile16BitBE(file, num_changed_custom_elements);
890 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
892 int element = EL_CUSTOM_START + i;
894 if (element_info[element].change.target_element != EL_EMPTY_SPACE)
896 if (check < num_changed_custom_elements)
898 putFile16BitBE(file, element);
899 putFile16BitBE(file, element_info[element].change.target_element);
906 if (check != num_changed_custom_elements) /* should not happen */
907 Error(ERR_WARN, "inconsistent number of custom target elements");
911 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
912 int num_changed_custom_elements)
914 int i, x, y, check = 0;
916 putFile16BitBE(file, num_changed_custom_elements);
918 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
920 int element = EL_CUSTOM_START + i;
922 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
924 if (check < num_changed_custom_elements)
926 putFile16BitBE(file, element);
927 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
929 /* some free bytes for future properties and padding */
930 WriteUnusedBytesToFile(file, 7);
932 putFile8Bit(file, element_info[element].use_gfx_element);
933 putFile16BitBE(file, element_info[element].gfx_element);
935 putFile8Bit(file, element_info[element].score);
936 putFile8Bit(file, element_info[element].gem_count);
938 putFile16BitBE(file, element_info[element].push_delay_fixed);
939 putFile16BitBE(file, element_info[element].push_delay_random);
940 putFile16BitBE(file, element_info[element].move_delay_fixed);
941 putFile16BitBE(file, element_info[element].move_delay_random);
943 putFile16BitBE(file, element_info[element].move_pattern);
944 putFile8Bit(file, element_info[element].move_direction_initial);
945 putFile8Bit(file, element_info[element].move_stepsize);
949 putFile16BitBE(file, element_info[element].content[x][y]);
951 putFile32BitBE(file, element_info[element].change.events);
953 putFile16BitBE(file, element_info[element].change.target_element);
955 putFile16BitBE(file, element_info[element].change.delay_fixed);
956 putFile16BitBE(file, element_info[element].change.delay_random);
957 putFile16BitBE(file, element_info[element].change.delay_frames);
959 putFile16BitBE(file, element_info[element].change.trigger_element);
961 putFile8Bit(file, element_info[element].change.explode);
962 putFile8Bit(file, element_info[element].change.use_content);
963 putFile8Bit(file, element_info[element].change.only_complete);
964 putFile8Bit(file, element_info[element].change.use_random_change);
966 putFile8Bit(file, element_info[element].change.random);
967 putFile8Bit(file, element_info[element].change.power);
971 putFile16BitBE(file, element_info[element].change.content[x][y]);
973 /* some free bytes for future properties and padding */
974 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
981 if (check != num_changed_custom_elements) /* should not happen */
982 Error(ERR_WARN, "inconsistent number of custom element properties");
985 void SaveLevel(int level_nr)
987 char *filename = getLevelFilename(level_nr);
989 int num_changed_custom_elements = 0;
990 int level_chunk_CUS3_size;
994 if (!(file = fopen(filename, MODE_WRITE)))
996 Error(ERR_WARN, "cannot save level file '%s'", filename);
1000 level.file_version = FILE_VERSION_ACTUAL;
1001 level.game_version = GAME_VERSION_ACTUAL;
1003 /* check level field for 16-bit elements */
1004 level.encoding_16bit_field = FALSE;
1005 for(y=0; y<level.fieldy; y++)
1006 for(x=0; x<level.fieldx; x++)
1008 level.encoding_16bit_field = TRUE;
1010 /* check yamyam content for 16-bit elements */
1011 level.encoding_16bit_yamyam = FALSE;
1012 for(i=0; i<level.num_yamyam_contents; i++)
1015 if (level.yamyam_content[i][x][y] > 255)
1016 level.encoding_16bit_yamyam = TRUE;
1018 /* check amoeba content for 16-bit elements */
1019 level.encoding_16bit_amoeba = FALSE;
1020 if (level.amoeba_content > 255)
1021 level.encoding_16bit_amoeba = TRUE;
1023 /* calculate size of "BODY" chunk */
1025 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
1027 /* check for non-standard custom elements and calculate "CUS3" chunk size */
1028 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1029 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1030 num_changed_custom_elements++;
1031 level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1033 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1034 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1036 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1037 SaveLevel_VERS(file, &level);
1039 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1040 SaveLevel_HEAD(file, &level);
1042 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1043 SaveLevel_AUTH(file, &level);
1045 putFileChunkBE(file, "BODY", body_chunk_size);
1046 SaveLevel_BODY(file, &level);
1048 if (level.encoding_16bit_yamyam ||
1049 level.num_yamyam_contents != STD_ELEMENT_CONTENTS)
1051 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1052 SaveLevel_CNT2(file, &level, EL_YAMYAM);
1055 if (level.encoding_16bit_amoeba)
1057 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1058 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
1061 if (num_changed_custom_elements > 0)
1063 putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1064 SaveLevel_CUS3(file, &level, num_changed_custom_elements);
1069 SetFilePermissions(filename, PERMS_PRIVATE);
1072 void DumpLevel(struct LevelInfo *level)
1074 printf_line("-", 79);
1075 printf("Level xxx (file version %08d, game version %08d)\n",
1076 level->file_version, level->game_version);
1077 printf_line("-", 79);
1079 printf("Level Author: '%s'\n", level->author);
1080 printf("Level Title: '%s'\n", level->name);
1082 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1084 printf("Level Time: %d seconds\n", level->time);
1085 printf("Gems needed: %d\n", level->gems_needed);
1087 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1088 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1089 printf("Time for Light: %d seconds\n", level->time_light);
1090 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1092 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1094 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1095 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1096 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1098 printf_line("-", 79);
1102 /* ========================================================================= */
1103 /* tape file functions */
1104 /* ========================================================================= */
1106 static void setTapeInfoToDefaults()
1110 /* always start with reliable default values (empty tape) */
1113 /* default values (also for pre-1.2 tapes) with only the first player */
1114 tape.player_participates[0] = TRUE;
1115 for(i=1; i<MAX_PLAYERS; i++)
1116 tape.player_participates[i] = FALSE;
1118 /* at least one (default: the first) player participates in every tape */
1119 tape.num_participating_players = 1;
1121 tape.level_nr = level_nr;
1123 tape.changed = FALSE;
1125 tape.recording = FALSE;
1126 tape.playing = FALSE;
1127 tape.pausing = FALSE;
1130 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1132 tape->file_version = getFileVersion(file);
1133 tape->game_version = getFileVersion(file);
1138 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1142 tape->random_seed = getFile32BitBE(file);
1143 tape->date = getFile32BitBE(file);
1144 tape->length = getFile32BitBE(file);
1146 /* read header fields that are new since version 1.2 */
1147 if (tape->file_version >= FILE_VERSION_1_2)
1149 byte store_participating_players = fgetc(file);
1152 /* since version 1.2, tapes store which players participate in the tape */
1153 tape->num_participating_players = 0;
1154 for(i=0; i<MAX_PLAYERS; i++)
1156 tape->player_participates[i] = FALSE;
1158 if (store_participating_players & (1 << i))
1160 tape->player_participates[i] = TRUE;
1161 tape->num_participating_players++;
1165 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1167 engine_version = getFileVersion(file);
1168 if (engine_version > 0)
1169 tape->engine_version = engine_version;
1175 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1177 int level_identifier_size;
1180 level_identifier_size = getFile16BitBE(file);
1182 tape->level_identifier =
1183 checked_realloc(tape->level_identifier, level_identifier_size);
1185 for(i=0; i < level_identifier_size; i++)
1186 tape->level_identifier[i] = fgetc(file);
1188 tape->level_nr = getFile16BitBE(file);
1190 chunk_size = 2 + level_identifier_size + 2;
1195 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1198 int chunk_size_expected =
1199 (tape->num_participating_players + 1) * tape->length;
1201 if (chunk_size_expected != chunk_size)
1203 ReadUnusedBytesFromFile(file, chunk_size);
1204 return chunk_size_expected;
1207 for(i=0; i<tape->length; i++)
1209 if (i >= MAX_TAPELEN)
1212 for(j=0; j<MAX_PLAYERS; j++)
1214 tape->pos[i].action[j] = MV_NO_MOVING;
1216 if (tape->player_participates[j])
1217 tape->pos[i].action[j] = fgetc(file);
1220 tape->pos[i].delay = fgetc(file);
1222 if (tape->file_version == FILE_VERSION_1_0)
1224 /* eliminate possible diagonal moves in old tapes */
1225 /* this is only for backward compatibility */
1227 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1228 byte action = tape->pos[i].action[0];
1229 int k, num_moves = 0;
1233 if (action & joy_dir[k])
1235 tape->pos[i + num_moves].action[0] = joy_dir[k];
1237 tape->pos[i + num_moves].delay = 0;
1246 tape->length += num_moves;
1249 else if (tape->file_version < FILE_VERSION_2_0)
1251 /* convert pre-2.0 tapes to new tape format */
1253 if (tape->pos[i].delay > 1)
1256 tape->pos[i + 1] = tape->pos[i];
1257 tape->pos[i + 1].delay = 1;
1260 for(j=0; j<MAX_PLAYERS; j++)
1261 tape->pos[i].action[j] = MV_NO_MOVING;
1262 tape->pos[i].delay--;
1273 if (i != tape->length)
1274 chunk_size = (tape->num_participating_players + 1) * i;
1279 void LoadTapeFromFilename(char *filename)
1281 char cookie[MAX_LINE_LEN];
1282 char chunk_name[CHUNK_ID_LEN + 1];
1286 /* always start with reliable default values */
1287 setTapeInfoToDefaults();
1289 if (!(file = fopen(filename, MODE_READ)))
1292 getFileChunkBE(file, chunk_name, NULL);
1293 if (strcmp(chunk_name, "RND1") == 0)
1295 getFile32BitBE(file); /* not used */
1297 getFileChunkBE(file, chunk_name, NULL);
1298 if (strcmp(chunk_name, "TAPE") != 0)
1300 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1305 else /* check for pre-2.0 file format with cookie string */
1307 strcpy(cookie, chunk_name);
1308 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1309 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1310 cookie[strlen(cookie) - 1] = '\0';
1312 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1314 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1319 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1321 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1326 /* pre-2.0 tape files have no game version, so use file version here */
1327 tape.game_version = tape.file_version;
1330 if (tape.file_version < FILE_VERSION_1_2)
1332 /* tape files from versions before 1.2.0 without chunk structure */
1333 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1334 LoadTape_BODY(file, 2 * tape.length, &tape);
1342 int (*loader)(FILE *, int, struct TapeInfo *);
1346 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1347 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1348 { "INFO", -1, LoadTape_INFO },
1349 { "BODY", -1, LoadTape_BODY },
1353 while (getFileChunkBE(file, chunk_name, &chunk_size))
1357 while (chunk_info[i].name != NULL &&
1358 strcmp(chunk_name, chunk_info[i].name) != 0)
1361 if (chunk_info[i].name == NULL)
1363 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1364 chunk_name, filename);
1365 ReadUnusedBytesFromFile(file, chunk_size);
1367 else if (chunk_info[i].size != -1 &&
1368 chunk_info[i].size != chunk_size)
1370 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1371 chunk_size, chunk_name, filename);
1372 ReadUnusedBytesFromFile(file, chunk_size);
1376 /* call function to load this tape chunk */
1377 int chunk_size_expected =
1378 (chunk_info[i].loader)(file, chunk_size, &tape);
1380 /* the size of some chunks cannot be checked before reading other
1381 chunks first (like "HEAD" and "BODY") that contain some header
1382 information, so check them here */
1383 if (chunk_size_expected != chunk_size)
1385 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1386 chunk_size, chunk_name, filename);
1394 tape.length_seconds = GetTapeLength();
1397 printf("tape version: %d\n", tape.game_version);
1401 void LoadTape(int level_nr)
1403 char *filename = getTapeFilename(level_nr);
1405 LoadTapeFromFilename(filename);
1408 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1410 putFileVersion(file, tape->file_version);
1411 putFileVersion(file, tape->game_version);
1414 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1417 byte store_participating_players = 0;
1419 /* set bits for participating players for compact storage */
1420 for(i=0; i<MAX_PLAYERS; i++)
1421 if (tape->player_participates[i])
1422 store_participating_players |= (1 << i);
1424 putFile32BitBE(file, tape->random_seed);
1425 putFile32BitBE(file, tape->date);
1426 putFile32BitBE(file, tape->length);
1428 fputc(store_participating_players, file);
1430 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1431 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1433 putFileVersion(file, tape->engine_version);
1436 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1438 int level_identifier_size = strlen(tape->level_identifier) + 1;
1441 putFile16BitBE(file, level_identifier_size);
1443 for(i=0; i < level_identifier_size; i++)
1444 fputc(tape->level_identifier[i], file);
1446 putFile16BitBE(file, tape->level_nr);
1449 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1453 for(i=0; i<tape->length; i++)
1455 for(j=0; j<MAX_PLAYERS; j++)
1456 if (tape->player_participates[j])
1457 fputc(tape->pos[i].action[j], file);
1459 fputc(tape->pos[i].delay, file);
1463 void SaveTape(int level_nr)
1465 char *filename = getTapeFilename(level_nr);
1467 boolean new_tape = TRUE;
1468 int num_participating_players = 0;
1469 int info_chunk_size;
1470 int body_chunk_size;
1473 InitTapeDirectory(leveldir_current->filename);
1475 /* if a tape still exists, ask to overwrite it */
1476 if (access(filename, F_OK) == 0)
1479 if (!Request("Replace old tape ?", REQ_ASK))
1483 if (!(file = fopen(filename, MODE_WRITE)))
1485 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1489 tape.file_version = FILE_VERSION_ACTUAL;
1490 tape.game_version = GAME_VERSION_ACTUAL;
1492 /* count number of participating players */
1493 for(i=0; i<MAX_PLAYERS; i++)
1494 if (tape.player_participates[i])
1495 num_participating_players++;
1497 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1498 body_chunk_size = (num_participating_players + 1) * tape.length;
1500 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1501 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1503 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1504 SaveTape_VERS(file, &tape);
1506 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1507 SaveTape_HEAD(file, &tape);
1509 putFileChunkBE(file, "INFO", info_chunk_size);
1510 SaveTape_INFO(file, &tape);
1512 putFileChunkBE(file, "BODY", body_chunk_size);
1513 SaveTape_BODY(file, &tape);
1517 SetFilePermissions(filename, PERMS_PRIVATE);
1519 tape.changed = FALSE;
1522 Request("tape saved !", REQ_CONFIRM);
1525 void DumpTape(struct TapeInfo *tape)
1529 if (TAPE_IS_EMPTY(*tape))
1531 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1535 printf_line("-", 79);
1536 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1537 tape->level_nr, tape->file_version, tape->game_version);
1538 printf("Level series identifier: '%s'\n", tape->level_identifier);
1539 printf_line("-", 79);
1541 for(i=0; i<tape->length; i++)
1543 if (i >= MAX_TAPELEN)
1546 printf("%03d: ", i);
1548 for(j=0; j<MAX_PLAYERS; j++)
1550 if (tape->player_participates[j])
1552 int action = tape->pos[i].action[j];
1554 printf("%d:%02x ", j, action);
1555 printf("[%c%c%c%c|%c%c] - ",
1556 (action & JOY_LEFT ? '<' : ' '),
1557 (action & JOY_RIGHT ? '>' : ' '),
1558 (action & JOY_UP ? '^' : ' '),
1559 (action & JOY_DOWN ? 'v' : ' '),
1560 (action & JOY_BUTTON_1 ? '1' : ' '),
1561 (action & JOY_BUTTON_2 ? '2' : ' '));
1565 printf("(%03d)\n", tape->pos[i].delay);
1568 printf_line("-", 79);
1572 /* ========================================================================= */
1573 /* score file functions */
1574 /* ========================================================================= */
1576 void LoadScore(int level_nr)
1579 char *filename = getScoreFilename(level_nr);
1580 char cookie[MAX_LINE_LEN];
1581 char line[MAX_LINE_LEN];
1585 /* always start with reliable default values */
1586 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1588 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1589 highscore[i].Score = 0;
1592 if (!(file = fopen(filename, MODE_READ)))
1595 /* check file identifier */
1596 fgets(cookie, MAX_LINE_LEN, file);
1597 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1598 cookie[strlen(cookie) - 1] = '\0';
1600 if (!checkCookieString(cookie, SCORE_COOKIE))
1602 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1607 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1609 fscanf(file, "%d", &highscore[i].Score);
1610 fgets(line, MAX_LINE_LEN, file);
1612 if (line[strlen(line) - 1] == '\n')
1613 line[strlen(line) - 1] = '\0';
1615 for (line_ptr = line; *line_ptr; line_ptr++)
1617 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1619 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1620 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1629 void SaveScore(int level_nr)
1632 char *filename = getScoreFilename(level_nr);
1635 InitScoreDirectory(leveldir_current->filename);
1637 if (!(file = fopen(filename, MODE_WRITE)))
1639 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1643 fprintf(file, "%s\n\n", SCORE_COOKIE);
1645 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1646 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1650 SetFilePermissions(filename, PERMS_PUBLIC);
1654 /* ========================================================================= */
1655 /* setup file functions */
1656 /* ========================================================================= */
1658 #define TOKEN_STR_PLAYER_PREFIX "player_"
1661 #define SETUP_TOKEN_PLAYER_NAME 0
1662 #define SETUP_TOKEN_SOUND 1
1663 #define SETUP_TOKEN_SOUND_LOOPS 2
1664 #define SETUP_TOKEN_SOUND_MUSIC 3
1665 #define SETUP_TOKEN_SOUND_SIMPLE 4
1666 #define SETUP_TOKEN_TOONS 5
1667 #define SETUP_TOKEN_SCROLL_DELAY 6
1668 #define SETUP_TOKEN_SOFT_SCROLLING 7
1669 #define SETUP_TOKEN_FADING 8
1670 #define SETUP_TOKEN_AUTORECORD 9
1671 #define SETUP_TOKEN_QUICK_DOORS 10
1672 #define SETUP_TOKEN_TEAM_MODE 11
1673 #define SETUP_TOKEN_HANDICAP 12
1674 #define SETUP_TOKEN_TIME_LIMIT 13
1675 #define SETUP_TOKEN_FULLSCREEN 14
1676 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1677 #define SETUP_TOKEN_GRAPHICS_SET 16
1678 #define SETUP_TOKEN_SOUNDS_SET 17
1679 #define SETUP_TOKEN_MUSIC_SET 18
1680 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1681 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1682 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1684 #define NUM_GLOBAL_SETUP_TOKENS 22
1687 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1688 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1689 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1690 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1691 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1692 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1693 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1694 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1695 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1697 #define NUM_EDITOR_SETUP_TOKENS 9
1699 /* shortcut setup */
1700 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1701 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1702 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1704 #define NUM_SHORTCUT_SETUP_TOKENS 3
1707 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1708 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1709 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1710 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1711 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1712 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1713 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1714 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1715 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1716 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1717 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1718 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1719 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1720 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1721 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1722 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1724 #define NUM_PLAYER_SETUP_TOKENS 16
1727 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1728 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1730 #define NUM_SYSTEM_SETUP_TOKENS 2
1733 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1735 #define NUM_OPTIONS_SETUP_TOKENS 1
1738 static struct SetupInfo si;
1739 static struct SetupEditorInfo sei;
1740 static struct SetupShortcutInfo ssi;
1741 static struct SetupInputInfo sii;
1742 static struct SetupSystemInfo syi;
1743 static struct OptionInfo soi;
1745 static struct TokenInfo global_setup_tokens[] =
1747 { TYPE_STRING, &si.player_name, "player_name" },
1748 { TYPE_SWITCH, &si.sound, "sound" },
1749 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1750 { TYPE_SWITCH, &si.sound_music, "background_music" },
1751 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1752 { TYPE_SWITCH, &si.toons, "toons" },
1753 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1754 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1755 { TYPE_SWITCH, &si.fading, "screen_fading" },
1756 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1757 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1758 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1759 { TYPE_SWITCH, &si.handicap, "handicap" },
1760 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1761 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1762 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1763 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1764 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1765 { TYPE_STRING, &si.music_set, "music_set" },
1766 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1767 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1768 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1771 static struct TokenInfo editor_setup_tokens[] =
1773 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1774 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1775 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1776 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1777 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1778 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1779 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1780 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1781 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1784 static struct TokenInfo shortcut_setup_tokens[] =
1786 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1787 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1788 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1791 static struct TokenInfo player_setup_tokens[] =
1793 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1794 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1795 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1796 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1797 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1798 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1799 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1800 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1801 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1802 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1803 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1804 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1805 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1806 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1807 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1808 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1811 static struct TokenInfo system_setup_tokens[] =
1813 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1814 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1817 static struct TokenInfo options_setup_tokens[] =
1819 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1822 static char *get_corrected_login_name(char *login_name)
1824 /* needed because player name must be a fixed length string */
1825 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1827 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1828 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1830 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1831 if (strchr(login_name_new, ' '))
1832 *strchr(login_name_new, ' ') = '\0';
1834 return login_name_new;
1837 static void setSetupInfoToDefaults(struct SetupInfo *si)
1841 si->player_name = get_corrected_login_name(getLoginName());
1844 si->sound_loops = TRUE;
1845 si->sound_music = TRUE;
1846 si->sound_simple = TRUE;
1848 si->double_buffering = TRUE;
1849 si->direct_draw = !si->double_buffering;
1850 si->scroll_delay = TRUE;
1851 si->soft_scrolling = TRUE;
1853 si->autorecord = TRUE;
1854 si->quick_doors = FALSE;
1855 si->team_mode = FALSE;
1856 si->handicap = TRUE;
1857 si->time_limit = TRUE;
1858 si->fullscreen = FALSE;
1859 si->ask_on_escape = TRUE;
1861 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1862 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1863 si->music_set = getStringCopy(MUSIC_SUBDIR);
1864 si->override_level_graphics = FALSE;
1865 si->override_level_sounds = FALSE;
1866 si->override_level_music = FALSE;
1868 si->editor.el_boulderdash = TRUE;
1869 si->editor.el_emerald_mine = TRUE;
1870 si->editor.el_more = TRUE;
1871 si->editor.el_sokoban = TRUE;
1872 si->editor.el_supaplex = TRUE;
1873 si->editor.el_diamond_caves = TRUE;
1874 si->editor.el_dx_boulderdash = TRUE;
1875 si->editor.el_chars = TRUE;
1876 si->editor.el_custom = TRUE;
1878 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1879 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1880 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1882 for (i=0; i<MAX_PLAYERS; i++)
1884 si->input[i].use_joystick = FALSE;
1885 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1886 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1887 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1888 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1889 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1890 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1891 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1892 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1893 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1894 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1895 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1896 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1897 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1898 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1899 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1902 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1903 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1905 si->options.verbose = FALSE;
1908 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
1912 if (!setup_file_hash)
1917 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1918 setSetupInfo(global_setup_tokens, i,
1919 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
1924 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1925 setSetupInfo(editor_setup_tokens, i,
1926 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
1929 /* shortcut setup */
1930 ssi = setup.shortcut;
1931 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1932 setSetupInfo(shortcut_setup_tokens, i,
1933 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
1934 setup.shortcut = ssi;
1937 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1941 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1943 sii = setup.input[pnr];
1944 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1946 char full_token[100];
1948 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1949 setSetupInfo(player_setup_tokens, i,
1950 getHashEntry(setup_file_hash, full_token));
1952 setup.input[pnr] = sii;
1957 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1958 setSetupInfo(system_setup_tokens, i,
1959 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
1963 soi = setup.options;
1964 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1965 setSetupInfo(options_setup_tokens, i,
1966 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
1967 setup.options = soi;
1972 char *filename = getSetupFilename();
1973 SetupFileHash *setup_file_hash = NULL;
1975 /* always start with reliable default values */
1976 setSetupInfoToDefaults(&setup);
1978 setup_file_hash = loadSetupFileHash(filename);
1980 if (setup_file_hash)
1982 char *player_name_new;
1984 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
1985 decodeSetupFileHash(setup_file_hash);
1987 setup.direct_draw = !setup.double_buffering;
1989 freeSetupFileHash(setup_file_hash);
1991 /* needed to work around problems with fixed length strings */
1992 player_name_new = get_corrected_login_name(setup.player_name);
1993 free(setup.player_name);
1994 setup.player_name = player_name_new;
1997 Error(ERR_WARN, "using default setup values");
2002 char *filename = getSetupFilename();
2006 InitUserDataDirectory();
2008 if (!(file = fopen(filename, MODE_WRITE)))
2010 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2014 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2015 getCookie("SETUP")));
2016 fprintf(file, "\n");
2020 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2022 /* just to make things nicer :) */
2023 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2024 i == SETUP_TOKEN_GRAPHICS_SET)
2025 fprintf(file, "\n");
2027 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2032 fprintf(file, "\n");
2033 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2034 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2036 /* shortcut setup */
2037 ssi = setup.shortcut;
2038 fprintf(file, "\n");
2039 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2040 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2043 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2047 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2048 fprintf(file, "\n");
2050 sii = setup.input[pnr];
2051 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2052 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2057 fprintf(file, "\n");
2058 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2059 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2062 soi = setup.options;
2063 fprintf(file, "\n");
2064 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2065 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2069 SetFilePermissions(filename, PERMS_PRIVATE);
2072 void LoadCustomElementDescriptions()
2074 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2075 SetupFileHash *setup_file_hash;
2078 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2080 if (element_info[i].custom_description != NULL)
2082 free(element_info[i].custom_description);
2083 element_info[i].custom_description = NULL;
2087 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2090 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2092 char *token = getStringCat2(element_info[i].token_name, ".name");
2093 char *value = getHashEntry(setup_file_hash, token);
2096 element_info[i].custom_description = getStringCopy(value);
2101 freeSetupFileHash(setup_file_hash);
2104 void LoadSpecialMenuDesignSettings()
2106 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2107 SetupFileHash *setup_file_hash;
2110 /* always start with reliable default values from default config */
2111 for (i=0; image_config_vars[i].token != NULL; i++)
2112 for (j=0; image_config[j].token != NULL; j++)
2113 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2114 *image_config_vars[i].value =
2115 get_integer_from_string(image_config[j].value);
2117 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2120 /* special case: initialize with default values that may be overwritten */
2121 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2123 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2124 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2125 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2127 if (value_x != NULL)
2128 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2129 if (value_y != NULL)
2130 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2131 if (list_size != NULL)
2132 menu.list_size[i] = get_integer_from_string(list_size);
2135 /* read (and overwrite with) values that may be specified in config file */
2136 for (i=0; image_config_vars[i].token != NULL; i++)
2138 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2141 *image_config_vars[i].value = get_integer_from_string(value);
2144 freeSetupFileHash(setup_file_hash);