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 134 /* 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 for(j=0; j<MAX_ELEMENT_NAME_LEN + 1; j++)
109 element_info[element].description[j] = '\0';
110 if (element_info[element].custom_description != NULL)
111 strncpy(element_info[element].description,
112 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
114 strcpy(element_info[element].description,
115 element_info[element].editor_description);
117 element_info[element].use_gfx_element = FALSE;
118 element_info[element].gfx_element = EL_EMPTY_SPACE;
120 element_info[element].score = 0;
121 element_info[element].gem_count = 0;
123 element_info[element].push_delay_fixed = 2; /* special default */
124 element_info[element].push_delay_random = 8; /* special default */
125 element_info[element].move_delay_fixed = 0;
126 element_info[element].move_delay_random = 0;
128 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
129 element_info[element].move_direction_initial = MV_NO_MOVING;
130 element_info[element].move_stepsize = TILEX / 8;
134 element_info[element].content[x][y] = EL_EMPTY_SPACE;
136 element_info[element].change.events = CE_BITMASK_DEFAULT;
137 element_info[element].change.target_element = EL_EMPTY_SPACE;
139 element_info[element].change.delay_fixed = 0;
140 element_info[element].change.delay_random = 0;
141 element_info[element].change.delay_frames = -1; /* use default */
143 element_info[element].change.trigger_element = EL_EMPTY_SPACE;
145 element_info[element].change.explode = FALSE;
146 element_info[element].change.use_content = FALSE;
147 element_info[element].change.only_complete = FALSE;
148 element_info[element].change.use_random_change = FALSE;
149 element_info[element].change.random = 0;
150 element_info[element].change.power = CP_NON_DESTRUCTIVE;
154 element_info[element].change.content[x][y] = EL_EMPTY_SPACE;
156 element_info[element].access_type = 0;
157 element_info[element].access_layer = 0;
158 element_info[element].walk_to_action = 0;
159 element_info[element].smash_targets = 0;
160 element_info[element].deadliness = 0;
161 element_info[element].consistency = 0;
162 element_info[element].change_player_action = 0;
163 element_info[element].change_collide_action = 0;
164 element_info[element].change_other_action = 0;
166 element_info[element].can_explode_by_fire = FALSE;
167 element_info[element].can_explode_smashed = FALSE;
168 element_info[element].can_explode_impact = FALSE;
170 /* start with no properties at all */
171 for (j=0; j < NUM_EP_BITFIELDS; j++)
172 Properties[element][j] = EP_BITMASK_DEFAULT;
175 BorderElement = EL_STEELWALL;
177 level.no_level_file = FALSE;
179 if (leveldir_current == NULL) /* only when dumping level */
182 /* try to determine better author name than 'anonymous' */
183 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
185 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
186 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
190 switch (LEVELCLASS(leveldir_current))
192 case LEVELCLASS_TUTORIAL:
193 strcpy(level.author, PROGRAM_AUTHOR_STRING);
196 case LEVELCLASS_CONTRIBUTION:
197 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
198 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
201 case LEVELCLASS_USER:
202 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
203 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
207 /* keep default value */
213 static int checkLevelElement(int element)
215 if (element >= NUM_FILE_ELEMENTS)
217 Error(ERR_WARN, "invalid level element %d", element);
218 element = EL_CHAR_QUESTION;
220 else if (element == EL_PLAYER_OBSOLETE)
221 element = EL_PLAYER_1;
222 else if (element == EL_KEY_OBSOLETE)
228 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
230 level->file_version = getFileVersion(file);
231 level->game_version = getFileVersion(file);
236 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
240 lev_fieldx = level->fieldx = fgetc(file);
241 lev_fieldy = level->fieldy = fgetc(file);
243 level->time = getFile16BitBE(file);
244 level->gems_needed = getFile16BitBE(file);
246 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
247 level->name[i] = fgetc(file);
248 level->name[MAX_LEVEL_NAME_LEN] = 0;
250 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
251 level->score[i] = fgetc(file);
253 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
254 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
257 level->yamyam_content[i][x][y] = checkLevelElement(fgetc(file));
259 level->amoeba_speed = fgetc(file);
260 level->time_magic_wall = fgetc(file);
261 level->time_wheel = fgetc(file);
262 level->amoeba_content = checkLevelElement(fgetc(file));
263 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
264 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
265 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
266 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
268 level->use_custom_template = (fgetc(file) == 1 ? TRUE : FALSE);
270 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
275 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
279 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
280 level->author[i] = fgetc(file);
281 level->author[MAX_LEVEL_NAME_LEN] = 0;
286 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
289 int chunk_size_expected = level->fieldx * level->fieldy;
291 /* Note: "chunk_size" was wrong before version 2.0 when elements are
292 stored with 16-bit encoding (and should be twice as big then).
293 Even worse, playfield data was stored 16-bit when only yamyam content
294 contained 16-bit elements and vice versa. */
296 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
297 chunk_size_expected *= 2;
299 if (chunk_size_expected != chunk_size)
301 ReadUnusedBytesFromFile(file, chunk_size);
302 return chunk_size_expected;
305 for(y=0; y<level->fieldy; y++)
306 for(x=0; x<level->fieldx; x++)
307 Feld[x][y] = Ur[x][y] =
308 checkLevelElement(level->encoding_16bit_field ?
309 getFile16BitBE(file) : fgetc(file));
313 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
317 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
318 int chunk_size_expected = header_size + content_size;
320 /* Note: "chunk_size" was wrong before version 2.0 when elements are
321 stored with 16-bit encoding (and should be twice as big then).
322 Even worse, playfield data was stored 16-bit when only yamyam content
323 contained 16-bit elements and vice versa. */
325 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
326 chunk_size_expected += content_size;
328 if (chunk_size_expected != chunk_size)
330 ReadUnusedBytesFromFile(file, chunk_size);
331 return chunk_size_expected;
335 level->num_yamyam_contents = fgetc(file);
339 /* correct invalid number of content fields -- should never happen */
340 if (level->num_yamyam_contents < 1 ||
341 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
342 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
344 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
347 level->yamyam_content[i][x][y] =
348 checkLevelElement(level->encoding_16bit_field ?
349 getFile16BitBE(file) : fgetc(file));
353 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
357 int num_contents, content_xsize, content_ysize;
358 int content_array[MAX_ELEMENT_CONTENTS][3][3];
360 element = checkLevelElement(getFile16BitBE(file));
361 num_contents = fgetc(file);
362 content_xsize = fgetc(file);
363 content_ysize = fgetc(file);
364 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
366 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
369 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
371 /* correct invalid number of content fields -- should never happen */
372 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
373 num_contents = STD_ELEMENT_CONTENTS;
375 if (element == EL_YAMYAM)
377 level->num_yamyam_contents = num_contents;
379 for(i=0; i<num_contents; i++)
382 level->yamyam_content[i][x][y] = content_array[i][x][y];
384 else if (element == EL_BD_AMOEBA)
386 level->amoeba_content = content_array[0][0][0];
390 Error(ERR_WARN, "cannot load content for element '%d'", element);
396 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
398 int num_changed_custom_elements = getFile16BitBE(file);
399 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
402 if (chunk_size_expected != chunk_size)
404 ReadUnusedBytesFromFile(file, chunk_size - 2);
405 return chunk_size_expected;
408 for (i=0; i < num_changed_custom_elements; i++)
410 int element = getFile16BitBE(file);
411 int properties = getFile32BitBE(file);
413 if (IS_CUSTOM_ELEMENT(element))
414 Properties[element][EP_BITFIELD_BASE] = properties;
416 Error(ERR_WARN, "invalid custom element number %d", element);
422 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
424 int num_changed_custom_elements = getFile16BitBE(file);
425 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
428 if (chunk_size_expected != chunk_size)
430 ReadUnusedBytesFromFile(file, chunk_size - 2);
431 return chunk_size_expected;
434 for (i=0; i < num_changed_custom_elements; i++)
436 int element = getFile16BitBE(file);
437 int custom_target_element = getFile16BitBE(file);
439 if (IS_CUSTOM_ELEMENT(element))
440 element_info[element].change.target_element = custom_target_element;
442 Error(ERR_WARN, "invalid custom element number %d", element);
448 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
450 int num_changed_custom_elements = getFile16BitBE(file);
451 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
454 if (chunk_size_expected != chunk_size)
456 ReadUnusedBytesFromFile(file, chunk_size - 2);
457 return chunk_size_expected;
460 for (i=0; i < num_changed_custom_elements; i++)
462 int element = getFile16BitBE(file);
464 if (!IS_CUSTOM_ELEMENT(element))
466 Error(ERR_WARN, "invalid custom element number %d", element);
468 element = EL_DEFAULT; /* dummy element used for artwork config */
471 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
472 element_info[element].description[j] = getFile8Bit(file);
473 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
475 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
477 /* some free bytes for future properties and padding */
478 ReadUnusedBytesFromFile(file, 7);
480 element_info[element].use_gfx_element = getFile8Bit(file);
481 element_info[element].gfx_element =
482 checkLevelElement(getFile16BitBE(file));
484 element_info[element].score = getFile8Bit(file);
485 element_info[element].gem_count = getFile8Bit(file);
487 element_info[element].push_delay_fixed = getFile16BitBE(file);
488 element_info[element].push_delay_random = getFile16BitBE(file);
489 element_info[element].move_delay_fixed = getFile16BitBE(file);
490 element_info[element].move_delay_random = getFile16BitBE(file);
492 element_info[element].move_pattern = getFile16BitBE(file);
493 element_info[element].move_direction_initial = getFile8Bit(file);
494 element_info[element].move_stepsize = getFile8Bit(file);
498 element_info[element].content[x][y] =
499 checkLevelElement(getFile16BitBE(file));
501 element_info[element].change.events = getFile32BitBE(file);
503 element_info[element].change.target_element =
504 checkLevelElement(getFile16BitBE(file));
506 element_info[element].change.delay_fixed = getFile16BitBE(file);
507 element_info[element].change.delay_random = getFile16BitBE(file);
508 element_info[element].change.delay_frames = getFile16BitBE(file);
510 element_info[element].change.trigger_element =
511 checkLevelElement(getFile16BitBE(file));
513 element_info[element].change.explode = getFile8Bit(file);
514 element_info[element].change.use_content = getFile8Bit(file);
515 element_info[element].change.only_complete = getFile8Bit(file);
516 element_info[element].change.use_random_change = getFile8Bit(file);
518 element_info[element].change.random = getFile8Bit(file);
519 element_info[element].change.power = getFile8Bit(file);
523 element_info[element].change.content[x][y] =
524 checkLevelElement(getFile16BitBE(file));
526 /* some free bytes for future properties and padding */
527 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
533 void LoadLevelFromFilename(char *filename)
535 char cookie[MAX_LINE_LEN];
536 char chunk_name[CHUNK_ID_LEN + 1];
540 /* always start with reliable default values */
541 setLevelInfoToDefaults();
543 if (!(file = fopen(filename, MODE_READ)))
545 level.no_level_file = TRUE;
547 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
551 getFileChunkBE(file, chunk_name, NULL);
552 if (strcmp(chunk_name, "RND1") == 0)
554 getFile32BitBE(file); /* not used */
556 getFileChunkBE(file, chunk_name, NULL);
557 if (strcmp(chunk_name, "CAVE") != 0)
559 Error(ERR_WARN, "unknown format of level file '%s'", filename);
564 else /* check for pre-2.0 file format with cookie string */
566 strcpy(cookie, chunk_name);
567 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
568 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
569 cookie[strlen(cookie) - 1] = '\0';
571 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
573 Error(ERR_WARN, "unknown format of level file '%s'", filename);
578 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
580 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
585 /* pre-2.0 level files have no game version, so use file version here */
586 level.game_version = level.file_version;
589 if (level.file_version < FILE_VERSION_1_2)
591 /* level files from versions before 1.2.0 without chunk structure */
592 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
593 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
601 int (*loader)(FILE *, int, struct LevelInfo *);
605 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
606 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
607 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
608 { "BODY", -1, LoadLevel_BODY },
609 { "CONT", -1, LoadLevel_CONT },
610 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
611 { "CUS1", -1, LoadLevel_CUS1 },
612 { "CUS2", -1, LoadLevel_CUS2 },
613 { "CUS3", -1, LoadLevel_CUS3 },
617 while (getFileChunkBE(file, chunk_name, &chunk_size))
621 while (chunk_info[i].name != NULL &&
622 strcmp(chunk_name, chunk_info[i].name) != 0)
625 if (chunk_info[i].name == NULL)
627 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
628 chunk_name, filename);
629 ReadUnusedBytesFromFile(file, chunk_size);
631 else if (chunk_info[i].size != -1 &&
632 chunk_info[i].size != chunk_size)
634 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
635 chunk_size, chunk_name, filename);
636 ReadUnusedBytesFromFile(file, chunk_size);
640 /* call function to load this level chunk */
641 int chunk_size_expected =
642 (chunk_info[i].loader)(file, chunk_size, &level);
644 /* the size of some chunks cannot be checked before reading other
645 chunks first (like "HEAD" and "BODY") that contain some header
646 information, so check them here */
647 if (chunk_size_expected != chunk_size)
649 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
650 chunk_size, chunk_name, filename);
658 if (leveldir_current == NULL) /* only when dumping level */
661 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
662 IS_LEVELCLASS_USER(leveldir_current))
664 /* For user contributed and private levels, use the version of
665 the game engine the levels were created for.
666 Since 2.0.1, the game engine version is now directly stored
667 in the level file (chunk "VERS"), so there is no need anymore
668 to set the game version from the file version (except for old,
669 pre-2.0 levels, where the game version is still taken from the
670 file format version used to store the level -- see above). */
672 /* do some special adjustments to support older level versions */
673 if (level.file_version == FILE_VERSION_1_0)
675 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
676 Error(ERR_WARN, "using high speed movement for player");
678 /* player was faster than monsters in (pre-)1.0 levels */
679 level.double_speed = TRUE;
682 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
683 if (level.game_version == VERSION_IDENT(2,0,1))
684 level.em_slippery_gems = TRUE;
688 /* Always use the latest version of the game engine for all but
689 user contributed and private levels; this allows for actual
690 corrections in the game engine to take effect for existing,
691 converted levels (from "classic" or other existing games) to
692 make the game emulation more accurate, while (hopefully) not
693 breaking existing levels created from other players. */
695 level.game_version = GAME_VERSION_ACTUAL;
697 /* Set special EM style gems behaviour: EM style gems slip down from
698 normal, steel and growing wall. As this is a more fundamental change,
699 it seems better to set the default behaviour to "off" (as it is more
700 natural) and make it configurable in the level editor (as a property
701 of gem style elements). Already existing converted levels (neither
702 private nor contributed levels) are changed to the new behaviour. */
704 if (level.file_version < FILE_VERSION_2_0)
705 level.em_slippery_gems = TRUE;
708 /* map some elements which have changed in newer versions */
709 if (level.game_version <= VERSION_IDENT(2,2,0))
713 /* map game font elements */
714 for(y=0; y<level.fieldy; y++)
716 for(x=0; x<level.fieldx; x++)
718 int element = Ur[x][y];
720 if (element == EL_CHAR('['))
721 element = EL_CHAR_AUMLAUT;
722 else if (element == EL_CHAR('\\'))
723 element = EL_CHAR_OUMLAUT;
724 else if (element == EL_CHAR(']'))
725 element = EL_CHAR_UUMLAUT;
726 else if (element == EL_CHAR('^'))
727 element = EL_CHAR_COPYRIGHT;
729 Feld[x][y] = Ur[x][y] = element;
734 /* determine border element for this level */
738 void LoadLevel(int level_nr)
740 char *filename = getLevelFilename(level_nr);
742 LoadLevelFromFilename(filename);
743 InitElementPropertiesEngine(level.game_version);
746 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
748 putFileVersion(file, level->file_version);
749 putFileVersion(file, level->game_version);
752 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
756 fputc(level->fieldx, file);
757 fputc(level->fieldy, file);
759 putFile16BitBE(file, level->time);
760 putFile16BitBE(file, level->gems_needed);
762 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
763 fputc(level->name[i], file);
765 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
766 fputc(level->score[i], file);
768 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
771 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
772 level->yamyam_content[i][x][y]),
774 fputc(level->amoeba_speed, file);
775 fputc(level->time_magic_wall, file);
776 fputc(level->time_wheel, file);
777 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
779 fputc((level->double_speed ? 1 : 0), file);
780 fputc((level->gravity ? 1 : 0), file);
781 fputc((level->encoding_16bit_field ? 1 : 0), file);
782 fputc((level->em_slippery_gems ? 1 : 0), file);
784 fputc((level->use_custom_template ? 1 : 0), file);
786 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
789 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
793 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
794 fputc(level->author[i], file);
797 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
801 for(y=0; y<level->fieldy; y++)
802 for(x=0; x<level->fieldx; x++)
803 if (level->encoding_16bit_field)
804 putFile16BitBE(file, Ur[x][y]);
806 fputc(Ur[x][y], file);
810 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
814 fputc(EL_YAMYAM, file);
815 fputc(level->num_yamyam_contents, file);
819 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
822 if (level->encoding_16bit_field)
823 putFile16BitBE(file, level->yamyam_content[i][x][y]);
825 fputc(level->yamyam_content[i][x][y], file);
829 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
832 int num_contents, content_xsize, content_ysize;
833 int content_array[MAX_ELEMENT_CONTENTS][3][3];
835 if (element == EL_YAMYAM)
837 num_contents = level->num_yamyam_contents;
841 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
844 content_array[i][x][y] = level->yamyam_content[i][x][y];
846 else if (element == EL_BD_AMOEBA)
852 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
855 content_array[i][x][y] = EL_EMPTY;
856 content_array[0][0][0] = level->amoeba_content;
860 /* chunk header already written -- write empty chunk data */
861 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
863 Error(ERR_WARN, "cannot save content for element '%d'", element);
867 putFile16BitBE(file, element);
868 fputc(num_contents, file);
869 fputc(content_xsize, file);
870 fputc(content_ysize, file);
872 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
874 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
877 putFile16BitBE(file, content_array[i][x][y]);
881 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
882 int num_changed_custom_elements)
886 putFile16BitBE(file, num_changed_custom_elements);
888 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
890 int element = EL_CUSTOM_START + i;
892 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
894 if (check < num_changed_custom_elements)
896 putFile16BitBE(file, element);
897 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
904 if (check != num_changed_custom_elements) /* should not happen */
905 Error(ERR_WARN, "inconsistent number of custom element properties");
910 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
911 int num_changed_custom_elements)
915 putFile16BitBE(file, num_changed_custom_elements);
917 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
919 int element = EL_CUSTOM_START + i;
921 if (element_info[element].change.target_element != EL_EMPTY_SPACE)
923 if (check < num_changed_custom_elements)
925 putFile16BitBE(file, element);
926 putFile16BitBE(file, element_info[element].change.target_element);
933 if (check != num_changed_custom_elements) /* should not happen */
934 Error(ERR_WARN, "inconsistent number of custom target elements");
938 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
939 int num_changed_custom_elements)
941 int i, j, x, y, check = 0;
943 putFile16BitBE(file, num_changed_custom_elements);
945 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
947 int element = EL_CUSTOM_START + i;
949 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
951 if (check < num_changed_custom_elements)
953 putFile16BitBE(file, element);
955 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
956 putFile8Bit(file, element_info[element].description[j]);
958 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
960 /* some free bytes for future properties and padding */
961 WriteUnusedBytesToFile(file, 7);
963 putFile8Bit(file, element_info[element].use_gfx_element);
964 putFile16BitBE(file, element_info[element].gfx_element);
966 putFile8Bit(file, element_info[element].score);
967 putFile8Bit(file, element_info[element].gem_count);
969 putFile16BitBE(file, element_info[element].push_delay_fixed);
970 putFile16BitBE(file, element_info[element].push_delay_random);
971 putFile16BitBE(file, element_info[element].move_delay_fixed);
972 putFile16BitBE(file, element_info[element].move_delay_random);
974 putFile16BitBE(file, element_info[element].move_pattern);
975 putFile8Bit(file, element_info[element].move_direction_initial);
976 putFile8Bit(file, element_info[element].move_stepsize);
980 putFile16BitBE(file, element_info[element].content[x][y]);
982 putFile32BitBE(file, element_info[element].change.events);
984 putFile16BitBE(file, element_info[element].change.target_element);
986 putFile16BitBE(file, element_info[element].change.delay_fixed);
987 putFile16BitBE(file, element_info[element].change.delay_random);
988 putFile16BitBE(file, element_info[element].change.delay_frames);
990 putFile16BitBE(file, element_info[element].change.trigger_element);
992 putFile8Bit(file, element_info[element].change.explode);
993 putFile8Bit(file, element_info[element].change.use_content);
994 putFile8Bit(file, element_info[element].change.only_complete);
995 putFile8Bit(file, element_info[element].change.use_random_change);
997 putFile8Bit(file, element_info[element].change.random);
998 putFile8Bit(file, element_info[element].change.power);
1002 putFile16BitBE(file, element_info[element].change.content[x][y]);
1004 /* some free bytes for future properties and padding */
1005 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1012 if (check != num_changed_custom_elements) /* should not happen */
1013 Error(ERR_WARN, "inconsistent number of custom element properties");
1016 void SaveLevel(int level_nr)
1018 char *filename = getLevelFilename(level_nr);
1019 int body_chunk_size;
1020 int num_changed_custom_elements = 0;
1021 int level_chunk_CUS3_size;
1025 if (!(file = fopen(filename, MODE_WRITE)))
1027 Error(ERR_WARN, "cannot save level file '%s'", filename);
1031 level.file_version = FILE_VERSION_ACTUAL;
1032 level.game_version = GAME_VERSION_ACTUAL;
1034 /* check level field for 16-bit elements */
1035 level.encoding_16bit_field = FALSE;
1036 for(y=0; y<level.fieldy; y++)
1037 for(x=0; x<level.fieldx; x++)
1039 level.encoding_16bit_field = TRUE;
1041 /* check yamyam content for 16-bit elements */
1042 level.encoding_16bit_yamyam = FALSE;
1043 for(i=0; i<level.num_yamyam_contents; i++)
1046 if (level.yamyam_content[i][x][y] > 255)
1047 level.encoding_16bit_yamyam = TRUE;
1049 /* check amoeba content for 16-bit elements */
1050 level.encoding_16bit_amoeba = FALSE;
1051 if (level.amoeba_content > 255)
1052 level.encoding_16bit_amoeba = TRUE;
1054 /* calculate size of "BODY" chunk */
1056 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
1058 /* check for non-standard custom elements and calculate "CUS3" chunk size */
1059 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1060 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1061 num_changed_custom_elements++;
1062 level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1064 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1065 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1067 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1068 SaveLevel_VERS(file, &level);
1070 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1071 SaveLevel_HEAD(file, &level);
1073 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1074 SaveLevel_AUTH(file, &level);
1076 putFileChunkBE(file, "BODY", body_chunk_size);
1077 SaveLevel_BODY(file, &level);
1079 if (level.encoding_16bit_yamyam ||
1080 level.num_yamyam_contents != STD_ELEMENT_CONTENTS)
1082 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1083 SaveLevel_CNT2(file, &level, EL_YAMYAM);
1086 if (level.encoding_16bit_amoeba)
1088 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1089 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
1092 if (num_changed_custom_elements > 0)
1094 putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1095 SaveLevel_CUS3(file, &level, num_changed_custom_elements);
1100 SetFilePermissions(filename, PERMS_PRIVATE);
1103 void DumpLevel(struct LevelInfo *level)
1105 printf_line("-", 79);
1106 printf("Level xxx (file version %08d, game version %08d)\n",
1107 level->file_version, level->game_version);
1108 printf_line("-", 79);
1110 printf("Level Author: '%s'\n", level->author);
1111 printf("Level Title: '%s'\n", level->name);
1113 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1115 printf("Level Time: %d seconds\n", level->time);
1116 printf("Gems needed: %d\n", level->gems_needed);
1118 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1119 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1120 printf("Time for Light: %d seconds\n", level->time_light);
1121 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1123 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1125 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1126 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1127 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1129 printf_line("-", 79);
1133 /* ========================================================================= */
1134 /* tape file functions */
1135 /* ========================================================================= */
1137 static void setTapeInfoToDefaults()
1141 /* always start with reliable default values (empty tape) */
1144 /* default values (also for pre-1.2 tapes) with only the first player */
1145 tape.player_participates[0] = TRUE;
1146 for(i=1; i<MAX_PLAYERS; i++)
1147 tape.player_participates[i] = FALSE;
1149 /* at least one (default: the first) player participates in every tape */
1150 tape.num_participating_players = 1;
1152 tape.level_nr = level_nr;
1154 tape.changed = FALSE;
1156 tape.recording = FALSE;
1157 tape.playing = FALSE;
1158 tape.pausing = FALSE;
1161 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1163 tape->file_version = getFileVersion(file);
1164 tape->game_version = getFileVersion(file);
1169 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1173 tape->random_seed = getFile32BitBE(file);
1174 tape->date = getFile32BitBE(file);
1175 tape->length = getFile32BitBE(file);
1177 /* read header fields that are new since version 1.2 */
1178 if (tape->file_version >= FILE_VERSION_1_2)
1180 byte store_participating_players = fgetc(file);
1183 /* since version 1.2, tapes store which players participate in the tape */
1184 tape->num_participating_players = 0;
1185 for(i=0; i<MAX_PLAYERS; i++)
1187 tape->player_participates[i] = FALSE;
1189 if (store_participating_players & (1 << i))
1191 tape->player_participates[i] = TRUE;
1192 tape->num_participating_players++;
1196 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1198 engine_version = getFileVersion(file);
1199 if (engine_version > 0)
1200 tape->engine_version = engine_version;
1206 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1208 int level_identifier_size;
1211 level_identifier_size = getFile16BitBE(file);
1213 tape->level_identifier =
1214 checked_realloc(tape->level_identifier, level_identifier_size);
1216 for(i=0; i < level_identifier_size; i++)
1217 tape->level_identifier[i] = fgetc(file);
1219 tape->level_nr = getFile16BitBE(file);
1221 chunk_size = 2 + level_identifier_size + 2;
1226 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1229 int chunk_size_expected =
1230 (tape->num_participating_players + 1) * tape->length;
1232 if (chunk_size_expected != chunk_size)
1234 ReadUnusedBytesFromFile(file, chunk_size);
1235 return chunk_size_expected;
1238 for(i=0; i<tape->length; i++)
1240 if (i >= MAX_TAPELEN)
1243 for(j=0; j<MAX_PLAYERS; j++)
1245 tape->pos[i].action[j] = MV_NO_MOVING;
1247 if (tape->player_participates[j])
1248 tape->pos[i].action[j] = fgetc(file);
1251 tape->pos[i].delay = fgetc(file);
1253 if (tape->file_version == FILE_VERSION_1_0)
1255 /* eliminate possible diagonal moves in old tapes */
1256 /* this is only for backward compatibility */
1258 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1259 byte action = tape->pos[i].action[0];
1260 int k, num_moves = 0;
1264 if (action & joy_dir[k])
1266 tape->pos[i + num_moves].action[0] = joy_dir[k];
1268 tape->pos[i + num_moves].delay = 0;
1277 tape->length += num_moves;
1280 else if (tape->file_version < FILE_VERSION_2_0)
1282 /* convert pre-2.0 tapes to new tape format */
1284 if (tape->pos[i].delay > 1)
1287 tape->pos[i + 1] = tape->pos[i];
1288 tape->pos[i + 1].delay = 1;
1291 for(j=0; j<MAX_PLAYERS; j++)
1292 tape->pos[i].action[j] = MV_NO_MOVING;
1293 tape->pos[i].delay--;
1304 if (i != tape->length)
1305 chunk_size = (tape->num_participating_players + 1) * i;
1310 void LoadTapeFromFilename(char *filename)
1312 char cookie[MAX_LINE_LEN];
1313 char chunk_name[CHUNK_ID_LEN + 1];
1317 /* always start with reliable default values */
1318 setTapeInfoToDefaults();
1320 if (!(file = fopen(filename, MODE_READ)))
1323 getFileChunkBE(file, chunk_name, NULL);
1324 if (strcmp(chunk_name, "RND1") == 0)
1326 getFile32BitBE(file); /* not used */
1328 getFileChunkBE(file, chunk_name, NULL);
1329 if (strcmp(chunk_name, "TAPE") != 0)
1331 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1336 else /* check for pre-2.0 file format with cookie string */
1338 strcpy(cookie, chunk_name);
1339 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1340 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1341 cookie[strlen(cookie) - 1] = '\0';
1343 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1345 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1350 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1352 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1357 /* pre-2.0 tape files have no game version, so use file version here */
1358 tape.game_version = tape.file_version;
1361 if (tape.file_version < FILE_VERSION_1_2)
1363 /* tape files from versions before 1.2.0 without chunk structure */
1364 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1365 LoadTape_BODY(file, 2 * tape.length, &tape);
1373 int (*loader)(FILE *, int, struct TapeInfo *);
1377 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1378 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1379 { "INFO", -1, LoadTape_INFO },
1380 { "BODY", -1, LoadTape_BODY },
1384 while (getFileChunkBE(file, chunk_name, &chunk_size))
1388 while (chunk_info[i].name != NULL &&
1389 strcmp(chunk_name, chunk_info[i].name) != 0)
1392 if (chunk_info[i].name == NULL)
1394 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1395 chunk_name, filename);
1396 ReadUnusedBytesFromFile(file, chunk_size);
1398 else if (chunk_info[i].size != -1 &&
1399 chunk_info[i].size != chunk_size)
1401 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1402 chunk_size, chunk_name, filename);
1403 ReadUnusedBytesFromFile(file, chunk_size);
1407 /* call function to load this tape chunk */
1408 int chunk_size_expected =
1409 (chunk_info[i].loader)(file, chunk_size, &tape);
1411 /* the size of some chunks cannot be checked before reading other
1412 chunks first (like "HEAD" and "BODY") that contain some header
1413 information, so check them here */
1414 if (chunk_size_expected != chunk_size)
1416 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1417 chunk_size, chunk_name, filename);
1425 tape.length_seconds = GetTapeLength();
1428 printf("tape version: %d\n", tape.game_version);
1432 void LoadTape(int level_nr)
1434 char *filename = getTapeFilename(level_nr);
1436 LoadTapeFromFilename(filename);
1439 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1441 putFileVersion(file, tape->file_version);
1442 putFileVersion(file, tape->game_version);
1445 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1448 byte store_participating_players = 0;
1450 /* set bits for participating players for compact storage */
1451 for(i=0; i<MAX_PLAYERS; i++)
1452 if (tape->player_participates[i])
1453 store_participating_players |= (1 << i);
1455 putFile32BitBE(file, tape->random_seed);
1456 putFile32BitBE(file, tape->date);
1457 putFile32BitBE(file, tape->length);
1459 fputc(store_participating_players, file);
1461 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1462 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1464 putFileVersion(file, tape->engine_version);
1467 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1469 int level_identifier_size = strlen(tape->level_identifier) + 1;
1472 putFile16BitBE(file, level_identifier_size);
1474 for(i=0; i < level_identifier_size; i++)
1475 fputc(tape->level_identifier[i], file);
1477 putFile16BitBE(file, tape->level_nr);
1480 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1484 for(i=0; i<tape->length; i++)
1486 for(j=0; j<MAX_PLAYERS; j++)
1487 if (tape->player_participates[j])
1488 fputc(tape->pos[i].action[j], file);
1490 fputc(tape->pos[i].delay, file);
1494 void SaveTape(int level_nr)
1496 char *filename = getTapeFilename(level_nr);
1498 boolean new_tape = TRUE;
1499 int num_participating_players = 0;
1500 int info_chunk_size;
1501 int body_chunk_size;
1504 InitTapeDirectory(leveldir_current->filename);
1506 /* if a tape still exists, ask to overwrite it */
1507 if (access(filename, F_OK) == 0)
1510 if (!Request("Replace old tape ?", REQ_ASK))
1514 if (!(file = fopen(filename, MODE_WRITE)))
1516 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1520 tape.file_version = FILE_VERSION_ACTUAL;
1521 tape.game_version = GAME_VERSION_ACTUAL;
1523 /* count number of participating players */
1524 for(i=0; i<MAX_PLAYERS; i++)
1525 if (tape.player_participates[i])
1526 num_participating_players++;
1528 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1529 body_chunk_size = (num_participating_players + 1) * tape.length;
1531 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1532 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1534 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1535 SaveTape_VERS(file, &tape);
1537 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1538 SaveTape_HEAD(file, &tape);
1540 putFileChunkBE(file, "INFO", info_chunk_size);
1541 SaveTape_INFO(file, &tape);
1543 putFileChunkBE(file, "BODY", body_chunk_size);
1544 SaveTape_BODY(file, &tape);
1548 SetFilePermissions(filename, PERMS_PRIVATE);
1550 tape.changed = FALSE;
1553 Request("tape saved !", REQ_CONFIRM);
1556 void DumpTape(struct TapeInfo *tape)
1560 if (TAPE_IS_EMPTY(*tape))
1562 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1566 printf_line("-", 79);
1567 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1568 tape->level_nr, tape->file_version, tape->game_version);
1569 printf("Level series identifier: '%s'\n", tape->level_identifier);
1570 printf_line("-", 79);
1572 for(i=0; i<tape->length; i++)
1574 if (i >= MAX_TAPELEN)
1577 printf("%03d: ", i);
1579 for(j=0; j<MAX_PLAYERS; j++)
1581 if (tape->player_participates[j])
1583 int action = tape->pos[i].action[j];
1585 printf("%d:%02x ", j, action);
1586 printf("[%c%c%c%c|%c%c] - ",
1587 (action & JOY_LEFT ? '<' : ' '),
1588 (action & JOY_RIGHT ? '>' : ' '),
1589 (action & JOY_UP ? '^' : ' '),
1590 (action & JOY_DOWN ? 'v' : ' '),
1591 (action & JOY_BUTTON_1 ? '1' : ' '),
1592 (action & JOY_BUTTON_2 ? '2' : ' '));
1596 printf("(%03d)\n", tape->pos[i].delay);
1599 printf_line("-", 79);
1603 /* ========================================================================= */
1604 /* score file functions */
1605 /* ========================================================================= */
1607 void LoadScore(int level_nr)
1610 char *filename = getScoreFilename(level_nr);
1611 char cookie[MAX_LINE_LEN];
1612 char line[MAX_LINE_LEN];
1616 /* always start with reliable default values */
1617 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1619 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1620 highscore[i].Score = 0;
1623 if (!(file = fopen(filename, MODE_READ)))
1626 /* check file identifier */
1627 fgets(cookie, MAX_LINE_LEN, file);
1628 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1629 cookie[strlen(cookie) - 1] = '\0';
1631 if (!checkCookieString(cookie, SCORE_COOKIE))
1633 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1638 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1640 fscanf(file, "%d", &highscore[i].Score);
1641 fgets(line, MAX_LINE_LEN, file);
1643 if (line[strlen(line) - 1] == '\n')
1644 line[strlen(line) - 1] = '\0';
1646 for (line_ptr = line; *line_ptr; line_ptr++)
1648 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1650 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1651 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1660 void SaveScore(int level_nr)
1663 char *filename = getScoreFilename(level_nr);
1666 InitScoreDirectory(leveldir_current->filename);
1668 if (!(file = fopen(filename, MODE_WRITE)))
1670 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1674 fprintf(file, "%s\n\n", SCORE_COOKIE);
1676 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1677 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1681 SetFilePermissions(filename, PERMS_PUBLIC);
1685 /* ========================================================================= */
1686 /* setup file functions */
1687 /* ========================================================================= */
1689 #define TOKEN_STR_PLAYER_PREFIX "player_"
1692 #define SETUP_TOKEN_PLAYER_NAME 0
1693 #define SETUP_TOKEN_SOUND 1
1694 #define SETUP_TOKEN_SOUND_LOOPS 2
1695 #define SETUP_TOKEN_SOUND_MUSIC 3
1696 #define SETUP_TOKEN_SOUND_SIMPLE 4
1697 #define SETUP_TOKEN_TOONS 5
1698 #define SETUP_TOKEN_SCROLL_DELAY 6
1699 #define SETUP_TOKEN_SOFT_SCROLLING 7
1700 #define SETUP_TOKEN_FADING 8
1701 #define SETUP_TOKEN_AUTORECORD 9
1702 #define SETUP_TOKEN_QUICK_DOORS 10
1703 #define SETUP_TOKEN_TEAM_MODE 11
1704 #define SETUP_TOKEN_HANDICAP 12
1705 #define SETUP_TOKEN_TIME_LIMIT 13
1706 #define SETUP_TOKEN_FULLSCREEN 14
1707 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1708 #define SETUP_TOKEN_GRAPHICS_SET 16
1709 #define SETUP_TOKEN_SOUNDS_SET 17
1710 #define SETUP_TOKEN_MUSIC_SET 18
1711 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1712 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1713 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1715 #define NUM_GLOBAL_SETUP_TOKENS 22
1718 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1719 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1720 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1721 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1722 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1723 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1724 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1725 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1726 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1728 #define NUM_EDITOR_SETUP_TOKENS 9
1730 /* shortcut setup */
1731 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1732 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1733 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1735 #define NUM_SHORTCUT_SETUP_TOKENS 3
1738 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1739 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1740 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1741 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1742 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1743 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1744 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1745 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1746 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1747 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1748 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1749 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1750 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1751 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1752 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1753 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1755 #define NUM_PLAYER_SETUP_TOKENS 16
1758 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1759 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1761 #define NUM_SYSTEM_SETUP_TOKENS 2
1764 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1766 #define NUM_OPTIONS_SETUP_TOKENS 1
1769 static struct SetupInfo si;
1770 static struct SetupEditorInfo sei;
1771 static struct SetupShortcutInfo ssi;
1772 static struct SetupInputInfo sii;
1773 static struct SetupSystemInfo syi;
1774 static struct OptionInfo soi;
1776 static struct TokenInfo global_setup_tokens[] =
1778 { TYPE_STRING, &si.player_name, "player_name" },
1779 { TYPE_SWITCH, &si.sound, "sound" },
1780 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1781 { TYPE_SWITCH, &si.sound_music, "background_music" },
1782 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1783 { TYPE_SWITCH, &si.toons, "toons" },
1784 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1785 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1786 { TYPE_SWITCH, &si.fading, "screen_fading" },
1787 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1788 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1789 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1790 { TYPE_SWITCH, &si.handicap, "handicap" },
1791 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1792 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1793 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1794 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1795 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1796 { TYPE_STRING, &si.music_set, "music_set" },
1797 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1798 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1799 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1802 static struct TokenInfo editor_setup_tokens[] =
1804 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1805 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1806 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1807 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1808 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1809 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1810 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1811 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1812 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1815 static struct TokenInfo shortcut_setup_tokens[] =
1817 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1818 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1819 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1822 static struct TokenInfo player_setup_tokens[] =
1824 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1825 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1826 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1827 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1828 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1829 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1830 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1831 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1832 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1833 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1834 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1835 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1836 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1837 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1838 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1839 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1842 static struct TokenInfo system_setup_tokens[] =
1844 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1845 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1848 static struct TokenInfo options_setup_tokens[] =
1850 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1853 static char *get_corrected_login_name(char *login_name)
1855 /* needed because player name must be a fixed length string */
1856 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1858 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1859 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1861 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1862 if (strchr(login_name_new, ' '))
1863 *strchr(login_name_new, ' ') = '\0';
1865 return login_name_new;
1868 static void setSetupInfoToDefaults(struct SetupInfo *si)
1872 si->player_name = get_corrected_login_name(getLoginName());
1875 si->sound_loops = TRUE;
1876 si->sound_music = TRUE;
1877 si->sound_simple = TRUE;
1879 si->double_buffering = TRUE;
1880 si->direct_draw = !si->double_buffering;
1881 si->scroll_delay = TRUE;
1882 si->soft_scrolling = TRUE;
1884 si->autorecord = TRUE;
1885 si->quick_doors = FALSE;
1886 si->team_mode = FALSE;
1887 si->handicap = TRUE;
1888 si->time_limit = TRUE;
1889 si->fullscreen = FALSE;
1890 si->ask_on_escape = TRUE;
1892 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1893 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1894 si->music_set = getStringCopy(MUSIC_SUBDIR);
1895 si->override_level_graphics = FALSE;
1896 si->override_level_sounds = FALSE;
1897 si->override_level_music = FALSE;
1899 si->editor.el_boulderdash = TRUE;
1900 si->editor.el_emerald_mine = TRUE;
1901 si->editor.el_more = TRUE;
1902 si->editor.el_sokoban = TRUE;
1903 si->editor.el_supaplex = TRUE;
1904 si->editor.el_diamond_caves = TRUE;
1905 si->editor.el_dx_boulderdash = TRUE;
1906 si->editor.el_chars = TRUE;
1907 si->editor.el_custom = TRUE;
1909 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1910 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1911 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1913 for (i=0; i<MAX_PLAYERS; i++)
1915 si->input[i].use_joystick = FALSE;
1916 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1917 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1918 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1919 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1920 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1921 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1922 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1923 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1924 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1925 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1926 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1927 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1928 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1929 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1930 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1933 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1934 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1936 si->options.verbose = FALSE;
1939 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
1943 if (!setup_file_hash)
1948 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1949 setSetupInfo(global_setup_tokens, i,
1950 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
1955 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1956 setSetupInfo(editor_setup_tokens, i,
1957 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
1960 /* shortcut setup */
1961 ssi = setup.shortcut;
1962 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1963 setSetupInfo(shortcut_setup_tokens, i,
1964 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
1965 setup.shortcut = ssi;
1968 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1972 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1974 sii = setup.input[pnr];
1975 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1977 char full_token[100];
1979 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1980 setSetupInfo(player_setup_tokens, i,
1981 getHashEntry(setup_file_hash, full_token));
1983 setup.input[pnr] = sii;
1988 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1989 setSetupInfo(system_setup_tokens, i,
1990 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
1994 soi = setup.options;
1995 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1996 setSetupInfo(options_setup_tokens, i,
1997 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
1998 setup.options = soi;
2003 char *filename = getSetupFilename();
2004 SetupFileHash *setup_file_hash = NULL;
2006 /* always start with reliable default values */
2007 setSetupInfoToDefaults(&setup);
2009 setup_file_hash = loadSetupFileHash(filename);
2011 if (setup_file_hash)
2013 char *player_name_new;
2015 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2016 decodeSetupFileHash(setup_file_hash);
2018 setup.direct_draw = !setup.double_buffering;
2020 freeSetupFileHash(setup_file_hash);
2022 /* needed to work around problems with fixed length strings */
2023 player_name_new = get_corrected_login_name(setup.player_name);
2024 free(setup.player_name);
2025 setup.player_name = player_name_new;
2028 Error(ERR_WARN, "using default setup values");
2033 char *filename = getSetupFilename();
2037 InitUserDataDirectory();
2039 if (!(file = fopen(filename, MODE_WRITE)))
2041 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2045 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2046 getCookie("SETUP")));
2047 fprintf(file, "\n");
2051 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2053 /* just to make things nicer :) */
2054 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2055 i == SETUP_TOKEN_GRAPHICS_SET)
2056 fprintf(file, "\n");
2058 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2063 fprintf(file, "\n");
2064 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2065 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2067 /* shortcut setup */
2068 ssi = setup.shortcut;
2069 fprintf(file, "\n");
2070 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2071 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2074 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2078 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2079 fprintf(file, "\n");
2081 sii = setup.input[pnr];
2082 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2083 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2088 fprintf(file, "\n");
2089 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2090 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2093 soi = setup.options;
2094 fprintf(file, "\n");
2095 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2096 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2100 SetFilePermissions(filename, PERMS_PRIVATE);
2103 void LoadCustomElementDescriptions()
2105 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2106 SetupFileHash *setup_file_hash;
2109 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2111 if (element_info[i].custom_description != NULL)
2113 free(element_info[i].custom_description);
2114 element_info[i].custom_description = NULL;
2118 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2121 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2123 char *token = getStringCat2(element_info[i].token_name, ".name");
2124 char *value = getHashEntry(setup_file_hash, token);
2127 element_info[i].custom_description = getStringCopy(value);
2132 freeSetupFileHash(setup_file_hash);
2135 void LoadSpecialMenuDesignSettings()
2137 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2138 SetupFileHash *setup_file_hash;
2141 /* always start with reliable default values from default config */
2142 for (i=0; image_config_vars[i].token != NULL; i++)
2143 for (j=0; image_config[j].token != NULL; j++)
2144 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2145 *image_config_vars[i].value =
2146 get_integer_from_string(image_config[j].value);
2148 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2151 /* special case: initialize with default values that may be overwritten */
2152 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2154 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2155 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2156 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2158 if (value_x != NULL)
2159 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2160 if (value_y != NULL)
2161 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2162 if (list_size != NULL)
2163 menu.list_size[i] = get_integer_from_string(list_size);
2166 /* read (and overwrite with) values that may be specified in config file */
2167 for (i=0; image_config_vars[i].token != NULL; i++)
2169 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2172 *image_config_vars[i].value = get_integer_from_string(value);
2175 freeSetupFileHash(setup_file_hash);