+
+ level.tempo_amoebe = fgetc(file);
+ level.dauer_sieb = fgetc(file);
+ level.dauer_ablenk = fgetc(file);
+ level.amoebe_inhalt = fgetc(file);
+
+ for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
+ fgetc(file);
+
+ /* read chunk "BODY" */
+ if (file_version >= FILE_VERSION_1_2)
+ {
+ fgets(chunk, CHUNK_ID_LEN + 1, file);
+ chunk_length =
+ (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
+
+ /* look for optional content chunk */
+ if (strcmp(chunk, "CONT") == 0 && chunk_length == 4 + 8 * 3 * 3)
+ {
+ fgetc(file);
+ MampferMax = fgetc(file);
+ fgetc(file);
+ fgetc(file);
+
+ for(i=0; i<8; i++)
+ for(y=0; y<3; y++)
+ for(x=0; x<3; x++)
+ level.mampfer_inhalt[i][x][y] = fgetc(file);
+
+ fgets(chunk, CHUNK_ID_LEN + 1, file);
+ chunk_length =
+ (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
+ }
+
+ /* next check body chunk identifier and chunk length */
+ if (strcmp(chunk, "BODY") || chunk_length != lev_fieldx * lev_fieldy)
+ {
+ Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
+ fclose(file);
+ return;
+ }
+ }
+
+ for(y=0; y<lev_fieldy; y++)
+ for(x=0; x<lev_fieldx; x++)
+ Feld[x][y] = Ur[x][y] = fgetc(file);
+
+ fclose(file);
+
+#if 0
+ if (level.time <= 10) /* minimum playing time of each level */
+ level.time = 10;
+#endif
+
+ /* player was faster than monsters in pre-1.0 levels */
+ if (file_version == FILE_VERSION_1_0 &&
+ IS_LEVELCLASS_CONTRIBUTION(leveldir_nr))
+ {
+ Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
+ Error(ERR_WARN, "using high speed movement for player");
+ level.high_speed = TRUE;
+ }
+}
+
+void SaveLevel(int level_nr)
+{
+ int i, x, y;
+ char *filename = getLevelFilename(level_nr);
+ FILE *file;
+ int chunk_length;
+
+ if (!(file = fopen(filename, "w")))
+ {
+ Error(ERR_WARN, "cannot save level file '%s'", filename);
+ return;
+ }
+
+ fputs(LEVEL_COOKIE, file); /* file identifier */
+ fputc('\n', file);
+
+ fputs("HEAD", file); /* chunk identifier for file header */
+
+ chunk_length = LEVEL_HEADER_SIZE;
+
+ fputc((chunk_length >> 24) & 0xff, file);
+ fputc((chunk_length >> 16) & 0xff, file);
+ fputc((chunk_length >> 8) & 0xff, file);
+ fputc((chunk_length >> 0) & 0xff, file);
+
+ fputc(level.fieldx, file);
+ fputc(level.fieldy, file);
+ fputc(level.time / 256, file);
+ fputc(level.time % 256, file);
+ fputc(level.edelsteine / 256, file);
+ fputc(level.edelsteine % 256, file);
+
+ for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
+ fputc(level.name[i], file);
+ for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
+ fputc(level.score[i], file);
+ for(i=0; i<4; i++)
+ for(y=0; y<3; y++)
+ for(x=0; x<3; x++)
+ fputc(level.mampfer_inhalt[i][x][y], file);
+ fputc(level.tempo_amoebe, file);
+ fputc(level.dauer_sieb, file);
+ fputc(level.dauer_ablenk, file);
+ fputc(level.amoebe_inhalt, file);
+
+ for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
+ fputc(0, file);
+
+ fputs("CONT", file); /* chunk identifier for contents */
+
+ chunk_length = 4 + 8 * 3 * 3;
+
+ fputc((chunk_length >> 24) & 0xff, file);
+ fputc((chunk_length >> 16) & 0xff, file);
+ fputc((chunk_length >> 8) & 0xff, file);
+ fputc((chunk_length >> 0) & 0xff, file);
+
+ fputc(EL_MAMPFER, file);
+ fputc(MampferMax, file);
+ fputc(0, file);
+ fputc(0, file);
+
+ for(i=0; i<8; i++)
+ for(y=0; y<3; y++)
+ for(x=0; x<3; x++)
+ fputc(level.mampfer_inhalt[i][x][y], file);
+
+ fputs("BODY", file); /* chunk identifier for file body */
+ chunk_length = lev_fieldx * lev_fieldy;
+
+ fputc((chunk_length >> 24) & 0xff, file);
+ fputc((chunk_length >> 16) & 0xff, file);
+ fputc((chunk_length >> 8) & 0xff, file);
+ fputc((chunk_length >> 0) & 0xff, file);
+
+ for(y=0; y<lev_fieldy; y++)
+ for(x=0; x<lev_fieldx; x++)
+ fputc(Ur[x][y], file);
+
+ fclose(file);
+
+ chmod(filename, LEVEL_PERMS);
+}
+
+void LoadTape(int level_nr)
+{
+ int i, j;
+ char *filename = getTapeFilename(level_nr);
+ char cookie[MAX_LINE_LEN];
+ char chunk[CHUNK_ID_LEN + 1];
+ FILE *file;
+ int num_participating_players;
+ int file_version = FILE_VERSION_1_2; /* last version of tape files */
+ int chunk_length;
+
+ /* always start with reliable default values (empty tape) */
+ TapeErase();
+
+ /* default values (also for pre-1.2 tapes) with only the first player */
+ tape.player_participates[0] = TRUE;
+ for(i=1; i<MAX_PLAYERS; i++)
+ tape.player_participates[i] = FALSE;
+
+ /* at least one (default: the first) player participates in every tape */
+ num_participating_players = 1;
+
+ if (!(file = fopen(filename, "r")))
+ return;
+
+ /* check file identifier */
+ fgets(cookie, MAX_LINE_LEN, file);
+ if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
+ cookie[strlen(cookie) - 1] = '\0';
+
+ if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
+ file_version = FILE_VERSION_1_0;
+ else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
+ {
+ Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
+ fclose(file);
+ return;
+ }
+
+ /* read chunk "HEAD" */
+ if (file_version >= FILE_VERSION_1_2)
+ {
+ /* first check header chunk identifier and chunk length */
+ fgets(chunk, CHUNK_ID_LEN + 1, file);
+ chunk_length =
+ (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
+
+ if (strcmp(chunk, "HEAD") || chunk_length != TAPE_HEADER_SIZE)
+ {
+ Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
+ fclose(file);
+ return;
+ }
+ }
+
+ tape.random_seed =
+ (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
+ tape.date =
+ (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
+ tape.length =
+ (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
+
+ /* read header fields that are new since version 1.2 */
+ if (file_version >= FILE_VERSION_1_2)
+ {
+ byte store_participating_players = fgetc(file);
+
+ for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
+ fgetc(file);
+
+ /* since version 1.2, tapes store which players participate in the tape */
+ num_participating_players = 0;
+ for(i=0; i<MAX_PLAYERS; i++)
+ {
+ tape.player_participates[i] = FALSE;
+
+ if (store_participating_players & (1 << i))
+ {
+ tape.player_participates[i] = TRUE;
+ num_participating_players++;
+ }
+ }
+ }
+
+ tape.level_nr = level_nr;
+ tape.counter = 0;
+ tape.changed = FALSE;
+
+ tape.recording = FALSE;
+ tape.playing = FALSE;
+ tape.pausing = FALSE;
+
+ /* read chunk "BODY" */
+ if (file_version >= FILE_VERSION_1_2)
+ {
+ /* next check body chunk identifier and chunk length */
+ fgets(chunk, CHUNK_ID_LEN + 1, file);
+ chunk_length =
+ (fgetc(file)<<24) | (fgetc(file)<<16) | (fgetc(file)<<8) | fgetc(file);
+ if (strcmp(chunk, "BODY") ||
+ chunk_length != (num_participating_players + 1) * tape.length)
+ {
+ Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
+ fclose(file);
+ return;
+ }
+ }
+
+ for(i=0; i<tape.length; i++)
+ {
+ if (i >= MAX_TAPELEN)
+ break;
+
+ for(j=0; j<MAX_PLAYERS; j++)
+ {
+ tape.pos[i].action[j] = MV_NO_MOVING;
+
+ if (tape.player_participates[j])
+ tape.pos[i].action[j] = fgetc(file);
+ }
+
+ tape.pos[i].delay = fgetc(file);
+
+ if (file_version == FILE_VERSION_1_0)
+ {
+ /* eliminate possible diagonal moves in old tapes */
+ /* this is only for backward compatibility */
+
+ byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
+ byte action = tape.pos[i].action[0];
+ int k, num_moves = 0;
+
+ for (k=0; k<4; k++)
+ {
+ if (action & joy_dir[k])
+ {
+ tape.pos[i + num_moves].action[0] = joy_dir[k];
+ if (num_moves > 0)
+ tape.pos[i + num_moves].delay = 0;
+ num_moves++;
+ }
+ }
+
+ if (num_moves > 1)
+ {
+ num_moves--;
+ i += num_moves;
+ tape.length += num_moves;
+ }
+ }
+
+ if (feof(file))
+ break;
+ }
+
+ fclose(file);
+
+ if (i != tape.length)
+ Error(ERR_WARN, "level recording file '%s' corrupted", filename);
+
+ tape.length_seconds = GetTapeLength();
+}
+
+void SaveTape(int level_nr)
+{
+ int i;
+ char *filename = getTapeFilename(level_nr);
+ FILE *file;
+ boolean new_tape = TRUE;
+ byte store_participating_players;
+ int num_participating_players;
+ int chunk_length;
+
+ InitTapeDirectory(leveldir[leveldir_nr].filename);
+
+ /* if a tape still exists, ask to overwrite it */
+ if (access(filename, F_OK) == 0)
+ {
+ new_tape = FALSE;
+ if (!Request("Replace old tape ?", REQ_ASK))
+ return;
+ }
+
+ /* count number of players and set corresponding bits for compact storage */
+ store_participating_players = 0;
+ num_participating_players = 0;
+ for(i=0; i<MAX_PLAYERS; i++)
+ {
+ if (tape.player_participates[i])
+ {
+ num_participating_players++;
+ store_participating_players |= (1 << i);
+ }
+ }
+
+ if (!(file = fopen(filename, "w")))
+ {
+ Error(ERR_WARN, "cannot save level recording file '%s'", filename);
+ return;
+ }
+
+ fputs(TAPE_COOKIE, file); /* file identifier */
+ fputc('\n', file);
+
+ fputs("HEAD", file); /* chunk identifier for file header */
+
+ chunk_length = TAPE_HEADER_SIZE;
+
+ fputc((chunk_length >> 24) & 0xff, file);
+ fputc((chunk_length >> 16) & 0xff, file);
+ fputc((chunk_length >> 8) & 0xff, file);
+ fputc((chunk_length >> 0) & 0xff, file);
+
+ fputc((tape.random_seed >> 24) & 0xff, file);
+ fputc((tape.random_seed >> 16) & 0xff, file);
+ fputc((tape.random_seed >> 8) & 0xff, file);
+ fputc((tape.random_seed >> 0) & 0xff, file);
+
+ fputc((tape.date >> 24) & 0xff, file);
+ fputc((tape.date >> 16) & 0xff, file);
+ fputc((tape.date >> 8) & 0xff, file);
+ fputc((tape.date >> 0) & 0xff, file);
+
+ fputc((tape.length >> 24) & 0xff, file);
+ fputc((tape.length >> 16) & 0xff, file);
+ fputc((tape.length >> 8) & 0xff, file);
+ fputc((tape.length >> 0) & 0xff, file);
+
+ fputc(store_participating_players, file);
+
+ for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
+ fputc(0, file);
+
+ fputs("BODY", file); /* chunk identifier for file body */
+ chunk_length = (num_participating_players + 1) * tape.length;
+
+ fputc((chunk_length >> 24) & 0xff, file);
+ fputc((chunk_length >> 16) & 0xff, file);
+ fputc((chunk_length >> 8) & 0xff, file);
+ fputc((chunk_length >> 0) & 0xff, file);
+
+ for(i=0; i<tape.length; i++)
+ {
+ int j;
+
+ for(j=0; j<MAX_PLAYERS; j++)
+ if (tape.player_participates[j])
+ fputc(tape.pos[i].action[j], file);
+
+ fputc(tape.pos[i].delay, file);
+ }
+
+ fclose(file);
+
+ chmod(filename, TAPE_PERMS);
+
+ tape.changed = FALSE;
+
+ if (new_tape)
+ Request("tape saved !", REQ_CONFIRM);
+}
+
+void LoadScore(int level_nr)
+{
+ int i;
+ char *filename = getScoreFilename(level_nr);
+ char cookie[MAX_LINE_LEN];
+ char line[MAX_LINE_LEN];
+ char *line_ptr;
+ FILE *file;
+
+ /* always start with reliable default values */
+ for(i=0; i<MAX_SCORE_ENTRIES; i++)
+ {
+ strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
+ highscore[i].Score = 0;
+ }
+
+ if (!(file = fopen(filename, "r")))
+ return;
+
+ /* check file identifier */
+ fgets(cookie, MAX_LINE_LEN, file);
+ if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
+ cookie[strlen(cookie) - 1] = '\0';
+
+ if (strcmp(cookie, SCORE_COOKIE) != 0)
+ {
+ Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
+ fclose(file);
+ return;
+ }
+
+ for(i=0; i<MAX_SCORE_ENTRIES; i++)
+ {
+ fscanf(file, "%d", &highscore[i].Score);
+ fgets(line, MAX_LINE_LEN, file);
+
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = '\0';
+
+ for (line_ptr = line; *line_ptr; line_ptr++)
+ {
+ if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
+ {
+ strncpy(highscore[i].Name, line_ptr, MAX_NAMELEN - 1);
+ highscore[i].Name[MAX_NAMELEN - 1] = '\0';
+ break;
+ }
+ }
+ }
+
+ fclose(file);
+}
+
+void SaveScore(int level_nr)
+{
+ int i;
+ char *filename = getScoreFilename(level_nr);
+ FILE *file;
+
+ InitScoreDirectory(leveldir[leveldir_nr].filename);
+
+ if (!(file = fopen(filename, "w")))
+ {
+ Error(ERR_WARN, "cannot save score for level %d", level_nr);
+ return;
+ }
+
+ fprintf(file, "%s\n\n", SCORE_COOKIE);
+
+ for(i=0; i<MAX_SCORE_ENTRIES; i++)
+ fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
+