1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2001 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 7 /* 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_ERDREICH;
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_DIAMANT;
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_FELSBROCKEN : EL_LEERRAUM);
93 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
94 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
95 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
97 BorderElement = EL_BETON;
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 >= EL_FIRST_RUNTIME_EL)
134 Error(ERR_WARN, "invalid level element %d", element);
135 element = EL_CHAR_FRAGE;
141 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
143 ReadChunk_VERS(file, &(level->file_version), &(level->game_version));
148 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
152 lev_fieldx = level->fieldx = fgetc(file);
153 lev_fieldy = level->fieldy = fgetc(file);
155 level->time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
156 level->gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
158 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
159 level->name[i] = fgetc(file);
160 level->name[MAX_LEVEL_NAME_LEN] = 0;
162 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
163 level->score[i] = fgetc(file);
165 level->num_yam_contents = STD_ELEMENT_CONTENTS;
166 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
169 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
171 level->amoeba_speed = fgetc(file);
172 level->time_magic_wall = fgetc(file);
173 level->time_wheel = fgetc(file);
174 level->amoeba_content = checkLevelElement(fgetc(file));
175 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
176 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
177 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
178 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
180 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
185 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
189 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
190 level->author[i] = fgetc(file);
191 level->author[MAX_LEVEL_NAME_LEN] = 0;
196 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
200 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
201 int chunk_size_expected = header_size + content_size;
203 /* Note: "chunk_size" was wrong before version 2.0 when elements are
204 stored with 16-bit encoding (and should be twice as big then).
205 Even worse, playfield data was stored 16-bit when only yamyam content
206 contained 16-bit elements and vice versa. */
208 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
209 chunk_size_expected += content_size;
211 if (chunk_size_expected != chunk_size)
213 ReadUnusedBytesFromFile(file, chunk_size);
214 return chunk_size_expected;
218 level->num_yam_contents = fgetc(file);
222 /* correct invalid number of content fields -- should never happen */
223 if (level->num_yam_contents < 1 ||
224 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
225 level->num_yam_contents = STD_ELEMENT_CONTENTS;
227 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
230 level->yam_content[i][x][y] =
231 checkLevelElement(level->encoding_16bit_field ?
232 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
237 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
240 int chunk_size_expected = level->fieldx * level->fieldy;
242 /* Note: "chunk_size" was wrong before version 2.0 when elements are
243 stored with 16-bit encoding (and should be twice as big then).
244 Even worse, playfield data was stored 16-bit when only yamyam content
245 contained 16-bit elements and vice versa. */
247 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
248 chunk_size_expected *= 2;
250 if (chunk_size_expected != chunk_size)
252 ReadUnusedBytesFromFile(file, chunk_size);
253 return chunk_size_expected;
256 for(y=0; y<level->fieldy; y++)
257 for(x=0; x<level->fieldx; x++)
258 Feld[x][y] = Ur[x][y] =
259 checkLevelElement(level->encoding_16bit_field ?
260 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
265 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
269 int num_contents, content_xsize, content_ysize;
270 int content_array[MAX_ELEMENT_CONTENTS][3][3];
272 element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
273 num_contents = fgetc(file);
274 content_xsize = fgetc(file);
275 content_ysize = fgetc(file);
276 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
278 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
281 content_array[i][x][y] =
282 checkLevelElement(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
284 /* correct invalid number of content fields -- should never happen */
285 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
286 num_contents = STD_ELEMENT_CONTENTS;
288 if (element == EL_MAMPFER)
290 level->num_yam_contents = num_contents;
292 for(i=0; i<num_contents; i++)
295 level->yam_content[i][x][y] = content_array[i][x][y];
297 else if (element == EL_AMOEBE_BD)
299 level->amoeba_content = content_array[0][0][0];
303 Error(ERR_WARN, "cannot load content for element '%d'", element);
309 void LoadLevel(int level_nr)
311 char *filename = getLevelFilename(level_nr);
312 char cookie[MAX_LINE_LEN];
313 char chunk_name[CHUNK_ID_LEN + 1];
317 /* always start with reliable default values */
318 setLevelInfoToDefaults();
320 if (!(file = fopen(filename, MODE_READ)))
322 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
326 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
327 if (strcmp(chunk_name, "RND1") == 0)
329 getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN); /* not used */
331 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
332 if (strcmp(chunk_name, "CAVE") != 0)
334 Error(ERR_WARN, "unknown format of level file '%s'", filename);
339 else /* check for pre-2.0 file format with cookie string */
341 strcpy(cookie, chunk_name);
342 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
343 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
344 cookie[strlen(cookie) - 1] = '\0';
346 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
348 Error(ERR_WARN, "unknown format of level file '%s'", filename);
353 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
355 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
360 /* pre-2.0 level files have no game version, so use file version here */
361 level.game_version = level.file_version;
364 if (level.file_version < FILE_VERSION_1_2)
366 /* level files from versions before 1.2.0 without chunk structure */
367 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
368 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
376 int (*loader)(FILE *, int, struct LevelInfo *);
380 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
381 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
382 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
383 { "CONT", -1, LoadLevel_CONT },
384 { "BODY", -1, LoadLevel_BODY },
385 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
389 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
393 while (chunk_info[i].name != NULL &&
394 strcmp(chunk_name, chunk_info[i].name) != 0)
397 if (chunk_info[i].name == NULL)
399 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
400 chunk_name, filename);
401 ReadUnusedBytesFromFile(file, chunk_size);
403 else if (chunk_info[i].size != -1 &&
404 chunk_info[i].size != chunk_size)
406 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
407 chunk_size, chunk_name, filename);
408 ReadUnusedBytesFromFile(file, chunk_size);
412 /* call function to load this level chunk */
413 int chunk_size_expected =
414 (chunk_info[i].loader)(file, chunk_size, &level);
416 /* the size of some chunks cannot be checked before reading other
417 chunks first (like "HEAD" and "BODY") that contain some header
418 information, so check them here */
419 if (chunk_size_expected != chunk_size)
421 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
422 chunk_size, chunk_name, filename);
430 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
431 IS_LEVELCLASS_USER(leveldir_current))
433 /* For user contributed and private levels, use the version of
434 the game engine the levels were created for.
435 Since 2.0.1, the game engine version is now directly stored
436 in the level file (chunk "VERS"), so there is no need anymore
437 to set the game version from the file version (except for old,
438 pre-2.0 levels, where the game version is still taken from the
439 file format version used to store the level -- see above). */
441 /* do some special adjustments to support older level versions */
442 if (level.file_version == FILE_VERSION_1_0)
444 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
445 Error(ERR_WARN, "using high speed movement for player");
447 /* player was faster than monsters in (pre-)1.0 levels */
448 level.double_speed = TRUE;
453 /* Always use the latest version of the game engine for all but
454 user contributed and private levels; this allows for actual
455 corrections in the game engine to take effect for existing,
456 converted levels (from "classic" or other existing games) to
457 make the game emulation more accurate, while (hopefully) not
458 breaking existing levels created from other players. */
460 level.game_version = GAME_VERSION_ACTUAL;
462 /* Set special EM style gems behaviour: EM style gems slip down from
463 normal, steel and growing wall. As this is a more fundamental change,
464 it seems better to set the default behaviour to "off" (as it is more
465 natural) and make it configurable in the level editor (as a property
466 of gem style elements). Already existing converted levels (neither
467 private nor contributed levels) are changed to the new behaviour. */
469 if (level.file_version < FILE_VERSION_2_0)
470 level.em_slippery_gems = TRUE;
473 /* determine border element for this level */
477 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
481 fputc(level->fieldx, file);
482 fputc(level->fieldy, file);
484 putFile16BitInteger(file, level->time, BYTE_ORDER_BIG_ENDIAN);
485 putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
487 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
488 fputc(level->name[i], file);
490 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
491 fputc(level->score[i], file);
493 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
496 fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
497 level->yam_content[i][x][y]),
499 fputc(level->amoeba_speed, file);
500 fputc(level->time_magic_wall, file);
501 fputc(level->time_wheel, file);
502 fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
504 fputc((level->double_speed ? 1 : 0), file);
505 fputc((level->gravity ? 1 : 0), file);
506 fputc((level->encoding_16bit_field ? 1 : 0), file);
507 fputc((level->em_slippery_gems ? 1 : 0), file);
509 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
512 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
516 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
517 fputc(level->author[i], file);
521 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
525 fputc(EL_MAMPFER, file);
526 fputc(level->num_yam_contents, file);
530 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
533 if (level->encoding_16bit_field)
534 putFile16BitInteger(file, level->yam_content[i][x][y],
535 BYTE_ORDER_BIG_ENDIAN);
537 fputc(level->yam_content[i][x][y], file);
541 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
545 for(y=0; y<level->fieldy; y++)
546 for(x=0; x<level->fieldx; x++)
547 if (level->encoding_16bit_field)
548 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
550 fputc(Ur[x][y], file);
553 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
556 int num_contents, content_xsize, content_ysize;
557 int content_array[MAX_ELEMENT_CONTENTS][3][3];
559 if (element == EL_MAMPFER)
561 num_contents = level->num_yam_contents;
565 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
568 content_array[i][x][y] = level->yam_content[i][x][y];
570 else if (element == EL_AMOEBE_BD)
576 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
579 content_array[i][x][y] = EL_LEERRAUM;
580 content_array[0][0][0] = level->amoeba_content;
584 /* chunk header already written -- write empty chunk data */
585 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
587 Error(ERR_WARN, "cannot save content for element '%d'", element);
591 putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
592 fputc(num_contents, file);
593 fputc(content_xsize, file);
594 fputc(content_ysize, file);
596 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
598 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
601 putFile16BitInteger(file, content_array[i][x][y],
602 BYTE_ORDER_BIG_ENDIAN);
605 void SaveLevel(int level_nr)
608 char *filename = getLevelFilename(level_nr);
612 if (!(file = fopen(filename, MODE_WRITE)))
614 Error(ERR_WARN, "cannot save level file '%s'", filename);
619 /* check level field for 16-bit elements */
620 level.encoding_16bit_field = FALSE;
621 for(y=0; y<level.fieldy; y++)
622 for(x=0; x<level.fieldx; x++)
624 level.encoding_16bit_field = TRUE;
626 /* check yamyam content for 16-bit elements */
627 level.encoding_16bit_yamyam = FALSE;
628 for(i=0; i<level.num_yam_contents; i++)
631 if (level.yam_content[i][x][y] > 255)
632 level.encoding_16bit_yamyam = TRUE;
634 /* check amoeba content for 16-bit elements */
635 level.encoding_16bit_amoeba = FALSE;
636 if (level.amoeba_content > 255)
637 level.encoding_16bit_amoeba = TRUE;
640 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
642 putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
643 putFileChunk(file, "CAVE", CHUNK_SIZE_NONE, BYTE_ORDER_BIG_ENDIAN);
645 putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
646 WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
648 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
649 SaveLevel_HEAD(file, &level);
651 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
652 SaveLevel_AUTH(file, &level);
654 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
655 SaveLevel_BODY(file, &level);
657 if (level.encoding_16bit_yamyam ||
658 level.num_yam_contents != STD_ELEMENT_CONTENTS)
660 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
661 SaveLevel_CNT2(file, &level, EL_MAMPFER);
664 if (level.encoding_16bit_amoeba)
666 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
667 SaveLevel_CNT2(file, &level, EL_AMOEBE_BD);
672 SetFilePermissions(filename, PERMS_PRIVATE);
676 /* ========================================================================= */
677 /* tape file functions */
678 /* ========================================================================= */
680 static void setTapeInfoToDefaults()
684 /* always start with reliable default values (empty tape) */
685 tape.file_version = FILE_VERSION_ACTUAL;
686 tape.game_version = GAME_VERSION_ACTUAL;
689 /* default values (also for pre-1.2 tapes) with only the first player */
690 tape.player_participates[0] = TRUE;
691 for(i=1; i<MAX_PLAYERS; i++)
692 tape.player_participates[i] = FALSE;
694 /* at least one (default: the first) player participates in every tape */
695 tape.num_participating_players = 1;
697 tape.level_nr = level_nr;
699 tape.changed = FALSE;
701 tape.recording = FALSE;
702 tape.playing = FALSE;
703 tape.pausing = FALSE;
706 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
708 ReadChunk_VERS(file, &(tape->file_version), &(tape->game_version));
713 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
717 tape->random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
718 tape->date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
719 tape->length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
721 /* read header fields that are new since version 1.2 */
722 if (tape->file_version >= FILE_VERSION_1_2)
724 byte store_participating_players = fgetc(file);
726 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
728 /* since version 1.2, tapes store which players participate in the tape */
729 tape->num_participating_players = 0;
730 for(i=0; i<MAX_PLAYERS; i++)
732 tape->player_participates[i] = FALSE;
734 if (store_participating_players & (1 << i))
736 tape->player_participates[i] = TRUE;
737 tape->num_participating_players++;
745 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
748 int chunk_size_expected =
749 (tape->num_participating_players + 1) * tape->length;
751 if (chunk_size_expected != chunk_size)
753 ReadUnusedBytesFromFile(file, chunk_size);
754 return chunk_size_expected;
757 for(i=0; i<tape->length; i++)
759 if (i >= MAX_TAPELEN)
762 for(j=0; j<MAX_PLAYERS; j++)
764 tape->pos[i].action[j] = MV_NO_MOVING;
766 if (tape->player_participates[j])
767 tape->pos[i].action[j] = fgetc(file);
770 tape->pos[i].delay = fgetc(file);
772 if (tape->file_version == FILE_VERSION_1_0)
774 /* eliminate possible diagonal moves in old tapes */
775 /* this is only for backward compatibility */
777 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
778 byte action = tape->pos[i].action[0];
779 int k, num_moves = 0;
783 if (action & joy_dir[k])
785 tape->pos[i + num_moves].action[0] = joy_dir[k];
787 tape->pos[i + num_moves].delay = 0;
796 tape->length += num_moves;
799 else if (tape->file_version < FILE_VERSION_2_0)
801 /* convert pre-2.0 tapes to new tape format */
803 if (tape->pos[i].delay > 1)
806 tape->pos[i + 1] = tape->pos[i];
807 tape->pos[i + 1].delay = 1;
810 for(j=0; j<MAX_PLAYERS; j++)
811 tape->pos[i].action[j] = MV_NO_MOVING;
812 tape->pos[i].delay--;
823 if (i != tape->length)
824 chunk_size = (tape->num_participating_players + 1) * i;
829 void LoadTape(int level_nr)
831 char *filename = getTapeFilename(level_nr);
832 char cookie[MAX_LINE_LEN];
833 char chunk_name[CHUNK_ID_LEN + 1];
837 /* always start with reliable default values */
838 setTapeInfoToDefaults();
840 if (!(file = fopen(filename, MODE_READ)))
843 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
844 if (strcmp(chunk_name, "RND1") == 0)
846 getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN); /* not used */
848 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
849 if (strcmp(chunk_name, "TAPE") != 0)
851 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
856 else /* check for pre-2.0 file format with cookie string */
858 strcpy(cookie, chunk_name);
859 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
860 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
861 cookie[strlen(cookie) - 1] = '\0';
863 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
865 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
870 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
872 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
877 /* pre-2.0 tape files have no game version, so use file version here */
878 tape.game_version = tape.file_version;
881 if (tape.file_version < FILE_VERSION_1_2)
883 /* tape files from versions before 1.2.0 without chunk structure */
884 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
885 LoadTape_BODY(file, 2 * tape.length, &tape);
893 int (*loader)(FILE *, int, struct TapeInfo *);
897 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
898 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
899 { "BODY", -1, LoadTape_BODY },
903 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
907 while (chunk_info[i].name != NULL &&
908 strcmp(chunk_name, chunk_info[i].name) != 0)
911 if (chunk_info[i].name == NULL)
913 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
914 chunk_name, filename);
915 ReadUnusedBytesFromFile(file, chunk_size);
917 else if (chunk_info[i].size != -1 &&
918 chunk_info[i].size != chunk_size)
920 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
921 chunk_size, chunk_name, filename);
922 ReadUnusedBytesFromFile(file, chunk_size);
926 /* call function to load this tape chunk */
927 int chunk_size_expected =
928 (chunk_info[i].loader)(file, chunk_size, &tape);
930 /* the size of some chunks cannot be checked before reading other
931 chunks first (like "HEAD" and "BODY") that contain some header
932 information, so check them here */
933 if (chunk_size_expected != chunk_size)
935 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
936 chunk_size, chunk_name, filename);
944 tape.length_seconds = GetTapeLength();
947 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
950 byte store_participating_players = 0;
952 /* set bits for participating players for compact storage */
953 for(i=0; i<MAX_PLAYERS; i++)
954 if (tape->player_participates[i])
955 store_participating_players |= (1 << i);
957 putFile32BitInteger(file, tape->random_seed, BYTE_ORDER_BIG_ENDIAN);
958 putFile32BitInteger(file, tape->date, BYTE_ORDER_BIG_ENDIAN);
959 putFile32BitInteger(file, tape->length, BYTE_ORDER_BIG_ENDIAN);
961 fputc(store_participating_players, file);
963 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
966 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
970 for(i=0; i<tape->length; i++)
972 for(j=0; j<MAX_PLAYERS; j++)
973 if (tape->player_participates[j])
974 fputc(tape->pos[i].action[j], file);
976 fputc(tape->pos[i].delay, file);
980 void SaveTape(int level_nr)
983 char *filename = getTapeFilename(level_nr);
985 boolean new_tape = TRUE;
986 int num_participating_players = 0;
989 InitTapeDirectory(leveldir_current->filename);
991 /* if a tape still exists, ask to overwrite it */
992 if (access(filename, F_OK) == 0)
995 if (!Request("Replace old tape ?", REQ_ASK))
999 if (!(file = fopen(filename, MODE_WRITE)))
1001 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1005 /* count number of participating players */
1006 for(i=0; i<MAX_PLAYERS; i++)
1007 if (tape.player_participates[i])
1008 num_participating_players++;
1010 body_chunk_size = (num_participating_players + 1) * tape.length;
1012 putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
1013 putFileChunk(file, "TAPE", CHUNK_SIZE_NONE, BYTE_ORDER_BIG_ENDIAN);
1015 putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
1016 WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
1018 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1019 SaveTape_HEAD(file, &tape);
1021 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1022 SaveTape_BODY(file, &tape);
1026 SetFilePermissions(filename, PERMS_PRIVATE);
1028 tape.changed = FALSE;
1031 Request("tape saved !", REQ_CONFIRM);
1034 void DumpTape(struct TapeInfo *tape)
1038 if (TAPE_IS_EMPTY(*tape))
1040 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1045 printf("-------------------------------------------------------------------------------\n");
1046 printf("Tape of Level %d (file version %06d, game version %06d\n",
1047 tape->level_nr, tape->file_version, tape->game_version);
1048 printf("-------------------------------------------------------------------------------\n");
1050 for(i=0; i<tape->length; i++)
1052 if (i >= MAX_TAPELEN)
1055 for(j=0; j<MAX_PLAYERS; j++)
1057 if (tape->player_participates[j])
1059 int action = tape->pos[i].action[j];
1061 printf("%d:%02x ", j, action);
1062 printf("[%c%c%c%c|%c%c] - ",
1063 (action & JOY_LEFT ? '<' : ' '),
1064 (action & JOY_RIGHT ? '>' : ' '),
1065 (action & JOY_UP ? '^' : ' '),
1066 (action & JOY_DOWN ? 'v' : ' '),
1067 (action & JOY_BUTTON_1 ? '1' : ' '),
1068 (action & JOY_BUTTON_2 ? '2' : ' '));
1072 printf("(%03d)\n", tape->pos[i].delay);
1075 printf("-------------------------------------------------------------------------------\n");
1079 /* ========================================================================= */
1080 /* score file functions */
1081 /* ========================================================================= */
1083 void LoadScore(int level_nr)
1086 char *filename = getScoreFilename(level_nr);
1087 char cookie[MAX_LINE_LEN];
1088 char line[MAX_LINE_LEN];
1092 /* always start with reliable default values */
1093 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1095 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1096 highscore[i].Score = 0;
1099 if (!(file = fopen(filename, MODE_READ)))
1102 /* check file identifier */
1103 fgets(cookie, MAX_LINE_LEN, file);
1104 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1105 cookie[strlen(cookie) - 1] = '\0';
1107 if (!checkCookieString(cookie, SCORE_COOKIE))
1109 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1114 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1116 fscanf(file, "%d", &highscore[i].Score);
1117 fgets(line, MAX_LINE_LEN, file);
1119 if (line[strlen(line) - 1] == '\n')
1120 line[strlen(line) - 1] = '\0';
1122 for (line_ptr = line; *line_ptr; line_ptr++)
1124 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1126 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1127 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1136 void SaveScore(int level_nr)
1139 char *filename = getScoreFilename(level_nr);
1142 InitScoreDirectory(leveldir_current->filename);
1144 if (!(file = fopen(filename, MODE_WRITE)))
1146 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1150 fprintf(file, "%s\n\n", SCORE_COOKIE);
1152 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1153 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1157 SetFilePermissions(filename, PERMS_PUBLIC);
1161 /* ========================================================================= */
1162 /* setup file functions */
1163 /* ========================================================================= */
1165 #define TOKEN_STR_PLAYER_PREFIX "player_"
1168 #define SETUP_TOKEN_PLAYER_NAME 0
1169 #define SETUP_TOKEN_SOUND 1
1170 #define SETUP_TOKEN_SOUND_LOOPS 2
1171 #define SETUP_TOKEN_SOUND_MUSIC 3
1172 #define SETUP_TOKEN_SOUND_SIMPLE 4
1173 #define SETUP_TOKEN_TOONS 5
1174 #define SETUP_TOKEN_SCROLL_DELAY 6
1175 #define SETUP_TOKEN_SOFT_SCROLLING 7
1176 #define SETUP_TOKEN_FADING 8
1177 #define SETUP_TOKEN_AUTORECORD 9
1178 #define SETUP_TOKEN_QUICK_DOORS 10
1179 #define SETUP_TOKEN_TEAM_MODE 11
1180 #define SETUP_TOKEN_HANDICAP 12
1181 #define SETUP_TOKEN_TIME_LIMIT 13
1182 #define SETUP_TOKEN_FULLSCREEN 14
1183 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1185 #define NUM_GLOBAL_SETUP_TOKENS 16
1187 /* shortcut setup */
1188 #define SETUP_TOKEN_SAVE_GAME 0
1189 #define SETUP_TOKEN_LOAD_GAME 1
1191 #define NUM_SHORTCUT_SETUP_TOKENS 2
1194 #define SETUP_TOKEN_USE_JOYSTICK 0
1195 #define SETUP_TOKEN_JOY_DEVICE_NAME 1
1196 #define SETUP_TOKEN_JOY_XLEFT 2
1197 #define SETUP_TOKEN_JOY_XMIDDLE 3
1198 #define SETUP_TOKEN_JOY_XRIGHT 4
1199 #define SETUP_TOKEN_JOY_YUPPER 5
1200 #define SETUP_TOKEN_JOY_YMIDDLE 6
1201 #define SETUP_TOKEN_JOY_YLOWER 7
1202 #define SETUP_TOKEN_JOY_SNAP 8
1203 #define SETUP_TOKEN_JOY_BOMB 9
1204 #define SETUP_TOKEN_KEY_LEFT 10
1205 #define SETUP_TOKEN_KEY_RIGHT 11
1206 #define SETUP_TOKEN_KEY_UP 12
1207 #define SETUP_TOKEN_KEY_DOWN 13
1208 #define SETUP_TOKEN_KEY_SNAP 14
1209 #define SETUP_TOKEN_KEY_BOMB 15
1211 #define NUM_PLAYER_SETUP_TOKENS 16
1213 static struct SetupInfo si;
1214 static struct SetupShortcutInfo ssi;
1215 static struct SetupInputInfo sii;
1217 static struct TokenInfo global_setup_tokens[] =
1220 { TYPE_STRING, &si.player_name, "player_name" },
1221 { TYPE_SWITCH, &si.sound, "sound" },
1222 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1223 { TYPE_SWITCH, &si.sound_music, "background_music" },
1224 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1225 { TYPE_SWITCH, &si.toons, "toons" },
1226 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1227 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1228 { TYPE_SWITCH, &si.fading, "screen_fading" },
1229 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1230 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1231 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1232 { TYPE_SWITCH, &si.handicap, "handicap" },
1233 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1234 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1235 { TYPE_SWITCH, &si.fullscreen, "fullscreen" }
1238 static struct TokenInfo shortcut_setup_tokens[] =
1240 /* shortcut setup */
1241 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1242 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" }
1245 static struct TokenInfo player_setup_tokens[] =
1248 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1249 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1250 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1251 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1252 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1253 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1254 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1255 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1256 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1257 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1258 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1259 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1260 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1261 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1262 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1263 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1266 static void setSetupInfoToDefaults(struct SetupInfo *si)
1270 si->player_name = getStringCopy(getLoginName());
1273 si->sound_loops = TRUE;
1274 si->sound_music = TRUE;
1275 si->sound_simple = TRUE;
1277 si->double_buffering = TRUE;
1278 si->direct_draw = !si->double_buffering;
1279 si->scroll_delay = TRUE;
1280 si->soft_scrolling = TRUE;
1282 si->autorecord = TRUE;
1283 si->quick_doors = FALSE;
1284 si->team_mode = FALSE;
1285 si->handicap = TRUE;
1286 si->time_limit = TRUE;
1287 si->fullscreen = FALSE;
1288 si->ask_on_escape = TRUE;
1290 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1291 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1293 for (i=0; i<MAX_PLAYERS; i++)
1295 si->input[i].use_joystick = FALSE;
1296 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1297 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1298 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1299 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1300 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1301 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1302 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1303 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1304 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1305 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1306 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1307 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1308 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1309 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1310 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1314 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1318 if (!setup_file_list)
1323 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1324 setSetupInfo(global_setup_tokens, i,
1325 getTokenValue(setup_file_list, global_setup_tokens[i].text));
1328 /* shortcut setup */
1329 ssi = setup.shortcut;
1330 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1331 setSetupInfo(shortcut_setup_tokens, i,
1332 getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
1333 setup.shortcut = ssi;
1336 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1340 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1342 sii = setup.input[pnr];
1343 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1345 char full_token[100];
1347 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1348 setSetupInfo(player_setup_tokens, i,
1349 getTokenValue(setup_file_list, full_token));
1351 setup.input[pnr] = sii;
1357 char *filename = getSetupFilename();
1358 struct SetupFileList *setup_file_list = NULL;
1360 /* always start with reliable default values */
1361 setSetupInfoToDefaults(&setup);
1363 setup_file_list = loadSetupFileList(filename);
1365 if (setup_file_list)
1367 checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
1368 decodeSetupFileList(setup_file_list);
1370 setup.direct_draw = !setup.double_buffering;
1372 freeSetupFileList(setup_file_list);
1374 /* needed to work around problems with fixed length strings */
1375 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1376 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1377 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1379 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1381 strcpy(new_name, setup.player_name);
1382 free(setup.player_name);
1383 setup.player_name = new_name;
1387 Error(ERR_WARN, "using default setup values");
1392 char *filename = getSetupFilename();
1396 InitUserDataDirectory();
1398 if (!(file = fopen(filename, MODE_WRITE)))
1400 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1404 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1405 getCookie("SETUP")));
1406 fprintf(file, "\n");
1410 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1412 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1414 /* just to make things nicer :) */
1415 if (i == SETUP_TOKEN_PLAYER_NAME)
1416 fprintf(file, "\n");
1419 /* shortcut setup */
1420 ssi = setup.shortcut;
1421 fprintf(file, "\n");
1422 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1423 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1426 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1430 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1431 fprintf(file, "\n");
1433 sii = setup.input[pnr];
1434 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1435 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1440 SetFilePermissions(filename, PERMS_PRIVATE);