- for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
- {
- int element = EL_CUSTOM_START + i;
- struct ElementInfo *ei = &element_info[element];
-
- for (j = 0; j < ei->num_change_pages; j++)
- {
- struct ElementChangeInfo *change = &ei->change_page[j];
-
- if (change->trigger_player == CH_PLAYER_NONE)
- change->trigger_player = CH_PLAYER_ANY;
-
- if (change->trigger_side == CH_SIDE_NONE)
- change->trigger_side = CH_SIDE_ANY;
- }
- }
- }
-
- /* initialize "can_explode" field for old levels which did not store this */
- /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
- if (level->game_version <= VERSION_IDENT(3,1,0,0))
- {
- for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
- {
- int element = EL_CUSTOM_START + i;
-
- if (EXPLODES_1X1_OLD(element))
- element_info[element].explosion_type = EXPLODES_1X1;
-
- SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
- EXPLODES_SMASHED(element) ||
- EXPLODES_IMPACT(element)));
- }
- }
-
- /* correct previously hard-coded move delay values for maze runner style */
- if (level->game_version < VERSION_IDENT(3,1,1,0))
- {
- for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
- {
- int element = EL_CUSTOM_START + i;
-
- if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
- {
- /* previously hard-coded and therefore ignored */
- element_info[element].move_delay_fixed = 9;
- element_info[element].move_delay_random = 0;
- }
- }
- }
-
- /* map elements that have changed in newer versions */
- level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
- level->game_version);
- for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
- for (x = 0; x < 3; x++)
- for (y = 0; y < 3; y++)
- level->yamyam_content[i].e[x][y] =
- getMappedElementByVersion(level->yamyam_content[i].e[x][y],
- level->game_version);
-
- /* initialize element properties for level editor etc. */
- InitElementPropertiesEngine(level->game_version);
- InitElementPropertiesAfterLoading(level->game_version);
- InitElementPropertiesGfxElement();
-}
-
-static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
-{
- int x, y;
-
- /* map elements that have changed in newer versions */
- for (y = 0; y < level->fieldy; y++)
- for (x = 0; x < level->fieldx; x++)
- level->field[x][y] = getMappedElementByVersion(level->field[x][y],
- level->game_version);
-
- /* copy elements to runtime playfield array */
- for (x = 0; x < MAX_LEV_FIELDX; x++)
- for (y = 0; y < MAX_LEV_FIELDY; y++)
- Feld[x][y] = level->field[x][y];
-
- /* initialize level size variables for faster access */
- lev_fieldx = level->fieldx;
- lev_fieldy = level->fieldy;
-
- /* determine border element for this level */
- if (level->file_info.type == LEVEL_FILE_TYPE_DC)
- BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
- else
- SetBorderElement();
-}
-
-static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
-{
- struct LevelFileInfo *level_file_info = &level->file_info;
-
- if (level_file_info->type == LEVEL_FILE_TYPE_RND)
- CopyNativeLevel_RND_to_Native(level);
-}
-
-void LoadLevelTemplate(int nr)
-{
- char *filename;
-
- setLevelFileInfo(&level_template.file_info, nr);
- filename = level_template.file_info.filename;
-
- LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
-
- LoadLevel_InitVersion(&level_template, filename);
- LoadLevel_InitElements(&level_template, filename);
-
- ActivateLevelTemplate();
-}
-
-void LoadLevel(int nr)
-{
- char *filename;
-
- setLevelFileInfo(&level.file_info, nr);
- filename = level.file_info.filename;
-
- LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
-
- if (level.use_custom_template)
- LoadLevelTemplate(-1);
-
- LoadLevel_InitVersion(&level, filename);
- LoadLevel_InitElements(&level, filename);
- LoadLevel_InitPlayfield(&level, filename);
-
- LoadLevel_InitNativeEngines(&level, filename);
-}
-
-void LoadLevelInfoOnly(int nr)
-{
-#if 0
- char *filename;
-#endif
-
- setLevelFileInfo(&level.file_info, nr);
-#if 0
- filename = level.file_info.filename;
-#endif
-
- LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
-}
-
-static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
-{
- int chunk_size = 0;
-
- chunk_size += putFileVersion(file, level->file_version);
- chunk_size += putFileVersion(file, level->game_version);
-
- return chunk_size;
-}
-
-static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
-{
- int chunk_size = 0;
-
- chunk_size += putFile16BitBE(file, level->creation_date.year);
- chunk_size += putFile8Bit(file, level->creation_date.month);
- chunk_size += putFile8Bit(file, level->creation_date.day);
-
- return chunk_size;
-}
-
-#if 0
-static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
-{
- int i, x, y;
-
- putFile8Bit(file, level->fieldx);
- putFile8Bit(file, level->fieldy);
-
- putFile16BitBE(file, level->time);
- putFile16BitBE(file, level->gems_needed);
-
- for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
- putFile8Bit(file, level->name[i]);
-
- for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
- putFile8Bit(file, level->score[i]);
-
- for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
- for (y = 0; y < 3; y++)
- for (x = 0; x < 3; x++)
- putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
- level->yamyam_content[i].e[x][y]));
- putFile8Bit(file, level->amoeba_speed);
- putFile8Bit(file, level->time_magic_wall);
- putFile8Bit(file, level->time_wheel);
- putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
- level->amoeba_content));
- putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
- putFile8Bit(file, (level->initial_gravity ? 1 : 0));
- putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
- putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
-
- putFile8Bit(file, (level->use_custom_template ? 1 : 0));
-
- putFile8Bit(file, (level->block_last_field ? 1 : 0));
- putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
- putFile32BitBE(file, level->can_move_into_acid_bits);
- putFile8Bit(file, level->dont_collide_with_bits);
-
- putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
- putFile8Bit(file, (level->use_step_counter ? 1 : 0));
-
- putFile8Bit(file, (level->instant_relocation ? 1 : 0));
- putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
- putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
-
- putFile8Bit(file, level->game_engine_type);
-
- WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
-}
-#endif
-
-static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
-{
- int chunk_size = 0;
- int i;
-
- for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
- chunk_size += putFile8Bit(file, level->name[i]);
-
- return chunk_size;
-}
-
-static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
-{
- int chunk_size = 0;
- int i;
-
- for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
- chunk_size += putFile8Bit(file, level->author[i]);
-
- return chunk_size;
-}
-
-#if 0
-static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
-{
- int chunk_size = 0;
- int x, y;
-
- for (y = 0; y < level->fieldy; y++)
- for (x = 0; x < level->fieldx; x++)
- if (level->encoding_16bit_field)
- chunk_size += putFile16BitBE(file, level->field[x][y]);
- else
- chunk_size += putFile8Bit(file, level->field[x][y]);
-
- return chunk_size;
-}
-#endif
-
-static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
-{
- int chunk_size = 0;
- int x, y;
-
- for (y = 0; y < level->fieldy; y++)
- for (x = 0; x < level->fieldx; x++)
- chunk_size += putFile16BitBE(file, level->field[x][y]);
-
- return chunk_size;
-}
-
-#if 0
-static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
-{
- int i, x, y;
-
- putFile8Bit(file, EL_YAMYAM);
- putFile8Bit(file, level->num_yamyam_contents);
- putFile8Bit(file, 0);
- putFile8Bit(file, 0);
-
- for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
- for (y = 0; y < 3; y++)
- for (x = 0; x < 3; x++)
- if (level->encoding_16bit_field)
- putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
- else
- putFile8Bit(file, level->yamyam_content[i].e[x][y]);
-}
-#endif
-
-#if 0
-static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
-{
- int i, x, y;
- int num_contents, content_xsize, content_ysize;
- int content_array[MAX_ELEMENT_CONTENTS][3][3];
-
- if (element == EL_YAMYAM)
- {
- num_contents = level->num_yamyam_contents;
- content_xsize = 3;
- content_ysize = 3;
-
- for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
- for (y = 0; y < 3; y++)
- for (x = 0; x < 3; x++)
- content_array[i][x][y] = level->yamyam_content[i].e[x][y];
- }
- else if (element == EL_BD_AMOEBA)
- {
- num_contents = 1;
- content_xsize = 1;
- content_ysize = 1;
-
- for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
- for (y = 0; y < 3; y++)
- for (x = 0; x < 3; x++)
- content_array[i][x][y] = EL_EMPTY;
- content_array[0][0][0] = level->amoeba_content;
- }
- else
- {
- /* chunk header already written -- write empty chunk data */
- WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
-
- Error(ERR_WARN, "cannot save content for element '%d'", element);
- return;
- }
-
- putFile16BitBE(file, element);
- putFile8Bit(file, num_contents);
- putFile8Bit(file, content_xsize);
- putFile8Bit(file, content_ysize);
-
- WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
-
- for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
- for (y = 0; y < 3; y++)
- for (x = 0; x < 3; x++)
- putFile16BitBE(file, content_array[i][x][y]);
-}
-#endif
-
-#if 0
-static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
-{
- int envelope_nr = element - EL_ENVELOPE_1;
- int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
- int chunk_size = 0;
- int i;
-
- chunk_size += putFile16BitBE(file, element);
- chunk_size += putFile16BitBE(file, envelope_len);
- chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
- chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
-
- WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
- chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
-
- for (i = 0; i < envelope_len; i++)
- chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
-
- return chunk_size;
-}
-#endif
-
-#if 0
-static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
- int num_changed_custom_elements)
-{
- int i, check = 0;
-
- putFile16BitBE(file, num_changed_custom_elements);
-
- for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
- {
- int element = EL_CUSTOM_START + i;
-
- struct ElementInfo *ei = &element_info[element];
-
- if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
- {
- if (check < num_changed_custom_elements)
- {
- putFile16BitBE(file, element);
- putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
- }
-
- check++;
- }
- }
-
- if (check != num_changed_custom_elements) /* should not happen */
- Error(ERR_WARN, "inconsistent number of custom element properties");
-}
-#endif
-
-#if 0
-static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
- int num_changed_custom_elements)
-{
- int i, check = 0;
-
- putFile16BitBE(file, num_changed_custom_elements);
-
- for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
- {
- int element = EL_CUSTOM_START + i;
-
- if (element_info[element].change->target_element != EL_EMPTY_SPACE)
- {
- if (check < num_changed_custom_elements)
- {
- putFile16BitBE(file, element);
- putFile16BitBE(file, element_info[element].change->target_element);
- }
-
- check++;
- }
- }
-
- if (check != num_changed_custom_elements) /* should not happen */
- Error(ERR_WARN, "inconsistent number of custom target elements");
-}
-#endif
-
-#if 0
-static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
- int num_changed_custom_elements)
-{
- int i, j, x, y, check = 0;
-
- putFile16BitBE(file, num_changed_custom_elements);
-
- for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
- {
- int element = EL_CUSTOM_START + i;
- struct ElementInfo *ei = &element_info[element];
-
- if (ei->modified_settings)
- {
- if (check < num_changed_custom_elements)
- {
- putFile16BitBE(file, element);
-
- for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
- putFile8Bit(file, ei->description[j]);
-
- putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
-
- /* some free bytes for future properties and padding */
- WriteUnusedBytesToFile(file, 7);
-
- putFile8Bit(file, ei->use_gfx_element);
- putFile16BitBE(file, ei->gfx_element_initial);
-
- putFile8Bit(file, ei->collect_score_initial);
- putFile8Bit(file, ei->collect_count_initial);
-
- putFile16BitBE(file, ei->push_delay_fixed);
- putFile16BitBE(file, ei->push_delay_random);
- putFile16BitBE(file, ei->move_delay_fixed);
- putFile16BitBE(file, ei->move_delay_random);
-
- putFile16BitBE(file, ei->move_pattern);
- putFile8Bit(file, ei->move_direction_initial);
- putFile8Bit(file, ei->move_stepsize);
-
- for (y = 0; y < 3; y++)
- for (x = 0; x < 3; x++)
- putFile16BitBE(file, ei->content.e[x][y]);
-
- putFile32BitBE(file, ei->change->events);
-
- putFile16BitBE(file, ei->change->target_element);
-
- putFile16BitBE(file, ei->change->delay_fixed);
- putFile16BitBE(file, ei->change->delay_random);
- putFile16BitBE(file, ei->change->delay_frames);
-
- putFile16BitBE(file, ei->change->initial_trigger_element);
-
- putFile8Bit(file, ei->change->explode);
- putFile8Bit(file, ei->change->use_target_content);
- putFile8Bit(file, ei->change->only_if_complete);
- putFile8Bit(file, ei->change->use_random_replace);
-
- putFile8Bit(file, ei->change->random_percentage);
- putFile8Bit(file, ei->change->replace_when);
-
- for (y = 0; y < 3; y++)
- for (x = 0; x < 3; x++)
- putFile16BitBE(file, ei->change->content.e[x][y]);
-
- putFile8Bit(file, ei->slippery_type);
-
- /* some free bytes for future properties and padding */
- WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
- }
-
- check++;
- }
- }
-
- if (check != num_changed_custom_elements) /* should not happen */
- Error(ERR_WARN, "inconsistent number of custom element properties");
-}
-#endif
-
-#if 0
-static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
-{
- struct ElementInfo *ei = &element_info[element];
- int i, j, x, y;
-
- /* ---------- custom element base property values (96 bytes) ------------- */
-
- putFile16BitBE(file, element);
-
- for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
- putFile8Bit(file, ei->description[i]);
-
- putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
-
- WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
-
- putFile8Bit(file, ei->num_change_pages);
-
- putFile16BitBE(file, ei->ce_value_fixed_initial);
- putFile16BitBE(file, ei->ce_value_random_initial);
- putFile8Bit(file, ei->use_last_ce_value);
-
- putFile8Bit(file, ei->use_gfx_element);
- putFile16BitBE(file, ei->gfx_element_initial);
-
- putFile8Bit(file, ei->collect_score_initial);
- putFile8Bit(file, ei->collect_count_initial);
-
- putFile8Bit(file, ei->drop_delay_fixed);
- putFile8Bit(file, ei->push_delay_fixed);
- putFile8Bit(file, ei->drop_delay_random);
- putFile8Bit(file, ei->push_delay_random);
- putFile16BitBE(file, ei->move_delay_fixed);
- putFile16BitBE(file, ei->move_delay_random);
-
- /* bits 0 - 15 of "move_pattern" ... */
- putFile16BitBE(file, ei->move_pattern & 0xffff);
- putFile8Bit(file, ei->move_direction_initial);
- putFile8Bit(file, ei->move_stepsize);
-
- putFile8Bit(file, ei->slippery_type);
-
- for (y = 0; y < 3; y++)
- for (x = 0; x < 3; x++)
- putFile16BitBE(file, ei->content.e[x][y]);
-
- putFile16BitBE(file, ei->move_enter_element);
- putFile16BitBE(file, ei->move_leave_element);
- putFile8Bit(file, ei->move_leave_type);
-
- /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
- putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
-
- putFile8Bit(file, ei->access_direction);
-
- putFile8Bit(file, ei->explosion_delay);
- putFile8Bit(file, ei->ignition_delay);
- putFile8Bit(file, ei->explosion_type);
-
- /* some free bytes for future custom property values and padding */
- WriteUnusedBytesToFile(file, 1);
-
- /* ---------- change page property values (48 bytes) --------------------- */
-
- for (i = 0; i < ei->num_change_pages; i++)
- {
- struct ElementChangeInfo *change = &ei->change_page[i];
- unsigned int event_bits;
-
- /* bits 0 - 31 of "has_event[]" ... */
- event_bits = 0;
- for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
- if (change->has_event[j])
- event_bits |= (1 << j);
- putFile32BitBE(file, event_bits);
-
- putFile16BitBE(file, change->target_element);
-
- putFile16BitBE(file, change->delay_fixed);
- putFile16BitBE(file, change->delay_random);
- putFile16BitBE(file, change->delay_frames);
-
- putFile16BitBE(file, change->initial_trigger_element);
-
- putFile8Bit(file, change->explode);
- putFile8Bit(file, change->use_target_content);
- putFile8Bit(file, change->only_if_complete);
- putFile8Bit(file, change->use_random_replace);
-
- putFile8Bit(file, change->random_percentage);
- putFile8Bit(file, change->replace_when);
-
- for (y = 0; y < 3; y++)
- for (x = 0; x < 3; x++)
- putFile16BitBE(file, change->target_content.e[x][y]);
-
- putFile8Bit(file, change->can_change);
-
- putFile8Bit(file, change->trigger_side);
-
- putFile8Bit(file, change->trigger_player);
- putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
- log_2(change->trigger_page)));
-
- putFile8Bit(file, change->has_action);
- putFile8Bit(file, change->action_type);
- putFile8Bit(file, change->action_mode);
- putFile16BitBE(file, change->action_arg);
-
- /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
- event_bits = 0;
- for (j = 32; j < NUM_CHANGE_EVENTS; j++)
- if (change->has_event[j])
- event_bits |= (1 << (j - 32));
- putFile8Bit(file, event_bits);
- }
-}
-#endif
-
-#if 0
-static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
-{
- struct ElementInfo *ei = &element_info[element];
- struct ElementGroupInfo *group = ei->group;
- int i;
-
- putFile16BitBE(file, element);
-
- for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
- putFile8Bit(file, ei->description[i]);
-
- putFile8Bit(file, group->num_elements);
-
- putFile8Bit(file, ei->use_gfx_element);
- putFile16BitBE(file, ei->gfx_element_initial);
-
- putFile8Bit(file, group->choice_mode);
-
- /* some free bytes for future values and padding */
- WriteUnusedBytesToFile(file, 3);
-
- for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
- putFile16BitBE(file, group->element[i]);
-}
-#endif
-
-static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
- boolean write_element)
-{
- int save_type = entry->save_type;
- int data_type = entry->data_type;
- int conf_type = entry->conf_type;
- int byte_mask = conf_type & CONF_MASK_BYTES;
- int element = entry->element;
- int default_value = entry->default_value;
- int num_bytes = 0;
- boolean modified = FALSE;
-
- if (byte_mask != CONF_MASK_MULTI_BYTES)
- {
- void *value_ptr = entry->value;
- int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
- *(int *)value_ptr);
-
- /* check if any settings have been modified before saving them */
- if (value != default_value)
- modified = TRUE;
-
- /* do not save if explicitly told or if unmodified default settings */
- if ((save_type == SAVE_CONF_NEVER) ||
- (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
- return 0;
-
- if (write_element)
- num_bytes += putFile16BitBE(file, element);
-
- num_bytes += putFile8Bit(file, conf_type);
- num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
- byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
- byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
- 0);
- }
- else if (data_type == TYPE_STRING)
- {
- char *default_string = entry->default_string;
- char *string = (char *)(entry->value);
- int string_length = strlen(string);
- int i;
-
- /* check if any settings have been modified before saving them */
- if (!strEqual(string, default_string))
- modified = TRUE;
-
- /* do not save if explicitly told or if unmodified default settings */
- if ((save_type == SAVE_CONF_NEVER) ||
- (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
- return 0;
-
- if (write_element)
- num_bytes += putFile16BitBE(file, element);
-
- num_bytes += putFile8Bit(file, conf_type);
- num_bytes += putFile16BitBE(file, string_length);
-
- for (i = 0; i < string_length; i++)
- num_bytes += putFile8Bit(file, string[i]);
- }
- else if (data_type == TYPE_ELEMENT_LIST)
- {
- int *element_array = (int *)(entry->value);
- int num_elements = *(int *)(entry->num_entities);
- int i;
-
- /* check if any settings have been modified before saving them */
- for (i = 0; i < num_elements; i++)
- if (element_array[i] != default_value)
- modified = TRUE;
-
- /* do not save if explicitly told or if unmodified default settings */
- if ((save_type == SAVE_CONF_NEVER) ||
- (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
- return 0;
-
- if (write_element)
- num_bytes += putFile16BitBE(file, element);
-
- num_bytes += putFile8Bit(file, conf_type);
- num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
-
- for (i = 0; i < num_elements; i++)
- num_bytes += putFile16BitBE(file, element_array[i]);
- }
- else if (data_type == TYPE_CONTENT_LIST)
- {
- struct Content *content = (struct Content *)(entry->value);
- int num_contents = *(int *)(entry->num_entities);
- int i, x, y;
-
- /* check if any settings have been modified before saving them */
- for (i = 0; i < num_contents; i++)
- for (y = 0; y < 3; y++)
- for (x = 0; x < 3; x++)
- if (content[i].e[x][y] != default_value)
- modified = TRUE;
-
- /* do not save if explicitly told or if unmodified default settings */
- if ((save_type == SAVE_CONF_NEVER) ||
- (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
- return 0;
-
- if (write_element)
- num_bytes += putFile16BitBE(file, element);
-
- num_bytes += putFile8Bit(file, conf_type);
- num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
-
- for (i = 0; i < num_contents; i++)
- for (y = 0; y < 3; y++)
- for (x = 0; x < 3; x++)
- num_bytes += putFile16BitBE(file, content[i].e[x][y]);
- }
-
- return num_bytes;
-}
-
-static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
-{
- int chunk_size = 0;
- int i;
-
- li = *level; /* copy level data into temporary buffer */
-
- for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
- chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
-
- return chunk_size;
-}
-
-static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
-{
- int chunk_size = 0;
- int i;
-
- li = *level; /* copy level data into temporary buffer */
-
- for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
- chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
-
- return chunk_size;
-}
-
-static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
-{
- int envelope_nr = element - EL_ENVELOPE_1;
- int chunk_size = 0;
- int i;
-
- chunk_size += putFile16BitBE(file, element);
-
- /* copy envelope data into temporary buffer */
- xx_envelope = level->envelope[envelope_nr];
-
- for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
- chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
-
- return chunk_size;
-}
-
-static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
-{
- struct ElementInfo *ei = &element_info[element];
- int chunk_size = 0;
- int i, j;
-
- chunk_size += putFile16BitBE(file, element);
-
- xx_ei = *ei; /* copy element data into temporary buffer */
-
- /* set default description string for this specific element */
- strcpy(xx_default_description, getDefaultElementDescription(ei));
-
-#if 0
- /* set (fixed) number of content areas (may be wrong by broken level file) */
- /* (this is now directly corrected for broken level files after loading) */
- xx_num_contents = 1;
-#endif
-
- for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
- chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
-
- for (i = 0; i < ei->num_change_pages; i++)
- {
- struct ElementChangeInfo *change = &ei->change_page[i];
-
- xx_current_change_page = i;
-
- xx_change = *change; /* copy change data into temporary buffer */
-
- resetEventBits();
- setEventBitsFromEventFlags(change);
-
- for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
- chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
- FALSE);
- }
-
- return chunk_size;
-}
-
-static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
-{
- struct ElementInfo *ei = &element_info[element];
- struct ElementGroupInfo *group = ei->group;
- int chunk_size = 0;
- int i;
-
- chunk_size += putFile16BitBE(file, element);
-
- xx_ei = *ei; /* copy element data into temporary buffer */
- xx_group = *group; /* copy group data into temporary buffer */
-
- /* set default description string for this specific element */
- strcpy(xx_default_description, getDefaultElementDescription(ei));
-
- for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
- chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
-
- return chunk_size;
-}
-
-static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
-{
- int chunk_size;
- int i;
- FILE *file;
-
- 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;
-
- level->creation_date = getCurrentDate();
-
- putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
- putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
-
- chunk_size = SaveLevel_VERS(NULL, level);
- putFileChunkBE(file, "VERS", chunk_size);
- SaveLevel_VERS(file, level);
-
- chunk_size = SaveLevel_DATE(NULL, level);
- putFileChunkBE(file, "DATE", chunk_size);
- SaveLevel_DATE(file, level);
-
- chunk_size = SaveLevel_NAME(NULL, level);
- putFileChunkBE(file, "NAME", chunk_size);
- SaveLevel_NAME(file, level);
-
- chunk_size = SaveLevel_AUTH(NULL, level);
- putFileChunkBE(file, "AUTH", chunk_size);
- SaveLevel_AUTH(file, level);
-
- chunk_size = SaveLevel_INFO(NULL, level);
- putFileChunkBE(file, "INFO", chunk_size);
- SaveLevel_INFO(file, level);
-
- chunk_size = SaveLevel_BODY(NULL, level);
- putFileChunkBE(file, "BODY", chunk_size);
- SaveLevel_BODY(file, level);
-
- chunk_size = SaveLevel_ELEM(NULL, level);
- if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
- {
- putFileChunkBE(file, "ELEM", chunk_size);
- SaveLevel_ELEM(file, level);
- }
-
- for (i = 0; i < NUM_ENVELOPES; i++)
- {
- int element = EL_ENVELOPE_1 + i;
-
- chunk_size = SaveLevel_NOTE(NULL, level, element);
- if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
- {
- putFileChunkBE(file, "NOTE", chunk_size);
- SaveLevel_NOTE(file, level, element);
- }
- }
-
- /* if not using template level, check for non-default custom/group elements */
- if (!level->use_custom_template)
- {
- for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
- {
- int element = EL_CUSTOM_START + i;
-
- chunk_size = SaveLevel_CUSX(NULL, level, element);
- if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
- {
- putFileChunkBE(file, "CUSX", chunk_size);
- SaveLevel_CUSX(file, level, element);
- }
- }
-
- for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
- {
- int element = EL_GROUP_START + i;
-
- chunk_size = SaveLevel_GRPX(NULL, level, element);
- if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
- {
- putFileChunkBE(file, "GRPX", chunk_size);
- SaveLevel_GRPX(file, level, element);
- }
- }
- }
-
- fclose(file);
-
- SetFilePermissions(filename, PERMS_PRIVATE);
-}
-
-void SaveLevel(int nr)
-{
- char *filename = getDefaultLevelFilename(nr);
-
- SaveLevelFromFilename(&level, filename);
-}
-
-void SaveLevelTemplate()
-{
- char *filename = getDefaultLevelFilename(-1);
-
- SaveLevelFromFilename(&level, filename);
-}
-
-boolean SaveLevelChecked(int nr)
-{
- char *filename = getDefaultLevelFilename(nr);
- boolean new_level = !fileExists(filename);
- boolean level_saved = FALSE;
-
- if (new_level || Request("Save this level and kill the old ?", REQ_ASK))
- {
- SaveLevel(nr);
-
- if (new_level)
- Request("Level saved !", REQ_CONFIRM);
-
- level_saved = TRUE;
- }
-
- return level_saved;
-}
-
-void DumpLevel(struct LevelInfo *level)
-{
- if (level->no_valid_file)
- {
- Error(ERR_WARN, "cannot dump -- no valid level file found");
-
- return;
- }
-
- 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("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
- printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
- printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
- printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
- printf("use step counter: %s\n", (level->use_step_counter ? "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;
-
- tape.no_valid_file = FALSE;
-}
-
-#if 1
-
-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)
- {
- byte store_participating_players = getFile8Bit(file);
- int engine_version;
-
- /* since version 1.2, tapes store which players participate in the tape */
- 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;
- tape->num_participating_players++;
- }
- }
-
- ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
-
- engine_version = getFileVersion(file);
- if (engine_version > 0)
- tape->engine_version = engine_version;
- else
- tape->engine_version = tape->game_version;
- }
-
- return chunk_size;
-}
-
-static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
-{
- int level_identifier_size;
- int i;
-
- level_identifier_size = getFile16BitBE(file);
-
- tape->level_identifier =
- checked_realloc(tape->level_identifier, level_identifier_size);
-
- for (i = 0; i < level_identifier_size; i++)
- tape->level_identifier[i] = getFile8Bit(file);
-
- tape->level_nr = getFile16BitBE(file);
-
- chunk_size = 2 + level_identifier_size + 2;
-
- return chunk_size;
-}
-
-static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
-{
- int i, j;
- int chunk_size_expected =
- (tape->num_participating_players + 1) * tape->length;
-
- if (chunk_size_expected != chunk_size)
- {
- ReadUnusedBytesFromFile(file, chunk_size);
- return chunk_size_expected;
- }
-
- for (i = 0; i < tape->length; i++)
- {
- if (i >= MAX_TAPE_LEN)
- break;
-
- for (j = 0; j < MAX_PLAYERS; j++)
- {
- tape->pos[i].action[j] = MV_NONE;
-
- if (tape->player_participates[j])
- tape->pos[i].action[j] = getFile8Bit(file);
- }
-
- tape->pos[i].delay = getFile8Bit(file);
-
- if (tape->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;
- }
- }
- else if (tape->file_version < FILE_VERSION_2_0)
- {
- /* convert pre-2.0 tapes to new tape format */
-
- if (tape->pos[i].delay > 1)
- {
- /* action part */
- tape->pos[i + 1] = tape->pos[i];
- tape->pos[i + 1].delay = 1;
-
- /* delay part */
- for (j = 0; j < MAX_PLAYERS; j++)
- tape->pos[i].action[j] = MV_NONE;
- tape->pos[i].delay--;
-
- i++;
- tape->length++;
- }
- }
-
- if (checkEndOfFile(file))
- break;
- }
-
- if (i != tape->length)
- chunk_size = (tape->num_participating_players + 1) * i;
-
- return chunk_size;
-}
-
-#else
-
-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)
- {
- byte store_participating_players = getFile8Bit(file);
- int engine_version;
-
- /* since version 1.2, tapes store which players participate in the tape */
- 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;
- tape->num_participating_players++;
- }
- }
-
- ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
-
- engine_version = getFileVersion(file);
- if (engine_version > 0)
- tape->engine_version = engine_version;
- else
- tape->engine_version = tape->game_version;
- }
-
- return chunk_size;
-}
-
-static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
-{
- int level_identifier_size;
- int i;
-
- level_identifier_size = getFile16BitBE(file);
-
- tape->level_identifier =
- checked_realloc(tape->level_identifier, level_identifier_size);
-
- for (i = 0; i < level_identifier_size; i++)
- tape->level_identifier[i] = getFile8Bit(file);
-
- tape->level_nr = getFile16BitBE(file);
-
- chunk_size = 2 + level_identifier_size + 2;
-
- return chunk_size;
-}
-
-static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
-{
- int i, j;
- int chunk_size_expected =
- (tape->num_participating_players + 1) * tape->length;
-
- if (chunk_size_expected != chunk_size)
- {
- ReadUnusedBytesFromFile(file, chunk_size);
- return chunk_size_expected;
- }
-
- for (i = 0; i < tape->length; i++)
- {
- if (i >= MAX_TAPE_LEN)
- break;
-
- for (j = 0; j < MAX_PLAYERS; j++)
- {
- tape->pos[i].action[j] = MV_NONE;
-
- if (tape->player_participates[j])
- tape->pos[i].action[j] = getFile8Bit(file);
- }
-
- tape->pos[i].delay = getFile8Bit(file);
-
- if (tape->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;
- }
- }
- else if (tape->file_version < FILE_VERSION_2_0)
- {
- /* convert pre-2.0 tapes to new tape format */
-
- if (tape->pos[i].delay > 1)
- {
- /* action part */
- tape->pos[i + 1] = tape->pos[i];
- tape->pos[i + 1].delay = 1;
-
- /* delay part */
- for (j = 0; j < MAX_PLAYERS; j++)
- tape->pos[i].action[j] = MV_NONE;
- tape->pos[i].delay--;
-
- i++;
- tape->length++;
- }
- }
-
- if (feof(file))
- break;
- }
-
- if (i != tape->length)
- chunk_size = (tape->num_participating_players + 1) * i;
-
- return chunk_size;
-}
-
-#endif
-
-#if 1
-
-void LoadTape_SokobanSolution(char *filename)
-{
- File *file;
- int move_delay = TILESIZE / level.initial_player_stepsize[0];
-
- if (!(file = openFile(filename, MODE_READ)))
- {
- tape.no_valid_file = TRUE;
-
- return;
- }
-
- while (!checkEndOfFile(file))
- {
- unsigned char c = getByteFromFile(file);
-
- if (checkEndOfFile(file))
- break;
-
- switch (c)
- {
- case 'u':
- case 'U':
- tape.pos[tape.length].action[0] = MV_UP;
- tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
- tape.length++;
- break;
-
- case 'd':
- case 'D':
- tape.pos[tape.length].action[0] = MV_DOWN;
- tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
- tape.length++;
- break;
-
- case 'l':
- case 'L':
- tape.pos[tape.length].action[0] = MV_LEFT;
- tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
- tape.length++;
- break;
-
- case 'r':
- case 'R':
- tape.pos[tape.length].action[0] = MV_RIGHT;
- tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
- tape.length++;
- break;
-
- case '\n':
- case '\r':
- case '\t':
- case ' ':
- /* ignore white-space characters */
- break;
-
- default:
- tape.no_valid_file = TRUE;
-
- Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
-
- break;
- }
- }
-
- closeFile(file);
-
- if (tape.no_valid_file)
- return;
-
- tape.length_seconds = GetTapeLength();
-}
-
-#else
-
-void LoadTape_SokobanSolution(char *filename)
-{
- FILE *file;
- int move_delay = TILESIZE / level.initial_player_stepsize[0];
-
- if (!(file = fopen(filename, MODE_READ)))
- {
- tape.no_valid_file = TRUE;
-
- return;
- }
-
- while (!feof(file))
- {
- unsigned char c = fgetc(file);
-
- if (feof(file))
- break;
-
- switch (c)
- {
- case 'u':
- case 'U':
- tape.pos[tape.length].action[0] = MV_UP;
- tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
- tape.length++;
- break;
-
- case 'd':
- case 'D':
- tape.pos[tape.length].action[0] = MV_DOWN;
- tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
- tape.length++;
- break;
-
- case 'l':
- case 'L':
- tape.pos[tape.length].action[0] = MV_LEFT;
- tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
- tape.length++;
- break;
-
- case 'r':
- case 'R':
- tape.pos[tape.length].action[0] = MV_RIGHT;
- tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
- tape.length++;
- break;
-
- case '\n':
- case '\r':
- case '\t':
- case ' ':
- /* ignore white-space characters */
- break;
-
- default:
- tape.no_valid_file = TRUE;
-
- Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
-
- break;
- }
- }
-
- fclose(file);
-
- if (tape.no_valid_file)
- return;
-
- tape.length_seconds = GetTapeLength();
-}
-
-#endif
-
-#if 1
-
-void LoadTapeFromFilename(char *filename)
-{
- char cookie[MAX_LINE_LEN];
- char chunk_name[CHUNK_ID_LEN + 1];
- File *file;
- int chunk_size;
-
- /* always start with reliable default values */
- setTapeInfoToDefaults();
-
- if (strSuffix(filename, ".sln"))
- {
- LoadTape_SokobanSolution(filename);
-
- return;
- }
-
- if (!(file = openFile(filename, MODE_READ)))
- {
- tape.no_valid_file = TRUE;
-
- return;
- }
-
- getFileChunkBE(file, chunk_name, NULL);
- if (strEqual(chunk_name, "RND1"))
- {
- getFile32BitBE(file); /* not used */
-
- getFileChunkBE(file, chunk_name, NULL);
- if (!strEqual(chunk_name, "TAPE"))
- {
- tape.no_valid_file = TRUE;
-
- Error(ERR_WARN, "unknown format of tape file '%s'", filename);
-
- closeFile(file);
-
- return;
- }
- }
- else /* check for pre-2.0 file format with cookie string */
- {
- strcpy(cookie, chunk_name);
- if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
- cookie[4] = '\0';
- if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
- cookie[strlen(cookie) - 1] = '\0';
-
- if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
- {
- tape.no_valid_file = TRUE;
-
- Error(ERR_WARN, "unknown format of tape file '%s'", filename);
-
- closeFile(file);
-
- return;
- }
-
- if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
- {
- tape.no_valid_file = TRUE;
-
- Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
-
- closeFile(file);
-
- return;
- }
-
- /* pre-2.0 tape files have no game version, so use file version here */
- tape.game_version = tape.file_version;
- }
-
- if (tape.file_version < FILE_VERSION_1_2)
- {
- /* tape files from versions before 1.2.0 without chunk structure */
- LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
- LoadTape_BODY(file, 2 * tape.length, &tape);
- }
- else
- {
- static struct
- {
- char *name;
- int size;
- int (*loader)(File *, int, struct TapeInfo *);
- }
- chunk_info[] =
- {
- { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
- { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
- { "INFO", -1, LoadTape_INFO },
- { "BODY", -1, LoadTape_BODY },
- { NULL, 0, NULL }
- };
-
- while (getFileChunkBE(file, chunk_name, &chunk_size))
- {
- int i = 0;
-
- while (chunk_info[i].name != NULL &&
- !strEqual(chunk_name, chunk_info[i].name))
- i++;
-
- if (chunk_info[i].name == NULL)
- {
- Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
- chunk_name, filename);
- ReadUnusedBytesFromFile(file, chunk_size);
- }
- else if (chunk_info[i].size != -1 &&
- chunk_info[i].size != chunk_size)
- {
- Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
- chunk_size, chunk_name, filename);
- ReadUnusedBytesFromFile(file, chunk_size);
- }
- else
- {
- /* call function to load this tape chunk */
- int chunk_size_expected =
- (chunk_info[i].loader)(file, chunk_size, &tape);
-
- /* the size of some chunks cannot be checked before reading other
- chunks first (like "HEAD" and "BODY") that contain some header
- information, so check them here */
- if (chunk_size_expected != chunk_size)
- {
- Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
- chunk_size, chunk_name, filename);
- }
- }
- }
- }
-
- closeFile(file);
-
- tape.length_seconds = GetTapeLength();
-
-#if 0
- printf("::: tape file version: %d\n", tape.file_version);
- printf("::: tape game version: %d\n", tape.game_version);
- printf("::: tape engine version: %d\n", tape.engine_version);
-#endif
-}
-
-#else
-
-void LoadTapeFromFilename(char *filename)
-{
- char cookie[MAX_LINE_LEN];
- char chunk_name[CHUNK_ID_LEN + 1];
- FILE *file;
- int chunk_size;
-
- /* always start with reliable default values */
- setTapeInfoToDefaults();
-
- if (strSuffix(filename, ".sln"))
- {
- LoadTape_SokobanSolution(filename);
-
- return;
- }
-
- if (!(file = fopen(filename, MODE_READ)))
- {
- tape.no_valid_file = TRUE;
-
- return;
- }
-
- getFileChunkBE(file, chunk_name, NULL);
- if (strEqual(chunk_name, "RND1"))
- {
- getFile32BitBE(file); /* not used */
-
- getFileChunkBE(file, chunk_name, NULL);
- if (!strEqual(chunk_name, "TAPE"))
- {
- tape.no_valid_file = TRUE;
-
- Error(ERR_WARN, "unknown format of tape file '%s'", filename);
- fclose(file);
- return;
- }
- }
- else /* check for pre-2.0 file format with cookie string */
- {
- strcpy(cookie, chunk_name);
- if (fgets(&cookie[4], MAX_LINE_LEN - 4, file) == NULL)
- cookie[4] = '\0';
- if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
- cookie[strlen(cookie) - 1] = '\0';
-
- if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
- {
- tape.no_valid_file = TRUE;
-
- Error(ERR_WARN, "unknown format of tape file '%s'", filename);
- fclose(file);
- return;
- }
-
- if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
- {
- tape.no_valid_file = TRUE;
-
- Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
- fclose(file);
-
- return;
- }
-
- /* pre-2.0 tape files have no game version, so use file version here */
- tape.game_version = tape.file_version;
- }
-
- if (tape.file_version < FILE_VERSION_1_2)
- {
- /* tape files from versions before 1.2.0 without chunk structure */
- LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
- LoadTape_BODY(file, 2 * tape.length, &tape);
- }
- else
- {
- static struct
- {
- char *name;
- int size;
- int (*loader)(File *, int, struct TapeInfo *);
- }
- chunk_info[] =
- {
- { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
- { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
- { "INFO", -1, LoadTape_INFO },
- { "BODY", -1, LoadTape_BODY },
- { NULL, 0, NULL }
- };
-
- while (getFileChunkBE(file, chunk_name, &chunk_size))
- {
- int i = 0;
-
- while (chunk_info[i].name != NULL &&
- !strEqual(chunk_name, chunk_info[i].name))
- i++;
-
- if (chunk_info[i].name == NULL)
- {
- Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
- chunk_name, filename);
- ReadUnusedBytesFromFile(file, chunk_size);
- }
- else if (chunk_info[i].size != -1 &&
- chunk_info[i].size != chunk_size)
- {
- Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
- chunk_size, chunk_name, filename);
- ReadUnusedBytesFromFile(file, chunk_size);
- }
- else
- {
- /* call function to load this tape chunk */
- int chunk_size_expected =
- (chunk_info[i].loader)(file, chunk_size, &tape);
-
- /* the size of some chunks cannot be checked before reading other
- chunks first (like "HEAD" and "BODY") that contain some header
- information, so check them here */
- if (chunk_size_expected != chunk_size)
- {
- Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
- chunk_size, chunk_name, filename);
- }
- }
- }
- }
-
- fclose(file);
-
- tape.length_seconds = GetTapeLength();
-
-#if 0
- printf("::: tape file version: %d\n", tape.file_version);
- printf("::: tape game version: %d\n", tape.game_version);
- printf("::: tape engine version: %d\n", tape.engine_version);
-#endif
-}
-
-#endif
-
-void LoadTape(int nr)
-{
- char *filename = getTapeFilename(nr);
-
- LoadTapeFromFilename(filename);
-}
-
-void LoadSolutionTape(int nr)
-{
- char *filename = getSolutionTapeFilename(nr);
-
- LoadTapeFromFilename(filename);
-
-#if 1
- if (TAPE_IS_EMPTY(tape) &&
- level.game_engine_type == GAME_ENGINE_TYPE_SP &&
- level.native_sp_level->demo.is_available)
- CopyNativeTape_SP_to_RND(&level);
-#endif
-}
-
-static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
-{
- putFileVersion(file, tape->file_version);
- putFileVersion(file, tape->game_version);
-}
-
-static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
-{
- int i;
- byte store_participating_players = 0;
-
- /* set bits for participating players for compact storage */
- for (i = 0; i < MAX_PLAYERS; i++)
- if (tape->player_participates[i])
- store_participating_players |= (1 << i);
-
- putFile32BitBE(file, tape->random_seed);
- putFile32BitBE(file, tape->date);
- putFile32BitBE(file, tape->length);
-
- putFile8Bit(file, store_participating_players);
-
- /* unused bytes not at the end here for 4-byte alignment of engine_version */
- WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
-
- putFileVersion(file, tape->engine_version);
-}
-
-static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
-{
- int level_identifier_size = strlen(tape->level_identifier) + 1;
- int i;
-
- putFile16BitBE(file, level_identifier_size);
-
- for (i = 0; i < level_identifier_size; i++)
- putFile8Bit(file, tape->level_identifier[i]);
-
- putFile16BitBE(file, tape->level_nr);
-}
-
-static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
-{
- int i, j;
-
- for (i = 0; i < tape->length; i++)