+ if (!(file = fopen(filename, MODE_WRITE)))
+ {
+ Error(ERR_WARN, "cannot save level file '%s'", filename);
+ return;
+ }
+
+ level->file_version = FILE_VERSION_ACTUAL;
+ level->game_version = GAME_VERSION_ACTUAL;
+
+ /* check level field for 16-bit elements */
+ level->encoding_16bit_field = FALSE;
+ for(y=0; y<level->fieldy; y++)
+ for(x=0; x<level->fieldx; x++)
+ if (level->field[x][y] > 255)
+ level->encoding_16bit_field = TRUE;
+
+ /* check yamyam content for 16-bit elements */
+ level->encoding_16bit_yamyam = FALSE;
+ for(i=0; i<level->num_yamyam_contents; i++)
+ for(y=0; y<3; y++)
+ for(x=0; x<3; x++)
+ if (level->yamyam_content[i][x][y] > 255)
+ level->encoding_16bit_yamyam = TRUE;
+
+ /* check amoeba content for 16-bit elements */
+ level->encoding_16bit_amoeba = FALSE;
+ if (level->amoeba_content > 255)
+ level->encoding_16bit_amoeba = TRUE;
+
+ /* calculate size of "BODY" chunk */
+ body_chunk_size =
+ level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
+
+ putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
+ putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
+
+ putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
+ SaveLevel_VERS(file, level);
+
+ putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
+ SaveLevel_HEAD(file, level);
+
+ putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
+ SaveLevel_AUTH(file, level);
+
+ putFileChunkBE(file, "BODY", body_chunk_size);
+ SaveLevel_BODY(file, level);
+
+ if (level->encoding_16bit_yamyam ||
+ level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
+ {
+ putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
+ SaveLevel_CNT2(file, level, EL_YAMYAM);
+ }
+
+ if (level->encoding_16bit_amoeba)
+ {
+ putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
+ SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
+ }
+
+ /* check for envelope content */
+ for (i=0; i<4; i++)
+ {
+ if (strlen(level->envelope_text[i]) > 0)
+ {
+ int envelope_len = strlen(level->envelope_text[i]) + 1;
+
+ putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
+ SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
+ }
+ }
+
+ /* check for non-default custom elements (unless using template level) */
+ if (!level->use_custom_template)
+ {
+ for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+
+ if (element_info[element].modified_settings)
+ {
+ int num_change_pages = element_info[element].num_change_pages;
+
+ putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
+ SaveLevel_CUS4(file, level, element);
+ }
+ }
+ }
+
+ fclose(file);
+
+ SetFilePermissions(filename, PERMS_PRIVATE);
+}
+
+void SaveLevel(int level_nr)
+{
+ char *filename = getLevelFilename(level_nr);
+
+ SaveLevelFromFilename(&level, filename);
+}
+
+void SaveLevelTemplate()
+{
+ char *filename = getLevelFilename(-1);
+
+ SaveLevelFromFilename(&level, filename);
+}
+
+void DumpLevel(struct LevelInfo *level)
+{
+ printf_line("-", 79);
+ printf("Level xxx (file version %08d, game version %08d)\n",
+ level->file_version, level->game_version);
+ printf_line("-", 79);
+
+ printf("Level Author: '%s'\n", level->author);
+ printf("Level Title: '%s'\n", level->name);
+ printf("\n");
+ printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
+ printf("\n");
+ printf("Level Time: %d seconds\n", level->time);
+ printf("Gems needed: %d\n", level->gems_needed);
+ printf("\n");
+ printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
+ printf("Time for Wheel: %d seconds\n", level->time_wheel);
+ printf("Time for Light: %d seconds\n", level->time_light);
+ printf("Time for Timegate: %d seconds\n", level->time_timegate);
+ printf("\n");
+ printf("Amoeba Speed: %d\n", level->amoeba_speed);
+ printf("\n");
+ printf("Gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
+ printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
+ printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
+
+ printf_line("-", 79);
+}
+
+
+/* ========================================================================= */
+/* tape file functions */
+/* ========================================================================= */
+
+static void setTapeInfoToDefaults()
+{
+ int i;
+
+ /* 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 */
+ tape.num_participating_players = 1;
+
+ tape.level_nr = level_nr;
+ tape.counter = 0;
+ tape.changed = FALSE;
+
+ tape.recording = FALSE;
+ tape.playing = FALSE;
+ tape.pausing = FALSE;
+}
+
+static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
+{
+ tape->file_version = getFileVersion(file);
+ tape->game_version = getFileVersion(file);
+
+ return chunk_size;
+}
+
+static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
+{
+ int i;
+
+ tape->random_seed = getFile32BitBE(file);
+ tape->date = getFile32BitBE(file);
+ tape->length = getFile32BitBE(file);
+
+ /* read header fields that are new since version 1.2 */
+ if (tape->file_version >= FILE_VERSION_1_2)