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; j++)
109 element_info[element].description[j] = '\0';
110 strcpy(element_info[element].description,
111 element_info[element].editor_description);
113 element_info[element].use_gfx_element = FALSE;
114 element_info[element].gfx_element = EL_EMPTY_SPACE;
116 element_info[element].score = 0;
117 element_info[element].gem_count = 0;
119 element_info[element].push_delay_fixed = 2; /* special default */
120 element_info[element].push_delay_random = 8; /* special default */
121 element_info[element].move_delay_fixed = 0;
122 element_info[element].move_delay_random = 0;
124 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
125 element_info[element].move_direction_initial = MV_NO_MOVING;
126 element_info[element].move_stepsize = TILEX / 8;
130 element_info[element].content[x][y] = EL_EMPTY_SPACE;
132 element_info[element].change.events = CE_BITMASK_DEFAULT;
133 element_info[element].change.target_element = EL_EMPTY_SPACE;
135 element_info[element].change.delay_fixed = 0;
136 element_info[element].change.delay_random = 0;
137 element_info[element].change.delay_frames = -1; /* use default */
139 element_info[element].change.trigger_element = EL_EMPTY_SPACE;
141 element_info[element].change.explode = FALSE;
142 element_info[element].change.use_content = FALSE;
143 element_info[element].change.only_complete = FALSE;
144 element_info[element].change.use_random_change = FALSE;
145 element_info[element].change.random = 0;
146 element_info[element].change.power = CP_NON_DESTRUCTIVE;
150 element_info[element].change.content[x][y] = EL_EMPTY_SPACE;
152 element_info[element].access_type = 0;
153 element_info[element].access_layer = 0;
154 element_info[element].walk_to_action = 0;
155 element_info[element].smash_targets = 0;
156 element_info[element].deadliness = 0;
157 element_info[element].consistency = 0;
158 element_info[element].change_player_action = 0;
159 element_info[element].change_collide_action = 0;
160 element_info[element].change_other_action = 0;
162 element_info[element].can_explode_by_fire = FALSE;
163 element_info[element].can_explode_smashed = FALSE;
164 element_info[element].can_explode_impact = FALSE;
166 /* start with no properties at all */
167 for (j=0; j < NUM_EP_BITFIELDS; j++)
168 Properties[element][j] = EP_BITMASK_DEFAULT;
171 BorderElement = EL_STEELWALL;
173 level.no_level_file = FALSE;
175 if (leveldir_current == NULL) /* only when dumping level */
178 /* try to determine better author name than 'anonymous' */
179 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
181 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
182 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
186 switch (LEVELCLASS(leveldir_current))
188 case LEVELCLASS_TUTORIAL:
189 strcpy(level.author, PROGRAM_AUTHOR_STRING);
192 case LEVELCLASS_CONTRIBUTION:
193 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
194 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
197 case LEVELCLASS_USER:
198 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
199 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
203 /* keep default value */
209 static int checkLevelElement(int element)
211 if (element >= NUM_FILE_ELEMENTS)
213 Error(ERR_WARN, "invalid level element %d", element);
214 element = EL_CHAR_QUESTION;
216 else if (element == EL_PLAYER_OBSOLETE)
217 element = EL_PLAYER_1;
218 else if (element == EL_KEY_OBSOLETE)
224 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
226 level->file_version = getFileVersion(file);
227 level->game_version = getFileVersion(file);
232 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
236 lev_fieldx = level->fieldx = fgetc(file);
237 lev_fieldy = level->fieldy = fgetc(file);
239 level->time = getFile16BitBE(file);
240 level->gems_needed = getFile16BitBE(file);
242 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
243 level->name[i] = fgetc(file);
244 level->name[MAX_LEVEL_NAME_LEN] = 0;
246 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
247 level->score[i] = fgetc(file);
249 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
250 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
253 level->yamyam_content[i][x][y] = checkLevelElement(fgetc(file));
255 level->amoeba_speed = fgetc(file);
256 level->time_magic_wall = fgetc(file);
257 level->time_wheel = fgetc(file);
258 level->amoeba_content = checkLevelElement(fgetc(file));
259 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
260 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
261 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
262 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
264 level->use_custom_template = (fgetc(file) == 1 ? TRUE : FALSE);
266 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
271 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
275 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
276 level->author[i] = fgetc(file);
277 level->author[MAX_LEVEL_NAME_LEN] = 0;
282 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
285 int chunk_size_expected = level->fieldx * level->fieldy;
287 /* Note: "chunk_size" was wrong before version 2.0 when elements are
288 stored with 16-bit encoding (and should be twice as big then).
289 Even worse, playfield data was stored 16-bit when only yamyam content
290 contained 16-bit elements and vice versa. */
292 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
293 chunk_size_expected *= 2;
295 if (chunk_size_expected != chunk_size)
297 ReadUnusedBytesFromFile(file, chunk_size);
298 return chunk_size_expected;
301 for(y=0; y<level->fieldy; y++)
302 for(x=0; x<level->fieldx; x++)
303 Feld[x][y] = Ur[x][y] =
304 checkLevelElement(level->encoding_16bit_field ?
305 getFile16BitBE(file) : fgetc(file));
309 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
313 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
314 int chunk_size_expected = header_size + content_size;
316 /* Note: "chunk_size" was wrong before version 2.0 when elements are
317 stored with 16-bit encoding (and should be twice as big then).
318 Even worse, playfield data was stored 16-bit when only yamyam content
319 contained 16-bit elements and vice versa. */
321 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
322 chunk_size_expected += content_size;
324 if (chunk_size_expected != chunk_size)
326 ReadUnusedBytesFromFile(file, chunk_size);
327 return chunk_size_expected;
331 level->num_yamyam_contents = fgetc(file);
335 /* correct invalid number of content fields -- should never happen */
336 if (level->num_yamyam_contents < 1 ||
337 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
338 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
340 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
343 level->yamyam_content[i][x][y] =
344 checkLevelElement(level->encoding_16bit_field ?
345 getFile16BitBE(file) : fgetc(file));
349 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
353 int num_contents, content_xsize, content_ysize;
354 int content_array[MAX_ELEMENT_CONTENTS][3][3];
356 element = checkLevelElement(getFile16BitBE(file));
357 num_contents = fgetc(file);
358 content_xsize = fgetc(file);
359 content_ysize = fgetc(file);
360 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
362 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
365 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
367 /* correct invalid number of content fields -- should never happen */
368 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
369 num_contents = STD_ELEMENT_CONTENTS;
371 if (element == EL_YAMYAM)
373 level->num_yamyam_contents = num_contents;
375 for(i=0; i<num_contents; i++)
378 level->yamyam_content[i][x][y] = content_array[i][x][y];
380 else if (element == EL_BD_AMOEBA)
382 level->amoeba_content = content_array[0][0][0];
386 Error(ERR_WARN, "cannot load content for element '%d'", element);
392 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
394 int num_changed_custom_elements = getFile16BitBE(file);
395 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
398 if (chunk_size_expected != chunk_size)
400 ReadUnusedBytesFromFile(file, chunk_size - 2);
401 return chunk_size_expected;
404 for (i=0; i < num_changed_custom_elements; i++)
406 int element = getFile16BitBE(file);
407 int properties = getFile32BitBE(file);
409 if (IS_CUSTOM_ELEMENT(element))
410 Properties[element][EP_BITFIELD_BASE] = properties;
412 Error(ERR_WARN, "invalid custom element number %d", element);
418 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
420 int num_changed_custom_elements = getFile16BitBE(file);
421 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
424 if (chunk_size_expected != chunk_size)
426 ReadUnusedBytesFromFile(file, chunk_size - 2);
427 return chunk_size_expected;
430 for (i=0; i < num_changed_custom_elements; i++)
432 int element = getFile16BitBE(file);
433 int custom_target_element = getFile16BitBE(file);
435 if (IS_CUSTOM_ELEMENT(element))
436 element_info[element].change.target_element = custom_target_element;
438 Error(ERR_WARN, "invalid custom element number %d", element);
444 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
446 int num_changed_custom_elements = getFile16BitBE(file);
447 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
450 if (chunk_size_expected != chunk_size)
452 ReadUnusedBytesFromFile(file, chunk_size - 2);
453 return chunk_size_expected;
456 for (i=0; i < num_changed_custom_elements; i++)
458 int element = getFile16BitBE(file);
460 if (!IS_CUSTOM_ELEMENT(element))
462 Error(ERR_WARN, "invalid custom element number %d", element);
464 element = EL_DEFAULT; /* dummy element used for artwork config */
467 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
468 element_info[element].description[j] = getFile8Bit(file);
469 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
471 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
473 /* some free bytes for future properties and padding */
474 ReadUnusedBytesFromFile(file, 7);
476 element_info[element].use_gfx_element = getFile8Bit(file);
477 element_info[element].gfx_element =
478 checkLevelElement(getFile16BitBE(file));
480 element_info[element].score = getFile8Bit(file);
481 element_info[element].gem_count = getFile8Bit(file);
483 element_info[element].push_delay_fixed = getFile16BitBE(file);
484 element_info[element].push_delay_random = getFile16BitBE(file);
485 element_info[element].move_delay_fixed = getFile16BitBE(file);
486 element_info[element].move_delay_random = getFile16BitBE(file);
488 element_info[element].move_pattern = getFile16BitBE(file);
489 element_info[element].move_direction_initial = getFile8Bit(file);
490 element_info[element].move_stepsize = getFile8Bit(file);
494 element_info[element].content[x][y] =
495 checkLevelElement(getFile16BitBE(file));
497 element_info[element].change.events = getFile32BitBE(file);
499 element_info[element].change.target_element =
500 checkLevelElement(getFile16BitBE(file));
502 element_info[element].change.delay_fixed = getFile16BitBE(file);
503 element_info[element].change.delay_random = getFile16BitBE(file);
504 element_info[element].change.delay_frames = getFile16BitBE(file);
506 element_info[element].change.trigger_element =
507 checkLevelElement(getFile16BitBE(file));
509 element_info[element].change.explode = getFile8Bit(file);
510 element_info[element].change.use_content = getFile8Bit(file);
511 element_info[element].change.only_complete = getFile8Bit(file);
512 element_info[element].change.use_random_change = getFile8Bit(file);
514 element_info[element].change.random = getFile8Bit(file);
515 element_info[element].change.power = getFile8Bit(file);
519 element_info[element].change.content[x][y] =
520 checkLevelElement(getFile16BitBE(file));
522 /* some free bytes for future properties and padding */
523 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
529 void LoadLevelFromFilename(char *filename)
531 char cookie[MAX_LINE_LEN];
532 char chunk_name[CHUNK_ID_LEN + 1];
536 /* always start with reliable default values */
537 setLevelInfoToDefaults();
539 if (!(file = fopen(filename, MODE_READ)))
541 level.no_level_file = TRUE;
543 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
547 getFileChunkBE(file, chunk_name, NULL);
548 if (strcmp(chunk_name, "RND1") == 0)
550 getFile32BitBE(file); /* not used */
552 getFileChunkBE(file, chunk_name, NULL);
553 if (strcmp(chunk_name, "CAVE") != 0)
555 Error(ERR_WARN, "unknown format of level file '%s'", filename);
560 else /* check for pre-2.0 file format with cookie string */
562 strcpy(cookie, chunk_name);
563 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
564 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
565 cookie[strlen(cookie) - 1] = '\0';
567 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
569 Error(ERR_WARN, "unknown format of level file '%s'", filename);
574 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
576 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
581 /* pre-2.0 level files have no game version, so use file version here */
582 level.game_version = level.file_version;
585 if (level.file_version < FILE_VERSION_1_2)
587 /* level files from versions before 1.2.0 without chunk structure */
588 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
589 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
597 int (*loader)(FILE *, int, struct LevelInfo *);
601 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
602 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
603 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
604 { "BODY", -1, LoadLevel_BODY },
605 { "CONT", -1, LoadLevel_CONT },
606 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
607 { "CUS1", -1, LoadLevel_CUS1 },
608 { "CUS2", -1, LoadLevel_CUS2 },
609 { "CUS3", -1, LoadLevel_CUS3 },
613 while (getFileChunkBE(file, chunk_name, &chunk_size))
617 while (chunk_info[i].name != NULL &&
618 strcmp(chunk_name, chunk_info[i].name) != 0)
621 if (chunk_info[i].name == NULL)
623 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
624 chunk_name, filename);
625 ReadUnusedBytesFromFile(file, chunk_size);
627 else if (chunk_info[i].size != -1 &&
628 chunk_info[i].size != chunk_size)
630 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
631 chunk_size, chunk_name, filename);
632 ReadUnusedBytesFromFile(file, chunk_size);
636 /* call function to load this level chunk */
637 int chunk_size_expected =
638 (chunk_info[i].loader)(file, chunk_size, &level);
640 /* the size of some chunks cannot be checked before reading other
641 chunks first (like "HEAD" and "BODY") that contain some header
642 information, so check them here */
643 if (chunk_size_expected != chunk_size)
645 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
646 chunk_size, chunk_name, filename);
654 if (leveldir_current == NULL) /* only when dumping level */
657 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
658 IS_LEVELCLASS_USER(leveldir_current))
660 /* For user contributed and private levels, use the version of
661 the game engine the levels were created for.
662 Since 2.0.1, the game engine version is now directly stored
663 in the level file (chunk "VERS"), so there is no need anymore
664 to set the game version from the file version (except for old,
665 pre-2.0 levels, where the game version is still taken from the
666 file format version used to store the level -- see above). */
668 /* do some special adjustments to support older level versions */
669 if (level.file_version == FILE_VERSION_1_0)
671 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
672 Error(ERR_WARN, "using high speed movement for player");
674 /* player was faster than monsters in (pre-)1.0 levels */
675 level.double_speed = TRUE;
678 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
679 if (level.game_version == VERSION_IDENT(2,0,1))
680 level.em_slippery_gems = TRUE;
684 /* Always use the latest version of the game engine for all but
685 user contributed and private levels; this allows for actual
686 corrections in the game engine to take effect for existing,
687 converted levels (from "classic" or other existing games) to
688 make the game emulation more accurate, while (hopefully) not
689 breaking existing levels created from other players. */
691 level.game_version = GAME_VERSION_ACTUAL;
693 /* Set special EM style gems behaviour: EM style gems slip down from
694 normal, steel and growing wall. As this is a more fundamental change,
695 it seems better to set the default behaviour to "off" (as it is more
696 natural) and make it configurable in the level editor (as a property
697 of gem style elements). Already existing converted levels (neither
698 private nor contributed levels) are changed to the new behaviour. */
700 if (level.file_version < FILE_VERSION_2_0)
701 level.em_slippery_gems = TRUE;
704 /* map some elements which have changed in newer versions */
705 if (level.game_version <= VERSION_IDENT(2,2,0))
709 /* map game font elements */
710 for(y=0; y<level.fieldy; y++)
712 for(x=0; x<level.fieldx; x++)
714 int element = Ur[x][y];
716 if (element == EL_CHAR('['))
717 element = EL_CHAR_AUMLAUT;
718 else if (element == EL_CHAR('\\'))
719 element = EL_CHAR_OUMLAUT;
720 else if (element == EL_CHAR(']'))
721 element = EL_CHAR_UUMLAUT;
722 else if (element == EL_CHAR('^'))
723 element = EL_CHAR_COPYRIGHT;
725 Feld[x][y] = Ur[x][y] = element;
730 /* determine border element for this level */
734 void LoadLevel(int level_nr)
736 char *filename = getLevelFilename(level_nr);
738 LoadLevelFromFilename(filename);
739 InitElementPropertiesEngine(level.game_version);
742 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
744 putFileVersion(file, level->file_version);
745 putFileVersion(file, level->game_version);
748 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
752 fputc(level->fieldx, file);
753 fputc(level->fieldy, file);
755 putFile16BitBE(file, level->time);
756 putFile16BitBE(file, level->gems_needed);
758 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
759 fputc(level->name[i], file);
761 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
762 fputc(level->score[i], file);
764 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
767 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
768 level->yamyam_content[i][x][y]),
770 fputc(level->amoeba_speed, file);
771 fputc(level->time_magic_wall, file);
772 fputc(level->time_wheel, file);
773 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
775 fputc((level->double_speed ? 1 : 0), file);
776 fputc((level->gravity ? 1 : 0), file);
777 fputc((level->encoding_16bit_field ? 1 : 0), file);
778 fputc((level->em_slippery_gems ? 1 : 0), file);
780 fputc((level->use_custom_template ? 1 : 0), file);
782 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
785 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
789 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
790 fputc(level->author[i], file);
793 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
797 for(y=0; y<level->fieldy; y++)
798 for(x=0; x<level->fieldx; x++)
799 if (level->encoding_16bit_field)
800 putFile16BitBE(file, Ur[x][y]);
802 fputc(Ur[x][y], file);
806 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
810 fputc(EL_YAMYAM, file);
811 fputc(level->num_yamyam_contents, file);
815 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
818 if (level->encoding_16bit_field)
819 putFile16BitBE(file, level->yamyam_content[i][x][y]);
821 fputc(level->yamyam_content[i][x][y], file);
825 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
828 int num_contents, content_xsize, content_ysize;
829 int content_array[MAX_ELEMENT_CONTENTS][3][3];
831 if (element == EL_YAMYAM)
833 num_contents = level->num_yamyam_contents;
837 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
840 content_array[i][x][y] = level->yamyam_content[i][x][y];
842 else if (element == EL_BD_AMOEBA)
848 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
851 content_array[i][x][y] = EL_EMPTY;
852 content_array[0][0][0] = level->amoeba_content;
856 /* chunk header already written -- write empty chunk data */
857 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
859 Error(ERR_WARN, "cannot save content for element '%d'", element);
863 putFile16BitBE(file, element);
864 fputc(num_contents, file);
865 fputc(content_xsize, file);
866 fputc(content_ysize, file);
868 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
870 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
873 putFile16BitBE(file, content_array[i][x][y]);
877 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
878 int num_changed_custom_elements)
882 putFile16BitBE(file, num_changed_custom_elements);
884 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
886 int element = EL_CUSTOM_START + i;
888 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
890 if (check < num_changed_custom_elements)
892 putFile16BitBE(file, element);
893 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
900 if (check != num_changed_custom_elements) /* should not happen */
901 Error(ERR_WARN, "inconsistent number of custom element properties");
906 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
907 int num_changed_custom_elements)
911 putFile16BitBE(file, num_changed_custom_elements);
913 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
915 int element = EL_CUSTOM_START + i;
917 if (element_info[element].change.target_element != EL_EMPTY_SPACE)
919 if (check < num_changed_custom_elements)
921 putFile16BitBE(file, element);
922 putFile16BitBE(file, element_info[element].change.target_element);
929 if (check != num_changed_custom_elements) /* should not happen */
930 Error(ERR_WARN, "inconsistent number of custom target elements");
934 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
935 int num_changed_custom_elements)
937 int i, j, x, y, check = 0;
939 putFile16BitBE(file, num_changed_custom_elements);
941 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
943 int element = EL_CUSTOM_START + i;
945 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
947 if (check < num_changed_custom_elements)
949 putFile16BitBE(file, element);
951 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
952 putFile8Bit(file, element_info[element].description[j]);
954 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
956 /* some free bytes for future properties and padding */
957 WriteUnusedBytesToFile(file, 7);
959 putFile8Bit(file, element_info[element].use_gfx_element);
960 putFile16BitBE(file, element_info[element].gfx_element);
962 putFile8Bit(file, element_info[element].score);
963 putFile8Bit(file, element_info[element].gem_count);
965 putFile16BitBE(file, element_info[element].push_delay_fixed);
966 putFile16BitBE(file, element_info[element].push_delay_random);
967 putFile16BitBE(file, element_info[element].move_delay_fixed);
968 putFile16BitBE(file, element_info[element].move_delay_random);
970 putFile16BitBE(file, element_info[element].move_pattern);
971 putFile8Bit(file, element_info[element].move_direction_initial);
972 putFile8Bit(file, element_info[element].move_stepsize);
976 putFile16BitBE(file, element_info[element].content[x][y]);
978 putFile32BitBE(file, element_info[element].change.events);
980 putFile16BitBE(file, element_info[element].change.target_element);
982 putFile16BitBE(file, element_info[element].change.delay_fixed);
983 putFile16BitBE(file, element_info[element].change.delay_random);
984 putFile16BitBE(file, element_info[element].change.delay_frames);
986 putFile16BitBE(file, element_info[element].change.trigger_element);
988 putFile8Bit(file, element_info[element].change.explode);
989 putFile8Bit(file, element_info[element].change.use_content);
990 putFile8Bit(file, element_info[element].change.only_complete);
991 putFile8Bit(file, element_info[element].change.use_random_change);
993 putFile8Bit(file, element_info[element].change.random);
994 putFile8Bit(file, element_info[element].change.power);
998 putFile16BitBE(file, element_info[element].change.content[x][y]);
1000 /* some free bytes for future properties and padding */
1001 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1008 if (check != num_changed_custom_elements) /* should not happen */
1009 Error(ERR_WARN, "inconsistent number of custom element properties");
1012 void SaveLevel(int level_nr)
1014 char *filename = getLevelFilename(level_nr);
1015 int body_chunk_size;
1016 int num_changed_custom_elements = 0;
1017 int level_chunk_CUS3_size;
1021 if (!(file = fopen(filename, MODE_WRITE)))
1023 Error(ERR_WARN, "cannot save level file '%s'", filename);
1027 level.file_version = FILE_VERSION_ACTUAL;
1028 level.game_version = GAME_VERSION_ACTUAL;
1030 /* check level field for 16-bit elements */
1031 level.encoding_16bit_field = FALSE;
1032 for(y=0; y<level.fieldy; y++)
1033 for(x=0; x<level.fieldx; x++)
1035 level.encoding_16bit_field = TRUE;
1037 /* check yamyam content for 16-bit elements */
1038 level.encoding_16bit_yamyam = FALSE;
1039 for(i=0; i<level.num_yamyam_contents; i++)
1042 if (level.yamyam_content[i][x][y] > 255)
1043 level.encoding_16bit_yamyam = TRUE;
1045 /* check amoeba content for 16-bit elements */
1046 level.encoding_16bit_amoeba = FALSE;
1047 if (level.amoeba_content > 255)
1048 level.encoding_16bit_amoeba = TRUE;
1050 /* calculate size of "BODY" chunk */
1052 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
1054 /* check for non-standard custom elements and calculate "CUS3" chunk size */
1055 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1056 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1057 num_changed_custom_elements++;
1058 level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1060 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1061 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1063 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1064 SaveLevel_VERS(file, &level);
1066 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1067 SaveLevel_HEAD(file, &level);
1069 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1070 SaveLevel_AUTH(file, &level);
1072 putFileChunkBE(file, "BODY", body_chunk_size);
1073 SaveLevel_BODY(file, &level);
1075 if (level.encoding_16bit_yamyam ||
1076 level.num_yamyam_contents != STD_ELEMENT_CONTENTS)
1078 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1079 SaveLevel_CNT2(file, &level, EL_YAMYAM);
1082 if (level.encoding_16bit_amoeba)
1084 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1085 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
1088 if (num_changed_custom_elements > 0)
1090 putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1091 SaveLevel_CUS3(file, &level, num_changed_custom_elements);
1096 SetFilePermissions(filename, PERMS_PRIVATE);
1099 void DumpLevel(struct LevelInfo *level)
1101 printf_line("-", 79);
1102 printf("Level xxx (file version %08d, game version %08d)\n",
1103 level->file_version, level->game_version);
1104 printf_line("-", 79);
1106 printf("Level Author: '%s'\n", level->author);
1107 printf("Level Title: '%s'\n", level->name);
1109 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1111 printf("Level Time: %d seconds\n", level->time);
1112 printf("Gems needed: %d\n", level->gems_needed);
1114 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1115 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1116 printf("Time for Light: %d seconds\n", level->time_light);
1117 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1119 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1121 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1122 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1123 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1125 printf_line("-", 79);
1129 /* ========================================================================= */
1130 /* tape file functions */
1131 /* ========================================================================= */
1133 static void setTapeInfoToDefaults()
1137 /* always start with reliable default values (empty tape) */
1140 /* default values (also for pre-1.2 tapes) with only the first player */
1141 tape.player_participates[0] = TRUE;
1142 for(i=1; i<MAX_PLAYERS; i++)
1143 tape.player_participates[i] = FALSE;
1145 /* at least one (default: the first) player participates in every tape */
1146 tape.num_participating_players = 1;
1148 tape.level_nr = level_nr;
1150 tape.changed = FALSE;
1152 tape.recording = FALSE;
1153 tape.playing = FALSE;
1154 tape.pausing = FALSE;
1157 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1159 tape->file_version = getFileVersion(file);
1160 tape->game_version = getFileVersion(file);
1165 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1169 tape->random_seed = getFile32BitBE(file);
1170 tape->date = getFile32BitBE(file);
1171 tape->length = getFile32BitBE(file);
1173 /* read header fields that are new since version 1.2 */
1174 if (tape->file_version >= FILE_VERSION_1_2)
1176 byte store_participating_players = fgetc(file);
1179 /* since version 1.2, tapes store which players participate in the tape */
1180 tape->num_participating_players = 0;
1181 for(i=0; i<MAX_PLAYERS; i++)
1183 tape->player_participates[i] = FALSE;
1185 if (store_participating_players & (1 << i))
1187 tape->player_participates[i] = TRUE;
1188 tape->num_participating_players++;
1192 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1194 engine_version = getFileVersion(file);
1195 if (engine_version > 0)
1196 tape->engine_version = engine_version;
1202 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1204 int level_identifier_size;
1207 level_identifier_size = getFile16BitBE(file);
1209 tape->level_identifier =
1210 checked_realloc(tape->level_identifier, level_identifier_size);
1212 for(i=0; i < level_identifier_size; i++)
1213 tape->level_identifier[i] = fgetc(file);
1215 tape->level_nr = getFile16BitBE(file);
1217 chunk_size = 2 + level_identifier_size + 2;
1222 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1225 int chunk_size_expected =
1226 (tape->num_participating_players + 1) * tape->length;
1228 if (chunk_size_expected != chunk_size)
1230 ReadUnusedBytesFromFile(file, chunk_size);
1231 return chunk_size_expected;
1234 for(i=0; i<tape->length; i++)
1236 if (i >= MAX_TAPELEN)
1239 for(j=0; j<MAX_PLAYERS; j++)
1241 tape->pos[i].action[j] = MV_NO_MOVING;
1243 if (tape->player_participates[j])
1244 tape->pos[i].action[j] = fgetc(file);
1247 tape->pos[i].delay = fgetc(file);
1249 if (tape->file_version == FILE_VERSION_1_0)
1251 /* eliminate possible diagonal moves in old tapes */
1252 /* this is only for backward compatibility */
1254 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1255 byte action = tape->pos[i].action[0];
1256 int k, num_moves = 0;
1260 if (action & joy_dir[k])
1262 tape->pos[i + num_moves].action[0] = joy_dir[k];
1264 tape->pos[i + num_moves].delay = 0;
1273 tape->length += num_moves;
1276 else if (tape->file_version < FILE_VERSION_2_0)
1278 /* convert pre-2.0 tapes to new tape format */
1280 if (tape->pos[i].delay > 1)
1283 tape->pos[i + 1] = tape->pos[i];
1284 tape->pos[i + 1].delay = 1;
1287 for(j=0; j<MAX_PLAYERS; j++)
1288 tape->pos[i].action[j] = MV_NO_MOVING;
1289 tape->pos[i].delay--;
1300 if (i != tape->length)
1301 chunk_size = (tape->num_participating_players + 1) * i;
1306 void LoadTapeFromFilename(char *filename)
1308 char cookie[MAX_LINE_LEN];
1309 char chunk_name[CHUNK_ID_LEN + 1];
1313 /* always start with reliable default values */
1314 setTapeInfoToDefaults();
1316 if (!(file = fopen(filename, MODE_READ)))
1319 getFileChunkBE(file, chunk_name, NULL);
1320 if (strcmp(chunk_name, "RND1") == 0)
1322 getFile32BitBE(file); /* not used */
1324 getFileChunkBE(file, chunk_name, NULL);
1325 if (strcmp(chunk_name, "TAPE") != 0)
1327 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1332 else /* check for pre-2.0 file format with cookie string */
1334 strcpy(cookie, chunk_name);
1335 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1336 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1337 cookie[strlen(cookie) - 1] = '\0';
1339 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1341 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1346 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1348 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1353 /* pre-2.0 tape files have no game version, so use file version here */
1354 tape.game_version = tape.file_version;
1357 if (tape.file_version < FILE_VERSION_1_2)
1359 /* tape files from versions before 1.2.0 without chunk structure */
1360 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1361 LoadTape_BODY(file, 2 * tape.length, &tape);
1369 int (*loader)(FILE *, int, struct TapeInfo *);
1373 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1374 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1375 { "INFO", -1, LoadTape_INFO },
1376 { "BODY", -1, LoadTape_BODY },
1380 while (getFileChunkBE(file, chunk_name, &chunk_size))
1384 while (chunk_info[i].name != NULL &&
1385 strcmp(chunk_name, chunk_info[i].name) != 0)
1388 if (chunk_info[i].name == NULL)
1390 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1391 chunk_name, filename);
1392 ReadUnusedBytesFromFile(file, chunk_size);
1394 else if (chunk_info[i].size != -1 &&
1395 chunk_info[i].size != chunk_size)
1397 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1398 chunk_size, chunk_name, filename);
1399 ReadUnusedBytesFromFile(file, chunk_size);
1403 /* call function to load this tape chunk */
1404 int chunk_size_expected =
1405 (chunk_info[i].loader)(file, chunk_size, &tape);
1407 /* the size of some chunks cannot be checked before reading other
1408 chunks first (like "HEAD" and "BODY") that contain some header
1409 information, so check them here */
1410 if (chunk_size_expected != chunk_size)
1412 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1413 chunk_size, chunk_name, filename);
1421 tape.length_seconds = GetTapeLength();
1424 printf("tape version: %d\n", tape.game_version);
1428 void LoadTape(int level_nr)
1430 char *filename = getTapeFilename(level_nr);
1432 LoadTapeFromFilename(filename);
1435 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1437 putFileVersion(file, tape->file_version);
1438 putFileVersion(file, tape->game_version);
1441 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1444 byte store_participating_players = 0;
1446 /* set bits for participating players for compact storage */
1447 for(i=0; i<MAX_PLAYERS; i++)
1448 if (tape->player_participates[i])
1449 store_participating_players |= (1 << i);
1451 putFile32BitBE(file, tape->random_seed);
1452 putFile32BitBE(file, tape->date);
1453 putFile32BitBE(file, tape->length);
1455 fputc(store_participating_players, file);
1457 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1458 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1460 putFileVersion(file, tape->engine_version);
1463 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1465 int level_identifier_size = strlen(tape->level_identifier) + 1;
1468 putFile16BitBE(file, level_identifier_size);
1470 for(i=0; i < level_identifier_size; i++)
1471 fputc(tape->level_identifier[i], file);
1473 putFile16BitBE(file, tape->level_nr);
1476 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1480 for(i=0; i<tape->length; i++)
1482 for(j=0; j<MAX_PLAYERS; j++)
1483 if (tape->player_participates[j])
1484 fputc(tape->pos[i].action[j], file);
1486 fputc(tape->pos[i].delay, file);
1490 void SaveTape(int level_nr)
1492 char *filename = getTapeFilename(level_nr);
1494 boolean new_tape = TRUE;
1495 int num_participating_players = 0;
1496 int info_chunk_size;
1497 int body_chunk_size;
1500 InitTapeDirectory(leveldir_current->filename);
1502 /* if a tape still exists, ask to overwrite it */
1503 if (access(filename, F_OK) == 0)
1506 if (!Request("Replace old tape ?", REQ_ASK))
1510 if (!(file = fopen(filename, MODE_WRITE)))
1512 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1516 tape.file_version = FILE_VERSION_ACTUAL;
1517 tape.game_version = GAME_VERSION_ACTUAL;
1519 /* count number of participating players */
1520 for(i=0; i<MAX_PLAYERS; i++)
1521 if (tape.player_participates[i])
1522 num_participating_players++;
1524 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1525 body_chunk_size = (num_participating_players + 1) * tape.length;
1527 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1528 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1530 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1531 SaveTape_VERS(file, &tape);
1533 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1534 SaveTape_HEAD(file, &tape);
1536 putFileChunkBE(file, "INFO", info_chunk_size);
1537 SaveTape_INFO(file, &tape);
1539 putFileChunkBE(file, "BODY", body_chunk_size);
1540 SaveTape_BODY(file, &tape);
1544 SetFilePermissions(filename, PERMS_PRIVATE);
1546 tape.changed = FALSE;
1549 Request("tape saved !", REQ_CONFIRM);
1552 void DumpTape(struct TapeInfo *tape)
1556 if (TAPE_IS_EMPTY(*tape))
1558 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1562 printf_line("-", 79);
1563 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1564 tape->level_nr, tape->file_version, tape->game_version);
1565 printf("Level series identifier: '%s'\n", tape->level_identifier);
1566 printf_line("-", 79);
1568 for(i=0; i<tape->length; i++)
1570 if (i >= MAX_TAPELEN)
1573 printf("%03d: ", i);
1575 for(j=0; j<MAX_PLAYERS; j++)
1577 if (tape->player_participates[j])
1579 int action = tape->pos[i].action[j];
1581 printf("%d:%02x ", j, action);
1582 printf("[%c%c%c%c|%c%c] - ",
1583 (action & JOY_LEFT ? '<' : ' '),
1584 (action & JOY_RIGHT ? '>' : ' '),
1585 (action & JOY_UP ? '^' : ' '),
1586 (action & JOY_DOWN ? 'v' : ' '),
1587 (action & JOY_BUTTON_1 ? '1' : ' '),
1588 (action & JOY_BUTTON_2 ? '2' : ' '));
1592 printf("(%03d)\n", tape->pos[i].delay);
1595 printf_line("-", 79);
1599 /* ========================================================================= */
1600 /* score file functions */
1601 /* ========================================================================= */
1603 void LoadScore(int level_nr)
1606 char *filename = getScoreFilename(level_nr);
1607 char cookie[MAX_LINE_LEN];
1608 char line[MAX_LINE_LEN];
1612 /* always start with reliable default values */
1613 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1615 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1616 highscore[i].Score = 0;
1619 if (!(file = fopen(filename, MODE_READ)))
1622 /* check file identifier */
1623 fgets(cookie, MAX_LINE_LEN, file);
1624 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1625 cookie[strlen(cookie) - 1] = '\0';
1627 if (!checkCookieString(cookie, SCORE_COOKIE))
1629 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1634 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1636 fscanf(file, "%d", &highscore[i].Score);
1637 fgets(line, MAX_LINE_LEN, file);
1639 if (line[strlen(line) - 1] == '\n')
1640 line[strlen(line) - 1] = '\0';
1642 for (line_ptr = line; *line_ptr; line_ptr++)
1644 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1646 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1647 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1656 void SaveScore(int level_nr)
1659 char *filename = getScoreFilename(level_nr);
1662 InitScoreDirectory(leveldir_current->filename);
1664 if (!(file = fopen(filename, MODE_WRITE)))
1666 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1670 fprintf(file, "%s\n\n", SCORE_COOKIE);
1672 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1673 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1677 SetFilePermissions(filename, PERMS_PUBLIC);
1681 /* ========================================================================= */
1682 /* setup file functions */
1683 /* ========================================================================= */
1685 #define TOKEN_STR_PLAYER_PREFIX "player_"
1688 #define SETUP_TOKEN_PLAYER_NAME 0
1689 #define SETUP_TOKEN_SOUND 1
1690 #define SETUP_TOKEN_SOUND_LOOPS 2
1691 #define SETUP_TOKEN_SOUND_MUSIC 3
1692 #define SETUP_TOKEN_SOUND_SIMPLE 4
1693 #define SETUP_TOKEN_TOONS 5
1694 #define SETUP_TOKEN_SCROLL_DELAY 6
1695 #define SETUP_TOKEN_SOFT_SCROLLING 7
1696 #define SETUP_TOKEN_FADING 8
1697 #define SETUP_TOKEN_AUTORECORD 9
1698 #define SETUP_TOKEN_QUICK_DOORS 10
1699 #define SETUP_TOKEN_TEAM_MODE 11
1700 #define SETUP_TOKEN_HANDICAP 12
1701 #define SETUP_TOKEN_TIME_LIMIT 13
1702 #define SETUP_TOKEN_FULLSCREEN 14
1703 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1704 #define SETUP_TOKEN_GRAPHICS_SET 16
1705 #define SETUP_TOKEN_SOUNDS_SET 17
1706 #define SETUP_TOKEN_MUSIC_SET 18
1707 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1708 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1709 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1711 #define NUM_GLOBAL_SETUP_TOKENS 22
1714 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1715 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1716 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1717 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1718 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1719 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1720 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1721 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1722 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1724 #define NUM_EDITOR_SETUP_TOKENS 9
1726 /* shortcut setup */
1727 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1728 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1729 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1731 #define NUM_SHORTCUT_SETUP_TOKENS 3
1734 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1735 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1736 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1737 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1738 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1739 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1740 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1741 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1742 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1743 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1744 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1745 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1746 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1747 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1748 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1749 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1751 #define NUM_PLAYER_SETUP_TOKENS 16
1754 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1755 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1757 #define NUM_SYSTEM_SETUP_TOKENS 2
1760 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1762 #define NUM_OPTIONS_SETUP_TOKENS 1
1765 static struct SetupInfo si;
1766 static struct SetupEditorInfo sei;
1767 static struct SetupShortcutInfo ssi;
1768 static struct SetupInputInfo sii;
1769 static struct SetupSystemInfo syi;
1770 static struct OptionInfo soi;
1772 static struct TokenInfo global_setup_tokens[] =
1774 { TYPE_STRING, &si.player_name, "player_name" },
1775 { TYPE_SWITCH, &si.sound, "sound" },
1776 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1777 { TYPE_SWITCH, &si.sound_music, "background_music" },
1778 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1779 { TYPE_SWITCH, &si.toons, "toons" },
1780 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1781 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1782 { TYPE_SWITCH, &si.fading, "screen_fading" },
1783 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1784 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1785 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1786 { TYPE_SWITCH, &si.handicap, "handicap" },
1787 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1788 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1789 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1790 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1791 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1792 { TYPE_STRING, &si.music_set, "music_set" },
1793 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1794 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1795 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1798 static struct TokenInfo editor_setup_tokens[] =
1800 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1801 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1802 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1803 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1804 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1805 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1806 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1807 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1808 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1811 static struct TokenInfo shortcut_setup_tokens[] =
1813 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1814 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1815 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1818 static struct TokenInfo player_setup_tokens[] =
1820 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1821 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1822 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1823 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1824 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1825 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1826 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1827 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1828 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1829 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1830 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1831 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1832 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1833 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1834 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1835 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1838 static struct TokenInfo system_setup_tokens[] =
1840 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1841 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1844 static struct TokenInfo options_setup_tokens[] =
1846 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1849 static char *get_corrected_login_name(char *login_name)
1851 /* needed because player name must be a fixed length string */
1852 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1854 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1855 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1857 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1858 if (strchr(login_name_new, ' '))
1859 *strchr(login_name_new, ' ') = '\0';
1861 return login_name_new;
1864 static void setSetupInfoToDefaults(struct SetupInfo *si)
1868 si->player_name = get_corrected_login_name(getLoginName());
1871 si->sound_loops = TRUE;
1872 si->sound_music = TRUE;
1873 si->sound_simple = TRUE;
1875 si->double_buffering = TRUE;
1876 si->direct_draw = !si->double_buffering;
1877 si->scroll_delay = TRUE;
1878 si->soft_scrolling = TRUE;
1880 si->autorecord = TRUE;
1881 si->quick_doors = FALSE;
1882 si->team_mode = FALSE;
1883 si->handicap = TRUE;
1884 si->time_limit = TRUE;
1885 si->fullscreen = FALSE;
1886 si->ask_on_escape = TRUE;
1888 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1889 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1890 si->music_set = getStringCopy(MUSIC_SUBDIR);
1891 si->override_level_graphics = FALSE;
1892 si->override_level_sounds = FALSE;
1893 si->override_level_music = FALSE;
1895 si->editor.el_boulderdash = TRUE;
1896 si->editor.el_emerald_mine = TRUE;
1897 si->editor.el_more = TRUE;
1898 si->editor.el_sokoban = TRUE;
1899 si->editor.el_supaplex = TRUE;
1900 si->editor.el_diamond_caves = TRUE;
1901 si->editor.el_dx_boulderdash = TRUE;
1902 si->editor.el_chars = TRUE;
1903 si->editor.el_custom = TRUE;
1905 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1906 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1907 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1909 for (i=0; i<MAX_PLAYERS; i++)
1911 si->input[i].use_joystick = FALSE;
1912 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1913 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1914 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1915 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1916 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1917 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1918 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1919 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1920 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1921 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1922 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1923 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1924 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1925 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1926 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1929 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1930 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1932 si->options.verbose = FALSE;
1935 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
1939 if (!setup_file_hash)
1944 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1945 setSetupInfo(global_setup_tokens, i,
1946 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
1951 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1952 setSetupInfo(editor_setup_tokens, i,
1953 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
1956 /* shortcut setup */
1957 ssi = setup.shortcut;
1958 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1959 setSetupInfo(shortcut_setup_tokens, i,
1960 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
1961 setup.shortcut = ssi;
1964 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1968 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1970 sii = setup.input[pnr];
1971 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1973 char full_token[100];
1975 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1976 setSetupInfo(player_setup_tokens, i,
1977 getHashEntry(setup_file_hash, full_token));
1979 setup.input[pnr] = sii;
1984 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1985 setSetupInfo(system_setup_tokens, i,
1986 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
1990 soi = setup.options;
1991 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1992 setSetupInfo(options_setup_tokens, i,
1993 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
1994 setup.options = soi;
1999 char *filename = getSetupFilename();
2000 SetupFileHash *setup_file_hash = NULL;
2002 /* always start with reliable default values */
2003 setSetupInfoToDefaults(&setup);
2005 setup_file_hash = loadSetupFileHash(filename);
2007 if (setup_file_hash)
2009 char *player_name_new;
2011 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2012 decodeSetupFileHash(setup_file_hash);
2014 setup.direct_draw = !setup.double_buffering;
2016 freeSetupFileHash(setup_file_hash);
2018 /* needed to work around problems with fixed length strings */
2019 player_name_new = get_corrected_login_name(setup.player_name);
2020 free(setup.player_name);
2021 setup.player_name = player_name_new;
2024 Error(ERR_WARN, "using default setup values");
2029 char *filename = getSetupFilename();
2033 InitUserDataDirectory();
2035 if (!(file = fopen(filename, MODE_WRITE)))
2037 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2041 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2042 getCookie("SETUP")));
2043 fprintf(file, "\n");
2047 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2049 /* just to make things nicer :) */
2050 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2051 i == SETUP_TOKEN_GRAPHICS_SET)
2052 fprintf(file, "\n");
2054 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2059 fprintf(file, "\n");
2060 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2061 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2063 /* shortcut setup */
2064 ssi = setup.shortcut;
2065 fprintf(file, "\n");
2066 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2067 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2070 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2074 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2075 fprintf(file, "\n");
2077 sii = setup.input[pnr];
2078 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2079 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2084 fprintf(file, "\n");
2085 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2086 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2089 soi = setup.options;
2090 fprintf(file, "\n");
2091 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2092 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2096 SetFilePermissions(filename, PERMS_PRIVATE);
2099 void LoadCustomElementDescriptions()
2101 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2102 SetupFileHash *setup_file_hash;
2105 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2107 if (element_info[i].custom_description != NULL)
2109 free(element_info[i].custom_description);
2110 element_info[i].custom_description = NULL;
2114 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2117 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2119 char *token = getStringCat2(element_info[i].token_name, ".name");
2120 char *value = getHashEntry(setup_file_hash, token);
2123 element_info[i].custom_description = getStringCopy(value);
2128 freeSetupFileHash(setup_file_hash);
2131 void LoadSpecialMenuDesignSettings()
2133 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2134 SetupFileHash *setup_file_hash;
2137 /* always start with reliable default values from default config */
2138 for (i=0; image_config_vars[i].token != NULL; i++)
2139 for (j=0; image_config[j].token != NULL; j++)
2140 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2141 *image_config_vars[i].value =
2142 get_integer_from_string(image_config[j].value);
2144 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2147 /* special case: initialize with default values that may be overwritten */
2148 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2150 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2151 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2152 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2154 if (value_x != NULL)
2155 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2156 if (value_y != NULL)
2157 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2158 if (list_size != NULL)
2159 menu.list_size[i] = get_integer_from_string(list_size);
2162 /* read (and overwrite with) values that may be specified in config file */
2163 for (i=0; image_config_vars[i].token != NULL; i++)
2165 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2168 *image_config_vars[i].value = get_integer_from_string(value);
2171 freeSetupFileHash(setup_file_hash);