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 element_info[element].access_type = 0;
148 element_info[element].access_layer = 0;
149 element_info[element].walk_to_action = 0;
150 element_info[element].smash_targets = 0;
151 element_info[element].deadliness = 0;
152 element_info[element].consistency = 0;
153 element_info[element].change_player_action = 0;
154 element_info[element].change_collide_action = 0;
155 element_info[element].change_other_action = 0;
157 element_info[element].can_explode_by_fire = FALSE;
158 element_info[element].can_explode_smashed = FALSE;
159 element_info[element].can_explode_impact = FALSE;
161 /* start with no properties at all */
162 for (j=0; j < NUM_EP_BITFIELDS; j++)
163 Properties[element][j] = EP_BITMASK_DEFAULT;
166 BorderElement = EL_STEELWALL;
168 level.no_level_file = FALSE;
170 if (leveldir_current == NULL) /* only when dumping level */
173 /* try to determine better author name than 'anonymous' */
174 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
176 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
177 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
181 switch (LEVELCLASS(leveldir_current))
183 case LEVELCLASS_TUTORIAL:
184 strcpy(level.author, PROGRAM_AUTHOR_STRING);
187 case LEVELCLASS_CONTRIBUTION:
188 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
189 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
192 case LEVELCLASS_USER:
193 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
194 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
198 /* keep default value */
204 static int checkLevelElement(int element)
206 if (element >= NUM_FILE_ELEMENTS)
208 Error(ERR_WARN, "invalid level element %d", element);
209 element = EL_CHAR_QUESTION;
211 else if (element == EL_PLAYER_OBSOLETE)
212 element = EL_PLAYER_1;
213 else if (element == EL_KEY_OBSOLETE)
219 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
221 level->file_version = getFileVersion(file);
222 level->game_version = getFileVersion(file);
227 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
231 lev_fieldx = level->fieldx = fgetc(file);
232 lev_fieldy = level->fieldy = fgetc(file);
234 level->time = getFile16BitBE(file);
235 level->gems_needed = getFile16BitBE(file);
237 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
238 level->name[i] = fgetc(file);
239 level->name[MAX_LEVEL_NAME_LEN] = 0;
241 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
242 level->score[i] = fgetc(file);
244 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
245 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
248 level->yamyam_content[i][x][y] = checkLevelElement(fgetc(file));
250 level->amoeba_speed = fgetc(file);
251 level->time_magic_wall = fgetc(file);
252 level->time_wheel = fgetc(file);
253 level->amoeba_content = checkLevelElement(fgetc(file));
254 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
255 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
256 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
257 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
259 level->use_custom_template = (fgetc(file) == 1 ? TRUE : FALSE);
261 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
266 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
270 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
271 level->author[i] = fgetc(file);
272 level->author[MAX_LEVEL_NAME_LEN] = 0;
277 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
280 int chunk_size_expected = level->fieldx * level->fieldy;
282 /* Note: "chunk_size" was wrong before version 2.0 when elements are
283 stored with 16-bit encoding (and should be twice as big then).
284 Even worse, playfield data was stored 16-bit when only yamyam content
285 contained 16-bit elements and vice versa. */
287 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
288 chunk_size_expected *= 2;
290 if (chunk_size_expected != chunk_size)
292 ReadUnusedBytesFromFile(file, chunk_size);
293 return chunk_size_expected;
296 for(y=0; y<level->fieldy; y++)
297 for(x=0; x<level->fieldx; x++)
298 Feld[x][y] = Ur[x][y] =
299 checkLevelElement(level->encoding_16bit_field ?
300 getFile16BitBE(file) : fgetc(file));
304 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
308 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
309 int chunk_size_expected = header_size + content_size;
311 /* Note: "chunk_size" was wrong before version 2.0 when elements are
312 stored with 16-bit encoding (and should be twice as big then).
313 Even worse, playfield data was stored 16-bit when only yamyam content
314 contained 16-bit elements and vice versa. */
316 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
317 chunk_size_expected += content_size;
319 if (chunk_size_expected != chunk_size)
321 ReadUnusedBytesFromFile(file, chunk_size);
322 return chunk_size_expected;
326 level->num_yamyam_contents = fgetc(file);
330 /* correct invalid number of content fields -- should never happen */
331 if (level->num_yamyam_contents < 1 ||
332 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
333 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
335 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
338 level->yamyam_content[i][x][y] =
339 checkLevelElement(level->encoding_16bit_field ?
340 getFile16BitBE(file) : fgetc(file));
344 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
348 int num_contents, content_xsize, content_ysize;
349 int content_array[MAX_ELEMENT_CONTENTS][3][3];
351 element = checkLevelElement(getFile16BitBE(file));
352 num_contents = fgetc(file);
353 content_xsize = fgetc(file);
354 content_ysize = fgetc(file);
355 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
357 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
360 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
362 /* correct invalid number of content fields -- should never happen */
363 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
364 num_contents = STD_ELEMENT_CONTENTS;
366 if (element == EL_YAMYAM)
368 level->num_yamyam_contents = num_contents;
370 for(i=0; i<num_contents; i++)
373 level->yamyam_content[i][x][y] = content_array[i][x][y];
375 else if (element == EL_BD_AMOEBA)
377 level->amoeba_content = content_array[0][0][0];
381 Error(ERR_WARN, "cannot load content for element '%d'", element);
387 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
389 int num_changed_custom_elements = getFile16BitBE(file);
390 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
393 if (chunk_size_expected != chunk_size)
395 ReadUnusedBytesFromFile(file, chunk_size - 2);
396 return chunk_size_expected;
399 for (i=0; i < num_changed_custom_elements; i++)
401 int element = getFile16BitBE(file);
402 int properties = getFile32BitBE(file);
404 if (IS_CUSTOM_ELEMENT(element))
405 Properties[element][EP_BITFIELD_BASE] = properties;
407 Error(ERR_WARN, "invalid custom element number %d", element);
413 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
415 int num_changed_custom_elements = getFile16BitBE(file);
416 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
419 if (chunk_size_expected != chunk_size)
421 ReadUnusedBytesFromFile(file, chunk_size - 2);
422 return chunk_size_expected;
425 for (i=0; i < num_changed_custom_elements; i++)
427 int element = getFile16BitBE(file);
428 int custom_target_element = getFile16BitBE(file);
430 if (IS_CUSTOM_ELEMENT(element))
431 element_info[element].change.target_element = custom_target_element;
433 Error(ERR_WARN, "invalid custom element number %d", element);
439 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
441 int num_changed_custom_elements = getFile16BitBE(file);
442 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
445 if (chunk_size_expected != chunk_size)
447 ReadUnusedBytesFromFile(file, chunk_size - 2);
448 return chunk_size_expected;
451 for (i=0; i < num_changed_custom_elements; i++)
453 int element = getFile16BitBE(file);
455 if (!IS_CUSTOM_ELEMENT(element))
457 Error(ERR_WARN, "invalid custom element number %d", element);
459 element = EL_DEFAULT; /* dummy element used for artwork config */
462 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
464 /* some free bytes for future properties and padding */
465 ReadUnusedBytesFromFile(file, 7);
467 element_info[element].use_gfx_element = getFile8Bit(file);
468 element_info[element].gfx_element =
469 checkLevelElement(getFile16BitBE(file));
471 element_info[element].score = getFile8Bit(file);
472 element_info[element].gem_count = getFile8Bit(file);
474 element_info[element].push_delay_fixed = getFile16BitBE(file);
475 element_info[element].push_delay_random = getFile16BitBE(file);
476 element_info[element].move_delay_fixed = getFile16BitBE(file);
477 element_info[element].move_delay_random = getFile16BitBE(file);
479 element_info[element].move_pattern = getFile16BitBE(file);
480 element_info[element].move_direction_initial = getFile8Bit(file);
481 element_info[element].move_stepsize = getFile8Bit(file);
485 element_info[element].content[x][y] =
486 checkLevelElement(getFile16BitBE(file));
488 element_info[element].change.events = getFile32BitBE(file);
490 element_info[element].change.target_element =
491 checkLevelElement(getFile16BitBE(file));
493 element_info[element].change.delay_fixed = getFile16BitBE(file);
494 element_info[element].change.delay_random = getFile16BitBE(file);
495 element_info[element].change.delay_frames = getFile16BitBE(file);
497 element_info[element].change.trigger_element =
498 checkLevelElement(getFile16BitBE(file));
500 element_info[element].change.explode = getFile8Bit(file);
501 element_info[element].change.use_content = getFile8Bit(file);
502 element_info[element].change.only_complete = getFile8Bit(file);
503 element_info[element].change.use_random_change = getFile8Bit(file);
505 element_info[element].change.random = getFile8Bit(file);
506 element_info[element].change.power = getFile8Bit(file);
510 element_info[element].change.content[x][y] =
511 checkLevelElement(getFile16BitBE(file));
513 /* some free bytes for future properties and padding */
514 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
520 void LoadLevelFromFilename(char *filename)
522 char cookie[MAX_LINE_LEN];
523 char chunk_name[CHUNK_ID_LEN + 1];
527 /* always start with reliable default values */
528 setLevelInfoToDefaults();
530 if (!(file = fopen(filename, MODE_READ)))
532 level.no_level_file = TRUE;
534 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
538 getFileChunkBE(file, chunk_name, NULL);
539 if (strcmp(chunk_name, "RND1") == 0)
541 getFile32BitBE(file); /* not used */
543 getFileChunkBE(file, chunk_name, NULL);
544 if (strcmp(chunk_name, "CAVE") != 0)
546 Error(ERR_WARN, "unknown format of level file '%s'", filename);
551 else /* check for pre-2.0 file format with cookie string */
553 strcpy(cookie, chunk_name);
554 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
555 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
556 cookie[strlen(cookie) - 1] = '\0';
558 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
560 Error(ERR_WARN, "unknown format of level file '%s'", filename);
565 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
567 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
572 /* pre-2.0 level files have no game version, so use file version here */
573 level.game_version = level.file_version;
576 if (level.file_version < FILE_VERSION_1_2)
578 /* level files from versions before 1.2.0 without chunk structure */
579 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
580 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
588 int (*loader)(FILE *, int, struct LevelInfo *);
592 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
593 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
594 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
595 { "BODY", -1, LoadLevel_BODY },
596 { "CONT", -1, LoadLevel_CONT },
597 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
598 { "CUS1", -1, LoadLevel_CUS1 },
599 { "CUS2", -1, LoadLevel_CUS2 },
600 { "CUS3", -1, LoadLevel_CUS3 },
604 while (getFileChunkBE(file, chunk_name, &chunk_size))
608 while (chunk_info[i].name != NULL &&
609 strcmp(chunk_name, chunk_info[i].name) != 0)
612 if (chunk_info[i].name == NULL)
614 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
615 chunk_name, filename);
616 ReadUnusedBytesFromFile(file, chunk_size);
618 else if (chunk_info[i].size != -1 &&
619 chunk_info[i].size != chunk_size)
621 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
622 chunk_size, chunk_name, filename);
623 ReadUnusedBytesFromFile(file, chunk_size);
627 /* call function to load this level chunk */
628 int chunk_size_expected =
629 (chunk_info[i].loader)(file, chunk_size, &level);
631 /* the size of some chunks cannot be checked before reading other
632 chunks first (like "HEAD" and "BODY") that contain some header
633 information, so check them here */
634 if (chunk_size_expected != chunk_size)
636 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
637 chunk_size, chunk_name, filename);
645 if (leveldir_current == NULL) /* only when dumping level */
648 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
649 IS_LEVELCLASS_USER(leveldir_current))
651 /* For user contributed and private levels, use the version of
652 the game engine the levels were created for.
653 Since 2.0.1, the game engine version is now directly stored
654 in the level file (chunk "VERS"), so there is no need anymore
655 to set the game version from the file version (except for old,
656 pre-2.0 levels, where the game version is still taken from the
657 file format version used to store the level -- see above). */
659 /* do some special adjustments to support older level versions */
660 if (level.file_version == FILE_VERSION_1_0)
662 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
663 Error(ERR_WARN, "using high speed movement for player");
665 /* player was faster than monsters in (pre-)1.0 levels */
666 level.double_speed = TRUE;
669 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
670 if (level.game_version == VERSION_IDENT(2,0,1))
671 level.em_slippery_gems = TRUE;
675 /* Always use the latest version of the game engine for all but
676 user contributed and private levels; this allows for actual
677 corrections in the game engine to take effect for existing,
678 converted levels (from "classic" or other existing games) to
679 make the game emulation more accurate, while (hopefully) not
680 breaking existing levels created from other players. */
682 level.game_version = GAME_VERSION_ACTUAL;
684 /* Set special EM style gems behaviour: EM style gems slip down from
685 normal, steel and growing wall. As this is a more fundamental change,
686 it seems better to set the default behaviour to "off" (as it is more
687 natural) and make it configurable in the level editor (as a property
688 of gem style elements). Already existing converted levels (neither
689 private nor contributed levels) are changed to the new behaviour. */
691 if (level.file_version < FILE_VERSION_2_0)
692 level.em_slippery_gems = TRUE;
695 /* map some elements which have changed in newer versions */
696 if (level.game_version <= VERSION_IDENT(2,2,0))
700 /* map game font elements */
701 for(y=0; y<level.fieldy; y++)
703 for(x=0; x<level.fieldx; x++)
705 int element = Ur[x][y];
707 if (element == EL_CHAR('['))
708 element = EL_CHAR_AUMLAUT;
709 else if (element == EL_CHAR('\\'))
710 element = EL_CHAR_OUMLAUT;
711 else if (element == EL_CHAR(']'))
712 element = EL_CHAR_UUMLAUT;
713 else if (element == EL_CHAR('^'))
714 element = EL_CHAR_COPYRIGHT;
716 Feld[x][y] = Ur[x][y] = element;
721 /* determine border element for this level */
725 void LoadLevel(int level_nr)
727 char *filename = getLevelFilename(level_nr);
729 LoadLevelFromFilename(filename);
730 InitElementPropertiesEngine(level.game_version);
733 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
735 putFileVersion(file, level->file_version);
736 putFileVersion(file, level->game_version);
739 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
743 fputc(level->fieldx, file);
744 fputc(level->fieldy, file);
746 putFile16BitBE(file, level->time);
747 putFile16BitBE(file, level->gems_needed);
749 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
750 fputc(level->name[i], file);
752 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
753 fputc(level->score[i], file);
755 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
758 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
759 level->yamyam_content[i][x][y]),
761 fputc(level->amoeba_speed, file);
762 fputc(level->time_magic_wall, file);
763 fputc(level->time_wheel, file);
764 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
766 fputc((level->double_speed ? 1 : 0), file);
767 fputc((level->gravity ? 1 : 0), file);
768 fputc((level->encoding_16bit_field ? 1 : 0), file);
769 fputc((level->em_slippery_gems ? 1 : 0), file);
771 fputc((level->use_custom_template ? 1 : 0), file);
773 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
776 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
780 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
781 fputc(level->author[i], file);
784 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
788 for(y=0; y<level->fieldy; y++)
789 for(x=0; x<level->fieldx; x++)
790 if (level->encoding_16bit_field)
791 putFile16BitBE(file, Ur[x][y]);
793 fputc(Ur[x][y], file);
797 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
801 fputc(EL_YAMYAM, file);
802 fputc(level->num_yamyam_contents, file);
806 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
809 if (level->encoding_16bit_field)
810 putFile16BitBE(file, level->yamyam_content[i][x][y]);
812 fputc(level->yamyam_content[i][x][y], file);
816 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
819 int num_contents, content_xsize, content_ysize;
820 int content_array[MAX_ELEMENT_CONTENTS][3][3];
822 if (element == EL_YAMYAM)
824 num_contents = level->num_yamyam_contents;
828 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
831 content_array[i][x][y] = level->yamyam_content[i][x][y];
833 else if (element == EL_BD_AMOEBA)
839 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
842 content_array[i][x][y] = EL_EMPTY;
843 content_array[0][0][0] = level->amoeba_content;
847 /* chunk header already written -- write empty chunk data */
848 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
850 Error(ERR_WARN, "cannot save content for element '%d'", element);
854 putFile16BitBE(file, element);
855 fputc(num_contents, file);
856 fputc(content_xsize, file);
857 fputc(content_ysize, file);
859 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
861 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
864 putFile16BitBE(file, content_array[i][x][y]);
868 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
869 int num_changed_custom_elements)
873 putFile16BitBE(file, num_changed_custom_elements);
875 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
877 int element = EL_CUSTOM_START + i;
879 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
881 if (check < num_changed_custom_elements)
883 putFile16BitBE(file, element);
884 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
891 if (check != num_changed_custom_elements) /* should not happen */
892 Error(ERR_WARN, "inconsistent number of custom element properties");
897 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
898 int num_changed_custom_elements)
902 putFile16BitBE(file, num_changed_custom_elements);
904 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
906 int element = EL_CUSTOM_START + i;
908 if (element_info[element].change.target_element != EL_EMPTY_SPACE)
910 if (check < num_changed_custom_elements)
912 putFile16BitBE(file, element);
913 putFile16BitBE(file, element_info[element].change.target_element);
920 if (check != num_changed_custom_elements) /* should not happen */
921 Error(ERR_WARN, "inconsistent number of custom target elements");
925 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
926 int num_changed_custom_elements)
928 int i, x, y, check = 0;
930 putFile16BitBE(file, num_changed_custom_elements);
932 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
934 int element = EL_CUSTOM_START + i;
936 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
938 if (check < num_changed_custom_elements)
940 putFile16BitBE(file, element);
941 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
943 /* some free bytes for future properties and padding */
944 WriteUnusedBytesToFile(file, 7);
946 putFile8Bit(file, element_info[element].use_gfx_element);
947 putFile16BitBE(file, element_info[element].gfx_element);
949 putFile8Bit(file, element_info[element].score);
950 putFile8Bit(file, element_info[element].gem_count);
952 putFile16BitBE(file, element_info[element].push_delay_fixed);
953 putFile16BitBE(file, element_info[element].push_delay_random);
954 putFile16BitBE(file, element_info[element].move_delay_fixed);
955 putFile16BitBE(file, element_info[element].move_delay_random);
957 putFile16BitBE(file, element_info[element].move_pattern);
958 putFile8Bit(file, element_info[element].move_direction_initial);
959 putFile8Bit(file, element_info[element].move_stepsize);
963 putFile16BitBE(file, element_info[element].content[x][y]);
965 putFile32BitBE(file, element_info[element].change.events);
967 putFile16BitBE(file, element_info[element].change.target_element);
969 putFile16BitBE(file, element_info[element].change.delay_fixed);
970 putFile16BitBE(file, element_info[element].change.delay_random);
971 putFile16BitBE(file, element_info[element].change.delay_frames);
973 putFile16BitBE(file, element_info[element].change.trigger_element);
975 putFile8Bit(file, element_info[element].change.explode);
976 putFile8Bit(file, element_info[element].change.use_content);
977 putFile8Bit(file, element_info[element].change.only_complete);
978 putFile8Bit(file, element_info[element].change.use_random_change);
980 putFile8Bit(file, element_info[element].change.random);
981 putFile8Bit(file, element_info[element].change.power);
985 putFile16BitBE(file, element_info[element].change.content[x][y]);
987 /* some free bytes for future properties and padding */
988 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
995 if (check != num_changed_custom_elements) /* should not happen */
996 Error(ERR_WARN, "inconsistent number of custom element properties");
999 void SaveLevel(int level_nr)
1001 char *filename = getLevelFilename(level_nr);
1002 int body_chunk_size;
1003 int num_changed_custom_elements = 0;
1004 int level_chunk_CUS3_size;
1008 if (!(file = fopen(filename, MODE_WRITE)))
1010 Error(ERR_WARN, "cannot save level file '%s'", filename);
1014 level.file_version = FILE_VERSION_ACTUAL;
1015 level.game_version = GAME_VERSION_ACTUAL;
1017 /* check level field for 16-bit elements */
1018 level.encoding_16bit_field = FALSE;
1019 for(y=0; y<level.fieldy; y++)
1020 for(x=0; x<level.fieldx; x++)
1022 level.encoding_16bit_field = TRUE;
1024 /* check yamyam content for 16-bit elements */
1025 level.encoding_16bit_yamyam = FALSE;
1026 for(i=0; i<level.num_yamyam_contents; i++)
1029 if (level.yamyam_content[i][x][y] > 255)
1030 level.encoding_16bit_yamyam = TRUE;
1032 /* check amoeba content for 16-bit elements */
1033 level.encoding_16bit_amoeba = FALSE;
1034 if (level.amoeba_content > 255)
1035 level.encoding_16bit_amoeba = TRUE;
1037 /* calculate size of "BODY" chunk */
1039 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
1041 /* check for non-standard custom elements and calculate "CUS3" chunk size */
1042 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1043 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1044 num_changed_custom_elements++;
1045 level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1047 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1048 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1050 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1051 SaveLevel_VERS(file, &level);
1053 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1054 SaveLevel_HEAD(file, &level);
1056 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1057 SaveLevel_AUTH(file, &level);
1059 putFileChunkBE(file, "BODY", body_chunk_size);
1060 SaveLevel_BODY(file, &level);
1062 if (level.encoding_16bit_yamyam ||
1063 level.num_yamyam_contents != STD_ELEMENT_CONTENTS)
1065 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1066 SaveLevel_CNT2(file, &level, EL_YAMYAM);
1069 if (level.encoding_16bit_amoeba)
1071 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1072 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
1075 if (num_changed_custom_elements > 0)
1077 putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1078 SaveLevel_CUS3(file, &level, num_changed_custom_elements);
1083 SetFilePermissions(filename, PERMS_PRIVATE);
1086 void DumpLevel(struct LevelInfo *level)
1088 printf_line("-", 79);
1089 printf("Level xxx (file version %08d, game version %08d)\n",
1090 level->file_version, level->game_version);
1091 printf_line("-", 79);
1093 printf("Level Author: '%s'\n", level->author);
1094 printf("Level Title: '%s'\n", level->name);
1096 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1098 printf("Level Time: %d seconds\n", level->time);
1099 printf("Gems needed: %d\n", level->gems_needed);
1101 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1102 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1103 printf("Time for Light: %d seconds\n", level->time_light);
1104 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1106 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1108 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1109 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1110 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1112 printf_line("-", 79);
1116 /* ========================================================================= */
1117 /* tape file functions */
1118 /* ========================================================================= */
1120 static void setTapeInfoToDefaults()
1124 /* always start with reliable default values (empty tape) */
1127 /* default values (also for pre-1.2 tapes) with only the first player */
1128 tape.player_participates[0] = TRUE;
1129 for(i=1; i<MAX_PLAYERS; i++)
1130 tape.player_participates[i] = FALSE;
1132 /* at least one (default: the first) player participates in every tape */
1133 tape.num_participating_players = 1;
1135 tape.level_nr = level_nr;
1137 tape.changed = FALSE;
1139 tape.recording = FALSE;
1140 tape.playing = FALSE;
1141 tape.pausing = FALSE;
1144 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1146 tape->file_version = getFileVersion(file);
1147 tape->game_version = getFileVersion(file);
1152 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1156 tape->random_seed = getFile32BitBE(file);
1157 tape->date = getFile32BitBE(file);
1158 tape->length = getFile32BitBE(file);
1160 /* read header fields that are new since version 1.2 */
1161 if (tape->file_version >= FILE_VERSION_1_2)
1163 byte store_participating_players = fgetc(file);
1166 /* since version 1.2, tapes store which players participate in the tape */
1167 tape->num_participating_players = 0;
1168 for(i=0; i<MAX_PLAYERS; i++)
1170 tape->player_participates[i] = FALSE;
1172 if (store_participating_players & (1 << i))
1174 tape->player_participates[i] = TRUE;
1175 tape->num_participating_players++;
1179 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1181 engine_version = getFileVersion(file);
1182 if (engine_version > 0)
1183 tape->engine_version = engine_version;
1189 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1191 int level_identifier_size;
1194 level_identifier_size = getFile16BitBE(file);
1196 tape->level_identifier =
1197 checked_realloc(tape->level_identifier, level_identifier_size);
1199 for(i=0; i < level_identifier_size; i++)
1200 tape->level_identifier[i] = fgetc(file);
1202 tape->level_nr = getFile16BitBE(file);
1204 chunk_size = 2 + level_identifier_size + 2;
1209 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1212 int chunk_size_expected =
1213 (tape->num_participating_players + 1) * tape->length;
1215 if (chunk_size_expected != chunk_size)
1217 ReadUnusedBytesFromFile(file, chunk_size);
1218 return chunk_size_expected;
1221 for(i=0; i<tape->length; i++)
1223 if (i >= MAX_TAPELEN)
1226 for(j=0; j<MAX_PLAYERS; j++)
1228 tape->pos[i].action[j] = MV_NO_MOVING;
1230 if (tape->player_participates[j])
1231 tape->pos[i].action[j] = fgetc(file);
1234 tape->pos[i].delay = fgetc(file);
1236 if (tape->file_version == FILE_VERSION_1_0)
1238 /* eliminate possible diagonal moves in old tapes */
1239 /* this is only for backward compatibility */
1241 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1242 byte action = tape->pos[i].action[0];
1243 int k, num_moves = 0;
1247 if (action & joy_dir[k])
1249 tape->pos[i + num_moves].action[0] = joy_dir[k];
1251 tape->pos[i + num_moves].delay = 0;
1260 tape->length += num_moves;
1263 else if (tape->file_version < FILE_VERSION_2_0)
1265 /* convert pre-2.0 tapes to new tape format */
1267 if (tape->pos[i].delay > 1)
1270 tape->pos[i + 1] = tape->pos[i];
1271 tape->pos[i + 1].delay = 1;
1274 for(j=0; j<MAX_PLAYERS; j++)
1275 tape->pos[i].action[j] = MV_NO_MOVING;
1276 tape->pos[i].delay--;
1287 if (i != tape->length)
1288 chunk_size = (tape->num_participating_players + 1) * i;
1293 void LoadTapeFromFilename(char *filename)
1295 char cookie[MAX_LINE_LEN];
1296 char chunk_name[CHUNK_ID_LEN + 1];
1300 /* always start with reliable default values */
1301 setTapeInfoToDefaults();
1303 if (!(file = fopen(filename, MODE_READ)))
1306 getFileChunkBE(file, chunk_name, NULL);
1307 if (strcmp(chunk_name, "RND1") == 0)
1309 getFile32BitBE(file); /* not used */
1311 getFileChunkBE(file, chunk_name, NULL);
1312 if (strcmp(chunk_name, "TAPE") != 0)
1314 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1319 else /* check for pre-2.0 file format with cookie string */
1321 strcpy(cookie, chunk_name);
1322 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1323 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1324 cookie[strlen(cookie) - 1] = '\0';
1326 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1328 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1333 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1335 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1340 /* pre-2.0 tape files have no game version, so use file version here */
1341 tape.game_version = tape.file_version;
1344 if (tape.file_version < FILE_VERSION_1_2)
1346 /* tape files from versions before 1.2.0 without chunk structure */
1347 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1348 LoadTape_BODY(file, 2 * tape.length, &tape);
1356 int (*loader)(FILE *, int, struct TapeInfo *);
1360 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1361 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1362 { "INFO", -1, LoadTape_INFO },
1363 { "BODY", -1, LoadTape_BODY },
1367 while (getFileChunkBE(file, chunk_name, &chunk_size))
1371 while (chunk_info[i].name != NULL &&
1372 strcmp(chunk_name, chunk_info[i].name) != 0)
1375 if (chunk_info[i].name == NULL)
1377 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1378 chunk_name, filename);
1379 ReadUnusedBytesFromFile(file, chunk_size);
1381 else if (chunk_info[i].size != -1 &&
1382 chunk_info[i].size != chunk_size)
1384 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1385 chunk_size, chunk_name, filename);
1386 ReadUnusedBytesFromFile(file, chunk_size);
1390 /* call function to load this tape chunk */
1391 int chunk_size_expected =
1392 (chunk_info[i].loader)(file, chunk_size, &tape);
1394 /* the size of some chunks cannot be checked before reading other
1395 chunks first (like "HEAD" and "BODY") that contain some header
1396 information, so check them here */
1397 if (chunk_size_expected != chunk_size)
1399 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1400 chunk_size, chunk_name, filename);
1408 tape.length_seconds = GetTapeLength();
1411 printf("tape version: %d\n", tape.game_version);
1415 void LoadTape(int level_nr)
1417 char *filename = getTapeFilename(level_nr);
1419 LoadTapeFromFilename(filename);
1422 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1424 putFileVersion(file, tape->file_version);
1425 putFileVersion(file, tape->game_version);
1428 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1431 byte store_participating_players = 0;
1433 /* set bits for participating players for compact storage */
1434 for(i=0; i<MAX_PLAYERS; i++)
1435 if (tape->player_participates[i])
1436 store_participating_players |= (1 << i);
1438 putFile32BitBE(file, tape->random_seed);
1439 putFile32BitBE(file, tape->date);
1440 putFile32BitBE(file, tape->length);
1442 fputc(store_participating_players, file);
1444 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1445 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1447 putFileVersion(file, tape->engine_version);
1450 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1452 int level_identifier_size = strlen(tape->level_identifier) + 1;
1455 putFile16BitBE(file, level_identifier_size);
1457 for(i=0; i < level_identifier_size; i++)
1458 fputc(tape->level_identifier[i], file);
1460 putFile16BitBE(file, tape->level_nr);
1463 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1467 for(i=0; i<tape->length; i++)
1469 for(j=0; j<MAX_PLAYERS; j++)
1470 if (tape->player_participates[j])
1471 fputc(tape->pos[i].action[j], file);
1473 fputc(tape->pos[i].delay, file);
1477 void SaveTape(int level_nr)
1479 char *filename = getTapeFilename(level_nr);
1481 boolean new_tape = TRUE;
1482 int num_participating_players = 0;
1483 int info_chunk_size;
1484 int body_chunk_size;
1487 InitTapeDirectory(leveldir_current->filename);
1489 /* if a tape still exists, ask to overwrite it */
1490 if (access(filename, F_OK) == 0)
1493 if (!Request("Replace old tape ?", REQ_ASK))
1497 if (!(file = fopen(filename, MODE_WRITE)))
1499 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1503 tape.file_version = FILE_VERSION_ACTUAL;
1504 tape.game_version = GAME_VERSION_ACTUAL;
1506 /* count number of participating players */
1507 for(i=0; i<MAX_PLAYERS; i++)
1508 if (tape.player_participates[i])
1509 num_participating_players++;
1511 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1512 body_chunk_size = (num_participating_players + 1) * tape.length;
1514 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1515 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1517 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1518 SaveTape_VERS(file, &tape);
1520 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1521 SaveTape_HEAD(file, &tape);
1523 putFileChunkBE(file, "INFO", info_chunk_size);
1524 SaveTape_INFO(file, &tape);
1526 putFileChunkBE(file, "BODY", body_chunk_size);
1527 SaveTape_BODY(file, &tape);
1531 SetFilePermissions(filename, PERMS_PRIVATE);
1533 tape.changed = FALSE;
1536 Request("tape saved !", REQ_CONFIRM);
1539 void DumpTape(struct TapeInfo *tape)
1543 if (TAPE_IS_EMPTY(*tape))
1545 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1549 printf_line("-", 79);
1550 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1551 tape->level_nr, tape->file_version, tape->game_version);
1552 printf("Level series identifier: '%s'\n", tape->level_identifier);
1553 printf_line("-", 79);
1555 for(i=0; i<tape->length; i++)
1557 if (i >= MAX_TAPELEN)
1560 printf("%03d: ", i);
1562 for(j=0; j<MAX_PLAYERS; j++)
1564 if (tape->player_participates[j])
1566 int action = tape->pos[i].action[j];
1568 printf("%d:%02x ", j, action);
1569 printf("[%c%c%c%c|%c%c] - ",
1570 (action & JOY_LEFT ? '<' : ' '),
1571 (action & JOY_RIGHT ? '>' : ' '),
1572 (action & JOY_UP ? '^' : ' '),
1573 (action & JOY_DOWN ? 'v' : ' '),
1574 (action & JOY_BUTTON_1 ? '1' : ' '),
1575 (action & JOY_BUTTON_2 ? '2' : ' '));
1579 printf("(%03d)\n", tape->pos[i].delay);
1582 printf_line("-", 79);
1586 /* ========================================================================= */
1587 /* score file functions */
1588 /* ========================================================================= */
1590 void LoadScore(int level_nr)
1593 char *filename = getScoreFilename(level_nr);
1594 char cookie[MAX_LINE_LEN];
1595 char line[MAX_LINE_LEN];
1599 /* always start with reliable default values */
1600 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1602 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1603 highscore[i].Score = 0;
1606 if (!(file = fopen(filename, MODE_READ)))
1609 /* check file identifier */
1610 fgets(cookie, MAX_LINE_LEN, file);
1611 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1612 cookie[strlen(cookie) - 1] = '\0';
1614 if (!checkCookieString(cookie, SCORE_COOKIE))
1616 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1621 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1623 fscanf(file, "%d", &highscore[i].Score);
1624 fgets(line, MAX_LINE_LEN, file);
1626 if (line[strlen(line) - 1] == '\n')
1627 line[strlen(line) - 1] = '\0';
1629 for (line_ptr = line; *line_ptr; line_ptr++)
1631 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1633 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1634 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1643 void SaveScore(int level_nr)
1646 char *filename = getScoreFilename(level_nr);
1649 InitScoreDirectory(leveldir_current->filename);
1651 if (!(file = fopen(filename, MODE_WRITE)))
1653 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1657 fprintf(file, "%s\n\n", SCORE_COOKIE);
1659 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1660 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1664 SetFilePermissions(filename, PERMS_PUBLIC);
1668 /* ========================================================================= */
1669 /* setup file functions */
1670 /* ========================================================================= */
1672 #define TOKEN_STR_PLAYER_PREFIX "player_"
1675 #define SETUP_TOKEN_PLAYER_NAME 0
1676 #define SETUP_TOKEN_SOUND 1
1677 #define SETUP_TOKEN_SOUND_LOOPS 2
1678 #define SETUP_TOKEN_SOUND_MUSIC 3
1679 #define SETUP_TOKEN_SOUND_SIMPLE 4
1680 #define SETUP_TOKEN_TOONS 5
1681 #define SETUP_TOKEN_SCROLL_DELAY 6
1682 #define SETUP_TOKEN_SOFT_SCROLLING 7
1683 #define SETUP_TOKEN_FADING 8
1684 #define SETUP_TOKEN_AUTORECORD 9
1685 #define SETUP_TOKEN_QUICK_DOORS 10
1686 #define SETUP_TOKEN_TEAM_MODE 11
1687 #define SETUP_TOKEN_HANDICAP 12
1688 #define SETUP_TOKEN_TIME_LIMIT 13
1689 #define SETUP_TOKEN_FULLSCREEN 14
1690 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1691 #define SETUP_TOKEN_GRAPHICS_SET 16
1692 #define SETUP_TOKEN_SOUNDS_SET 17
1693 #define SETUP_TOKEN_MUSIC_SET 18
1694 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1695 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1696 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1698 #define NUM_GLOBAL_SETUP_TOKENS 22
1701 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1702 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1703 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1704 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1705 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1706 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1707 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1708 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1709 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1711 #define NUM_EDITOR_SETUP_TOKENS 9
1713 /* shortcut setup */
1714 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1715 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1716 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1718 #define NUM_SHORTCUT_SETUP_TOKENS 3
1721 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1722 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1723 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1724 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1725 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1726 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1727 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1728 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1729 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1730 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1731 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1732 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1733 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1734 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1735 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1736 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1738 #define NUM_PLAYER_SETUP_TOKENS 16
1741 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1742 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1744 #define NUM_SYSTEM_SETUP_TOKENS 2
1747 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1749 #define NUM_OPTIONS_SETUP_TOKENS 1
1752 static struct SetupInfo si;
1753 static struct SetupEditorInfo sei;
1754 static struct SetupShortcutInfo ssi;
1755 static struct SetupInputInfo sii;
1756 static struct SetupSystemInfo syi;
1757 static struct OptionInfo soi;
1759 static struct TokenInfo global_setup_tokens[] =
1761 { TYPE_STRING, &si.player_name, "player_name" },
1762 { TYPE_SWITCH, &si.sound, "sound" },
1763 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1764 { TYPE_SWITCH, &si.sound_music, "background_music" },
1765 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1766 { TYPE_SWITCH, &si.toons, "toons" },
1767 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1768 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1769 { TYPE_SWITCH, &si.fading, "screen_fading" },
1770 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1771 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1772 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1773 { TYPE_SWITCH, &si.handicap, "handicap" },
1774 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1775 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1776 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1777 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1778 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1779 { TYPE_STRING, &si.music_set, "music_set" },
1780 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1781 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1782 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1785 static struct TokenInfo editor_setup_tokens[] =
1787 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1788 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1789 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1790 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1791 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1792 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1793 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1794 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1795 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1798 static struct TokenInfo shortcut_setup_tokens[] =
1800 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1801 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1802 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1805 static struct TokenInfo player_setup_tokens[] =
1807 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1808 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1809 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1810 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1811 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1812 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1813 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1814 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1815 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1816 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1817 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1818 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1819 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1820 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1821 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1822 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1825 static struct TokenInfo system_setup_tokens[] =
1827 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1828 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1831 static struct TokenInfo options_setup_tokens[] =
1833 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1836 static char *get_corrected_login_name(char *login_name)
1838 /* needed because player name must be a fixed length string */
1839 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1841 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1842 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1844 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1845 if (strchr(login_name_new, ' '))
1846 *strchr(login_name_new, ' ') = '\0';
1848 return login_name_new;
1851 static void setSetupInfoToDefaults(struct SetupInfo *si)
1855 si->player_name = get_corrected_login_name(getLoginName());
1858 si->sound_loops = TRUE;
1859 si->sound_music = TRUE;
1860 si->sound_simple = TRUE;
1862 si->double_buffering = TRUE;
1863 si->direct_draw = !si->double_buffering;
1864 si->scroll_delay = TRUE;
1865 si->soft_scrolling = TRUE;
1867 si->autorecord = TRUE;
1868 si->quick_doors = FALSE;
1869 si->team_mode = FALSE;
1870 si->handicap = TRUE;
1871 si->time_limit = TRUE;
1872 si->fullscreen = FALSE;
1873 si->ask_on_escape = TRUE;
1875 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1876 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1877 si->music_set = getStringCopy(MUSIC_SUBDIR);
1878 si->override_level_graphics = FALSE;
1879 si->override_level_sounds = FALSE;
1880 si->override_level_music = FALSE;
1882 si->editor.el_boulderdash = TRUE;
1883 si->editor.el_emerald_mine = TRUE;
1884 si->editor.el_more = TRUE;
1885 si->editor.el_sokoban = TRUE;
1886 si->editor.el_supaplex = TRUE;
1887 si->editor.el_diamond_caves = TRUE;
1888 si->editor.el_dx_boulderdash = TRUE;
1889 si->editor.el_chars = TRUE;
1890 si->editor.el_custom = TRUE;
1892 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1893 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1894 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1896 for (i=0; i<MAX_PLAYERS; i++)
1898 si->input[i].use_joystick = FALSE;
1899 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1900 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1901 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1902 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1903 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1904 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1905 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1906 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1907 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1908 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1909 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1910 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1911 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1912 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1913 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1916 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1917 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1919 si->options.verbose = FALSE;
1922 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
1926 if (!setup_file_hash)
1931 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1932 setSetupInfo(global_setup_tokens, i,
1933 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
1938 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1939 setSetupInfo(editor_setup_tokens, i,
1940 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
1943 /* shortcut setup */
1944 ssi = setup.shortcut;
1945 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1946 setSetupInfo(shortcut_setup_tokens, i,
1947 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
1948 setup.shortcut = ssi;
1951 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1955 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1957 sii = setup.input[pnr];
1958 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1960 char full_token[100];
1962 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1963 setSetupInfo(player_setup_tokens, i,
1964 getHashEntry(setup_file_hash, full_token));
1966 setup.input[pnr] = sii;
1971 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1972 setSetupInfo(system_setup_tokens, i,
1973 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
1977 soi = setup.options;
1978 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1979 setSetupInfo(options_setup_tokens, i,
1980 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
1981 setup.options = soi;
1986 char *filename = getSetupFilename();
1987 SetupFileHash *setup_file_hash = NULL;
1989 /* always start with reliable default values */
1990 setSetupInfoToDefaults(&setup);
1992 setup_file_hash = loadSetupFileHash(filename);
1994 if (setup_file_hash)
1996 char *player_name_new;
1998 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
1999 decodeSetupFileHash(setup_file_hash);
2001 setup.direct_draw = !setup.double_buffering;
2003 freeSetupFileHash(setup_file_hash);
2005 /* needed to work around problems with fixed length strings */
2006 player_name_new = get_corrected_login_name(setup.player_name);
2007 free(setup.player_name);
2008 setup.player_name = player_name_new;
2011 Error(ERR_WARN, "using default setup values");
2016 char *filename = getSetupFilename();
2020 InitUserDataDirectory();
2022 if (!(file = fopen(filename, MODE_WRITE)))
2024 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2028 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2029 getCookie("SETUP")));
2030 fprintf(file, "\n");
2034 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2036 /* just to make things nicer :) */
2037 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2038 i == SETUP_TOKEN_GRAPHICS_SET)
2039 fprintf(file, "\n");
2041 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2046 fprintf(file, "\n");
2047 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2048 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2050 /* shortcut setup */
2051 ssi = setup.shortcut;
2052 fprintf(file, "\n");
2053 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2054 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2057 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2061 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2062 fprintf(file, "\n");
2064 sii = setup.input[pnr];
2065 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2066 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2071 fprintf(file, "\n");
2072 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2073 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2076 soi = setup.options;
2077 fprintf(file, "\n");
2078 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2079 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2083 SetFilePermissions(filename, PERMS_PRIVATE);
2086 void LoadCustomElementDescriptions()
2088 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2089 SetupFileHash *setup_file_hash;
2092 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2094 if (element_info[i].custom_description != NULL)
2096 free(element_info[i].custom_description);
2097 element_info[i].custom_description = NULL;
2101 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2104 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2106 char *token = getStringCat2(element_info[i].token_name, ".name");
2107 char *value = getHashEntry(setup_file_hash, token);
2110 element_info[i].custom_description = getStringCopy(value);
2115 freeSetupFileHash(setup_file_hash);
2118 void LoadSpecialMenuDesignSettings()
2120 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2121 SetupFileHash *setup_file_hash;
2124 /* always start with reliable default values from default config */
2125 for (i=0; image_config_vars[i].token != NULL; i++)
2126 for (j=0; image_config[j].token != NULL; j++)
2127 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2128 *image_config_vars[i].value =
2129 get_integer_from_string(image_config[j].value);
2131 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2134 /* special case: initialize with default values that may be overwritten */
2135 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2137 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2138 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2139 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2141 if (value_x != NULL)
2142 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2143 if (value_y != NULL)
2144 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2145 if (list_size != NULL)
2146 menu.list_size[i] = get_integer_from_string(list_size);
2149 /* read (and overwrite with) values that may be specified in config file */
2150 for (i=0; image_config_vars[i].token != NULL; i++)
2152 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2155 *image_config_vars[i].value = get_integer_from_string(value);
2158 freeSetupFileHash(setup_file_hash);