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"
24 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
25 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
26 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
27 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
28 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
29 #define LEVEL_HEADER_UNUSED 14 /* unused level header bytes */
30 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
31 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
32 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
33 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
35 /* file identifier strings */
36 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
37 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
38 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
41 /* ========================================================================= */
42 /* level file functions */
43 /* ========================================================================= */
45 static void setLevelInfoToDefaults()
49 level.file_version = FILE_VERSION_ACTUAL;
50 level.game_version = GAME_VERSION_ACTUAL;
52 level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
53 level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
54 level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
56 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
57 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
59 for(x=0; x<MAX_LEV_FIELDX; x++)
60 for(y=0; y<MAX_LEV_FIELDY; y++)
61 Feld[x][y] = Ur[x][y] = EL_SAND;
64 level.gems_needed = 0;
65 level.amoeba_speed = 10;
66 level.time_magic_wall = 10;
67 level.time_wheel = 10;
68 level.time_light = 10;
69 level.time_timegate = 10;
70 level.amoeba_content = EL_DIAMOND;
71 level.double_speed = FALSE;
72 level.gravity = FALSE;
73 level.em_slippery_gems = FALSE;
75 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
77 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
78 level.author[i] = '\0';
80 strcpy(level.name, NAMELESS_LEVEL_NAME);
81 strcpy(level.author, ANONYMOUS_NAME);
83 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
86 level.num_yam_contents = STD_ELEMENT_CONTENTS;
87 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
90 level.yam_content[i][x][y] =
91 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
93 Feld[0][0] = Ur[0][0] = EL_PLAYER1;
94 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
95 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
97 BorderElement = EL_STEELWALL;
99 /* try to determine better author name than 'anonymous' */
100 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
102 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
103 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
107 switch (LEVELCLASS(leveldir_current))
109 case LEVELCLASS_TUTORIAL:
110 strcpy(level.author, PROGRAM_AUTHOR_STRING);
113 case LEVELCLASS_CONTRIBUTION:
114 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
115 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
118 case LEVELCLASS_USER:
119 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
120 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
124 /* keep default value */
130 static int checkLevelElement(int element)
132 if (element >= NUM_FILE_ELEMENTS)
134 Error(ERR_WARN, "invalid level element %d", element);
135 element = EL_CHAR_QUESTION;
137 else if (element == EL_PLAYER_OBSOLETE)
138 element = EL_PLAYER1;
139 else if (element == EL_KEY_OBSOLETE)
145 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
147 level->file_version = getFileVersion(file);
148 level->game_version = getFileVersion(file);
153 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
157 lev_fieldx = level->fieldx = fgetc(file);
158 lev_fieldy = level->fieldy = fgetc(file);
160 level->time = getFile16BitBE(file);
161 level->gems_needed = getFile16BitBE(file);
163 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
164 level->name[i] = fgetc(file);
165 level->name[MAX_LEVEL_NAME_LEN] = 0;
167 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
168 level->score[i] = fgetc(file);
170 level->num_yam_contents = STD_ELEMENT_CONTENTS;
171 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
174 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
176 level->amoeba_speed = fgetc(file);
177 level->time_magic_wall = fgetc(file);
178 level->time_wheel = fgetc(file);
179 level->amoeba_content = checkLevelElement(fgetc(file));
180 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
181 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
182 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
183 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
185 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
190 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
194 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
195 level->author[i] = fgetc(file);
196 level->author[MAX_LEVEL_NAME_LEN] = 0;
201 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
205 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
206 int chunk_size_expected = header_size + content_size;
208 /* Note: "chunk_size" was wrong before version 2.0 when elements are
209 stored with 16-bit encoding (and should be twice as big then).
210 Even worse, playfield data was stored 16-bit when only yamyam content
211 contained 16-bit elements and vice versa. */
213 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
214 chunk_size_expected += content_size;
216 if (chunk_size_expected != chunk_size)
218 ReadUnusedBytesFromFile(file, chunk_size);
219 return chunk_size_expected;
223 level->num_yam_contents = fgetc(file);
227 /* correct invalid number of content fields -- should never happen */
228 if (level->num_yam_contents < 1 ||
229 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
230 level->num_yam_contents = STD_ELEMENT_CONTENTS;
232 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
235 level->yam_content[i][x][y] =
236 checkLevelElement(level->encoding_16bit_field ?
237 getFile16BitBE(file) : fgetc(file));
241 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
244 int chunk_size_expected = level->fieldx * level->fieldy;
246 /* Note: "chunk_size" was wrong before version 2.0 when elements are
247 stored with 16-bit encoding (and should be twice as big then).
248 Even worse, playfield data was stored 16-bit when only yamyam content
249 contained 16-bit elements and vice versa. */
251 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
252 chunk_size_expected *= 2;
254 if (chunk_size_expected != chunk_size)
256 ReadUnusedBytesFromFile(file, chunk_size);
257 return chunk_size_expected;
260 for(y=0; y<level->fieldy; y++)
261 for(x=0; x<level->fieldx; x++)
262 Feld[x][y] = Ur[x][y] =
263 checkLevelElement(level->encoding_16bit_field ?
264 getFile16BitBE(file) : fgetc(file));
268 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
272 int num_contents, content_xsize, content_ysize;
273 int content_array[MAX_ELEMENT_CONTENTS][3][3];
275 element = checkLevelElement(getFile16BitBE(file));
276 num_contents = fgetc(file);
277 content_xsize = fgetc(file);
278 content_ysize = fgetc(file);
279 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
281 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
284 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
286 /* correct invalid number of content fields -- should never happen */
287 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
288 num_contents = STD_ELEMENT_CONTENTS;
290 if (element == EL_YAMYAM)
292 level->num_yam_contents = num_contents;
294 for(i=0; i<num_contents; i++)
297 level->yam_content[i][x][y] = content_array[i][x][y];
299 else if (element == EL_BD_AMOEBA)
301 level->amoeba_content = content_array[0][0][0];
305 Error(ERR_WARN, "cannot load content for element '%d'", element);
311 void LoadLevel(int level_nr)
313 char *filename = getLevelFilename(level_nr);
314 char cookie[MAX_LINE_LEN];
315 char chunk_name[CHUNK_ID_LEN + 1];
319 /* always start with reliable default values */
320 setLevelInfoToDefaults();
322 if (!(file = fopen(filename, MODE_READ)))
324 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
328 getFileChunkBE(file, chunk_name, NULL);
329 if (strcmp(chunk_name, "RND1") == 0)
331 getFile32BitBE(file); /* not used */
333 getFileChunkBE(file, chunk_name, NULL);
334 if (strcmp(chunk_name, "CAVE") != 0)
336 Error(ERR_WARN, "unknown format of level file '%s'", filename);
341 else /* check for pre-2.0 file format with cookie string */
343 strcpy(cookie, chunk_name);
344 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
345 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
346 cookie[strlen(cookie) - 1] = '\0';
348 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
350 Error(ERR_WARN, "unknown format of level file '%s'", filename);
355 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
357 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
362 /* pre-2.0 level files have no game version, so use file version here */
363 level.game_version = level.file_version;
366 if (level.file_version < FILE_VERSION_1_2)
368 /* level files from versions before 1.2.0 without chunk structure */
369 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
370 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
378 int (*loader)(FILE *, int, struct LevelInfo *);
382 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
383 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
384 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
385 { "CONT", -1, LoadLevel_CONT },
386 { "BODY", -1, LoadLevel_BODY },
387 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
391 while (getFileChunkBE(file, chunk_name, &chunk_size))
395 while (chunk_info[i].name != NULL &&
396 strcmp(chunk_name, chunk_info[i].name) != 0)
399 if (chunk_info[i].name == NULL)
401 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
402 chunk_name, filename);
403 ReadUnusedBytesFromFile(file, chunk_size);
405 else if (chunk_info[i].size != -1 &&
406 chunk_info[i].size != chunk_size)
408 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
409 chunk_size, chunk_name, filename);
410 ReadUnusedBytesFromFile(file, chunk_size);
414 /* call function to load this level chunk */
415 int chunk_size_expected =
416 (chunk_info[i].loader)(file, chunk_size, &level);
418 /* the size of some chunks cannot be checked before reading other
419 chunks first (like "HEAD" and "BODY") that contain some header
420 information, so check them here */
421 if (chunk_size_expected != chunk_size)
423 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
424 chunk_size, chunk_name, filename);
432 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
433 IS_LEVELCLASS_USER(leveldir_current))
435 /* For user contributed and private levels, use the version of
436 the game engine the levels were created for.
437 Since 2.0.1, the game engine version is now directly stored
438 in the level file (chunk "VERS"), so there is no need anymore
439 to set the game version from the file version (except for old,
440 pre-2.0 levels, where the game version is still taken from the
441 file format version used to store the level -- see above). */
443 /* do some special adjustments to support older level versions */
444 if (level.file_version == FILE_VERSION_1_0)
446 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
447 Error(ERR_WARN, "using high speed movement for player");
449 /* player was faster than monsters in (pre-)1.0 levels */
450 level.double_speed = TRUE;
453 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
454 if (level.game_version == VERSION_IDENT(2,0,1))
455 level.em_slippery_gems = TRUE;
459 /* Always use the latest version of the game engine for all but
460 user contributed and private levels; this allows for actual
461 corrections in the game engine to take effect for existing,
462 converted levels (from "classic" or other existing games) to
463 make the game emulation more accurate, while (hopefully) not
464 breaking existing levels created from other players. */
466 level.game_version = GAME_VERSION_ACTUAL;
468 /* Set special EM style gems behaviour: EM style gems slip down from
469 normal, steel and growing wall. As this is a more fundamental change,
470 it seems better to set the default behaviour to "off" (as it is more
471 natural) and make it configurable in the level editor (as a property
472 of gem style elements). Already existing converted levels (neither
473 private nor contributed levels) are changed to the new behaviour. */
475 if (level.file_version < FILE_VERSION_2_0)
476 level.em_slippery_gems = TRUE;
479 /* determine border element for this level */
483 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
485 putFileVersion(file, level->file_version);
486 putFileVersion(file, level->game_version);
489 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
493 fputc(level->fieldx, file);
494 fputc(level->fieldy, file);
496 putFile16BitBE(file, level->time);
497 putFile16BitBE(file, level->gems_needed);
499 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
500 fputc(level->name[i], file);
502 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
503 fputc(level->score[i], file);
505 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
508 fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
509 level->yam_content[i][x][y]),
511 fputc(level->amoeba_speed, file);
512 fputc(level->time_magic_wall, file);
513 fputc(level->time_wheel, file);
514 fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
516 fputc((level->double_speed ? 1 : 0), file);
517 fputc((level->gravity ? 1 : 0), file);
518 fputc((level->encoding_16bit_field ? 1 : 0), file);
519 fputc((level->em_slippery_gems ? 1 : 0), file);
521 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
524 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
528 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
529 fputc(level->author[i], file);
533 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
537 fputc(EL_YAMYAM, file);
538 fputc(level->num_yam_contents, file);
542 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
545 if (level->encoding_16bit_field)
546 putFile16BitBE(file, level->yam_content[i][x][y]);
548 fputc(level->yam_content[i][x][y], file);
552 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
556 for(y=0; y<level->fieldy; y++)
557 for(x=0; x<level->fieldx; x++)
558 if (level->encoding_16bit_field)
559 putFile16BitBE(file, Ur[x][y]);
561 fputc(Ur[x][y], file);
564 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
567 int num_contents, content_xsize, content_ysize;
568 int content_array[MAX_ELEMENT_CONTENTS][3][3];
570 if (element == EL_YAMYAM)
572 num_contents = level->num_yam_contents;
576 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
579 content_array[i][x][y] = level->yam_content[i][x][y];
581 else if (element == EL_BD_AMOEBA)
587 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
590 content_array[i][x][y] = EL_EMPTY;
591 content_array[0][0][0] = level->amoeba_content;
595 /* chunk header already written -- write empty chunk data */
596 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
598 Error(ERR_WARN, "cannot save content for element '%d'", element);
602 putFile16BitBE(file, element);
603 fputc(num_contents, file);
604 fputc(content_xsize, file);
605 fputc(content_ysize, file);
607 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
609 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
612 putFile16BitBE(file, content_array[i][x][y]);
615 void SaveLevel(int level_nr)
618 char *filename = getLevelFilename(level_nr);
622 if (!(file = fopen(filename, MODE_WRITE)))
624 Error(ERR_WARN, "cannot save level file '%s'", filename);
628 level.file_version = FILE_VERSION_ACTUAL;
629 level.game_version = GAME_VERSION_ACTUAL;
631 /* check level field for 16-bit elements */
632 level.encoding_16bit_field = FALSE;
633 for(y=0; y<level.fieldy; y++)
634 for(x=0; x<level.fieldx; x++)
636 level.encoding_16bit_field = TRUE;
638 /* check yamyam content for 16-bit elements */
639 level.encoding_16bit_yamyam = FALSE;
640 for(i=0; i<level.num_yam_contents; i++)
643 if (level.yam_content[i][x][y] > 255)
644 level.encoding_16bit_yamyam = TRUE;
646 /* check amoeba content for 16-bit elements */
647 level.encoding_16bit_amoeba = FALSE;
648 if (level.amoeba_content > 255)
649 level.encoding_16bit_amoeba = TRUE;
652 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
654 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
655 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
657 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
658 SaveLevel_VERS(file, &level);
660 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
661 SaveLevel_HEAD(file, &level);
663 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
664 SaveLevel_AUTH(file, &level);
666 putFileChunkBE(file, "BODY", body_chunk_size);
667 SaveLevel_BODY(file, &level);
669 if (level.encoding_16bit_yamyam ||
670 level.num_yam_contents != STD_ELEMENT_CONTENTS)
672 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
673 SaveLevel_CNT2(file, &level, EL_YAMYAM);
676 if (level.encoding_16bit_amoeba)
678 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
679 SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
684 SetFilePermissions(filename, PERMS_PRIVATE);
688 /* ========================================================================= */
689 /* tape file functions */
690 /* ========================================================================= */
692 static void setTapeInfoToDefaults()
696 /* always start with reliable default values (empty tape) */
699 /* default values (also for pre-1.2 tapes) with only the first player */
700 tape.player_participates[0] = TRUE;
701 for(i=1; i<MAX_PLAYERS; i++)
702 tape.player_participates[i] = FALSE;
704 /* at least one (default: the first) player participates in every tape */
705 tape.num_participating_players = 1;
707 tape.level_nr = level_nr;
709 tape.changed = FALSE;
711 tape.recording = FALSE;
712 tape.playing = FALSE;
713 tape.pausing = FALSE;
716 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
718 tape->file_version = getFileVersion(file);
719 tape->game_version = getFileVersion(file);
724 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
728 tape->random_seed = getFile32BitBE(file);
729 tape->date = getFile32BitBE(file);
730 tape->length = getFile32BitBE(file);
732 /* read header fields that are new since version 1.2 */
733 if (tape->file_version >= FILE_VERSION_1_2)
735 byte store_participating_players = fgetc(file);
738 /* since version 1.2, tapes store which players participate in the tape */
739 tape->num_participating_players = 0;
740 for(i=0; i<MAX_PLAYERS; i++)
742 tape->player_participates[i] = FALSE;
744 if (store_participating_players & (1 << i))
746 tape->player_participates[i] = TRUE;
747 tape->num_participating_players++;
751 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
753 engine_version = getFileVersion(file);
754 if (engine_version > 0)
755 tape->engine_version = engine_version;
761 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
764 int chunk_size_expected =
765 (tape->num_participating_players + 1) * tape->length;
767 if (chunk_size_expected != chunk_size)
769 ReadUnusedBytesFromFile(file, chunk_size);
770 return chunk_size_expected;
773 for(i=0; i<tape->length; i++)
775 if (i >= MAX_TAPELEN)
778 for(j=0; j<MAX_PLAYERS; j++)
780 tape->pos[i].action[j] = MV_NO_MOVING;
782 if (tape->player_participates[j])
783 tape->pos[i].action[j] = fgetc(file);
786 tape->pos[i].delay = fgetc(file);
788 if (tape->file_version == FILE_VERSION_1_0)
790 /* eliminate possible diagonal moves in old tapes */
791 /* this is only for backward compatibility */
793 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
794 byte action = tape->pos[i].action[0];
795 int k, num_moves = 0;
799 if (action & joy_dir[k])
801 tape->pos[i + num_moves].action[0] = joy_dir[k];
803 tape->pos[i + num_moves].delay = 0;
812 tape->length += num_moves;
815 else if (tape->file_version < FILE_VERSION_2_0)
817 /* convert pre-2.0 tapes to new tape format */
819 if (tape->pos[i].delay > 1)
822 tape->pos[i + 1] = tape->pos[i];
823 tape->pos[i + 1].delay = 1;
826 for(j=0; j<MAX_PLAYERS; j++)
827 tape->pos[i].action[j] = MV_NO_MOVING;
828 tape->pos[i].delay--;
839 if (i != tape->length)
840 chunk_size = (tape->num_participating_players + 1) * i;
845 void LoadTape(int level_nr)
847 char *filename = getTapeFilename(level_nr);
848 char cookie[MAX_LINE_LEN];
849 char chunk_name[CHUNK_ID_LEN + 1];
853 /* always start with reliable default values */
854 setTapeInfoToDefaults();
856 if (!(file = fopen(filename, MODE_READ)))
859 getFileChunkBE(file, chunk_name, NULL);
860 if (strcmp(chunk_name, "RND1") == 0)
862 getFile32BitBE(file); /* not used */
864 getFileChunkBE(file, chunk_name, NULL);
865 if (strcmp(chunk_name, "TAPE") != 0)
867 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
872 else /* check for pre-2.0 file format with cookie string */
874 strcpy(cookie, chunk_name);
875 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
876 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
877 cookie[strlen(cookie) - 1] = '\0';
879 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
881 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
886 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
888 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
893 /* pre-2.0 tape files have no game version, so use file version here */
894 tape.game_version = tape.file_version;
897 if (tape.file_version < FILE_VERSION_1_2)
899 /* tape files from versions before 1.2.0 without chunk structure */
900 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
901 LoadTape_BODY(file, 2 * tape.length, &tape);
909 int (*loader)(FILE *, int, struct TapeInfo *);
913 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
914 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
915 { "BODY", -1, LoadTape_BODY },
919 while (getFileChunkBE(file, chunk_name, &chunk_size))
923 while (chunk_info[i].name != NULL &&
924 strcmp(chunk_name, chunk_info[i].name) != 0)
927 if (chunk_info[i].name == NULL)
929 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
930 chunk_name, filename);
931 ReadUnusedBytesFromFile(file, chunk_size);
933 else if (chunk_info[i].size != -1 &&
934 chunk_info[i].size != chunk_size)
936 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
937 chunk_size, chunk_name, filename);
938 ReadUnusedBytesFromFile(file, chunk_size);
942 /* call function to load this tape chunk */
943 int chunk_size_expected =
944 (chunk_info[i].loader)(file, chunk_size, &tape);
946 /* the size of some chunks cannot be checked before reading other
947 chunks first (like "HEAD" and "BODY") that contain some header
948 information, so check them here */
949 if (chunk_size_expected != chunk_size)
951 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
952 chunk_size, chunk_name, filename);
960 tape.length_seconds = GetTapeLength();
963 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
965 putFileVersion(file, tape->file_version);
966 putFileVersion(file, tape->game_version);
969 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
972 byte store_participating_players = 0;
974 /* set bits for participating players for compact storage */
975 for(i=0; i<MAX_PLAYERS; i++)
976 if (tape->player_participates[i])
977 store_participating_players |= (1 << i);
979 putFile32BitBE(file, tape->random_seed);
980 putFile32BitBE(file, tape->date);
981 putFile32BitBE(file, tape->length);
983 fputc(store_participating_players, file);
985 /* unused bytes not at the end here for 4-byte alignment of engine_version */
986 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
988 putFileVersion(file, tape->engine_version);
991 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
995 for(i=0; i<tape->length; i++)
997 for(j=0; j<MAX_PLAYERS; j++)
998 if (tape->player_participates[j])
999 fputc(tape->pos[i].action[j], file);
1001 fputc(tape->pos[i].delay, file);
1005 void SaveTape(int level_nr)
1008 char *filename = getTapeFilename(level_nr);
1010 boolean new_tape = TRUE;
1011 int num_participating_players = 0;
1012 int body_chunk_size;
1014 InitTapeDirectory(leveldir_current->filename);
1016 /* if a tape still exists, ask to overwrite it */
1017 if (access(filename, F_OK) == 0)
1020 if (!Request("Replace old tape ?", REQ_ASK))
1024 if (!(file = fopen(filename, MODE_WRITE)))
1026 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1030 tape.file_version = FILE_VERSION_ACTUAL;
1031 tape.game_version = GAME_VERSION_ACTUAL;
1033 /* count number of participating players */
1034 for(i=0; i<MAX_PLAYERS; i++)
1035 if (tape.player_participates[i])
1036 num_participating_players++;
1038 body_chunk_size = (num_participating_players + 1) * tape.length;
1040 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1041 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1043 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1044 SaveTape_VERS(file, &tape);
1046 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1047 SaveTape_HEAD(file, &tape);
1049 putFileChunkBE(file, "BODY", body_chunk_size);
1050 SaveTape_BODY(file, &tape);
1054 SetFilePermissions(filename, PERMS_PRIVATE);
1056 tape.changed = FALSE;
1059 Request("tape saved !", REQ_CONFIRM);
1062 void DumpTape(struct TapeInfo *tape)
1066 if (TAPE_IS_EMPTY(*tape))
1068 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1073 printf("-------------------------------------------------------------------------------\n");
1074 printf("Tape of Level %d (file version %06d, game version %06d)\n",
1075 tape->level_nr, tape->file_version, tape->game_version);
1076 printf("-------------------------------------------------------------------------------\n");
1078 for(i=0; i<tape->length; i++)
1080 if (i >= MAX_TAPELEN)
1083 for(j=0; j<MAX_PLAYERS; j++)
1085 if (tape->player_participates[j])
1087 int action = tape->pos[i].action[j];
1089 printf("%d:%02x ", j, action);
1090 printf("[%c%c%c%c|%c%c] - ",
1091 (action & JOY_LEFT ? '<' : ' '),
1092 (action & JOY_RIGHT ? '>' : ' '),
1093 (action & JOY_UP ? '^' : ' '),
1094 (action & JOY_DOWN ? 'v' : ' '),
1095 (action & JOY_BUTTON_1 ? '1' : ' '),
1096 (action & JOY_BUTTON_2 ? '2' : ' '));
1100 printf("(%03d)\n", tape->pos[i].delay);
1103 printf("-------------------------------------------------------------------------------\n");
1107 /* ========================================================================= */
1108 /* score file functions */
1109 /* ========================================================================= */
1111 void LoadScore(int level_nr)
1114 char *filename = getScoreFilename(level_nr);
1115 char cookie[MAX_LINE_LEN];
1116 char line[MAX_LINE_LEN];
1120 /* always start with reliable default values */
1121 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1123 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1124 highscore[i].Score = 0;
1127 if (!(file = fopen(filename, MODE_READ)))
1130 /* check file identifier */
1131 fgets(cookie, MAX_LINE_LEN, file);
1132 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1133 cookie[strlen(cookie) - 1] = '\0';
1135 if (!checkCookieString(cookie, SCORE_COOKIE))
1137 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1142 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1144 fscanf(file, "%d", &highscore[i].Score);
1145 fgets(line, MAX_LINE_LEN, file);
1147 if (line[strlen(line) - 1] == '\n')
1148 line[strlen(line) - 1] = '\0';
1150 for (line_ptr = line; *line_ptr; line_ptr++)
1152 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1154 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1155 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1164 void SaveScore(int level_nr)
1167 char *filename = getScoreFilename(level_nr);
1170 InitScoreDirectory(leveldir_current->filename);
1172 if (!(file = fopen(filename, MODE_WRITE)))
1174 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1178 fprintf(file, "%s\n\n", SCORE_COOKIE);
1180 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1181 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1185 SetFilePermissions(filename, PERMS_PUBLIC);
1189 /* ========================================================================= */
1190 /* setup file functions */
1191 /* ========================================================================= */
1193 #define TOKEN_STR_PLAYER_PREFIX "player_"
1196 #define SETUP_TOKEN_PLAYER_NAME 0
1197 #define SETUP_TOKEN_SOUND 1
1198 #define SETUP_TOKEN_SOUND_LOOPS 2
1199 #define SETUP_TOKEN_SOUND_MUSIC 3
1200 #define SETUP_TOKEN_SOUND_SIMPLE 4
1201 #define SETUP_TOKEN_TOONS 5
1202 #define SETUP_TOKEN_SCROLL_DELAY 6
1203 #define SETUP_TOKEN_SOFT_SCROLLING 7
1204 #define SETUP_TOKEN_FADING 8
1205 #define SETUP_TOKEN_AUTORECORD 9
1206 #define SETUP_TOKEN_QUICK_DOORS 10
1207 #define SETUP_TOKEN_TEAM_MODE 11
1208 #define SETUP_TOKEN_HANDICAP 12
1209 #define SETUP_TOKEN_TIME_LIMIT 13
1210 #define SETUP_TOKEN_FULLSCREEN 14
1211 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1212 #define SETUP_TOKEN_GRAPHICS_SET 16
1213 #define SETUP_TOKEN_SOUNDS_SET 17
1214 #define SETUP_TOKEN_MUSIC_SET 18
1215 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1216 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1217 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1219 #define NUM_GLOBAL_SETUP_TOKENS 22
1221 /* shortcut setup */
1222 #define SETUP_TOKEN_SAVE_GAME 0
1223 #define SETUP_TOKEN_LOAD_GAME 1
1224 #define SETUP_TOKEN_TOGGLE_PAUSE 2
1226 #define NUM_SHORTCUT_SETUP_TOKENS 3
1229 #define SETUP_TOKEN_USE_JOYSTICK 0
1230 #define SETUP_TOKEN_JOY_DEVICE_NAME 1
1231 #define SETUP_TOKEN_JOY_XLEFT 2
1232 #define SETUP_TOKEN_JOY_XMIDDLE 3
1233 #define SETUP_TOKEN_JOY_XRIGHT 4
1234 #define SETUP_TOKEN_JOY_YUPPER 5
1235 #define SETUP_TOKEN_JOY_YMIDDLE 6
1236 #define SETUP_TOKEN_JOY_YLOWER 7
1237 #define SETUP_TOKEN_JOY_SNAP 8
1238 #define SETUP_TOKEN_JOY_BOMB 9
1239 #define SETUP_TOKEN_KEY_LEFT 10
1240 #define SETUP_TOKEN_KEY_RIGHT 11
1241 #define SETUP_TOKEN_KEY_UP 12
1242 #define SETUP_TOKEN_KEY_DOWN 13
1243 #define SETUP_TOKEN_KEY_SNAP 14
1244 #define SETUP_TOKEN_KEY_BOMB 15
1246 #define NUM_PLAYER_SETUP_TOKENS 16
1248 static struct SetupInfo si;
1249 static struct SetupShortcutInfo ssi;
1250 static struct SetupInputInfo sii;
1252 static struct TokenInfo global_setup_tokens[] =
1255 { TYPE_STRING, &si.player_name, "player_name" },
1256 { TYPE_SWITCH, &si.sound, "sound" },
1257 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1258 { TYPE_SWITCH, &si.sound_music, "background_music" },
1259 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1260 { TYPE_SWITCH, &si.toons, "toons" },
1261 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1262 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1263 { TYPE_SWITCH, &si.fading, "screen_fading" },
1264 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1265 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1266 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1267 { TYPE_SWITCH, &si.handicap, "handicap" },
1268 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1269 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1270 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1271 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1272 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1273 { TYPE_STRING, &si.music_set, "music_set" },
1274 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1275 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1276 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1279 static struct TokenInfo shortcut_setup_tokens[] =
1281 /* shortcut setup */
1282 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1283 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1284 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1287 static struct TokenInfo player_setup_tokens[] =
1290 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1291 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1292 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1293 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1294 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1295 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1296 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1297 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1298 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1299 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1300 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1301 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1302 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1303 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1304 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1305 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1308 static void setSetupInfoToDefaults(struct SetupInfo *si)
1312 si->player_name = getStringCopy(getLoginName());
1315 si->sound_loops = TRUE;
1316 si->sound_music = TRUE;
1317 si->sound_simple = TRUE;
1319 si->double_buffering = TRUE;
1320 si->direct_draw = !si->double_buffering;
1321 si->scroll_delay = TRUE;
1322 si->soft_scrolling = TRUE;
1324 si->autorecord = TRUE;
1325 si->quick_doors = FALSE;
1326 si->team_mode = FALSE;
1327 si->handicap = TRUE;
1328 si->time_limit = TRUE;
1329 si->fullscreen = FALSE;
1330 si->ask_on_escape = TRUE;
1332 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1333 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1334 si->music_set = getStringCopy(MUSIC_SUBDIR);
1335 si->override_level_graphics = FALSE;
1336 si->override_level_sounds = FALSE;
1337 si->override_level_music = FALSE;
1339 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1340 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1341 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1343 for (i=0; i<MAX_PLAYERS; i++)
1345 si->input[i].use_joystick = FALSE;
1346 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1347 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1348 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1349 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1350 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1351 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1352 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1353 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1354 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1355 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1356 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1357 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1358 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1359 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1360 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1364 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1368 if (!setup_file_list)
1373 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1374 setSetupInfo(global_setup_tokens, i,
1375 getTokenValue(setup_file_list, global_setup_tokens[i].text));
1378 /* shortcut setup */
1379 ssi = setup.shortcut;
1380 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1381 setSetupInfo(shortcut_setup_tokens, i,
1382 getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
1383 setup.shortcut = ssi;
1386 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1390 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1392 sii = setup.input[pnr];
1393 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1395 char full_token[100];
1397 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1398 setSetupInfo(player_setup_tokens, i,
1399 getTokenValue(setup_file_list, full_token));
1401 setup.input[pnr] = sii;
1407 char *filename = getSetupFilename();
1408 struct SetupFileList *setup_file_list = NULL;
1410 /* always start with reliable default values */
1411 setSetupInfoToDefaults(&setup);
1413 setup_file_list = loadSetupFileList(filename);
1415 if (setup_file_list)
1417 checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
1418 decodeSetupFileList(setup_file_list);
1420 setup.direct_draw = !setup.double_buffering;
1422 freeSetupFileList(setup_file_list);
1424 /* needed to work around problems with fixed length strings */
1425 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1426 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1427 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1429 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1431 strcpy(new_name, setup.player_name);
1432 free(setup.player_name);
1433 setup.player_name = new_name;
1437 Error(ERR_WARN, "using default setup values");
1442 char *filename = getSetupFilename();
1446 InitUserDataDirectory();
1448 if (!(file = fopen(filename, MODE_WRITE)))
1450 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1454 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1455 getCookie("SETUP")));
1456 fprintf(file, "\n");
1460 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1462 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1464 /* just to make things nicer :) */
1465 if (i == SETUP_TOKEN_PLAYER_NAME || i == SETUP_TOKEN_GRAPHICS_SET - 1)
1466 fprintf(file, "\n");
1469 /* shortcut setup */
1470 ssi = setup.shortcut;
1471 fprintf(file, "\n");
1472 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1473 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1476 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1480 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1481 fprintf(file, "\n");
1483 sii = setup.input[pnr];
1484 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1485 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1490 SetFilePermissions(filename, PERMS_PRIVATE);