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 if (tape->pos[i].delay > 1)
804 tape->pos[i + 1] = tape->pos[i];
805 tape->pos[i + 1].delay = 1;
808 for(j=0; j<MAX_PLAYERS; j++)
809 tape->pos[i].action[j] = MV_NO_MOVING;
810 tape->pos[i].delay--;
821 if (i != tape->length)
822 chunk_size = (tape->num_participating_players + 1) * i;
827 void LoadTape(int level_nr)
829 char *filename = getTapeFilename(level_nr);
830 char cookie[MAX_LINE_LEN];
831 char chunk_name[CHUNK_ID_LEN + 1];
835 /* always start with reliable default values */
836 setTapeInfoToDefaults();
838 if (!(file = fopen(filename, MODE_READ)))
841 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
842 if (strcmp(chunk_name, "RND1") == 0)
844 getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN); /* not used */
846 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
847 if (strcmp(chunk_name, "TAPE") != 0)
849 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
854 else /* check for pre-2.0 file format with cookie string */
856 strcpy(cookie, chunk_name);
857 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
858 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
859 cookie[strlen(cookie) - 1] = '\0';
861 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
863 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
868 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
870 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
875 /* pre-2.0 tape files have no game version, so use file version here */
876 tape.game_version = tape.file_version;
879 if (tape.file_version < FILE_VERSION_1_2)
881 /* tape files from versions before 1.2.0 without chunk structure */
882 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
883 LoadTape_BODY(file, 2 * tape.length, &tape);
891 int (*loader)(FILE *, int, struct TapeInfo *);
895 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
896 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
897 { "BODY", -1, LoadTape_BODY },
901 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
905 while (chunk_info[i].name != NULL &&
906 strcmp(chunk_name, chunk_info[i].name) != 0)
909 if (chunk_info[i].name == NULL)
911 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
912 chunk_name, filename);
913 ReadUnusedBytesFromFile(file, chunk_size);
915 else if (chunk_info[i].size != -1 &&
916 chunk_info[i].size != chunk_size)
918 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
919 chunk_size, chunk_name, filename);
920 ReadUnusedBytesFromFile(file, chunk_size);
924 /* call function to load this tape chunk */
925 int chunk_size_expected =
926 (chunk_info[i].loader)(file, chunk_size, &tape);
928 /* the size of some chunks cannot be checked before reading other
929 chunks first (like "HEAD" and "BODY") that contain some header
930 information, so check them here */
931 if (chunk_size_expected != chunk_size)
933 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
934 chunk_size, chunk_name, filename);
942 tape.length_seconds = GetTapeLength();
945 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
948 byte store_participating_players = 0;
950 /* set bits for participating players for compact storage */
951 for(i=0; i<MAX_PLAYERS; i++)
952 if (tape->player_participates[i])
953 store_participating_players |= (1 << i);
955 putFile32BitInteger(file, tape->random_seed, BYTE_ORDER_BIG_ENDIAN);
956 putFile32BitInteger(file, tape->date, BYTE_ORDER_BIG_ENDIAN);
957 putFile32BitInteger(file, tape->length, BYTE_ORDER_BIG_ENDIAN);
959 fputc(store_participating_players, file);
961 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
964 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
968 for(i=0; i<tape->length; i++)
970 for(j=0; j<MAX_PLAYERS; j++)
971 if (tape->player_participates[j])
972 fputc(tape->pos[i].action[j], file);
974 fputc(tape->pos[i].delay, file);
978 void SaveTape(int level_nr)
981 char *filename = getTapeFilename(level_nr);
983 boolean new_tape = TRUE;
984 int num_participating_players = 0;
987 InitTapeDirectory(leveldir_current->filename);
989 /* if a tape still exists, ask to overwrite it */
990 if (access(filename, F_OK) == 0)
993 if (!Request("Replace old tape ?", REQ_ASK))
997 if (!(file = fopen(filename, MODE_WRITE)))
999 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1003 /* count number of participating players */
1004 for(i=0; i<MAX_PLAYERS; i++)
1005 if (tape.player_participates[i])
1006 num_participating_players++;
1008 body_chunk_size = (num_participating_players + 1) * tape.length;
1010 putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
1011 putFileChunk(file, "TAPE", CHUNK_SIZE_NONE, BYTE_ORDER_BIG_ENDIAN);
1013 putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
1014 WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
1016 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1017 SaveTape_HEAD(file, &tape);
1019 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1020 SaveTape_BODY(file, &tape);
1024 SetFilePermissions(filename, PERMS_PRIVATE);
1026 tape.changed = FALSE;
1029 Request("tape saved !", REQ_CONFIRM);
1032 void DumpTape(struct TapeInfo *tape)
1036 if (TAPE_IS_EMPTY(*tape))
1038 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1043 printf("-------------------------------------------------------------------------------\n");
1044 printf("Tape of Level %d (file version %06d, game version %06d\n",
1045 tape->level_nr, tape->file_version, tape->game_version);
1046 printf("-------------------------------------------------------------------------------\n");
1048 for(i=0; i<tape->length; i++)
1050 if (i >= MAX_TAPELEN)
1053 for(j=0; j<MAX_PLAYERS; j++)
1055 if (tape->player_participates[j])
1057 int action = tape->pos[i].action[j];
1059 printf("%d:%02x ", j, action);
1060 printf("[%c%c%c%c|%c%c] - ",
1061 (action & JOY_LEFT ? '<' : ' '),
1062 (action & JOY_RIGHT ? '>' : ' '),
1063 (action & JOY_UP ? '^' : ' '),
1064 (action & JOY_DOWN ? 'v' : ' '),
1065 (action & JOY_BUTTON_1 ? '1' : ' '),
1066 (action & JOY_BUTTON_2 ? '2' : ' '));
1070 printf("(%03d)\n", tape->pos[i].delay);
1073 printf("-------------------------------------------------------------------------------\n");
1077 /* ========================================================================= */
1078 /* score file functions */
1079 /* ========================================================================= */
1081 void LoadScore(int level_nr)
1084 char *filename = getScoreFilename(level_nr);
1085 char cookie[MAX_LINE_LEN];
1086 char line[MAX_LINE_LEN];
1090 /* always start with reliable default values */
1091 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1093 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1094 highscore[i].Score = 0;
1097 if (!(file = fopen(filename, MODE_READ)))
1100 /* check file identifier */
1101 fgets(cookie, MAX_LINE_LEN, file);
1102 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1103 cookie[strlen(cookie) - 1] = '\0';
1105 if (!checkCookieString(cookie, SCORE_COOKIE))
1107 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1112 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1114 fscanf(file, "%d", &highscore[i].Score);
1115 fgets(line, MAX_LINE_LEN, file);
1117 if (line[strlen(line) - 1] == '\n')
1118 line[strlen(line) - 1] = '\0';
1120 for (line_ptr = line; *line_ptr; line_ptr++)
1122 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1124 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1125 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1134 void SaveScore(int level_nr)
1137 char *filename = getScoreFilename(level_nr);
1140 InitScoreDirectory(leveldir_current->filename);
1142 if (!(file = fopen(filename, MODE_WRITE)))
1144 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1148 fprintf(file, "%s\n\n", SCORE_COOKIE);
1150 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1151 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1155 SetFilePermissions(filename, PERMS_PUBLIC);
1159 /* ========================================================================= */
1160 /* setup file functions */
1161 /* ========================================================================= */
1163 #define TOKEN_STR_PLAYER_PREFIX "player_"
1166 #define SETUP_TOKEN_PLAYER_NAME 0
1167 #define SETUP_TOKEN_SOUND 1
1168 #define SETUP_TOKEN_SOUND_LOOPS 2
1169 #define SETUP_TOKEN_SOUND_MUSIC 3
1170 #define SETUP_TOKEN_SOUND_SIMPLE 4
1171 #define SETUP_TOKEN_SCROLL_DELAY 5
1172 #define SETUP_TOKEN_SOFT_SCROLLING 6
1173 #define SETUP_TOKEN_FADING 7
1174 #define SETUP_TOKEN_AUTORECORD 8
1175 #define SETUP_TOKEN_QUICK_DOORS 9
1176 #define SETUP_TOKEN_TEAM_MODE 10
1177 #define SETUP_TOKEN_HANDICAP 11
1178 #define SETUP_TOKEN_TIME_LIMIT 12
1179 #define SETUP_TOKEN_FULLSCREEN 13
1181 #define NUM_GLOBAL_SETUP_TOKENS 14
1184 #define SETUP_TOKEN_USE_JOYSTICK 0
1185 #define SETUP_TOKEN_JOY_DEVICE_NAME 1
1186 #define SETUP_TOKEN_JOY_XLEFT 2
1187 #define SETUP_TOKEN_JOY_XMIDDLE 3
1188 #define SETUP_TOKEN_JOY_XRIGHT 4
1189 #define SETUP_TOKEN_JOY_YUPPER 5
1190 #define SETUP_TOKEN_JOY_YMIDDLE 6
1191 #define SETUP_TOKEN_JOY_YLOWER 7
1192 #define SETUP_TOKEN_JOY_SNAP 8
1193 #define SETUP_TOKEN_JOY_BOMB 9
1194 #define SETUP_TOKEN_KEY_LEFT 10
1195 #define SETUP_TOKEN_KEY_RIGHT 11
1196 #define SETUP_TOKEN_KEY_UP 12
1197 #define SETUP_TOKEN_KEY_DOWN 13
1198 #define SETUP_TOKEN_KEY_SNAP 14
1199 #define SETUP_TOKEN_KEY_BOMB 15
1201 #define NUM_PLAYER_SETUP_TOKENS 16
1203 static struct SetupInfo si;
1204 static struct SetupInputInfo sii;
1206 static struct TokenInfo global_setup_tokens[] =
1209 { TYPE_STRING, &si.player_name, "player_name" },
1210 { TYPE_SWITCH, &si.sound, "sound" },
1211 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1212 { TYPE_SWITCH, &si.sound_music, "background_music" },
1213 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1214 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1215 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1216 { TYPE_SWITCH, &si.fading, "screen_fading" },
1217 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1218 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1219 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1220 { TYPE_SWITCH, &si.handicap, "handicap" },
1221 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1222 { TYPE_SWITCH, &si.fullscreen, "fullscreen" }
1225 static struct TokenInfo player_setup_tokens[] =
1228 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1229 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1230 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1231 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1232 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1233 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1234 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1235 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1236 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1237 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1238 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1239 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1240 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1241 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1242 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1243 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" }
1246 static void setSetupInfoToDefaults(struct SetupInfo *si)
1250 si->player_name = getStringCopy(getLoginName());
1253 si->sound_loops = TRUE;
1254 si->sound_music = TRUE;
1255 si->sound_simple = TRUE;
1257 si->double_buffering = TRUE;
1258 si->direct_draw = !si->double_buffering;
1259 si->scroll_delay = TRUE;
1260 si->soft_scrolling = TRUE;
1262 si->autorecord = TRUE;
1263 si->quick_doors = FALSE;
1264 si->team_mode = FALSE;
1265 si->handicap = TRUE;
1266 si->time_limit = TRUE;
1267 si->fullscreen = FALSE;
1269 for (i=0; i<MAX_PLAYERS; i++)
1271 si->input[i].use_joystick = FALSE;
1272 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1273 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1274 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1275 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1276 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1277 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1278 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1279 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1280 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1281 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1282 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1283 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1284 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1285 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1286 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1290 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1294 if (!setup_file_list)
1297 /* handle global setup values */
1299 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1300 setSetupInfo(global_setup_tokens, i,
1301 getTokenValue(setup_file_list, global_setup_tokens[i].text));
1304 /* handle player specific setup values */
1305 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1309 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1311 sii = setup.input[pnr];
1312 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1314 char full_token[100];
1316 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1317 setSetupInfo(player_setup_tokens, i,
1318 getTokenValue(setup_file_list, full_token));
1320 setup.input[pnr] = sii;
1326 char *filename = getSetupFilename();
1327 struct SetupFileList *setup_file_list = NULL;
1329 /* always start with reliable default values */
1330 setSetupInfoToDefaults(&setup);
1332 setup_file_list = loadSetupFileList(filename);
1334 if (setup_file_list)
1336 checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
1337 decodeSetupFileList(setup_file_list);
1339 setup.direct_draw = !setup.double_buffering;
1341 freeSetupFileList(setup_file_list);
1343 /* needed to work around problems with fixed length strings */
1344 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1345 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1346 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1348 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1350 strcpy(new_name, setup.player_name);
1351 free(setup.player_name);
1352 setup.player_name = new_name;
1356 Error(ERR_WARN, "using default setup values");
1361 char *filename = getSetupFilename();
1365 InitUserDataDirectory();
1367 if (!(file = fopen(filename, MODE_WRITE)))
1369 Error(ERR_WARN, "cannot write setup file '%s'", filename);
1373 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1374 getCookie("SETUP")));
1375 fprintf(file, "\n");
1377 /* handle global setup values */
1379 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1381 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1383 /* just to make things nicer :) */
1384 if (i == SETUP_TOKEN_PLAYER_NAME)
1385 fprintf(file, "\n");
1388 /* handle player specific setup values */
1389 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1393 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1394 fprintf(file, "\n");
1396 sii = setup.input[pnr];
1397 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1398 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1403 SetFilePermissions(filename, PERMS_PRIVATE);