1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
19 #include "libgame/libgame.h"
27 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
28 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
29 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
30 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
31 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
32 #define LEVEL_HEADER_UNUSED 1 /* unused level header bytes */
33 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
34 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
35 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
36 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
37 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
38 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
39 #define LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
40 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
41 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
43 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
44 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
45 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
47 /* file identifier strings */
48 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
49 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
50 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
53 /* ========================================================================= */
54 /* level file functions */
55 /* ========================================================================= */
57 void setElementChangePages(struct ElementInfo *ei, int change_pages)
59 int change_page_size = sizeof(struct ElementChangeInfo);
61 ei->num_change_pages = MAX(1, change_pages);
64 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
66 if (ei->current_change_page >= ei->num_change_pages)
67 ei->current_change_page = ei->num_change_pages - 1;
69 ei->change = &ei->change_page[ei->current_change_page];
72 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
76 change->can_change = FALSE;
78 change->events = CE_BITMASK_DEFAULT;
80 change->trigger_player = CH_PLAYER_ANY;
81 change->trigger_side = CH_SIDE_ANY;
82 change->trigger_page = CH_PAGE_ANY;
84 change->target_element = EL_EMPTY_SPACE;
86 change->delay_fixed = 0;
87 change->delay_random = 0;
88 change->delay_frames = 1;
90 change->trigger_element = EL_EMPTY_SPACE;
92 change->explode = FALSE;
93 change->use_target_content = FALSE;
94 change->only_if_complete = FALSE;
95 change->use_random_replace = FALSE;
96 change->random_percentage = 100;
97 change->replace_when = CP_WHEN_EMPTY;
99 for (x = 0; x < 3; x++)
100 for (y = 0; y < 3; y++)
101 change->target_content[x][y] = EL_EMPTY_SPACE;
103 change->direct_action = 0;
104 change->other_action = 0;
106 change->pre_change_function = NULL;
107 change->change_function = NULL;
108 change->post_change_function = NULL;
111 static void setLevelInfoToDefaults(struct LevelInfo *level)
113 static boolean clipboard_elements_initialized = FALSE;
117 level->file_version = FILE_VERSION_ACTUAL;
118 level->game_version = GAME_VERSION_ACTUAL;
120 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
121 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
122 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
124 level->fieldx = STD_LEV_FIELDX;
125 level->fieldy = STD_LEV_FIELDY;
127 for (x = 0; x < MAX_LEV_FIELDX; x++)
128 for (y = 0; y < MAX_LEV_FIELDY; y++)
129 level->field[x][y] = EL_SAND;
132 level->gems_needed = 0;
134 level->amoeba_speed = 10;
136 level->time_magic_wall = 10;
137 level->time_wheel = 10;
138 level->time_light = 10;
139 level->time_timegate = 10;
141 level->amoeba_content = EL_DIAMOND;
143 level->double_speed = FALSE;
144 level->initial_gravity = FALSE;
145 level->em_slippery_gems = FALSE;
146 level->instant_relocation = FALSE;
147 level->can_pass_to_walkable = FALSE;
148 level->grow_into_diggable = TRUE;
150 level->block_last_field = FALSE; /* EM does not block by default */
151 level->sp_block_last_field = TRUE; /* SP blocks the last field */
152 level->block_delay = 8; /* when blocking, block 8 frames */
153 level->sp_block_delay = 9; /* SP indeed blocks 9 frames, not 8 */
155 level->can_move_into_acid_bits = ~0; /* everything can move into acid */
156 level->dont_collide_with_bits = ~0; /* always deadly when colliding */
158 level->use_spring_bug = FALSE;
159 level->use_step_counter = FALSE;
161 level->use_custom_template = FALSE;
163 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
164 level->name[i] = '\0';
165 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
166 level->author[i] = '\0';
168 strcpy(level->name, NAMELESS_LEVEL_NAME);
169 strcpy(level->author, ANONYMOUS_NAME);
171 for (i = 0; i < 4; i++)
173 level->envelope_text[i][0] = '\0';
174 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
175 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
178 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
179 level->score[i] = 10;
181 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
182 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
183 for (x = 0; x < 3; x++)
184 for (y = 0; y < 3; y++)
185 level->yamyam_content[i][x][y] =
186 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
188 level->field[0][0] = EL_PLAYER_1;
189 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
191 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
195 /* never initialize clipboard elements after the very first time */
196 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
199 setElementChangePages(&element_info[element], 1);
200 setElementChangeInfoToDefaults(element_info[element].change);
202 if (IS_CUSTOM_ELEMENT(element) ||
203 IS_GROUP_ELEMENT(element) ||
204 IS_INTERNAL_ELEMENT(element))
206 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
207 element_info[element].description[j] = '\0';
209 if (element_info[element].custom_description != NULL)
210 strncpy(element_info[element].description,
211 element_info[element].custom_description,MAX_ELEMENT_NAME_LEN);
213 strcpy(element_info[element].description,
214 element_info[element].editor_description);
216 element_info[element].use_gfx_element = FALSE;
217 element_info[element].gfx_element = EL_EMPTY_SPACE;
219 element_info[element].modified_settings = FALSE;
222 if (IS_CUSTOM_ELEMENT(element) ||
223 IS_INTERNAL_ELEMENT(element))
225 element_info[element].access_direction = MV_ALL_DIRECTIONS;
227 element_info[element].collect_score = 10; /* special default */
228 element_info[element].collect_count = 1; /* special default */
230 element_info[element].push_delay_fixed = -1; /* initialize later */
231 element_info[element].push_delay_random = -1; /* initialize later */
232 element_info[element].drop_delay_fixed = 0;
233 element_info[element].drop_delay_random = 0;
234 element_info[element].move_delay_fixed = 0;
235 element_info[element].move_delay_random = 0;
237 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
238 element_info[element].move_direction_initial = MV_START_AUTOMATIC;
239 element_info[element].move_stepsize = TILEX / 8;
241 element_info[element].move_enter_element = EL_EMPTY_SPACE;
242 element_info[element].move_leave_element = EL_EMPTY_SPACE;
243 element_info[element].move_leave_type = LEAVE_TYPE_UNLIMITED;
245 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
247 element_info[element].explosion_type = EXPLODES_3X3;
248 element_info[element].explosion_delay = 16;
249 element_info[element].ignition_delay = 8;
251 for (x = 0; x < 3; x++)
252 for (y = 0; y < 3; y++)
253 element_info[element].content[x][y] = EL_EMPTY_SPACE;
255 element_info[element].access_type = 0;
256 element_info[element].access_layer = 0;
257 element_info[element].access_protected = 0;
258 element_info[element].walk_to_action = 0;
259 element_info[element].smash_targets = 0;
260 element_info[element].deadliness = 0;
262 element_info[element].can_explode_by_fire = FALSE;
263 element_info[element].can_explode_smashed = FALSE;
264 element_info[element].can_explode_impact = FALSE;
266 element_info[element].current_change_page = 0;
268 /* start with no properties at all */
269 for (j = 0; j < NUM_EP_BITFIELDS; j++)
270 Properties[element][j] = EP_BITMASK_DEFAULT;
272 /* now set default properties */
273 SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE);
276 if (IS_GROUP_ELEMENT(element) ||
277 IS_INTERNAL_ELEMENT(element))
279 /* initialize memory for list of elements in group */
280 if (element_info[element].group == NULL)
281 element_info[element].group =
282 checked_malloc(sizeof(struct ElementGroupInfo));
284 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
285 element_info[element].group->element[j] = EL_EMPTY_SPACE;
287 /* default: only one element in group */
288 element_info[element].group->num_elements = 1;
290 element_info[element].group->choice_mode = ANIM_RANDOM;
294 clipboard_elements_initialized = TRUE;
296 BorderElement = EL_STEELWALL;
298 level->no_valid_file = FALSE;
300 level->changed = FALSE;
302 if (leveldir_current == NULL) /* only when dumping level */
305 /* try to determine better author name than 'anonymous' */
306 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
308 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
309 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
313 switch (LEVELCLASS(leveldir_current))
315 case LEVELCLASS_TUTORIAL:
316 strcpy(level->author, PROGRAM_AUTHOR_STRING);
319 case LEVELCLASS_CONTRIB:
320 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
321 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
324 case LEVELCLASS_PRIVATE:
325 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
326 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
330 /* keep default value */
336 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
338 level_file_info->nr = 0;
339 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
340 level_file_info->packed = FALSE;
341 level_file_info->basename = NULL;
342 level_file_info->filename = NULL;
345 static void ActivateLevelTemplate()
347 /* Currently there is no special action needed to activate the template
348 data, because 'element_info' and 'Properties' overwrite the original
349 level data, while all other variables do not change. */
352 static char *getLevelFilenameFromBasename(char *basename)
354 static char *filename = NULL;
356 checked_free(filename);
358 filename = getPath2(getCurrentLevelDir(), basename);
363 static int getFileTypeFromBasename(char *basename)
365 static char *filename = NULL;
366 struct stat file_status;
368 /* ---------- try to determine file type from filename ---------- */
370 /* check for typical filename of a Supaplex level package file */
371 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
372 strncmp(basename, "LEVELS.D", 8) == 0))
373 return LEVEL_FILE_TYPE_SP;
375 /* ---------- try to determine file type from filesize ---------- */
377 checked_free(filename);
378 filename = getPath2(getCurrentLevelDir(), basename);
380 if (stat(filename, &file_status) == 0)
382 /* check for typical filesize of a Supaplex level package file */
383 if (file_status.st_size == 170496)
384 return LEVEL_FILE_TYPE_SP;
387 return LEVEL_FILE_TYPE_UNKNOWN;
390 static char *getSingleLevelBasename(int nr, int type)
392 static char basename[MAX_FILENAME_LEN];
393 char *level_filename = getStringCopy(leveldir_current->level_filename);
395 if (level_filename == NULL)
396 level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION);
400 case LEVEL_FILE_TYPE_RND:
402 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
404 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
407 case LEVEL_FILE_TYPE_EM:
408 sprintf(basename, "%d", nr);
411 case LEVEL_FILE_TYPE_UNKNOWN:
413 sprintf(basename, level_filename, nr);
417 free(level_filename);
422 static char *getPackedLevelBasename(int type)
424 static char basename[MAX_FILENAME_LEN];
425 char *directory = getCurrentLevelDir();
427 struct dirent *dir_entry;
429 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
431 if ((dir = opendir(directory)) == NULL)
433 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
438 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
440 char *entry_basename = dir_entry->d_name;
441 int entry_type = getFileTypeFromBasename(entry_basename);
443 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
445 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
448 strcpy(basename, entry_basename);
460 static char *getSingleLevelFilename(int nr, int type)
462 return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
466 static char *getPackedLevelFilename(int type)
468 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
472 char *getDefaultLevelFilename(int nr)
474 return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
477 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
482 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
483 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
486 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
491 lfi->basename = getPackedLevelBasename(lfi->type);
492 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
495 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
497 /* special case: level number is negative => check for level template file */
500 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
505 if (leveldir_current->level_filename != NULL)
507 /* check for file name/pattern specified in "levelinfo.conf" */
508 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
509 if (fileExists(lfi->filename))
513 /* check for native Rocks'n'Diamonds level file */
514 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
515 if (fileExists(lfi->filename))
518 /* check for classic Emerald Mine level file */
519 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_EM);
520 if (fileExists(lfi->filename))
523 /* check for various packed level file formats */
524 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
525 if (fileExists(lfi->filename))
528 /* no known level file found -- try to use default values */
529 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
532 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
534 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
535 lfi->type = getFileTypeFromBasename(lfi->basename);
539 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
541 /* always start with reliable default values */
542 setFileInfoToDefaults(level_file_info);
544 level_file_info->nr = nr; /* set requested level number */
546 determineLevelFileInfo_Filename(level_file_info);
547 determineLevelFileInfo_Filetype(level_file_info);
552 static struct LevelFileInfo *getLevelFileInfo(int nr)
554 static struct LevelFileInfo level_file_info;
556 /* always start with reliable default values */
557 setFileInfoToDefaults(&level_file_info);
559 level_file_info.nr = nr; /* set requested level number */
561 determineLevelFileInfo_Filename(&level_file_info);
562 determineLevelFileInfo_Filetype(&level_file_info);
564 return &level_file_info;
568 /* ------------------------------------------------------------------------- */
569 /* functions for loading R'n'D level */
570 /* ------------------------------------------------------------------------- */
572 int getMappedElement(int element)
574 /* remap some (historic, now obsolete) elements */
579 case EL_PLAYER_OBSOLETE:
580 element = EL_PLAYER_1;
583 case EL_KEY_OBSOLETE:
586 case EL_EM_KEY_1_FILE_OBSOLETE:
587 element = EL_EM_KEY_1;
590 case EL_EM_KEY_2_FILE_OBSOLETE:
591 element = EL_EM_KEY_2;
594 case EL_EM_KEY_3_FILE_OBSOLETE:
595 element = EL_EM_KEY_3;
598 case EL_EM_KEY_4_FILE_OBSOLETE:
599 element = EL_EM_KEY_4;
602 case EL_ENVELOPE_OBSOLETE:
603 element = EL_ENVELOPE_1;
611 if (element >= NUM_FILE_ELEMENTS)
613 Error(ERR_WARN, "invalid level element %d", element);
615 element = EL_UNKNOWN;
620 if (element >= NUM_FILE_ELEMENTS)
622 Error(ERR_WARN, "invalid level element %d", element);
624 element = EL_UNKNOWN;
626 else if (element == EL_PLAYER_OBSOLETE)
627 element = EL_PLAYER_1;
628 else if (element == EL_KEY_OBSOLETE)
635 int getMappedElementByVersion(int element, int game_version)
637 /* remap some elements due to certain game version */
639 if (game_version <= VERSION_IDENT(2,2,0,0))
641 /* map game font elements */
642 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
643 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
644 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
645 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
648 if (game_version < VERSION_IDENT(3,0,0,0))
650 /* map Supaplex gravity tube elements */
651 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
652 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
653 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
654 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
661 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
663 level->file_version = getFileVersion(file);
664 level->game_version = getFileVersion(file);
669 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
673 level->fieldx = getFile8Bit(file);
674 level->fieldy = getFile8Bit(file);
676 level->time = getFile16BitBE(file);
677 level->gems_needed = getFile16BitBE(file);
679 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
680 level->name[i] = getFile8Bit(file);
681 level->name[MAX_LEVEL_NAME_LEN] = 0;
683 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
684 level->score[i] = getFile8Bit(file);
686 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
687 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
688 for (y = 0; y < 3; y++)
689 for (x = 0; x < 3; x++)
690 level->yamyam_content[i][x][y] = getMappedElement(getFile8Bit(file));
692 level->amoeba_speed = getFile8Bit(file);
693 level->time_magic_wall = getFile8Bit(file);
694 level->time_wheel = getFile8Bit(file);
695 level->amoeba_content = getMappedElement(getFile8Bit(file));
696 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
697 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
698 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
699 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
701 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
703 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
704 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
705 level->can_move_into_acid_bits = getFile32BitBE(file);
706 level->dont_collide_with_bits = getFile8Bit(file);
708 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
709 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
711 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
712 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
713 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
715 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
720 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
724 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
725 level->author[i] = getFile8Bit(file);
726 level->author[MAX_LEVEL_NAME_LEN] = 0;
731 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
734 int chunk_size_expected = level->fieldx * level->fieldy;
736 /* Note: "chunk_size" was wrong before version 2.0 when elements are
737 stored with 16-bit encoding (and should be twice as big then).
738 Even worse, playfield data was stored 16-bit when only yamyam content
739 contained 16-bit elements and vice versa. */
741 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
742 chunk_size_expected *= 2;
744 if (chunk_size_expected != chunk_size)
746 ReadUnusedBytesFromFile(file, chunk_size);
747 return chunk_size_expected;
750 for (y = 0; y < level->fieldy; y++)
751 for (x = 0; x < level->fieldx; x++)
753 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
758 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
762 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
763 int chunk_size_expected = header_size + content_size;
765 /* Note: "chunk_size" was wrong before version 2.0 when elements are
766 stored with 16-bit encoding (and should be twice as big then).
767 Even worse, playfield data was stored 16-bit when only yamyam content
768 contained 16-bit elements and vice versa. */
770 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
771 chunk_size_expected += content_size;
773 if (chunk_size_expected != chunk_size)
775 ReadUnusedBytesFromFile(file, chunk_size);
776 return chunk_size_expected;
780 level->num_yamyam_contents = getFile8Bit(file);
784 /* correct invalid number of content fields -- should never happen */
785 if (level->num_yamyam_contents < 1 ||
786 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
787 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
789 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
790 for (y = 0; y < 3; y++)
791 for (x = 0; x < 3; x++)
792 level->yamyam_content[i][x][y] =
793 getMappedElement(level->encoding_16bit_field ?
794 getFile16BitBE(file) : getFile8Bit(file));
798 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
802 int num_contents, content_xsize, content_ysize;
803 int content_array[MAX_ELEMENT_CONTENTS][3][3];
805 element = getMappedElement(getFile16BitBE(file));
806 num_contents = getFile8Bit(file);
807 content_xsize = getFile8Bit(file);
808 content_ysize = getFile8Bit(file);
810 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
812 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
813 for (y = 0; y < 3; y++)
814 for (x = 0; x < 3; x++)
815 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
817 /* correct invalid number of content fields -- should never happen */
818 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
819 num_contents = STD_ELEMENT_CONTENTS;
821 if (element == EL_YAMYAM)
823 level->num_yamyam_contents = num_contents;
825 for (i = 0; i < num_contents; i++)
826 for (y = 0; y < 3; y++)
827 for (x = 0; x < 3; x++)
828 level->yamyam_content[i][x][y] = content_array[i][x][y];
830 else if (element == EL_BD_AMOEBA)
832 level->amoeba_content = content_array[0][0][0];
836 Error(ERR_WARN, "cannot load content for element '%d'", element);
842 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
848 int chunk_size_expected;
850 element = getMappedElement(getFile16BitBE(file));
851 if (!IS_ENVELOPE(element))
852 element = EL_ENVELOPE_1;
854 envelope_nr = element - EL_ENVELOPE_1;
856 envelope_len = getFile16BitBE(file);
858 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
859 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
861 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
863 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
864 if (chunk_size_expected != chunk_size)
866 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
867 return chunk_size_expected;
870 for (i = 0; i < envelope_len; i++)
871 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
876 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
878 int num_changed_custom_elements = getFile16BitBE(file);
879 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
882 if (chunk_size_expected != chunk_size)
884 ReadUnusedBytesFromFile(file, chunk_size - 2);
885 return chunk_size_expected;
888 for (i = 0; i < num_changed_custom_elements; i++)
890 int element = getFile16BitBE(file);
891 int properties = getFile32BitBE(file);
893 if (IS_CUSTOM_ELEMENT(element))
894 Properties[element][EP_BITFIELD_BASE] = properties;
896 Error(ERR_WARN, "invalid custom element number %d", element);
902 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
904 int num_changed_custom_elements = getFile16BitBE(file);
905 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
908 if (chunk_size_expected != chunk_size)
910 ReadUnusedBytesFromFile(file, chunk_size - 2);
911 return chunk_size_expected;
914 for (i = 0; i < num_changed_custom_elements; i++)
916 int element = getFile16BitBE(file);
917 int custom_target_element = getFile16BitBE(file);
919 if (IS_CUSTOM_ELEMENT(element))
920 element_info[element].change->target_element = custom_target_element;
922 Error(ERR_WARN, "invalid custom element number %d", element);
928 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
930 int num_changed_custom_elements = getFile16BitBE(file);
931 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
934 if (chunk_size_expected != chunk_size)
936 ReadUnusedBytesFromFile(file, chunk_size - 2);
937 return chunk_size_expected;
940 for (i = 0; i < num_changed_custom_elements; i++)
942 int element = getFile16BitBE(file);
944 if (!IS_CUSTOM_ELEMENT(element))
946 Error(ERR_WARN, "invalid custom element number %d", element);
948 element = EL_INTERNAL_DUMMY;
951 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
952 element_info[element].description[j] = getFile8Bit(file);
953 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
955 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
957 /* some free bytes for future properties and padding */
958 ReadUnusedBytesFromFile(file, 7);
960 element_info[element].use_gfx_element = getFile8Bit(file);
961 element_info[element].gfx_element =
962 getMappedElement(getFile16BitBE(file));
964 element_info[element].collect_score = getFile8Bit(file);
965 element_info[element].collect_count = getFile8Bit(file);
967 element_info[element].push_delay_fixed = getFile16BitBE(file);
968 element_info[element].push_delay_random = getFile16BitBE(file);
969 element_info[element].move_delay_fixed = getFile16BitBE(file);
970 element_info[element].move_delay_random = getFile16BitBE(file);
972 element_info[element].move_pattern = getFile16BitBE(file);
973 element_info[element].move_direction_initial = getFile8Bit(file);
974 element_info[element].move_stepsize = getFile8Bit(file);
976 for (y = 0; y < 3; y++)
977 for (x = 0; x < 3; x++)
978 element_info[element].content[x][y] =
979 getMappedElement(getFile16BitBE(file));
981 element_info[element].change->events = getFile32BitBE(file);
983 element_info[element].change->target_element =
984 getMappedElement(getFile16BitBE(file));
986 element_info[element].change->delay_fixed = getFile16BitBE(file);
987 element_info[element].change->delay_random = getFile16BitBE(file);
988 element_info[element].change->delay_frames = getFile16BitBE(file);
990 element_info[element].change->trigger_element =
991 getMappedElement(getFile16BitBE(file));
993 element_info[element].change->explode = getFile8Bit(file);
994 element_info[element].change->use_target_content = getFile8Bit(file);
995 element_info[element].change->only_if_complete = getFile8Bit(file);
996 element_info[element].change->use_random_replace = getFile8Bit(file);
998 element_info[element].change->random_percentage = getFile8Bit(file);
999 element_info[element].change->replace_when = getFile8Bit(file);
1001 for (y = 0; y < 3; y++)
1002 for (x = 0; x < 3; x++)
1003 element_info[element].change->target_content[x][y] =
1004 getMappedElement(getFile16BitBE(file));
1006 element_info[element].slippery_type = getFile8Bit(file);
1008 /* some free bytes for future properties and padding */
1009 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
1011 /* mark that this custom element has been modified */
1012 element_info[element].modified_settings = TRUE;
1018 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
1020 struct ElementInfo *ei;
1021 int chunk_size_expected;
1025 element = getFile16BitBE(file);
1027 if (!IS_CUSTOM_ELEMENT(element))
1029 Error(ERR_WARN, "invalid custom element number %d", element);
1031 ReadUnusedBytesFromFile(file, chunk_size - 2);
1035 ei = &element_info[element];
1037 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1038 ei->description[i] = getFile8Bit(file);
1039 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1041 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
1042 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
1044 ei->num_change_pages = getFile8Bit(file);
1046 /* some free bytes for future base property values and padding */
1047 ReadUnusedBytesFromFile(file, 5);
1049 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
1050 if (chunk_size_expected != chunk_size)
1052 ReadUnusedBytesFromFile(file, chunk_size - 48);
1053 return chunk_size_expected;
1056 /* read custom property values */
1058 ei->use_gfx_element = getFile8Bit(file);
1059 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1061 ei->collect_score = getFile8Bit(file);
1062 ei->collect_count = getFile8Bit(file);
1064 ei->drop_delay_fixed = getFile8Bit(file);
1065 ei->push_delay_fixed = getFile8Bit(file);
1066 ei->drop_delay_random = getFile8Bit(file);
1067 ei->push_delay_random = getFile8Bit(file);
1068 ei->move_delay_fixed = getFile16BitBE(file);
1069 ei->move_delay_random = getFile16BitBE(file);
1071 /* bits 0 - 15 of "move_pattern" ... */
1072 ei->move_pattern = getFile16BitBE(file);
1073 ei->move_direction_initial = getFile8Bit(file);
1074 ei->move_stepsize = getFile8Bit(file);
1076 ei->slippery_type = getFile8Bit(file);
1078 for (y = 0; y < 3; y++)
1079 for (x = 0; x < 3; x++)
1080 ei->content[x][y] = getMappedElement(getFile16BitBE(file));
1082 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
1083 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
1084 ei->move_leave_type = getFile8Bit(file);
1086 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
1087 ei->move_pattern |= (getFile16BitBE(file) << 16);
1089 ei->access_direction = getFile8Bit(file);
1091 ei->explosion_delay = getFile8Bit(file);
1092 ei->ignition_delay = getFile8Bit(file);
1093 ei->explosion_type = getFile8Bit(file);
1095 /* some free bytes for future custom property values and padding */
1096 ReadUnusedBytesFromFile(file, 1);
1098 /* read change property values */
1100 setElementChangePages(ei, ei->num_change_pages);
1102 for (i = 0; i < ei->num_change_pages; i++)
1104 struct ElementChangeInfo *change = &ei->change_page[i];
1106 /* always start with reliable default values */
1107 setElementChangeInfoToDefaults(change);
1109 change->events = getFile32BitBE(file);
1111 change->target_element = getMappedElement(getFile16BitBE(file));
1113 change->delay_fixed = getFile16BitBE(file);
1114 change->delay_random = getFile16BitBE(file);
1115 change->delay_frames = getFile16BitBE(file);
1117 change->trigger_element = getMappedElement(getFile16BitBE(file));
1119 change->explode = getFile8Bit(file);
1120 change->use_target_content = getFile8Bit(file);
1121 change->only_if_complete = getFile8Bit(file);
1122 change->use_random_replace = getFile8Bit(file);
1124 change->random_percentage = getFile8Bit(file);
1125 change->replace_when = getFile8Bit(file);
1127 for (y = 0; y < 3; y++)
1128 for (x = 0; x < 3; x++)
1129 change->target_content[x][y] = getMappedElement(getFile16BitBE(file));
1131 change->can_change = getFile8Bit(file);
1133 change->trigger_side = getFile8Bit(file);
1136 change->trigger_player = getFile8Bit(file);
1137 change->trigger_page = getFile8Bit(file);
1139 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
1140 CH_PAGE_ANY : (1 << change->trigger_page));
1142 /* some free bytes for future change property values and padding */
1143 ReadUnusedBytesFromFile(file, 6);
1147 /* some free bytes for future change property values and padding */
1148 ReadUnusedBytesFromFile(file, 8);
1152 /* mark this custom element as modified */
1153 ei->modified_settings = TRUE;
1158 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
1160 struct ElementInfo *ei;
1161 struct ElementGroupInfo *group;
1165 element = getFile16BitBE(file);
1167 if (!IS_GROUP_ELEMENT(element))
1169 Error(ERR_WARN, "invalid group element number %d", element);
1171 ReadUnusedBytesFromFile(file, chunk_size - 2);
1175 ei = &element_info[element];
1177 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1178 ei->description[i] = getFile8Bit(file);
1179 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1181 group = element_info[element].group;
1183 group->num_elements = getFile8Bit(file);
1185 ei->use_gfx_element = getFile8Bit(file);
1186 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1188 group->choice_mode = getFile8Bit(file);
1190 /* some free bytes for future values and padding */
1191 ReadUnusedBytesFromFile(file, 3);
1193 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
1194 group->element[i] = getMappedElement(getFile16BitBE(file));
1196 /* mark this group element as modified */
1197 element_info[element].modified_settings = TRUE;
1202 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
1203 struct LevelFileInfo *level_file_info)
1205 char *filename = level_file_info->filename;
1206 char cookie[MAX_LINE_LEN];
1207 char chunk_name[CHUNK_ID_LEN + 1];
1211 if (!(file = fopen(filename, MODE_READ)))
1213 level->no_valid_file = TRUE;
1215 if (level != &level_template)
1216 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1221 getFileChunkBE(file, chunk_name, NULL);
1222 if (strcmp(chunk_name, "RND1") == 0)
1224 getFile32BitBE(file); /* not used */
1226 getFileChunkBE(file, chunk_name, NULL);
1227 if (strcmp(chunk_name, "CAVE") != 0)
1229 level->no_valid_file = TRUE;
1231 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1236 else /* check for pre-2.0 file format with cookie string */
1238 strcpy(cookie, chunk_name);
1239 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1240 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1241 cookie[strlen(cookie) - 1] = '\0';
1243 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1245 level->no_valid_file = TRUE;
1247 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1252 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1254 level->no_valid_file = TRUE;
1256 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1261 /* pre-2.0 level files have no game version, so use file version here */
1262 level->game_version = level->file_version;
1265 if (level->file_version < FILE_VERSION_1_2)
1267 /* level files from versions before 1.2.0 without chunk structure */
1268 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1269 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1277 int (*loader)(FILE *, int, struct LevelInfo *);
1281 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1282 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1283 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1284 { "BODY", -1, LoadLevel_BODY },
1285 { "CONT", -1, LoadLevel_CONT },
1286 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1287 { "CNT3", -1, LoadLevel_CNT3 },
1288 { "CUS1", -1, LoadLevel_CUS1 },
1289 { "CUS2", -1, LoadLevel_CUS2 },
1290 { "CUS3", -1, LoadLevel_CUS3 },
1291 { "CUS4", -1, LoadLevel_CUS4 },
1292 { "GRP1", -1, LoadLevel_GRP1 },
1296 while (getFileChunkBE(file, chunk_name, &chunk_size))
1300 while (chunk_info[i].name != NULL &&
1301 strcmp(chunk_name, chunk_info[i].name) != 0)
1304 if (chunk_info[i].name == NULL)
1306 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1307 chunk_name, filename);
1308 ReadUnusedBytesFromFile(file, chunk_size);
1310 else if (chunk_info[i].size != -1 &&
1311 chunk_info[i].size != chunk_size)
1313 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1314 chunk_size, chunk_name, filename);
1315 ReadUnusedBytesFromFile(file, chunk_size);
1319 /* call function to load this level chunk */
1320 int chunk_size_expected =
1321 (chunk_info[i].loader)(file, chunk_size, level);
1323 /* the size of some chunks cannot be checked before reading other
1324 chunks first (like "HEAD" and "BODY") that contain some header
1325 information, so check them here */
1326 if (chunk_size_expected != chunk_size)
1328 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1329 chunk_size, chunk_name, filename);
1338 /* ------------------------------------------------------------------------- */
1339 /* functions for loading EM level */
1340 /* ------------------------------------------------------------------------- */
1342 static int map_em_element_yam(int element)
1346 case 0x00: return EL_EMPTY;
1347 case 0x01: return EL_EMERALD;
1348 case 0x02: return EL_DIAMOND;
1349 case 0x03: return EL_ROCK;
1350 case 0x04: return EL_ROBOT;
1351 case 0x05: return EL_SPACESHIP_UP;
1352 case 0x06: return EL_BOMB;
1353 case 0x07: return EL_BUG_UP;
1354 case 0x08: return EL_AMOEBA_DROP;
1355 case 0x09: return EL_NUT;
1356 case 0x0a: return EL_YAMYAM;
1357 case 0x0b: return EL_QUICKSAND_FULL;
1358 case 0x0c: return EL_SAND;
1359 case 0x0d: return EL_WALL_SLIPPERY;
1360 case 0x0e: return EL_STEELWALL;
1361 case 0x0f: return EL_WALL;
1362 case 0x10: return EL_EM_KEY_1;
1363 case 0x11: return EL_EM_KEY_2;
1364 case 0x12: return EL_EM_KEY_4;
1365 case 0x13: return EL_EM_KEY_3;
1366 case 0x14: return EL_MAGIC_WALL;
1367 case 0x15: return EL_ROBOT_WHEEL;
1368 case 0x16: return EL_DYNAMITE;
1370 case 0x17: return EL_EM_KEY_1; /* EMC */
1371 case 0x18: return EL_BUG_UP; /* EMC */
1372 case 0x1a: return EL_DIAMOND; /* EMC */
1373 case 0x1b: return EL_EMERALD; /* EMC */
1374 case 0x25: return EL_NUT; /* EMC */
1375 case 0x80: return EL_EMPTY; /* EMC */
1376 case 0x85: return EL_EM_KEY_1; /* EMC */
1377 case 0x86: return EL_EM_KEY_2; /* EMC */
1378 case 0x87: return EL_EM_KEY_4; /* EMC */
1379 case 0x88: return EL_EM_KEY_3; /* EMC */
1380 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1381 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1382 case 0xaf: return EL_DYNAMITE; /* EMC */
1383 case 0xbd: return EL_SAND; /* EMC */
1386 Error(ERR_WARN, "invalid level element %d", element);
1391 static int map_em_element_field(int element)
1393 if (element >= 0xc8 && element <= 0xe1)
1394 return EL_CHAR_A + (element - 0xc8);
1395 else if (element >= 0xe2 && element <= 0xeb)
1396 return EL_CHAR_0 + (element - 0xe2);
1400 case 0x00: return EL_ROCK;
1401 case 0x01: return EL_ROCK; /* EMC */
1402 case 0x02: return EL_DIAMOND;
1403 case 0x03: return EL_DIAMOND;
1404 case 0x04: return EL_ROBOT;
1405 case 0x05: return EL_ROBOT; /* EMC */
1406 case 0x06: return EL_EMPTY_SPACE; /* EMC */
1407 case 0x07: return EL_EMPTY_SPACE; /* EMC */
1408 case 0x08: return EL_SPACESHIP_UP;
1409 case 0x09: return EL_SPACESHIP_RIGHT;
1410 case 0x0a: return EL_SPACESHIP_DOWN;
1411 case 0x0b: return EL_SPACESHIP_LEFT;
1412 case 0x0c: return EL_SPACESHIP_UP;
1413 case 0x0d: return EL_SPACESHIP_RIGHT;
1414 case 0x0e: return EL_SPACESHIP_DOWN;
1415 case 0x0f: return EL_SPACESHIP_LEFT;
1417 case 0x10: return EL_BOMB;
1418 case 0x11: return EL_BOMB; /* EMC */
1419 case 0x12: return EL_EMERALD;
1420 case 0x13: return EL_EMERALD;
1421 case 0x14: return EL_BUG_UP;
1422 case 0x15: return EL_BUG_RIGHT;
1423 case 0x16: return EL_BUG_DOWN;
1424 case 0x17: return EL_BUG_LEFT;
1425 case 0x18: return EL_BUG_UP;
1426 case 0x19: return EL_BUG_RIGHT;
1427 case 0x1a: return EL_BUG_DOWN;
1428 case 0x1b: return EL_BUG_LEFT;
1429 case 0x1c: return EL_AMOEBA_DROP;
1430 case 0x1d: return EL_AMOEBA_DROP; /* EMC */
1431 case 0x1e: return EL_AMOEBA_DROP; /* EMC */
1432 case 0x1f: return EL_AMOEBA_DROP; /* EMC */
1434 case 0x20: return EL_ROCK;
1435 case 0x21: return EL_BOMB; /* EMC */
1436 case 0x22: return EL_DIAMOND; /* EMC */
1437 case 0x23: return EL_EMERALD; /* EMC */
1438 case 0x24: return EL_MAGIC_WALL;
1439 case 0x25: return EL_NUT;
1440 case 0x26: return EL_NUT; /* EMC */
1441 case 0x27: return EL_NUT; /* EMC */
1443 /* looks like magic wheel, but is _always_ activated */
1444 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1446 case 0x29: return EL_YAMYAM; /* up */
1447 case 0x2a: return EL_YAMYAM; /* down */
1448 case 0x2b: return EL_YAMYAM; /* left */ /* EMC */
1449 case 0x2c: return EL_YAMYAM; /* right */ /* EMC */
1450 case 0x2d: return EL_QUICKSAND_FULL;
1451 case 0x2e: return EL_EMPTY_SPACE; /* EMC */
1452 case 0x2f: return EL_EMPTY_SPACE; /* EMC */
1454 case 0x30: return EL_EMPTY_SPACE; /* EMC */
1455 case 0x31: return EL_SAND; /* EMC */
1456 case 0x32: return EL_SAND; /* EMC */
1457 case 0x33: return EL_SAND; /* EMC */
1458 case 0x34: return EL_QUICKSAND_FULL; /* EMC */
1459 case 0x35: return EL_QUICKSAND_FULL; /* EMC */
1460 case 0x36: return EL_QUICKSAND_FULL; /* EMC */
1461 case 0x37: return EL_SAND; /* EMC */
1462 case 0x38: return EL_ROCK; /* EMC */
1463 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1464 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1465 case 0x3b: return EL_DYNAMITE_ACTIVE; /* 1 */
1466 case 0x3c: return EL_DYNAMITE_ACTIVE; /* 2 */
1467 case 0x3d: return EL_DYNAMITE_ACTIVE; /* 3 */
1468 case 0x3e: return EL_DYNAMITE_ACTIVE; /* 4 */
1469 case 0x3f: return EL_ACID_POOL_BOTTOM;
1471 case 0x40: return EL_EXIT_OPEN; /* 1 */
1472 case 0x41: return EL_EXIT_OPEN; /* 2 */
1473 case 0x42: return EL_EXIT_OPEN; /* 3 */
1474 case 0x43: return EL_BALLOON; /* EMC */
1475 case 0x44: return EL_UNKNOWN; /* EMC ("plant") */
1476 case 0x45: return EL_SPRING; /* EMC */
1477 case 0x46: return EL_SPRING; /* falling */ /* EMC */
1478 case 0x47: return EL_SPRING; /* left */ /* EMC */
1479 case 0x48: return EL_SPRING; /* right */ /* EMC */
1480 case 0x49: return EL_UNKNOWN; /* EMC ("ball 1") */
1481 case 0x4a: return EL_UNKNOWN; /* EMC ("ball 2") */
1482 case 0x4b: return EL_UNKNOWN; /* EMC ("android") */
1483 case 0x4c: return EL_EMPTY_SPACE; /* EMC */
1484 case 0x4d: return EL_UNKNOWN; /* EMC ("android") */
1485 case 0x4e: return EL_INVISIBLE_WALL; /* EMC (? "android") */
1486 case 0x4f: return EL_UNKNOWN; /* EMC ("android") */
1488 case 0x50: return EL_UNKNOWN; /* EMC ("android") */
1489 case 0x51: return EL_UNKNOWN; /* EMC ("android") */
1490 case 0x52: return EL_UNKNOWN; /* EMC ("android") */
1491 case 0x53: return EL_UNKNOWN; /* EMC ("android") */
1492 case 0x54: return EL_UNKNOWN; /* EMC ("android") */
1493 case 0x55: return EL_EMPTY_SPACE; /* EMC */
1494 case 0x56: return EL_EMPTY_SPACE; /* EMC */
1495 case 0x57: return EL_EMPTY_SPACE; /* EMC */
1496 case 0x58: return EL_EMPTY_SPACE; /* EMC */
1497 case 0x59: return EL_EMPTY_SPACE; /* EMC */
1498 case 0x5a: return EL_EMPTY_SPACE; /* EMC */
1499 case 0x5b: return EL_EMPTY_SPACE; /* EMC */
1500 case 0x5c: return EL_EMPTY_SPACE; /* EMC */
1501 case 0x5d: return EL_EMPTY_SPACE; /* EMC */
1502 case 0x5e: return EL_EMPTY_SPACE; /* EMC */
1503 case 0x5f: return EL_EMPTY_SPACE; /* EMC */
1505 case 0x60: return EL_EMPTY_SPACE; /* EMC */
1506 case 0x61: return EL_EMPTY_SPACE; /* EMC */
1507 case 0x62: return EL_EMPTY_SPACE; /* EMC */
1508 case 0x63: return EL_SPRING; /* left */ /* EMC */
1509 case 0x64: return EL_SPRING; /* right */ /* EMC */
1510 case 0x65: return EL_ACID; /* 1 */ /* EMC */
1511 case 0x66: return EL_ACID; /* 2 */ /* EMC */
1512 case 0x67: return EL_ACID; /* 3 */ /* EMC */
1513 case 0x68: return EL_ACID; /* 4 */ /* EMC */
1514 case 0x69: return EL_ACID; /* 5 */ /* EMC */
1515 case 0x6a: return EL_ACID; /* 6 */ /* EMC */
1516 case 0x6b: return EL_ACID; /* 7 */ /* EMC */
1517 case 0x6c: return EL_ACID; /* 8 */ /* EMC */
1518 case 0x6d: return EL_EMPTY_SPACE; /* EMC */
1519 case 0x6e: return EL_EMPTY_SPACE; /* EMC */
1520 case 0x6f: return EL_EMPTY_SPACE; /* EMC */
1522 case 0x70: return EL_EMPTY_SPACE; /* EMC */
1523 case 0x71: return EL_EMPTY_SPACE; /* EMC */
1524 case 0x72: return EL_NUT; /* left */ /* EMC */
1525 case 0x73: return EL_SAND; /* EMC (? "nut") */
1526 case 0x74: return EL_STEELWALL;
1527 case 0x75: return EL_EMPTY_SPACE; /* EMC */
1528 case 0x76: return EL_EMPTY_SPACE; /* EMC */
1529 case 0x77: return EL_BOMB; /* left */ /* EMC */
1530 case 0x78: return EL_BOMB; /* right */ /* EMC */
1531 case 0x79: return EL_ROCK; /* left */ /* EMC */
1532 case 0x7a: return EL_ROCK; /* right */ /* EMC */
1533 case 0x7b: return EL_ACID; /* (? EMC "blank") */
1534 case 0x7c: return EL_EMPTY_SPACE; /* EMC */
1535 case 0x7d: return EL_EMPTY_SPACE; /* EMC */
1536 case 0x7e: return EL_EMPTY_SPACE; /* EMC */
1537 case 0x7f: return EL_EMPTY_SPACE; /* EMC */
1539 case 0x80: return EL_EMPTY;
1540 case 0x81: return EL_WALL_SLIPPERY;
1541 case 0x82: return EL_SAND;
1542 case 0x83: return EL_STEELWALL;
1543 case 0x84: return EL_WALL;
1544 case 0x85: return EL_EM_KEY_1;
1545 case 0x86: return EL_EM_KEY_2;
1546 case 0x87: return EL_EM_KEY_4;
1547 case 0x88: return EL_EM_KEY_3;
1548 case 0x89: return EL_EM_GATE_1;
1549 case 0x8a: return EL_EM_GATE_2;
1550 case 0x8b: return EL_EM_GATE_4;
1551 case 0x8c: return EL_EM_GATE_3;
1552 case 0x8d: return EL_INVISIBLE_WALL; /* EMC (? "dripper") */
1553 case 0x8e: return EL_EM_GATE_1_GRAY;
1554 case 0x8f: return EL_EM_GATE_2_GRAY;
1556 case 0x90: return EL_EM_GATE_4_GRAY;
1557 case 0x91: return EL_EM_GATE_3_GRAY;
1558 case 0x92: return EL_MAGIC_WALL;
1559 case 0x93: return EL_ROBOT_WHEEL;
1560 case 0x94: return EL_QUICKSAND_EMPTY; /* (? EMC "sand") */
1561 case 0x95: return EL_ACID_POOL_TOPLEFT;
1562 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1563 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1564 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1565 case 0x99: return EL_ACID; /* (? EMC "fake blank") */
1566 case 0x9a: return EL_AMOEBA_DEAD; /* 1 */
1567 case 0x9b: return EL_AMOEBA_DEAD; /* 2 */
1568 case 0x9c: return EL_AMOEBA_DEAD; /* 3 */
1569 case 0x9d: return EL_AMOEBA_DEAD; /* 4 */
1570 case 0x9e: return EL_EXIT_CLOSED;
1571 case 0x9f: return EL_CHAR_LESS; /* arrow left */
1573 /* looks like normal sand, but behaves like wall */
1574 case 0xa0: return EL_UNKNOWN; /* EMC ("fake grass") */
1575 case 0xa1: return EL_UNKNOWN; /* EMC ("lenses") */
1576 case 0xa2: return EL_UNKNOWN; /* EMC ("magnify") */
1577 case 0xa3: return EL_UNKNOWN; /* EMC ("fake blank") */
1578 case 0xa4: return EL_UNKNOWN; /* EMC ("fake grass") */
1579 case 0xa5: return EL_UNKNOWN; /* EMC ("switch") */
1580 case 0xa6: return EL_UNKNOWN; /* EMC ("switch") */
1581 case 0xa7: return EL_EMPTY_SPACE; /* EMC */
1582 case 0xa8: return EL_EMC_WALL_1; /* EMC ("decor 8") */
1583 case 0xa9: return EL_EMC_WALL_2; /* EMC ("decor 9") */
1584 case 0xaa: return EL_EMC_WALL_3; /* EMC ("decor 10") */
1585 case 0xab: return EL_EMC_WALL_7; /* EMC ("decor 5") */
1586 case 0xac: return EL_CHAR_COMMA; /* EMC */
1587 case 0xad: return EL_CHAR_QUOTEDBL; /* EMC */
1588 case 0xae: return EL_CHAR_MINUS; /* EMC */
1589 case 0xaf: return EL_DYNAMITE;
1591 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC ("steel 3") */
1592 case 0xb1: return EL_EMC_WALL_8; /* EMC ("decor 6") */
1593 case 0xb2: return EL_UNKNOWN; /* EMC ("decor 7") */
1594 case 0xb3: return EL_STEELWALL; /* 2 */ /* EMC */
1595 case 0xb4: return EL_WALL_SLIPPERY; /* 2 */ /* EMC */
1596 case 0xb5: return EL_EMC_WALL_6; /* EMC ("decor 2") */
1597 case 0xb6: return EL_EMC_WALL_5; /* EMC ("decor 4") */
1598 case 0xb7: return EL_EMC_WALL_4; /* EMC ("decor 3") */
1599 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
1600 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
1601 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
1602 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
1603 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
1604 case 0xbd: return EL_SAND; /* EMC ("dirt") */
1605 case 0xbe: return EL_UNKNOWN; /* EMC ("plant") */
1606 case 0xbf: return EL_UNKNOWN; /* EMC ("key 5") */
1608 case 0xc0: return EL_UNKNOWN; /* EMC ("key 6") */
1609 case 0xc1: return EL_UNKNOWN; /* EMC ("key 7") */
1610 case 0xc2: return EL_UNKNOWN; /* EMC ("key 8") */
1611 case 0xc3: return EL_UNKNOWN; /* EMC ("door 5") */
1612 case 0xc4: return EL_UNKNOWN; /* EMC ("door 6") */
1613 case 0xc5: return EL_UNKNOWN; /* EMC ("door 7") */
1614 case 0xc6: return EL_UNKNOWN; /* EMC ("door 8") */
1615 case 0xc7: return EL_UNKNOWN; /* EMC ("bumper") */
1617 /* characters: see above */
1619 case 0xec: return EL_CHAR_PERIOD;
1620 case 0xed: return EL_CHAR_EXCLAM;
1621 case 0xee: return EL_CHAR_COLON;
1622 case 0xef: return EL_CHAR_QUESTION;
1624 case 0xf0: return EL_CHAR_GREATER; /* arrow right */
1625 case 0xf1: return EL_CHAR_COPYRIGHT; /* EMC: "decor 1" */
1626 case 0xf2: return EL_UNKNOWN; /* EMC ("fake door 5") */
1627 case 0xf3: return EL_UNKNOWN; /* EMC ("fake door 6") */
1628 case 0xf4: return EL_UNKNOWN; /* EMC ("fake door 7") */
1629 case 0xf5: return EL_UNKNOWN; /* EMC ("fake door 8") */
1630 case 0xf6: return EL_EMPTY_SPACE; /* EMC */
1631 case 0xf7: return EL_EMPTY_SPACE; /* EMC */
1633 case 0xf8: return EL_EMPTY_SPACE; /* EMC */
1634 case 0xf9: return EL_EMPTY_SPACE; /* EMC */
1635 case 0xfa: return EL_EMPTY_SPACE; /* EMC */
1636 case 0xfb: return EL_EMPTY_SPACE; /* EMC */
1637 case 0xfc: return EL_EMPTY_SPACE; /* EMC */
1638 case 0xfd: return EL_EMPTY_SPACE; /* EMC */
1640 case 0xfe: return EL_PLAYER_1; /* EMC: "blank" */
1641 case 0xff: return EL_PLAYER_2; /* EMC: "blank" */
1644 /* should never happen (all 8-bit value cases should be handled) */
1645 Error(ERR_WARN, "invalid level element %d", element);
1650 #define EM_LEVEL_SIZE 2106
1651 #define EM_LEVEL_XSIZE 64
1652 #define EM_LEVEL_YSIZE 32
1654 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1655 struct LevelFileInfo *level_file_info)
1657 char *filename = level_file_info->filename;
1659 unsigned char leveldata[EM_LEVEL_SIZE];
1660 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
1661 unsigned char code0 = 0x65;
1662 unsigned char code1 = 0x11;
1663 boolean level_is_crypted = FALSE;
1664 int nr = level_file_info->nr;
1667 if (!(file = fopen(filename, MODE_READ)))
1669 level->no_valid_file = TRUE;
1671 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1676 for(i = 0; i < EM_LEVEL_SIZE; i++)
1677 leveldata[i] = fgetc(file);
1681 /* check if level data is crypted by testing against known starting bytes
1682 of the few existing crypted level files (from Emerald Mine 1 + 2) */
1684 if ((leveldata[0] == 0xf1 ||
1685 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1687 level_is_crypted = TRUE;
1689 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
1690 leveldata[0] = 0xf1;
1693 if (level_is_crypted) /* decode crypted level data */
1695 for(i = 0; i < EM_LEVEL_SIZE; i++)
1697 leveldata[i] ^= code0;
1698 leveldata[i] -= code1;
1700 code0 = (code0 + 7) & 0xff;
1704 level->fieldx = EM_LEVEL_XSIZE;
1705 level->fieldy = EM_LEVEL_YSIZE;
1707 level->time = header[46] * 10;
1708 level->gems_needed = header[47];
1710 /* The original Emerald Mine levels have their level number stored
1711 at the second byte of the level file...
1712 Do not trust this information at other level files, e.g. EMC,
1713 but correct it anyway (normally the first row is completely
1714 steel wall, so the correction does not hurt anyway). */
1716 if (leveldata[1] == nr)
1717 leveldata[1] = leveldata[2]; /* correct level number field */
1719 sprintf(level->name, "Level %d", nr); /* set level name */
1721 level->score[SC_EMERALD] = header[36];
1722 level->score[SC_DIAMOND] = header[37];
1723 level->score[SC_ROBOT] = header[38];
1724 level->score[SC_SPACESHIP] = header[39];
1725 level->score[SC_BUG] = header[40];
1726 level->score[SC_YAMYAM] = header[41];
1727 level->score[SC_NUT] = header[42];
1728 level->score[SC_DYNAMITE] = header[43];
1729 level->score[SC_TIME_BONUS] = header[44];
1731 level->num_yamyam_contents = 4;
1733 for(i = 0; i < level->num_yamyam_contents; i++)
1734 for(y = 0; y < 3; y++)
1735 for(x = 0; x < 3; x++)
1736 level->yamyam_content[i][x][y] =
1737 map_em_element_yam(header[i * 9 + y * 3 + x]);
1739 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
1740 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
1741 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
1742 level->amoeba_content = EL_DIAMOND;
1744 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1746 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
1748 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1749 new_element = EL_AMOEBA_WET;
1751 level->field[x][y] = new_element;
1754 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
1755 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
1756 level->field[x][y] = EL_PLAYER_1;
1758 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
1759 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
1760 level->field[x][y] = EL_PLAYER_2;
1763 /* ------------------------------------------------------------------------- */
1764 /* functions for loading SP level */
1765 /* ------------------------------------------------------------------------- */
1767 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
1768 #define SP_LEVEL_SIZE 1536
1769 #define SP_LEVEL_XSIZE 60
1770 #define SP_LEVEL_YSIZE 24
1771 #define SP_LEVEL_NAME_LEN 23
1773 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
1776 int num_special_ports;
1779 /* for details of the Supaplex level format, see Herman Perk's Supaplex
1780 documentation file "SPFIX63.DOC" from his Supaplex "SpeedFix" package */
1782 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
1783 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1785 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1787 int element_old = fgetc(file);
1790 if (element_old <= 0x27)
1791 element_new = getMappedElement(EL_SP_START + element_old);
1792 else if (element_old == 0x28)
1793 element_new = EL_INVISIBLE_WALL;
1796 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
1797 Error(ERR_WARN, "invalid level element %d", element_old);
1799 element_new = EL_UNKNOWN;
1802 level->field[x][y] = element_new;
1806 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
1808 /* initial gravity: 1 == "on", anything else (0) == "off" */
1809 level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
1811 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
1813 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
1814 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
1815 level->name[i] = fgetc(file);
1816 level->name[SP_LEVEL_NAME_LEN] = '\0';
1818 /* initial "freeze zonks": 2 == "on", anything else (0, 1) == "off" */
1819 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
1821 /* number of infotrons needed; 0 means that Supaplex will count the total
1822 amount of infotrons in the level and use the low byte of that number
1823 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
1824 level->gems_needed = fgetc(file);
1826 /* number of special ("gravity") port entries below (maximum 10 allowed) */
1827 num_special_ports = fgetc(file);
1829 /* database of properties of up to 10 special ports (6 bytes per port) */
1830 for (i = 0; i < 10; i++)
1832 int port_location, port_x, port_y, port_element;
1835 /* high and low byte of the location of a special port; if (x, y) are the
1836 coordinates of a port in the field and (0, 0) is the top-left corner,
1837 the 16 bit value here calculates as 2 * (x + (y * 60)) (this is twice
1838 of what may be expected: Supaplex works with a game field in memory
1839 which is 2 bytes per tile) */
1840 port_location = getFile16BitBE(file);
1842 /* change gravity: 1 == "turn on", anything else (0) == "turn off" */
1843 gravity = fgetc(file);
1845 /* "freeze zonks": 2 == "turn on", anything else (0, 1) == "turn off" */
1846 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
1848 /* "freeze enemies": 1 == "turn on", anything else (0) == "turn off" */
1849 ReadUnusedBytesFromFile(file, 1); /* (not used by R'n'D engine) */
1851 ReadUnusedBytesFromFile(file, 1); /* (not used by Supaplex engine) */
1853 if (i >= num_special_ports)
1856 port_x = (port_location / 2) % SP_LEVEL_XSIZE;
1857 port_y = (port_location / 2) / SP_LEVEL_XSIZE;
1859 if (port_x < 0 || port_x >= SP_LEVEL_XSIZE ||
1860 port_y < 0 || port_y >= SP_LEVEL_YSIZE)
1862 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
1868 port_element = level->field[port_x][port_y];
1870 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
1871 port_element > EL_SP_GRAVITY_PORT_UP)
1873 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
1878 /* change previous (wrong) gravity inverting special port to either
1879 gravity enabling special port or gravity disabling special port */
1880 level->field[port_x][port_y] +=
1881 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
1882 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
1885 ReadUnusedBytesFromFile(file, 4); /* (not used by Supaplex engine) */
1887 /* change special gravity ports without database entries to normal ports */
1888 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1889 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1890 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
1891 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
1892 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
1894 /* auto-determine number of infotrons if it was stored as "0" -- see above */
1895 if (level->gems_needed == 0)
1897 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1898 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1899 if (level->field[x][y] == EL_SP_INFOTRON)
1900 level->gems_needed++;
1902 level->gems_needed &= 0xff; /* only use low byte -- see above */
1905 level->fieldx = SP_LEVEL_XSIZE;
1906 level->fieldy = SP_LEVEL_YSIZE;
1908 level->time = 0; /* no time limit */
1909 level->amoeba_speed = 0;
1910 level->time_magic_wall = 0;
1911 level->time_wheel = 0;
1912 level->amoeba_content = EL_EMPTY;
1914 for(i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1915 level->score[i] = 0; /* !!! CORRECT THIS !!! */
1917 /* there are no yamyams in supaplex levels */
1918 for(i = 0; i < level->num_yamyam_contents; i++)
1919 for(y = 0; y < 3; y++)
1920 for(x = 0; x < 3; x++)
1921 level->yamyam_content[i][x][y] = EL_EMPTY;
1924 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
1925 struct LevelFileInfo *level_file_info)
1927 char *filename = level_file_info->filename;
1929 int nr = level_file_info->nr - leveldir_current->first_level;
1931 char name_first, name_last;
1932 struct LevelInfo multipart_level;
1933 int multipart_xpos, multipart_ypos;
1934 boolean is_multipart_level;
1935 boolean is_first_part;
1936 boolean reading_multipart_level = FALSE;
1937 boolean use_empty_level = FALSE;
1939 if (!(file = fopen(filename, MODE_READ)))
1941 level->no_valid_file = TRUE;
1943 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1948 /* position file stream to the requested level inside the level package */
1949 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
1951 level->no_valid_file = TRUE;
1953 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
1958 /* there exist Supaplex level package files with multi-part levels which
1959 can be detected as follows: instead of leading and trailing dashes ('-')
1960 to pad the level name, they have leading and trailing numbers which are
1961 the x and y coordinations of the current part of the multi-part level;
1962 if there are '?' characters instead of numbers on the left or right side
1963 of the level name, the multi-part level consists of only horizontal or
1966 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
1968 LoadLevelFromFileStream_SP(file, level, l);
1970 /* check if this level is a part of a bigger multi-part level */
1972 name_first = level->name[0];
1973 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
1975 is_multipart_level =
1976 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
1977 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
1980 ((name_first == '?' || name_first == '1') &&
1981 (name_last == '?' || name_last == '1'));
1983 /* correct leading multipart level meta information in level name */
1984 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
1985 level->name[i] = '-';
1987 /* correct trailing multipart level meta information in level name */
1988 for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
1989 level->name[i] = '-';
1991 /* ---------- check for normal single level ---------- */
1993 if (!reading_multipart_level && !is_multipart_level)
1995 /* the current level is simply a normal single-part level, and we are
1996 not reading a multi-part level yet, so return the level as it is */
2001 /* ---------- check for empty level (unused multi-part) ---------- */
2003 if (!reading_multipart_level && is_multipart_level && !is_first_part)
2005 /* this is a part of a multi-part level, but not the first part
2006 (and we are not already reading parts of a multi-part level);
2007 in this case, use an empty level instead of the single part */
2009 use_empty_level = TRUE;
2014 /* ---------- check for finished multi-part level ---------- */
2016 if (reading_multipart_level &&
2017 (!is_multipart_level ||
2018 strcmp(level->name, multipart_level.name) != 0))
2020 /* we are already reading parts of a multi-part level, but this level is
2021 either not a multi-part level, or a part of a different multi-part
2022 level; in both cases, the multi-part level seems to be complete */
2027 /* ---------- here we have one part of a multi-part level ---------- */
2029 reading_multipart_level = TRUE;
2031 if (is_first_part) /* start with first part of new multi-part level */
2033 /* copy level info structure from first part */
2034 multipart_level = *level;
2036 /* clear playfield of new multi-part level */
2037 for (y = 0; y < MAX_LEV_FIELDY; y++)
2038 for (x = 0; x < MAX_LEV_FIELDX; x++)
2039 multipart_level.field[x][y] = EL_EMPTY;
2042 if (name_first == '?')
2044 if (name_last == '?')
2047 multipart_xpos = (int)(name_first - '0');
2048 multipart_ypos = (int)(name_last - '0');
2051 printf("----------> part (%d/%d) of multi-part level '%s'\n",
2052 multipart_xpos, multipart_ypos, multipart_level.name);
2055 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
2056 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
2058 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
2063 multipart_level.fieldx = MAX(multipart_level.fieldx,
2064 multipart_xpos * SP_LEVEL_XSIZE);
2065 multipart_level.fieldy = MAX(multipart_level.fieldy,
2066 multipart_ypos * SP_LEVEL_YSIZE);
2068 /* copy level part at the right position of multi-part level */
2069 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2071 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2073 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
2074 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
2076 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
2083 if (use_empty_level)
2085 setLevelInfoToDefaults(level);
2087 level->fieldx = SP_LEVEL_XSIZE;
2088 level->fieldy = SP_LEVEL_YSIZE;
2090 for (y = 0; y < SP_LEVEL_YSIZE; y++)
2091 for (x = 0; x < SP_LEVEL_XSIZE; x++)
2092 level->field[x][y] = EL_EMPTY;
2094 strcpy(level->name, "-------- EMPTY --------");
2096 Error(ERR_WARN, "single part of multi-part level -- using empty level");
2099 if (reading_multipart_level)
2100 *level = multipart_level;
2103 /* ------------------------------------------------------------------------- */
2104 /* functions for loading generic level */
2105 /* ------------------------------------------------------------------------- */
2107 void LoadLevelFromFileInfo(struct LevelInfo *level,
2108 struct LevelFileInfo *level_file_info)
2110 /* always start with reliable default values */
2111 setLevelInfoToDefaults(level);
2113 switch (level_file_info->type)
2115 case LEVEL_FILE_TYPE_RND:
2116 LoadLevelFromFileInfo_RND(level, level_file_info);
2119 case LEVEL_FILE_TYPE_EM:
2120 LoadLevelFromFileInfo_EM(level, level_file_info);
2123 case LEVEL_FILE_TYPE_SP:
2124 LoadLevelFromFileInfo_SP(level, level_file_info);
2128 LoadLevelFromFileInfo_RND(level, level_file_info);
2133 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
2135 static struct LevelFileInfo level_file_info;
2137 /* always start with reliable default values */
2138 setFileInfoToDefaults(&level_file_info);
2140 level_file_info.nr = 0; /* unknown level number */
2141 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
2142 level_file_info.filename = filename;
2144 LoadLevelFromFileInfo(level, &level_file_info);
2147 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
2149 if (leveldir_current == NULL) /* only when dumping level */
2153 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
2156 /* determine correct game engine version of current level */
2158 if (!leveldir_current->latest_engine)
2160 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
2161 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
2162 IS_LEVELCLASS_UNDEFINED(leveldir_current))
2166 printf("\n::: This level is private or contributed: '%s'\n", filename);
2170 printf("\n::: Use the stored game engine version for this level\n");
2173 /* For all levels which are not forced to use the latest game engine
2174 version (normally user contributed, private and undefined levels),
2175 use the version of the game engine the levels were created for.
2177 Since 2.0.1, the game engine version is now directly stored
2178 in the level file (chunk "VERS"), so there is no need anymore
2179 to set the game version from the file version (except for old,
2180 pre-2.0 levels, where the game version is still taken from the
2181 file format version used to store the level -- see above). */
2184 /* player was faster than enemies in 1.0.0 and before */
2185 if (level->file_version == FILE_VERSION_1_0)
2186 level->double_speed = TRUE;
2188 /* do some special adjustments to support older level versions */
2189 if (level->file_version == FILE_VERSION_1_0)
2191 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
2192 Error(ERR_WARN, "using high speed movement for player");
2194 /* player was faster than monsters in (pre-)1.0 levels */
2195 level->double_speed = TRUE;
2199 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
2200 if (level->game_version == VERSION_IDENT(2,0,1,0))
2201 level->em_slippery_gems = TRUE;
2203 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
2204 if (level->game_version < VERSION_IDENT(2,2,0,0))
2205 level->use_spring_bug = TRUE;
2207 /* only few elements were able to actively move into acid before 3.1.0 */
2208 if (level->game_version < VERSION_IDENT(3,1,0,0))
2212 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
2213 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
2215 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
2216 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
2217 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
2218 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
2220 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2221 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
2223 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2225 int element = EL_CUSTOM_START + i;
2226 struct ElementInfo *ei = &element_info[element];
2228 for (j = 0; j < ei->num_change_pages; j++)
2230 struct ElementChangeInfo *change = &ei->change_page[j];
2232 change->trigger_player = CH_PLAYER_ANY;
2233 change->trigger_page = CH_PAGE_ANY;
2238 #if 1 /* USE_NEW_BLOCK_STYLE */
2239 /* blocking the last field when moving was corrected in version 3.1.1 */
2240 if (level->game_version < VERSION_IDENT(3,1,1,0))
2243 printf("::: %d\n", level->block_last_field);
2246 /* even "not blocking" was blocking the last field for one frame */
2247 level->block_delay = (level->block_last_field ? 7 : 1);
2248 level->sp_block_delay = (level->sp_block_last_field ? 7 : 1);
2250 level->block_last_field = TRUE;
2251 level->sp_block_last_field = TRUE;
2255 else /* always use the latest game engine version */
2258 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
2259 leveldir_current->sort_priority, filename);
2263 printf("\n::: Use latest game engine version for this level.\n");
2266 /* For all levels which are forced to use the latest game engine version
2267 (normally all but user contributed, private and undefined levels), set
2268 the game engine version to the actual version; this allows for actual
2269 corrections in the game engine to take effect for existing, converted
2270 levels (from "classic" or other existing games) to make the emulation
2271 of the corresponding game more accurate, while (hopefully) not breaking
2272 existing levels created from other players. */
2275 printf("::: changing engine from %d to %d\n",
2276 level->game_version, GAME_VERSION_ACTUAL);
2279 level->game_version = GAME_VERSION_ACTUAL;
2281 /* Set special EM style gems behaviour: EM style gems slip down from
2282 normal, steel and growing wall. As this is a more fundamental change,
2283 it seems better to set the default behaviour to "off" (as it is more
2284 natural) and make it configurable in the level editor (as a property
2285 of gem style elements). Already existing converted levels (neither
2286 private nor contributed levels) are changed to the new behaviour. */
2288 if (level->file_version < FILE_VERSION_2_0)
2289 level->em_slippery_gems = TRUE;
2293 printf("::: => %d\n", level->game_version);
2297 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
2301 /* map custom element change events that have changed in newer versions
2302 (these following values were accidentally changed in version 3.0.1) */
2303 if (level->game_version <= VERSION_IDENT(3,0,0,0))
2305 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2307 int element = EL_CUSTOM_START + i;
2309 /* order of checking and copying events to be mapped is important */
2310 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
2312 if (HAS_CHANGE_EVENT(element, j - 2))
2314 SET_CHANGE_EVENT(element, j - 2, FALSE);
2315 SET_CHANGE_EVENT(element, j, TRUE);
2319 /* order of checking and copying events to be mapped is important */
2320 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
2322 if (HAS_CHANGE_EVENT(element, j - 1))
2324 SET_CHANGE_EVENT(element, j - 1, FALSE);
2325 SET_CHANGE_EVENT(element, j, TRUE);
2331 /* some custom element change events get mapped since version 3.0.3 */
2332 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2334 int element = EL_CUSTOM_START + i;
2336 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
2337 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
2339 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
2340 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
2342 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
2346 /* initialize "can_change" field for old levels with only one change page */
2347 if (level->game_version <= VERSION_IDENT(3,0,2,0))
2349 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2351 int element = EL_CUSTOM_START + i;
2353 if (CAN_CHANGE(element))
2354 element_info[element].change->can_change = TRUE;
2358 /* correct custom element values (for old levels without these options) */
2359 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2361 int element = EL_CUSTOM_START + i;
2362 struct ElementInfo *ei = &element_info[element];
2364 if (ei->access_direction == MV_NO_MOVING)
2365 ei->access_direction = MV_ALL_DIRECTIONS;
2367 for (j = 0; j < ei->num_change_pages; j++)
2369 struct ElementChangeInfo *change = &ei->change_page[j];
2371 if (change->trigger_side == CH_SIDE_NONE)
2372 change->trigger_side = CH_SIDE_ANY;
2376 /* initialize "can_explode" field for old levels which did not store this */
2377 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
2378 if (level->game_version <= VERSION_IDENT(3,1,0,0))
2380 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2382 int element = EL_CUSTOM_START + i;
2384 if (EXPLODES_1X1_OLD(element))
2385 element_info[element].explosion_type = EXPLODES_1X1;
2387 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
2388 EXPLODES_SMASHED(element) ||
2389 EXPLODES_IMPACT(element)));
2393 /* correct previously hard-coded move delay values for maze runner style */
2394 if (level->game_version < VERSION_IDENT(3,1,1,0))
2396 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2398 int element = EL_CUSTOM_START + i;
2400 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
2402 /* previously hard-coded and therefore ignored */
2403 element_info[element].move_delay_fixed = 9;
2404 element_info[element].move_delay_random = 0;
2410 /* set default push delay values (corrected since version 3.0.7-1) */
2411 if (level->game_version < VERSION_IDENT(3,0,7,1))
2413 game.default_push_delay_fixed = 2;
2414 game.default_push_delay_random = 8;
2418 game.default_push_delay_fixed = 8;
2419 game.default_push_delay_random = 8;
2422 /* set uninitialized push delay values of custom elements in older levels */
2423 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2425 int element = EL_CUSTOM_START + i;
2427 if (element_info[element].push_delay_fixed == -1)
2428 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2429 if (element_info[element].push_delay_random == -1)
2430 element_info[element].push_delay_random = game.default_push_delay_random;
2434 /* map elements that have changed in newer versions */
2435 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
2436 level->game_version);
2437 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2438 for (x = 0; x < 3; x++)
2439 for (y = 0; y < 3; y++)
2440 level->yamyam_content[i][x][y] =
2441 getMappedElementByVersion(level->yamyam_content[i][x][y],
2442 level->game_version);
2444 /* initialize element properties for level editor etc. */
2445 InitElementPropertiesEngine(level->game_version);
2448 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2452 /* map elements that have changed in newer versions */
2453 for (y = 0; y < level->fieldy; y++)
2455 for (x = 0; x < level->fieldx; x++)
2457 int element = level->field[x][y];
2460 element = getMappedElementByVersion(element, level->game_version);
2462 if (level->game_version <= VERSION_IDENT(2,2,0,0))
2464 /* map game font elements */
2465 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2466 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2467 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2468 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2471 if (level->game_version < VERSION_IDENT(3,0,0,0))
2473 /* map Supaplex gravity tube elements */
2474 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2475 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2476 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2477 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2482 level->field[x][y] = element;
2486 /* copy elements to runtime playfield array */
2487 for (x = 0; x < MAX_LEV_FIELDX; x++)
2488 for (y = 0; y < MAX_LEV_FIELDY; y++)
2489 Feld[x][y] = level->field[x][y];
2491 /* initialize level size variables for faster access */
2492 lev_fieldx = level->fieldx;
2493 lev_fieldy = level->fieldy;
2495 /* determine border element for this level */
2499 void LoadLevelTemplate(int nr)
2504 setLevelFileInfo(&level_template.file_info, nr);
2505 filename = level_template.file_info.filename;
2507 LoadLevelFromFileInfo(&level_template, &level_template.file_info);
2512 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2513 char *filename = level_file_info->filename;
2515 LoadLevelFromFileInfo(&level_template, level_file_info);
2517 char *filename = getDefaultLevelFilename(nr);
2519 LoadLevelFromFilename_RND(&level_template, filename);
2524 LoadLevel_InitVersion(&level_template, filename);
2525 LoadLevel_InitElements(&level_template, filename);
2527 LoadLevel_InitVersion(&level, filename);
2528 LoadLevel_InitElements(&level, filename);
2531 ActivateLevelTemplate();
2534 void LoadLevel(int nr)
2539 setLevelFileInfo(&level.file_info, nr);
2540 filename = level.file_info.filename;
2542 LoadLevelFromFileInfo(&level, &level.file_info);
2547 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2548 char *filename = level_file_info->filename;
2550 LoadLevelFromFileInfo(&level, level_file_info);
2552 char *filename = getLevelFilename(nr);
2554 LoadLevelFromFilename_RND(&level, filename);
2558 if (level.use_custom_template)
2559 LoadLevelTemplate(-1);
2562 LoadLevel_InitVersion(&level, filename);
2563 LoadLevel_InitElements(&level, filename);
2564 LoadLevel_InitPlayfield(&level, filename);
2566 LoadLevel_InitLevel(&level, filename);
2570 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2572 putFileVersion(file, level->file_version);
2573 putFileVersion(file, level->game_version);
2576 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2580 putFile8Bit(file, level->fieldx);
2581 putFile8Bit(file, level->fieldy);
2583 putFile16BitBE(file, level->time);
2584 putFile16BitBE(file, level->gems_needed);
2586 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2587 putFile8Bit(file, level->name[i]);
2589 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2590 putFile8Bit(file, level->score[i]);
2592 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2593 for (y = 0; y < 3; y++)
2594 for (x = 0; x < 3; x++)
2595 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
2596 level->yamyam_content[i][x][y]));
2597 putFile8Bit(file, level->amoeba_speed);
2598 putFile8Bit(file, level->time_magic_wall);
2599 putFile8Bit(file, level->time_wheel);
2600 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
2601 level->amoeba_content));
2602 putFile8Bit(file, (level->double_speed ? 1 : 0));
2603 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
2604 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
2605 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
2607 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
2609 putFile8Bit(file, (level->block_last_field ? 1 : 0));
2610 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
2611 putFile32BitBE(file, level->can_move_into_acid_bits);
2612 putFile8Bit(file, level->dont_collide_with_bits);
2614 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
2615 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
2617 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
2618 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
2619 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
2621 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
2624 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
2628 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2629 putFile8Bit(file, level->author[i]);
2632 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
2636 for (y = 0; y < level->fieldy; y++)
2637 for (x = 0; x < level->fieldx; x++)
2638 if (level->encoding_16bit_field)
2639 putFile16BitBE(file, level->field[x][y]);
2641 putFile8Bit(file, level->field[x][y]);
2645 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
2649 putFile8Bit(file, EL_YAMYAM);
2650 putFile8Bit(file, level->num_yamyam_contents);
2651 putFile8Bit(file, 0);
2652 putFile8Bit(file, 0);
2654 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2655 for (y = 0; y < 3; y++)
2656 for (x = 0; x < 3; x++)
2657 if (level->encoding_16bit_field)
2658 putFile16BitBE(file, level->yamyam_content[i][x][y]);
2660 putFile8Bit(file, level->yamyam_content[i][x][y]);
2664 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
2667 int num_contents, content_xsize, content_ysize;
2668 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2670 if (element == EL_YAMYAM)
2672 num_contents = level->num_yamyam_contents;
2676 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2677 for (y = 0; y < 3; y++)
2678 for (x = 0; x < 3; x++)
2679 content_array[i][x][y] = level->yamyam_content[i][x][y];
2681 else if (element == EL_BD_AMOEBA)
2687 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2688 for (y = 0; y < 3; y++)
2689 for (x = 0; x < 3; x++)
2690 content_array[i][x][y] = EL_EMPTY;
2691 content_array[0][0][0] = level->amoeba_content;
2695 /* chunk header already written -- write empty chunk data */
2696 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
2698 Error(ERR_WARN, "cannot save content for element '%d'", element);
2702 putFile16BitBE(file, element);
2703 putFile8Bit(file, num_contents);
2704 putFile8Bit(file, content_xsize);
2705 putFile8Bit(file, content_ysize);
2707 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2709 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2710 for (y = 0; y < 3; y++)
2711 for (x = 0; x < 3; x++)
2712 putFile16BitBE(file, content_array[i][x][y]);
2715 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
2718 int envelope_nr = element - EL_ENVELOPE_1;
2719 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
2721 putFile16BitBE(file, element);
2722 putFile16BitBE(file, envelope_len);
2723 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
2724 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
2726 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2728 for (i = 0; i < envelope_len; i++)
2729 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
2733 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
2734 int num_changed_custom_elements)
2738 putFile16BitBE(file, num_changed_custom_elements);
2740 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2742 int element = EL_CUSTOM_START + i;
2744 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
2746 if (check < num_changed_custom_elements)
2748 putFile16BitBE(file, element);
2749 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2756 if (check != num_changed_custom_elements) /* should not happen */
2757 Error(ERR_WARN, "inconsistent number of custom element properties");
2762 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
2763 int num_changed_custom_elements)
2767 putFile16BitBE(file, num_changed_custom_elements);
2769 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2771 int element = EL_CUSTOM_START + i;
2773 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
2775 if (check < num_changed_custom_elements)
2777 putFile16BitBE(file, element);
2778 putFile16BitBE(file, element_info[element].change->target_element);
2785 if (check != num_changed_custom_elements) /* should not happen */
2786 Error(ERR_WARN, "inconsistent number of custom target elements");
2791 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
2792 int num_changed_custom_elements)
2794 int i, j, x, y, check = 0;
2796 putFile16BitBE(file, num_changed_custom_elements);
2798 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2800 int element = EL_CUSTOM_START + i;
2802 if (element_info[element].modified_settings)
2804 if (check < num_changed_custom_elements)
2806 putFile16BitBE(file, element);
2808 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2809 putFile8Bit(file, element_info[element].description[j]);
2811 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2813 /* some free bytes for future properties and padding */
2814 WriteUnusedBytesToFile(file, 7);
2816 putFile8Bit(file, element_info[element].use_gfx_element);
2817 putFile16BitBE(file, element_info[element].gfx_element);
2819 putFile8Bit(file, element_info[element].collect_score);
2820 putFile8Bit(file, element_info[element].collect_count);
2822 putFile16BitBE(file, element_info[element].push_delay_fixed);
2823 putFile16BitBE(file, element_info[element].push_delay_random);
2824 putFile16BitBE(file, element_info[element].move_delay_fixed);
2825 putFile16BitBE(file, element_info[element].move_delay_random);
2827 putFile16BitBE(file, element_info[element].move_pattern);
2828 putFile8Bit(file, element_info[element].move_direction_initial);
2829 putFile8Bit(file, element_info[element].move_stepsize);
2831 for (y = 0; y < 3; y++)
2832 for (x = 0; x < 3; x++)
2833 putFile16BitBE(file, element_info[element].content[x][y]);
2835 putFile32BitBE(file, element_info[element].change->events);
2837 putFile16BitBE(file, element_info[element].change->target_element);
2839 putFile16BitBE(file, element_info[element].change->delay_fixed);
2840 putFile16BitBE(file, element_info[element].change->delay_random);
2841 putFile16BitBE(file, element_info[element].change->delay_frames);
2843 putFile16BitBE(file, element_info[element].change->trigger_element);
2845 putFile8Bit(file, element_info[element].change->explode);
2846 putFile8Bit(file, element_info[element].change->use_target_content);
2847 putFile8Bit(file, element_info[element].change->only_if_complete);
2848 putFile8Bit(file, element_info[element].change->use_random_replace);
2850 putFile8Bit(file, element_info[element].change->random_percentage);
2851 putFile8Bit(file, element_info[element].change->replace_when);
2853 for (y = 0; y < 3; y++)
2854 for (x = 0; x < 3; x++)
2855 putFile16BitBE(file, element_info[element].change->content[x][y]);
2857 putFile8Bit(file, element_info[element].slippery_type);
2859 /* some free bytes for future properties and padding */
2860 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
2867 if (check != num_changed_custom_elements) /* should not happen */
2868 Error(ERR_WARN, "inconsistent number of custom element properties");
2872 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2874 struct ElementInfo *ei = &element_info[element];
2877 putFile16BitBE(file, element);
2879 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2880 putFile8Bit(file, ei->description[i]);
2882 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2883 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
2885 putFile8Bit(file, ei->num_change_pages);
2887 /* some free bytes for future base property values and padding */
2888 WriteUnusedBytesToFile(file, 5);
2890 /* write custom property values */
2892 putFile8Bit(file, ei->use_gfx_element);
2893 putFile16BitBE(file, ei->gfx_element);
2895 putFile8Bit(file, ei->collect_score);
2896 putFile8Bit(file, ei->collect_count);
2898 putFile8Bit(file, ei->drop_delay_fixed);
2899 putFile8Bit(file, ei->push_delay_fixed);
2900 putFile8Bit(file, ei->drop_delay_random);
2901 putFile8Bit(file, ei->push_delay_random);
2902 putFile16BitBE(file, ei->move_delay_fixed);
2903 putFile16BitBE(file, ei->move_delay_random);
2905 /* bits 0 - 15 of "move_pattern" ... */
2906 putFile16BitBE(file, ei->move_pattern & 0xffff);
2907 putFile8Bit(file, ei->move_direction_initial);
2908 putFile8Bit(file, ei->move_stepsize);
2910 putFile8Bit(file, ei->slippery_type);
2912 for (y = 0; y < 3; y++)
2913 for (x = 0; x < 3; x++)
2914 putFile16BitBE(file, ei->content[x][y]);
2916 putFile16BitBE(file, ei->move_enter_element);
2917 putFile16BitBE(file, ei->move_leave_element);
2918 putFile8Bit(file, ei->move_leave_type);
2920 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2921 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
2923 putFile8Bit(file, ei->access_direction);
2925 putFile8Bit(file, ei->explosion_delay);
2926 putFile8Bit(file, ei->ignition_delay);
2927 putFile8Bit(file, ei->explosion_type);
2929 /* some free bytes for future custom property values and padding */
2930 WriteUnusedBytesToFile(file, 1);
2932 /* write change property values */
2934 for (i = 0; i < ei->num_change_pages; i++)
2936 struct ElementChangeInfo *change = &ei->change_page[i];
2938 putFile32BitBE(file, change->events);
2940 putFile16BitBE(file, change->target_element);
2942 putFile16BitBE(file, change->delay_fixed);
2943 putFile16BitBE(file, change->delay_random);
2944 putFile16BitBE(file, change->delay_frames);
2946 putFile16BitBE(file, change->trigger_element);
2948 putFile8Bit(file, change->explode);
2949 putFile8Bit(file, change->use_target_content);
2950 putFile8Bit(file, change->only_if_complete);
2951 putFile8Bit(file, change->use_random_replace);
2953 putFile8Bit(file, change->random_percentage);
2954 putFile8Bit(file, change->replace_when);
2956 for (y = 0; y < 3; y++)
2957 for (x = 0; x < 3; x++)
2958 putFile16BitBE(file, change->target_content[x][y]);
2960 putFile8Bit(file, change->can_change);
2962 putFile8Bit(file, change->trigger_side);
2965 putFile8Bit(file, change->trigger_player);
2966 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
2967 log_2(change->trigger_page)));
2969 /* some free bytes for future change property values and padding */
2970 WriteUnusedBytesToFile(file, 6);
2974 /* some free bytes for future change property values and padding */
2975 WriteUnusedBytesToFile(file, 8);
2980 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2982 struct ElementInfo *ei = &element_info[element];
2983 struct ElementGroupInfo *group = ei->group;
2986 putFile16BitBE(file, element);
2988 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2989 putFile8Bit(file, ei->description[i]);
2991 putFile8Bit(file, group->num_elements);
2993 putFile8Bit(file, ei->use_gfx_element);
2994 putFile16BitBE(file, ei->gfx_element);
2996 putFile8Bit(file, group->choice_mode);
2998 /* some free bytes for future values and padding */
2999 WriteUnusedBytesToFile(file, 3);
3001 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3002 putFile16BitBE(file, group->element[i]);
3005 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
3007 int body_chunk_size;
3011 if (!(file = fopen(filename, MODE_WRITE)))
3013 Error(ERR_WARN, "cannot save level file '%s'", filename);
3017 level->file_version = FILE_VERSION_ACTUAL;
3018 level->game_version = GAME_VERSION_ACTUAL;
3020 /* check level field for 16-bit elements */
3021 level->encoding_16bit_field = FALSE;
3022 for (y = 0; y < level->fieldy; y++)
3023 for (x = 0; x < level->fieldx; x++)
3024 if (level->field[x][y] > 255)
3025 level->encoding_16bit_field = TRUE;
3027 /* check yamyam content for 16-bit elements */
3028 level->encoding_16bit_yamyam = FALSE;
3029 for (i = 0; i < level->num_yamyam_contents; i++)
3030 for (y = 0; y < 3; y++)
3031 for (x = 0; x < 3; x++)
3032 if (level->yamyam_content[i][x][y] > 255)
3033 level->encoding_16bit_yamyam = TRUE;
3035 /* check amoeba content for 16-bit elements */
3036 level->encoding_16bit_amoeba = FALSE;
3037 if (level->amoeba_content > 255)
3038 level->encoding_16bit_amoeba = TRUE;
3040 /* calculate size of "BODY" chunk */
3042 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
3044 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3045 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
3047 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3048 SaveLevel_VERS(file, level);
3050 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
3051 SaveLevel_HEAD(file, level);
3053 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
3054 SaveLevel_AUTH(file, level);
3056 putFileChunkBE(file, "BODY", body_chunk_size);
3057 SaveLevel_BODY(file, level);
3059 if (level->encoding_16bit_yamyam ||
3060 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
3062 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3063 SaveLevel_CNT2(file, level, EL_YAMYAM);
3066 if (level->encoding_16bit_amoeba)
3068 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3069 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
3072 /* check for envelope content */
3073 for (i = 0; i < 4; i++)
3075 if (strlen(level->envelope_text[i]) > 0)
3077 int envelope_len = strlen(level->envelope_text[i]) + 1;
3079 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
3080 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
3084 /* check for non-default custom elements (unless using template level) */
3085 if (!level->use_custom_template)
3087 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3089 int element = EL_CUSTOM_START + i;
3091 if (element_info[element].modified_settings)
3093 int num_change_pages = element_info[element].num_change_pages;
3095 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
3096 SaveLevel_CUS4(file, level, element);
3101 /* check for non-default group elements (unless using template level) */
3102 if (!level->use_custom_template)
3104 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
3106 int element = EL_GROUP_START + i;
3108 if (element_info[element].modified_settings)
3110 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
3111 SaveLevel_GRP1(file, level, element);
3118 SetFilePermissions(filename, PERMS_PRIVATE);
3121 void SaveLevel(int nr)
3123 char *filename = getDefaultLevelFilename(nr);
3125 SaveLevelFromFilename(&level, filename);
3128 void SaveLevelTemplate()
3130 char *filename = getDefaultLevelFilename(-1);
3132 SaveLevelFromFilename(&level, filename);
3135 void DumpLevel(struct LevelInfo *level)
3137 if (level->no_valid_file)
3139 Error(ERR_WARN, "cannot dump -- no valid level file found");
3144 printf_line("-", 79);
3145 printf("Level xxx (file version %08d, game version %08d)\n",
3146 level->file_version, level->game_version);
3147 printf_line("-", 79);
3149 printf("Level author: '%s'\n", level->author);
3150 printf("Level title: '%s'\n", level->name);
3152 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
3154 printf("Level time: %d seconds\n", level->time);
3155 printf("Gems needed: %d\n", level->gems_needed);
3157 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
3158 printf("Time for wheel: %d seconds\n", level->time_wheel);
3159 printf("Time for light: %d seconds\n", level->time_light);
3160 printf("Time for timegate: %d seconds\n", level->time_timegate);
3162 printf("Amoeba speed: %d\n", level->amoeba_speed);
3164 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
3165 printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no"));
3166 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
3167 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
3168 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
3169 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
3170 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
3172 printf_line("-", 79);
3176 /* ========================================================================= */
3177 /* tape file functions */
3178 /* ========================================================================= */
3180 static void setTapeInfoToDefaults()
3184 /* always start with reliable default values (empty tape) */
3187 /* default values (also for pre-1.2 tapes) with only the first player */
3188 tape.player_participates[0] = TRUE;
3189 for (i = 1; i < MAX_PLAYERS; i++)
3190 tape.player_participates[i] = FALSE;
3192 /* at least one (default: the first) player participates in every tape */
3193 tape.num_participating_players = 1;
3195 tape.level_nr = level_nr;
3197 tape.changed = FALSE;
3199 tape.recording = FALSE;
3200 tape.playing = FALSE;
3201 tape.pausing = FALSE;
3203 tape.no_valid_file = FALSE;
3206 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
3208 tape->file_version = getFileVersion(file);
3209 tape->game_version = getFileVersion(file);
3214 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
3218 tape->random_seed = getFile32BitBE(file);
3219 tape->date = getFile32BitBE(file);
3220 tape->length = getFile32BitBE(file);
3222 /* read header fields that are new since version 1.2 */
3223 if (tape->file_version >= FILE_VERSION_1_2)
3225 byte store_participating_players = getFile8Bit(file);
3228 /* since version 1.2, tapes store which players participate in the tape */
3229 tape->num_participating_players = 0;
3230 for (i = 0; i < MAX_PLAYERS; i++)
3232 tape->player_participates[i] = FALSE;
3234 if (store_participating_players & (1 << i))
3236 tape->player_participates[i] = TRUE;
3237 tape->num_participating_players++;
3241 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
3243 engine_version = getFileVersion(file);
3244 if (engine_version > 0)
3245 tape->engine_version = engine_version;
3247 tape->engine_version = tape->game_version;
3253 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
3255 int level_identifier_size;
3258 level_identifier_size = getFile16BitBE(file);
3260 tape->level_identifier =
3261 checked_realloc(tape->level_identifier, level_identifier_size);
3263 for (i = 0; i < level_identifier_size; i++)
3264 tape->level_identifier[i] = getFile8Bit(file);
3266 tape->level_nr = getFile16BitBE(file);
3268 chunk_size = 2 + level_identifier_size + 2;
3273 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
3276 int chunk_size_expected =
3277 (tape->num_participating_players + 1) * tape->length;
3279 if (chunk_size_expected != chunk_size)
3281 ReadUnusedBytesFromFile(file, chunk_size);
3282 return chunk_size_expected;
3285 for (i = 0; i < tape->length; i++)
3287 if (i >= MAX_TAPELEN)
3290 for (j = 0; j < MAX_PLAYERS; j++)
3292 tape->pos[i].action[j] = MV_NO_MOVING;
3294 if (tape->player_participates[j])
3295 tape->pos[i].action[j] = getFile8Bit(file);
3298 tape->pos[i].delay = getFile8Bit(file);
3300 if (tape->file_version == FILE_VERSION_1_0)
3302 /* eliminate possible diagonal moves in old tapes */
3303 /* this is only for backward compatibility */
3305 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
3306 byte action = tape->pos[i].action[0];
3307 int k, num_moves = 0;
3309 for (k = 0; k<4; k++)
3311 if (action & joy_dir[k])
3313 tape->pos[i + num_moves].action[0] = joy_dir[k];
3315 tape->pos[i + num_moves].delay = 0;
3324 tape->length += num_moves;
3327 else if (tape->file_version < FILE_VERSION_2_0)
3329 /* convert pre-2.0 tapes to new tape format */
3331 if (tape->pos[i].delay > 1)
3334 tape->pos[i + 1] = tape->pos[i];
3335 tape->pos[i + 1].delay = 1;
3338 for (j = 0; j < MAX_PLAYERS; j++)
3339 tape->pos[i].action[j] = MV_NO_MOVING;
3340 tape->pos[i].delay--;
3351 if (i != tape->length)
3352 chunk_size = (tape->num_participating_players + 1) * i;
3357 void LoadTapeFromFilename(char *filename)
3359 char cookie[MAX_LINE_LEN];
3360 char chunk_name[CHUNK_ID_LEN + 1];
3364 /* always start with reliable default values */
3365 setTapeInfoToDefaults();
3367 if (!(file = fopen(filename, MODE_READ)))
3369 tape.no_valid_file = TRUE;
3372 Error(ERR_WARN, "cannot read tape '%s' -- using empty tape", filename);
3378 getFileChunkBE(file, chunk_name, NULL);
3379 if (strcmp(chunk_name, "RND1") == 0)
3381 getFile32BitBE(file); /* not used */
3383 getFileChunkBE(file, chunk_name, NULL);
3384 if (strcmp(chunk_name, "TAPE") != 0)
3386 tape.no_valid_file = TRUE;
3388 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3393 else /* check for pre-2.0 file format with cookie string */
3395 strcpy(cookie, chunk_name);
3396 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3397 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3398 cookie[strlen(cookie) - 1] = '\0';
3400 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
3402 tape.no_valid_file = TRUE;
3404 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3409 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
3411 tape.no_valid_file = TRUE;
3413 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
3418 /* pre-2.0 tape files have no game version, so use file version here */
3419 tape.game_version = tape.file_version;
3422 if (tape.file_version < FILE_VERSION_1_2)
3424 /* tape files from versions before 1.2.0 without chunk structure */
3425 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
3426 LoadTape_BODY(file, 2 * tape.length, &tape);
3434 int (*loader)(FILE *, int, struct TapeInfo *);
3438 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
3439 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
3440 { "INFO", -1, LoadTape_INFO },
3441 { "BODY", -1, LoadTape_BODY },
3445 while (getFileChunkBE(file, chunk_name, &chunk_size))
3449 while (chunk_info[i].name != NULL &&
3450 strcmp(chunk_name, chunk_info[i].name) != 0)
3453 if (chunk_info[i].name == NULL)
3455 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
3456 chunk_name, filename);
3457 ReadUnusedBytesFromFile(file, chunk_size);
3459 else if (chunk_info[i].size != -1 &&
3460 chunk_info[i].size != chunk_size)
3462 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3463 chunk_size, chunk_name, filename);
3464 ReadUnusedBytesFromFile(file, chunk_size);
3468 /* call function to load this tape chunk */
3469 int chunk_size_expected =
3470 (chunk_info[i].loader)(file, chunk_size, &tape);
3472 /* the size of some chunks cannot be checked before reading other
3473 chunks first (like "HEAD" and "BODY") that contain some header
3474 information, so check them here */
3475 if (chunk_size_expected != chunk_size)
3477 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3478 chunk_size, chunk_name, filename);
3486 tape.length_seconds = GetTapeLength();
3489 printf("::: tape game version: %d\n", tape.game_version);
3490 printf("::: tape engine version: %d\n", tape.engine_version);
3494 void LoadTape(int nr)
3496 char *filename = getTapeFilename(nr);
3498 LoadTapeFromFilename(filename);
3501 void LoadSolutionTape(int nr)
3503 char *filename = getSolutionTapeFilename(nr);
3505 LoadTapeFromFilename(filename);
3508 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
3510 putFileVersion(file, tape->file_version);
3511 putFileVersion(file, tape->game_version);
3514 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
3517 byte store_participating_players = 0;
3519 /* set bits for participating players for compact storage */
3520 for (i = 0; i < MAX_PLAYERS; i++)
3521 if (tape->player_participates[i])
3522 store_participating_players |= (1 << i);
3524 putFile32BitBE(file, tape->random_seed);
3525 putFile32BitBE(file, tape->date);
3526 putFile32BitBE(file, tape->length);
3528 putFile8Bit(file, store_participating_players);
3530 /* unused bytes not at the end here for 4-byte alignment of engine_version */
3531 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3533 putFileVersion(file, tape->engine_version);
3536 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3538 int level_identifier_size = strlen(tape->level_identifier) + 1;
3541 putFile16BitBE(file, level_identifier_size);
3543 for (i = 0; i < level_identifier_size; i++)
3544 putFile8Bit(file, tape->level_identifier[i]);
3546 putFile16BitBE(file, tape->level_nr);
3549 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3553 for (i = 0; i < tape->length; i++)
3555 for (j = 0; j < MAX_PLAYERS; j++)
3556 if (tape->player_participates[j])
3557 putFile8Bit(file, tape->pos[i].action[j]);
3559 putFile8Bit(file, tape->pos[i].delay);
3563 void SaveTape(int nr)
3565 char *filename = getTapeFilename(nr);
3567 boolean new_tape = TRUE;
3568 int num_participating_players = 0;
3569 int info_chunk_size;
3570 int body_chunk_size;
3573 InitTapeDirectory(leveldir_current->subdir);
3575 /* if a tape still exists, ask to overwrite it */
3576 if (access(filename, F_OK) == 0)
3579 if (!Request("Replace old tape ?", REQ_ASK))
3583 if (!(file = fopen(filename, MODE_WRITE)))
3585 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
3589 tape.file_version = FILE_VERSION_ACTUAL;
3590 tape.game_version = GAME_VERSION_ACTUAL;
3592 /* count number of participating players */
3593 for (i = 0; i < MAX_PLAYERS; i++)
3594 if (tape.player_participates[i])
3595 num_participating_players++;
3597 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
3598 body_chunk_size = (num_participating_players + 1) * tape.length;
3600 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3601 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
3603 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3604 SaveTape_VERS(file, &tape);
3606 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
3607 SaveTape_HEAD(file, &tape);
3609 putFileChunkBE(file, "INFO", info_chunk_size);
3610 SaveTape_INFO(file, &tape);
3612 putFileChunkBE(file, "BODY", body_chunk_size);
3613 SaveTape_BODY(file, &tape);
3617 SetFilePermissions(filename, PERMS_PRIVATE);
3619 tape.changed = FALSE;
3622 Request("tape saved !", REQ_CONFIRM);
3625 void DumpTape(struct TapeInfo *tape)
3630 if (tape->no_valid_file)
3632 Error(ERR_WARN, "cannot dump -- no valid tape file found");
3637 if (TAPE_IS_EMPTY(*tape))
3639 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
3645 printf_line("-", 79);
3646 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
3647 tape->level_nr, tape->file_version, tape->game_version);
3648 printf(" (effective engine version %08d)\n",
3649 tape->engine_version);
3650 printf("Level series identifier: '%s'\n", tape->level_identifier);
3651 printf_line("-", 79);
3653 for (i = 0; i < tape->length; i++)
3655 if (i >= MAX_TAPELEN)
3658 printf("%03d: ", i);
3660 for (j = 0; j < MAX_PLAYERS; j++)
3662 if (tape->player_participates[j])
3664 int action = tape->pos[i].action[j];
3666 printf("%d:%02x ", j, action);
3667 printf("[%c%c%c%c|%c%c] - ",
3668 (action & JOY_LEFT ? '<' : ' '),
3669 (action & JOY_RIGHT ? '>' : ' '),
3670 (action & JOY_UP ? '^' : ' '),
3671 (action & JOY_DOWN ? 'v' : ' '),
3672 (action & JOY_BUTTON_1 ? '1' : ' '),
3673 (action & JOY_BUTTON_2 ? '2' : ' '));
3677 printf("(%03d)\n", tape->pos[i].delay);
3680 printf_line("-", 79);
3684 /* ========================================================================= */
3685 /* score file functions */
3686 /* ========================================================================= */
3688 void LoadScore(int nr)
3691 char *filename = getScoreFilename(nr);
3692 char cookie[MAX_LINE_LEN];
3693 char line[MAX_LINE_LEN];
3697 /* always start with reliable default values */
3698 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3700 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
3701 highscore[i].Score = 0;
3704 if (!(file = fopen(filename, MODE_READ)))
3707 /* check file identifier */
3708 fgets(cookie, MAX_LINE_LEN, file);
3709 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3710 cookie[strlen(cookie) - 1] = '\0';
3712 if (!checkCookieString(cookie, SCORE_COOKIE))
3714 Error(ERR_WARN, "unknown format of score file '%s'", filename);
3719 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3721 fscanf(file, "%d", &highscore[i].Score);
3722 fgets(line, MAX_LINE_LEN, file);
3724 if (line[strlen(line) - 1] == '\n')
3725 line[strlen(line) - 1] = '\0';
3727 for (line_ptr = line; *line_ptr; line_ptr++)
3729 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3731 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
3732 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
3741 void SaveScore(int nr)
3744 char *filename = getScoreFilename(nr);
3747 InitScoreDirectory(leveldir_current->subdir);
3749 if (!(file = fopen(filename, MODE_WRITE)))
3751 Error(ERR_WARN, "cannot save score for level %d", nr);
3755 fprintf(file, "%s\n\n", SCORE_COOKIE);
3757 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3758 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
3762 SetFilePermissions(filename, PERMS_PUBLIC);
3766 /* ========================================================================= */
3767 /* setup file functions */
3768 /* ========================================================================= */
3770 #define TOKEN_STR_PLAYER_PREFIX "player_"
3773 #define SETUP_TOKEN_PLAYER_NAME 0
3774 #define SETUP_TOKEN_SOUND 1
3775 #define SETUP_TOKEN_SOUND_LOOPS 2
3776 #define SETUP_TOKEN_SOUND_MUSIC 3
3777 #define SETUP_TOKEN_SOUND_SIMPLE 4
3778 #define SETUP_TOKEN_TOONS 5
3779 #define SETUP_TOKEN_SCROLL_DELAY 6
3780 #define SETUP_TOKEN_SOFT_SCROLLING 7
3781 #define SETUP_TOKEN_FADING 8
3782 #define SETUP_TOKEN_AUTORECORD 9
3783 #define SETUP_TOKEN_QUICK_DOORS 10
3784 #define SETUP_TOKEN_TEAM_MODE 11
3785 #define SETUP_TOKEN_HANDICAP 12
3786 #define SETUP_TOKEN_TIME_LIMIT 13
3787 #define SETUP_TOKEN_FULLSCREEN 14
3788 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
3789 #define SETUP_TOKEN_GRAPHICS_SET 16
3790 #define SETUP_TOKEN_SOUNDS_SET 17
3791 #define SETUP_TOKEN_MUSIC_SET 18
3792 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
3793 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
3794 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
3796 #define NUM_GLOBAL_SETUP_TOKENS 22
3799 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
3800 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
3801 #define SETUP_TOKEN_EDITOR_EL_MORE 2
3802 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
3803 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
3804 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
3805 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
3806 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
3807 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
3808 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
3809 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
3810 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
3812 #define NUM_EDITOR_SETUP_TOKENS 12
3814 /* shortcut setup */
3815 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
3816 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
3817 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
3819 #define NUM_SHORTCUT_SETUP_TOKENS 3
3822 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
3823 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
3824 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
3825 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
3826 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
3827 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
3828 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
3829 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
3830 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
3831 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
3832 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
3833 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
3834 #define SETUP_TOKEN_PLAYER_KEY_UP 12
3835 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
3836 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
3837 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
3839 #define NUM_PLAYER_SETUP_TOKENS 16
3842 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
3843 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
3845 #define NUM_SYSTEM_SETUP_TOKENS 2
3848 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
3850 #define NUM_OPTIONS_SETUP_TOKENS 1
3853 static struct SetupInfo si;
3854 static struct SetupEditorInfo sei;
3855 static struct SetupShortcutInfo ssi;
3856 static struct SetupInputInfo sii;
3857 static struct SetupSystemInfo syi;
3858 static struct OptionInfo soi;
3860 static struct TokenInfo global_setup_tokens[] =
3862 { TYPE_STRING, &si.player_name, "player_name" },
3863 { TYPE_SWITCH, &si.sound, "sound" },
3864 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
3865 { TYPE_SWITCH, &si.sound_music, "background_music" },
3866 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
3867 { TYPE_SWITCH, &si.toons, "toons" },
3868 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
3869 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
3870 { TYPE_SWITCH, &si.fading, "screen_fading" },
3871 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
3872 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
3873 { TYPE_SWITCH, &si.team_mode, "team_mode" },
3874 { TYPE_SWITCH, &si.handicap, "handicap" },
3875 { TYPE_SWITCH, &si.time_limit, "time_limit" },
3876 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
3877 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
3878 { TYPE_STRING, &si.graphics_set, "graphics_set" },
3879 { TYPE_STRING, &si.sounds_set, "sounds_set" },
3880 { TYPE_STRING, &si.music_set, "music_set" },
3881 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
3882 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
3883 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
3886 static struct TokenInfo editor_setup_tokens[] =
3888 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
3889 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
3890 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
3891 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
3892 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
3893 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
3894 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
3895 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
3896 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
3897 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
3898 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
3899 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
3902 static struct TokenInfo shortcut_setup_tokens[] =
3904 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
3905 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
3906 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
3909 static struct TokenInfo player_setup_tokens[] =
3911 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
3912 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
3913 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
3914 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
3915 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
3916 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
3917 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
3918 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
3919 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
3920 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
3921 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
3922 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
3923 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
3924 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
3925 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
3926 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
3929 static struct TokenInfo system_setup_tokens[] =
3931 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
3932 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
3935 static struct TokenInfo options_setup_tokens[] =
3937 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
3940 static char *get_corrected_login_name(char *login_name)
3942 /* needed because player name must be a fixed length string */
3943 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3945 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3946 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3948 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
3949 if (strchr(login_name_new, ' '))
3950 *strchr(login_name_new, ' ') = '\0';
3952 return login_name_new;
3955 static void setSetupInfoToDefaults(struct SetupInfo *si)
3959 si->player_name = get_corrected_login_name(getLoginName());
3962 si->sound_loops = TRUE;
3963 si->sound_music = TRUE;
3964 si->sound_simple = TRUE;
3966 si->double_buffering = TRUE;
3967 si->direct_draw = !si->double_buffering;
3968 si->scroll_delay = TRUE;
3969 si->soft_scrolling = TRUE;
3971 si->autorecord = TRUE;
3972 si->quick_doors = FALSE;
3973 si->team_mode = FALSE;
3974 si->handicap = TRUE;
3975 si->time_limit = TRUE;
3976 si->fullscreen = FALSE;
3977 si->ask_on_escape = TRUE;
3979 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3980 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3981 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3982 si->override_level_graphics = FALSE;
3983 si->override_level_sounds = FALSE;
3984 si->override_level_music = FALSE;
3986 si->editor.el_boulderdash = TRUE;
3987 si->editor.el_emerald_mine = TRUE;
3988 si->editor.el_more = TRUE;
3989 si->editor.el_sokoban = TRUE;
3990 si->editor.el_supaplex = TRUE;
3991 si->editor.el_diamond_caves = TRUE;
3992 si->editor.el_dx_boulderdash = TRUE;
3993 si->editor.el_chars = TRUE;
3994 si->editor.el_custom = TRUE;
3995 si->editor.el_custom_more = FALSE;
3997 si->editor.el_headlines = TRUE;
3998 si->editor.el_user_defined = FALSE;
4000 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
4001 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
4002 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
4004 for (i = 0; i < MAX_PLAYERS; i++)
4006 si->input[i].use_joystick = FALSE;
4007 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
4008 si->input[i].joy.xleft = JOYSTICK_XLEFT;
4009 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
4010 si->input[i].joy.xright = JOYSTICK_XRIGHT;
4011 si->input[i].joy.yupper = JOYSTICK_YUPPER;
4012 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
4013 si->input[i].joy.ylower = JOYSTICK_YLOWER;
4014 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
4015 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
4016 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
4017 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
4018 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
4019 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
4020 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
4021 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
4024 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
4025 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
4027 si->options.verbose = FALSE;
4030 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
4034 if (!setup_file_hash)
4039 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4040 setSetupInfo(global_setup_tokens, i,
4041 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
4046 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4047 setSetupInfo(editor_setup_tokens, i,
4048 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
4051 /* shortcut setup */
4052 ssi = setup.shortcut;
4053 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4054 setSetupInfo(shortcut_setup_tokens, i,
4055 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
4056 setup.shortcut = ssi;
4059 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4063 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4065 sii = setup.input[pnr];
4066 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4068 char full_token[100];
4070 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
4071 setSetupInfo(player_setup_tokens, i,
4072 getHashEntry(setup_file_hash, full_token));
4074 setup.input[pnr] = sii;
4079 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4080 setSetupInfo(system_setup_tokens, i,
4081 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
4085 soi = setup.options;
4086 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4087 setSetupInfo(options_setup_tokens, i,
4088 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
4089 setup.options = soi;
4094 char *filename = getSetupFilename();
4095 SetupFileHash *setup_file_hash = NULL;
4097 /* always start with reliable default values */
4098 setSetupInfoToDefaults(&setup);
4100 setup_file_hash = loadSetupFileHash(filename);
4102 if (setup_file_hash)
4104 char *player_name_new;
4106 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
4107 decodeSetupFileHash(setup_file_hash);
4109 setup.direct_draw = !setup.double_buffering;
4111 freeSetupFileHash(setup_file_hash);
4113 /* needed to work around problems with fixed length strings */
4114 player_name_new = get_corrected_login_name(setup.player_name);
4115 free(setup.player_name);
4116 setup.player_name = player_name_new;
4119 Error(ERR_WARN, "using default setup values");
4124 char *filename = getSetupFilename();
4128 InitUserDataDirectory();
4130 if (!(file = fopen(filename, MODE_WRITE)))
4132 Error(ERR_WARN, "cannot write setup file '%s'", filename);
4136 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
4137 getCookie("SETUP")));
4138 fprintf(file, "\n");
4142 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4144 /* just to make things nicer :) */
4145 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
4146 i == SETUP_TOKEN_GRAPHICS_SET)
4147 fprintf(file, "\n");
4149 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
4154 fprintf(file, "\n");
4155 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4156 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
4158 /* shortcut setup */
4159 ssi = setup.shortcut;
4160 fprintf(file, "\n");
4161 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4162 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
4165 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4169 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4170 fprintf(file, "\n");
4172 sii = setup.input[pnr];
4173 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4174 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
4179 fprintf(file, "\n");
4180 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4181 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
4184 soi = setup.options;
4185 fprintf(file, "\n");
4186 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4187 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
4191 SetFilePermissions(filename, PERMS_PRIVATE);
4194 void LoadCustomElementDescriptions()
4196 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4197 SetupFileHash *setup_file_hash;
4200 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4202 if (element_info[i].custom_description != NULL)
4204 free(element_info[i].custom_description);
4205 element_info[i].custom_description = NULL;
4209 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4212 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4214 char *token = getStringCat2(element_info[i].token_name, ".name");
4215 char *value = getHashEntry(setup_file_hash, token);
4218 element_info[i].custom_description = getStringCopy(value);
4223 freeSetupFileHash(setup_file_hash);
4226 void LoadSpecialMenuDesignSettings()
4228 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4229 SetupFileHash *setup_file_hash;
4232 /* always start with reliable default values from default config */
4233 for (i = 0; image_config_vars[i].token != NULL; i++)
4234 for (j = 0; image_config[j].token != NULL; j++)
4235 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
4236 *image_config_vars[i].value =
4237 get_auto_parameter_value(image_config_vars[i].token,
4238 image_config[j].value);
4240 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4243 /* special case: initialize with default values that may be overwritten */
4244 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
4246 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
4247 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
4248 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
4250 if (value_x != NULL)
4251 menu.draw_xoffset[i] = get_integer_from_string(value_x);
4252 if (value_y != NULL)
4253 menu.draw_yoffset[i] = get_integer_from_string(value_y);
4254 if (list_size != NULL)
4255 menu.list_size[i] = get_integer_from_string(list_size);
4258 /* read (and overwrite with) values that may be specified in config file */
4259 for (i = 0; image_config_vars[i].token != NULL; i++)
4261 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
4264 *image_config_vars[i].value =
4265 get_auto_parameter_value(image_config_vars[i].token, value);
4268 freeSetupFileHash(setup_file_hash);
4271 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
4273 char *filename = getEditorSetupFilename();
4274 SetupFileList *setup_file_list, *list;
4275 SetupFileHash *element_hash;
4276 int num_unknown_tokens = 0;
4279 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
4282 element_hash = newSetupFileHash();
4284 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4285 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4287 /* determined size may be larger than needed (due to unknown elements) */
4289 for (list = setup_file_list; list != NULL; list = list->next)
4292 /* add space for up to 3 more elements for padding that may be needed */
4295 *elements = checked_malloc(*num_elements * sizeof(int));
4298 for (list = setup_file_list; list != NULL; list = list->next)
4300 char *value = getHashEntry(element_hash, list->token);
4304 (*elements)[(*num_elements)++] = atoi(value);
4308 if (num_unknown_tokens == 0)
4310 Error(ERR_RETURN_LINE, "-");
4311 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4312 Error(ERR_RETURN, "- config file: '%s'", filename);
4314 num_unknown_tokens++;
4317 Error(ERR_RETURN, "- token: '%s'", list->token);
4321 if (num_unknown_tokens > 0)
4322 Error(ERR_RETURN_LINE, "-");
4324 while (*num_elements % 4) /* pad with empty elements, if needed */
4325 (*elements)[(*num_elements)++] = EL_EMPTY;
4327 freeSetupFileList(setup_file_list);
4328 freeSetupFileHash(element_hash);
4332 for (i = 0; i < *num_elements; i++)
4333 printf("editor: element '%s' [%d]\n",
4334 element_info[(*elements)[i]].token_name, (*elements)[i]);
4338 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
4341 SetupFileHash *setup_file_hash = NULL;
4342 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
4343 char *filename_music, *filename_prefix, *filename_info;
4349 token_to_value_ptr[] =
4351 { "title_header", &tmp_music_file_info.title_header },
4352 { "artist_header", &tmp_music_file_info.artist_header },
4353 { "album_header", &tmp_music_file_info.album_header },
4354 { "year_header", &tmp_music_file_info.year_header },
4356 { "title", &tmp_music_file_info.title },
4357 { "artist", &tmp_music_file_info.artist },
4358 { "album", &tmp_music_file_info.album },
4359 { "year", &tmp_music_file_info.year },
4365 filename_music = (is_sound ? getCustomSoundFilename(basename) :
4366 getCustomMusicFilename(basename));
4368 if (filename_music == NULL)
4371 /* ---------- try to replace file extension ---------- */
4373 filename_prefix = getStringCopy(filename_music);
4374 if (strrchr(filename_prefix, '.') != NULL)
4375 *strrchr(filename_prefix, '.') = '\0';
4376 filename_info = getStringCat2(filename_prefix, ".txt");
4379 printf("trying to load file '%s'...\n", filename_info);
4382 if (fileExists(filename_info))
4383 setup_file_hash = loadSetupFileHash(filename_info);
4385 free(filename_prefix);
4386 free(filename_info);
4388 if (setup_file_hash == NULL)
4390 /* ---------- try to add file extension ---------- */
4392 filename_prefix = getStringCopy(filename_music);
4393 filename_info = getStringCat2(filename_prefix, ".txt");
4396 printf("trying to load file '%s'...\n", filename_info);
4399 if (fileExists(filename_info))
4400 setup_file_hash = loadSetupFileHash(filename_info);
4402 free(filename_prefix);
4403 free(filename_info);
4406 if (setup_file_hash == NULL)
4409 /* ---------- music file info found ---------- */
4411 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
4413 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
4415 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
4417 *token_to_value_ptr[i].value_ptr =
4418 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
4421 tmp_music_file_info.basename = getStringCopy(basename);
4422 tmp_music_file_info.music = music;
4423 tmp_music_file_info.is_sound = is_sound;
4425 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
4426 *new_music_file_info = tmp_music_file_info;
4428 return new_music_file_info;
4431 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
4433 return get_music_file_info_ext(basename, music, FALSE);
4436 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
4438 return get_music_file_info_ext(basename, sound, TRUE);
4441 static boolean music_info_listed_ext(struct MusicFileInfo *list,
4442 char *basename, boolean is_sound)
4444 for (; list != NULL; list = list->next)
4445 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
4451 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
4453 return music_info_listed_ext(list, basename, FALSE);
4456 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
4458 return music_info_listed_ext(list, basename, TRUE);
4461 void LoadMusicInfo()
4463 char *music_directory = getCustomMusicDirectory();
4464 int num_music = getMusicListSize();
4465 int num_music_noconf = 0;
4466 int num_sounds = getSoundListSize();
4468 struct dirent *dir_entry;
4469 struct FileInfo *music, *sound;
4470 struct MusicFileInfo *next, **new;
4473 while (music_file_info != NULL)
4475 next = music_file_info->next;
4477 checked_free(music_file_info->basename);
4479 checked_free(music_file_info->title_header);
4480 checked_free(music_file_info->artist_header);
4481 checked_free(music_file_info->album_header);
4482 checked_free(music_file_info->year_header);
4484 checked_free(music_file_info->title);
4485 checked_free(music_file_info->artist);
4486 checked_free(music_file_info->album);
4487 checked_free(music_file_info->year);
4489 free(music_file_info);
4491 music_file_info = next;
4494 new = &music_file_info;
4497 printf("::: num_music == %d\n", num_music);
4500 for (i = 0; i < num_music; i++)
4502 music = getMusicListEntry(i);
4505 printf("::: %d [%08x]\n", i, music->filename);
4508 if (music->filename == NULL)
4511 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
4514 /* a configured file may be not recognized as music */
4515 if (!FileIsMusic(music->filename))
4519 printf("::: -> '%s' (configured)\n", music->filename);
4522 if (!music_info_listed(music_file_info, music->filename))
4524 *new = get_music_file_info(music->filename, i);
4526 new = &(*new)->next;
4530 if ((dir = opendir(music_directory)) == NULL)
4532 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4536 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
4538 char *basename = dir_entry->d_name;
4539 boolean music_already_used = FALSE;
4542 /* skip all music files that are configured in music config file */
4543 for (i = 0; i < num_music; i++)
4545 music = getMusicListEntry(i);
4547 if (music->filename == NULL)
4550 if (strcmp(basename, music->filename) == 0)
4552 music_already_used = TRUE;
4557 if (music_already_used)
4560 if (!FileIsMusic(basename))
4564 printf("::: -> '%s' (found in directory)\n", basename);
4567 if (!music_info_listed(music_file_info, basename))
4569 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
4571 new = &(*new)->next;
4579 for (i = 0; i < num_sounds; i++)
4581 sound = getSoundListEntry(i);
4583 if (sound->filename == NULL)
4586 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
4589 /* a configured file may be not recognized as sound */
4590 if (!FileIsSound(sound->filename))
4594 printf("::: -> '%s' (configured)\n", sound->filename);
4597 if (!sound_info_listed(music_file_info, sound->filename))
4599 *new = get_sound_file_info(sound->filename, i);
4601 new = &(*new)->next;
4607 for (next = music_file_info; next != NULL; next = next->next)
4608 printf("::: title == '%s'\n", next->title);
4612 void add_helpanim_entry(int element, int action, int direction, int delay,
4613 int *num_list_entries)
4615 struct HelpAnimInfo *new_list_entry;
4616 (*num_list_entries)++;
4619 checked_realloc(helpanim_info,
4620 *num_list_entries * sizeof(struct HelpAnimInfo));
4621 new_list_entry = &helpanim_info[*num_list_entries - 1];
4623 new_list_entry->element = element;
4624 new_list_entry->action = action;
4625 new_list_entry->direction = direction;
4626 new_list_entry->delay = delay;
4629 void print_unknown_token(char *filename, char *token, int token_nr)
4633 Error(ERR_RETURN_LINE, "-");
4634 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4635 Error(ERR_RETURN, "- config file: '%s'", filename);
4638 Error(ERR_RETURN, "- token: '%s'", token);
4641 void print_unknown_token_end(int token_nr)
4644 Error(ERR_RETURN_LINE, "-");
4647 void LoadHelpAnimInfo()
4649 char *filename = getHelpAnimFilename();
4650 SetupFileList *setup_file_list = NULL, *list;
4651 SetupFileHash *element_hash, *action_hash, *direction_hash;
4652 int num_list_entries = 0;
4653 int num_unknown_tokens = 0;
4656 if (fileExists(filename))
4657 setup_file_list = loadSetupFileList(filename);
4659 if (setup_file_list == NULL)
4661 /* use reliable default values from static configuration */
4662 SetupFileList *insert_ptr;
4664 insert_ptr = setup_file_list =
4665 newSetupFileList(helpanim_config[0].token,
4666 helpanim_config[0].value);
4668 for (i = 1; helpanim_config[i].token; i++)
4669 insert_ptr = addListEntry(insert_ptr,
4670 helpanim_config[i].token,
4671 helpanim_config[i].value);
4674 element_hash = newSetupFileHash();
4675 action_hash = newSetupFileHash();
4676 direction_hash = newSetupFileHash();
4678 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4679 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4681 for (i = 0; i < NUM_ACTIONS; i++)
4682 setHashEntry(action_hash, element_action_info[i].suffix,
4683 i_to_a(element_action_info[i].value));
4685 /* do not store direction index (bit) here, but direction value! */
4686 for (i = 0; i < NUM_DIRECTIONS; i++)
4687 setHashEntry(direction_hash, element_direction_info[i].suffix,
4688 i_to_a(1 << element_direction_info[i].value));
4690 for (list = setup_file_list; list != NULL; list = list->next)
4692 char *element_token, *action_token, *direction_token;
4693 char *element_value, *action_value, *direction_value;
4694 int delay = atoi(list->value);
4696 if (strcmp(list->token, "end") == 0)
4698 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4703 /* first try to break element into element/action/direction parts;
4704 if this does not work, also accept combined "element[.act][.dir]"
4705 elements (like "dynamite.active"), which are unique elements */
4707 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
4709 element_value = getHashEntry(element_hash, list->token);
4710 if (element_value != NULL) /* element found */
4711 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4715 /* no further suffixes found -- this is not an element */
4716 print_unknown_token(filename, list->token, num_unknown_tokens++);
4722 /* token has format "<prefix>.<something>" */
4724 action_token = strchr(list->token, '.'); /* suffix may be action ... */
4725 direction_token = action_token; /* ... or direction */
4727 element_token = getStringCopy(list->token);
4728 *strchr(element_token, '.') = '\0';
4730 element_value = getHashEntry(element_hash, element_token);
4732 if (element_value == NULL) /* this is no element */
4734 element_value = getHashEntry(element_hash, list->token);
4735 if (element_value != NULL) /* combined element found */
4736 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4739 print_unknown_token(filename, list->token, num_unknown_tokens++);
4741 free(element_token);
4746 action_value = getHashEntry(action_hash, action_token);
4748 if (action_value != NULL) /* action found */
4750 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
4753 free(element_token);
4758 direction_value = getHashEntry(direction_hash, direction_token);
4760 if (direction_value != NULL) /* direction found */
4762 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
4765 free(element_token);
4770 if (strchr(action_token + 1, '.') == NULL)
4772 /* no further suffixes found -- this is not an action nor direction */
4774 element_value = getHashEntry(element_hash, list->token);
4775 if (element_value != NULL) /* combined element found */
4776 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4779 print_unknown_token(filename, list->token, num_unknown_tokens++);
4781 free(element_token);
4786 /* token has format "<prefix>.<suffix>.<something>" */
4788 direction_token = strchr(action_token + 1, '.');
4790 action_token = getStringCopy(action_token);
4791 *strchr(action_token + 1, '.') = '\0';
4793 action_value = getHashEntry(action_hash, action_token);
4795 if (action_value == NULL) /* this is no action */
4797 element_value = getHashEntry(element_hash, list->token);
4798 if (element_value != NULL) /* combined element found */
4799 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4802 print_unknown_token(filename, list->token, num_unknown_tokens++);
4804 free(element_token);
4810 direction_value = getHashEntry(direction_hash, direction_token);
4812 if (direction_value != NULL) /* direction found */
4814 add_helpanim_entry(atoi(element_value), atoi(action_value),
4815 atoi(direction_value), delay, &num_list_entries);
4817 free(element_token);
4823 /* this is no direction */
4825 element_value = getHashEntry(element_hash, list->token);
4826 if (element_value != NULL) /* combined element found */
4827 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4830 print_unknown_token(filename, list->token, num_unknown_tokens++);
4832 free(element_token);
4836 print_unknown_token_end(num_unknown_tokens);
4838 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4839 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
4841 freeSetupFileList(setup_file_list);
4842 freeSetupFileHash(element_hash);
4843 freeSetupFileHash(action_hash);
4844 freeSetupFileHash(direction_hash);
4848 for (i = 0; i < num_list_entries; i++)
4849 printf("::: %d, %d, %d => %d\n",
4850 helpanim_info[i].element,
4851 helpanim_info[i].action,
4852 helpanim_info[i].direction,
4853 helpanim_info[i].delay);
4857 void LoadHelpTextInfo()
4859 char *filename = getHelpTextFilename();
4862 if (helptext_info != NULL)
4864 freeSetupFileHash(helptext_info);
4865 helptext_info = NULL;
4868 if (fileExists(filename))
4869 helptext_info = loadSetupFileHash(filename);
4871 if (helptext_info == NULL)
4873 /* use reliable default values from static configuration */
4874 helptext_info = newSetupFileHash();
4876 for (i = 0; helptext_config[i].token; i++)
4877 setHashEntry(helptext_info,
4878 helptext_config[i].token,
4879 helptext_config[i].value);
4884 BEGIN_HASH_ITERATION(helptext_info, itr)
4886 printf("::: '%s' => '%s'\n",
4887 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
4889 END_HASH_ITERATION(hash, itr)
4894 /* ------------------------------------------------------------------------- *
4896 * ------------------------------------------------------------------------- */
4898 #define MAX_NUM_CONVERT_LEVELS 1000
4900 void ConvertLevels()
4902 static LevelDirTree *convert_leveldir = NULL;
4903 static int convert_level_nr = -1;
4904 static int num_levels_handled = 0;
4905 static int num_levels_converted = 0;
4906 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
4909 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
4910 global.convert_leveldir);
4912 if (convert_leveldir == NULL)
4913 Error(ERR_EXIT, "no such level identifier: '%s'",
4914 global.convert_leveldir);
4916 leveldir_current = convert_leveldir;
4918 if (global.convert_level_nr != -1)
4920 convert_leveldir->first_level = global.convert_level_nr;
4921 convert_leveldir->last_level = global.convert_level_nr;
4924 convert_level_nr = convert_leveldir->first_level;
4926 printf_line("=", 79);
4927 printf("Converting levels\n");
4928 printf_line("-", 79);
4929 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
4930 printf("Level series name: '%s'\n", convert_leveldir->name);
4931 printf("Level series author: '%s'\n", convert_leveldir->author);
4932 printf("Number of levels: %d\n", convert_leveldir->levels);
4933 printf_line("=", 79);
4936 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
4937 levels_failed[i] = FALSE;
4939 while (convert_level_nr <= convert_leveldir->last_level)
4941 char *level_filename;
4944 level_nr = convert_level_nr++;
4946 printf("Level %03d: ", level_nr);
4948 LoadLevel(level_nr);
4949 if (level.no_valid_file)
4951 printf("(no level)\n");
4955 printf("converting level ... ");
4957 level_filename = getDefaultLevelFilename(level_nr);
4958 new_level = !fileExists(level_filename);
4962 SaveLevel(level_nr);
4964 num_levels_converted++;
4966 printf("converted.\n");
4970 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
4971 levels_failed[level_nr] = TRUE;
4973 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
4976 num_levels_handled++;
4980 printf_line("=", 79);
4981 printf("Number of levels handled: %d\n", num_levels_handled);
4982 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
4983 (num_levels_handled ?
4984 num_levels_converted * 100 / num_levels_handled : 0));
4985 printf_line("-", 79);
4986 printf("Summary (for automatic parsing by scripts):\n");
4987 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
4988 convert_leveldir->identifier, num_levels_converted,
4990 (num_levels_handled ?
4991 num_levels_converted * 100 / num_levels_handled : 0));
4993 if (num_levels_handled != num_levels_converted)
4995 printf(", FAILED:");
4996 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
4997 if (levels_failed[i])
5002 printf_line("=", 79);