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"
52 /* values for level file type identifier */
53 #define LEVEL_FILE_TYPE_UNKNOWN 0
54 #define LEVEL_FILE_TYPE_RND 1
55 #define LEVEL_FILE_TYPE_BD 2
56 #define LEVEL_FILE_TYPE_EM 3
57 #define LEVEL_FILE_TYPE_SP 4
58 #define LEVEL_FILE_TYPE_DX 5
59 #define LEVEL_FILE_TYPE_SB 6
60 #define LEVEL_FILE_TYPE_DC 7
62 #define LEVEL_FILE_TYPE_RND_PACKED (10 + LEVEL_FILE_TYPE_RND)
63 #define LEVEL_FILE_TYPE_EM_PACKED (10 + LEVEL_FILE_TYPE_EM)
65 #define IS_SINGLE_LEVEL_FILE(x) (x < 10)
66 #define IS_PACKED_LEVEL_FILE(x) (x > 10)
69 /* ========================================================================= */
70 /* level file functions */
71 /* ========================================================================= */
73 void setElementChangePages(struct ElementInfo *ei, int change_pages)
75 int change_page_size = sizeof(struct ElementChangeInfo);
77 ei->num_change_pages = MAX(1, change_pages);
80 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
82 if (ei->current_change_page >= ei->num_change_pages)
83 ei->current_change_page = ei->num_change_pages - 1;
85 ei->change = &ei->change_page[ei->current_change_page];
88 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
92 change->can_change = FALSE;
94 change->events = CE_BITMASK_DEFAULT;
96 change->trigger_player = CH_PLAYER_ANY;
97 change->trigger_side = CH_SIDE_ANY;
98 change->trigger_page = CH_PAGE_ANY;
100 change->target_element = EL_EMPTY_SPACE;
102 change->delay_fixed = 0;
103 change->delay_random = 0;
104 change->delay_frames = 1;
106 change->trigger_element = EL_EMPTY_SPACE;
108 change->explode = FALSE;
109 change->use_target_content = FALSE;
110 change->only_if_complete = FALSE;
111 change->use_random_replace = FALSE;
112 change->random_percentage = 100;
113 change->replace_when = CP_WHEN_EMPTY;
115 for (x = 0; x < 3; x++)
116 for (y = 0; y < 3; y++)
117 change->target_content[x][y] = EL_EMPTY_SPACE;
119 change->direct_action = 0;
120 change->other_action = 0;
122 change->pre_change_function = NULL;
123 change->change_function = NULL;
124 change->post_change_function = NULL;
127 static void setLevelInfoToDefaults(struct LevelInfo *level)
129 static boolean clipboard_elements_initialized = FALSE;
133 level->file_version = FILE_VERSION_ACTUAL;
134 level->game_version = GAME_VERSION_ACTUAL;
136 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
137 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
138 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
140 level->fieldx = STD_LEV_FIELDX;
141 level->fieldy = STD_LEV_FIELDY;
143 for (x = 0; x < MAX_LEV_FIELDX; x++)
144 for (y = 0; y < MAX_LEV_FIELDY; y++)
145 level->field[x][y] = EL_SAND;
148 level->gems_needed = 0;
150 level->amoeba_speed = 10;
152 level->time_magic_wall = 10;
153 level->time_wheel = 10;
154 level->time_light = 10;
155 level->time_timegate = 10;
157 level->amoeba_content = EL_DIAMOND;
159 level->double_speed = FALSE;
160 level->initial_gravity = FALSE;
161 level->em_slippery_gems = FALSE;
162 level->instant_relocation = FALSE;
163 level->can_pass_to_walkable = FALSE;
164 level->grow_into_diggable = TRUE;
166 level->block_last_field = FALSE; /* EM does not block by default */
167 level->sp_block_last_field = TRUE; /* SP blocks the last field */
168 level->block_delay = 8; /* when blocking, block 8 frames */
169 level->sp_block_delay = 9; /* SP indeed blocks 9 frames, not 8 */
171 level->can_move_into_acid_bits = ~0; /* everything can move into acid */
172 level->dont_collide_with_bits = ~0; /* always deadly when colliding */
174 level->use_spring_bug = FALSE;
175 level->use_step_counter = FALSE;
177 level->use_custom_template = FALSE;
179 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
180 level->name[i] = '\0';
181 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
182 level->author[i] = '\0';
184 strcpy(level->name, NAMELESS_LEVEL_NAME);
185 strcpy(level->author, ANONYMOUS_NAME);
187 for (i = 0; i < 4; i++)
189 level->envelope_text[i][0] = '\0';
190 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
191 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
194 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
195 level->score[i] = 10;
197 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
198 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
199 for (x = 0; x < 3; x++)
200 for (y = 0; y < 3; y++)
201 level->yamyam_content[i][x][y] =
202 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
204 level->field[0][0] = EL_PLAYER_1;
205 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
207 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
211 /* never initialize clipboard elements after the very first time */
212 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
215 setElementChangePages(&element_info[element], 1);
216 setElementChangeInfoToDefaults(element_info[element].change);
218 if (IS_CUSTOM_ELEMENT(element) ||
219 IS_GROUP_ELEMENT(element) ||
220 IS_INTERNAL_ELEMENT(element))
222 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
223 element_info[element].description[j] = '\0';
225 if (element_info[element].custom_description != NULL)
226 strncpy(element_info[element].description,
227 element_info[element].custom_description,MAX_ELEMENT_NAME_LEN);
229 strcpy(element_info[element].description,
230 element_info[element].editor_description);
232 element_info[element].use_gfx_element = FALSE;
233 element_info[element].gfx_element = EL_EMPTY_SPACE;
235 element_info[element].modified_settings = FALSE;
238 if (IS_CUSTOM_ELEMENT(element) ||
239 IS_INTERNAL_ELEMENT(element))
241 element_info[element].access_direction = MV_ALL_DIRECTIONS;
243 element_info[element].collect_score = 10; /* special default */
244 element_info[element].collect_count = 1; /* special default */
246 element_info[element].push_delay_fixed = -1; /* initialize later */
247 element_info[element].push_delay_random = -1; /* initialize later */
248 element_info[element].drop_delay_fixed = 0;
249 element_info[element].drop_delay_random = 0;
250 element_info[element].move_delay_fixed = 0;
251 element_info[element].move_delay_random = 0;
253 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
254 element_info[element].move_direction_initial = MV_START_AUTOMATIC;
255 element_info[element].move_stepsize = TILEX / 8;
257 element_info[element].move_enter_element = EL_EMPTY_SPACE;
258 element_info[element].move_leave_element = EL_EMPTY_SPACE;
259 element_info[element].move_leave_type = LEAVE_TYPE_UNLIMITED;
261 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
263 element_info[element].explosion_type = EXPLODES_3X3;
264 element_info[element].explosion_delay = 16;
265 element_info[element].ignition_delay = 8;
267 for (x = 0; x < 3; x++)
268 for (y = 0; y < 3; y++)
269 element_info[element].content[x][y] = EL_EMPTY_SPACE;
271 element_info[element].access_type = 0;
272 element_info[element].access_layer = 0;
273 element_info[element].access_protected = 0;
274 element_info[element].walk_to_action = 0;
275 element_info[element].smash_targets = 0;
276 element_info[element].deadliness = 0;
278 element_info[element].can_explode_by_fire = FALSE;
279 element_info[element].can_explode_smashed = FALSE;
280 element_info[element].can_explode_impact = FALSE;
282 element_info[element].current_change_page = 0;
284 /* start with no properties at all */
285 for (j = 0; j < NUM_EP_BITFIELDS; j++)
286 Properties[element][j] = EP_BITMASK_DEFAULT;
288 /* now set default properties */
289 SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE);
292 if (IS_GROUP_ELEMENT(element) ||
293 IS_INTERNAL_ELEMENT(element))
295 /* initialize memory for list of elements in group */
296 if (element_info[element].group == NULL)
297 element_info[element].group =
298 checked_malloc(sizeof(struct ElementGroupInfo));
300 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
301 element_info[element].group->element[j] = EL_EMPTY_SPACE;
303 /* default: only one element in group */
304 element_info[element].group->num_elements = 1;
306 element_info[element].group->choice_mode = ANIM_RANDOM;
310 clipboard_elements_initialized = TRUE;
312 BorderElement = EL_STEELWALL;
314 level->no_valid_file = FALSE;
316 if (leveldir_current == NULL) /* only when dumping level */
319 /* try to determine better author name than 'anonymous' */
320 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
322 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
323 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
327 switch (LEVELCLASS(leveldir_current))
329 case LEVELCLASS_TUTORIAL:
330 strcpy(level->author, PROGRAM_AUTHOR_STRING);
333 case LEVELCLASS_CONTRIB:
334 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
335 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
338 case LEVELCLASS_PRIVATE:
339 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
340 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
344 /* keep default value */
350 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
352 level_file_info->nr = 0;
353 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
354 level_file_info->packed = FALSE;
355 level_file_info->basename = NULL;
356 level_file_info->filename = NULL;
359 static void ActivateLevelTemplate()
361 /* Currently there is no special action needed to activate the template
362 data, because 'element_info' and 'Properties' overwrite the original
363 level data, while all other variables do not change. */
366 static char *getLevelFilenameFromBasename(char *basename)
368 static char *filename = NULL;
370 checked_free(filename);
372 filename = getPath2(getCurrentLevelDir(), basename);
377 static int getFileTypeFromBasename(char *basename)
379 static char *filename = NULL;
380 struct stat file_status;
382 /* ---------- try to determine file type from filename ---------- */
384 /* check for typical filename of a Supaplex level package file */
385 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
386 strncmp(basename, "LEVELS.D", 8) == 0))
387 return LEVEL_FILE_TYPE_SP;
389 /* ---------- try to determine file type from filesize ---------- */
391 checked_free(filename);
392 filename = getPath2(getCurrentLevelDir(), basename);
394 if (stat(filename, &file_status) == 0)
396 /* check for typical filesize of a Supaplex level package file */
397 if (file_status.st_size == 170496)
398 return LEVEL_FILE_TYPE_SP;
401 return LEVEL_FILE_TYPE_UNKNOWN;
404 static char *getSingleLevelBasename(int nr, int type)
406 static char basename[MAX_FILENAME_LEN];
407 char *level_filename = getStringCopy(leveldir_current->level_filename);
409 if (level_filename == NULL)
410 level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION);
414 case LEVEL_FILE_TYPE_RND:
416 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
418 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
421 case LEVEL_FILE_TYPE_EM:
422 sprintf(basename, "%d", nr);
425 case LEVEL_FILE_TYPE_UNKNOWN:
427 sprintf(basename, level_filename, nr);
431 free(level_filename);
436 static char *getPackedLevelBasename(int type)
438 static char basename[MAX_FILENAME_LEN];
439 char *directory = getCurrentLevelDir();
441 struct dirent *dir_entry;
443 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
445 if ((dir = opendir(directory)) == NULL)
447 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
452 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
454 char *entry_basename = dir_entry->d_name;
455 int entry_type = getFileTypeFromBasename(entry_basename);
457 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
459 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
462 strcpy(basename, entry_basename);
474 static char *getSingleLevelFilename(int nr, int type)
476 return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
480 static char *getPackedLevelFilename(int type)
482 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
486 char *getDefaultLevelFilename(int nr)
488 return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
491 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
496 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
497 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
500 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
505 lfi->basename = getPackedLevelBasename(lfi->type);
506 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
509 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
511 /* special case: level number is negative => check for level template file */
514 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
519 if (leveldir_current->level_filename != NULL)
521 /* check for file name/pattern specified in "levelinfo.conf" */
522 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
523 if (fileExists(lfi->filename))
527 /* check for native Rocks'n'Diamonds level file */
528 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
529 if (fileExists(lfi->filename))
532 /* check for classic Emerald Mine level file */
533 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_EM);
534 if (fileExists(lfi->filename))
537 /* check for various packed level file formats */
538 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
539 if (fileExists(lfi->filename))
542 /* no known level file found -- try to use default values */
543 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
546 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
548 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
549 lfi->type = getFileTypeFromBasename(lfi->basename);
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 if (level->game_version <= VERSION_IDENT(3,1,0,0))
2379 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2381 int element = EL_CUSTOM_START + i;
2383 if (EXPLODES_1X1_OLD(element))
2384 element_info[element].explosion_type = EXPLODES_1X1;
2386 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
2387 EXPLODES_SMASHED(element) ||
2388 EXPLODES_IMPACT(element)));
2393 /* set default push delay values (corrected since version 3.0.7-1) */
2394 if (level->game_version < VERSION_IDENT(3,0,7,1))
2396 game.default_push_delay_fixed = 2;
2397 game.default_push_delay_random = 8;
2401 game.default_push_delay_fixed = 8;
2402 game.default_push_delay_random = 8;
2405 /* set uninitialized push delay values of custom elements in older levels */
2406 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2408 int element = EL_CUSTOM_START + i;
2410 if (element_info[element].push_delay_fixed == -1)
2411 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2412 if (element_info[element].push_delay_random == -1)
2413 element_info[element].push_delay_random = game.default_push_delay_random;
2417 /* map elements that have changed in newer versions */
2418 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
2419 level->game_version);
2420 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2421 for (x = 0; x < 3; x++)
2422 for (y = 0; y < 3; y++)
2423 level->yamyam_content[i][x][y] =
2424 getMappedElementByVersion(level->yamyam_content[i][x][y],
2425 level->game_version);
2427 /* initialize element properties for level editor etc. */
2428 InitElementPropertiesEngine(level->game_version);
2431 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2435 /* map elements that have changed in newer versions */
2436 for (y = 0; y < level->fieldy; y++)
2438 for (x = 0; x < level->fieldx; x++)
2440 int element = level->field[x][y];
2443 element = getMappedElementByVersion(element, level->game_version);
2445 if (level->game_version <= VERSION_IDENT(2,2,0,0))
2447 /* map game font elements */
2448 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2449 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2450 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2451 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2454 if (level->game_version < VERSION_IDENT(3,0,0,0))
2456 /* map Supaplex gravity tube elements */
2457 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2458 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2459 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2460 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2465 level->field[x][y] = element;
2469 /* copy elements to runtime playfield array */
2470 for (x = 0; x < MAX_LEV_FIELDX; x++)
2471 for (y = 0; y < MAX_LEV_FIELDY; y++)
2472 Feld[x][y] = level->field[x][y];
2474 /* initialize level size variables for faster access */
2475 lev_fieldx = level->fieldx;
2476 lev_fieldy = level->fieldy;
2478 /* determine border element for this level */
2482 void LoadLevelTemplate(int nr)
2485 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2486 char *filename = level_file_info->filename;
2488 LoadLevelFromFileInfo(&level_template, level_file_info);
2490 char *filename = getDefaultLevelFilename(nr);
2492 LoadLevelFromFilename_RND(&level_template, filename);
2496 LoadLevel_InitVersion(&level_template, filename);
2497 LoadLevel_InitElements(&level_template, filename);
2499 LoadLevel_InitVersion(&level, filename);
2500 LoadLevel_InitElements(&level, filename);
2503 ActivateLevelTemplate();
2506 void LoadLevel(int nr)
2509 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2510 char *filename = level_file_info->filename;
2512 LoadLevelFromFileInfo(&level, level_file_info);
2514 char *filename = getLevelFilename(nr);
2516 LoadLevelFromFilename_RND(&level, filename);
2519 if (level.use_custom_template)
2520 LoadLevelTemplate(-1);
2523 LoadLevel_InitVersion(&level, filename);
2524 LoadLevel_InitElements(&level, filename);
2525 LoadLevel_InitPlayfield(&level, filename);
2527 LoadLevel_InitLevel(&level, filename);
2531 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2533 putFileVersion(file, level->file_version);
2534 putFileVersion(file, level->game_version);
2537 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2541 putFile8Bit(file, level->fieldx);
2542 putFile8Bit(file, level->fieldy);
2544 putFile16BitBE(file, level->time);
2545 putFile16BitBE(file, level->gems_needed);
2547 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2548 putFile8Bit(file, level->name[i]);
2550 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2551 putFile8Bit(file, level->score[i]);
2553 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2554 for (y = 0; y < 3; y++)
2555 for (x = 0; x < 3; x++)
2556 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
2557 level->yamyam_content[i][x][y]));
2558 putFile8Bit(file, level->amoeba_speed);
2559 putFile8Bit(file, level->time_magic_wall);
2560 putFile8Bit(file, level->time_wheel);
2561 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
2562 level->amoeba_content));
2563 putFile8Bit(file, (level->double_speed ? 1 : 0));
2564 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
2565 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
2566 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
2568 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
2570 putFile8Bit(file, (level->block_last_field ? 1 : 0));
2571 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
2572 putFile32BitBE(file, level->can_move_into_acid_bits);
2573 putFile8Bit(file, level->dont_collide_with_bits);
2575 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
2576 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
2578 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
2579 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
2580 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
2582 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
2585 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
2589 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2590 putFile8Bit(file, level->author[i]);
2593 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
2597 for (y = 0; y < level->fieldy; y++)
2598 for (x = 0; x < level->fieldx; x++)
2599 if (level->encoding_16bit_field)
2600 putFile16BitBE(file, level->field[x][y]);
2602 putFile8Bit(file, level->field[x][y]);
2606 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
2610 putFile8Bit(file, EL_YAMYAM);
2611 putFile8Bit(file, level->num_yamyam_contents);
2612 putFile8Bit(file, 0);
2613 putFile8Bit(file, 0);
2615 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2616 for (y = 0; y < 3; y++)
2617 for (x = 0; x < 3; x++)
2618 if (level->encoding_16bit_field)
2619 putFile16BitBE(file, level->yamyam_content[i][x][y]);
2621 putFile8Bit(file, level->yamyam_content[i][x][y]);
2625 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
2628 int num_contents, content_xsize, content_ysize;
2629 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2631 if (element == EL_YAMYAM)
2633 num_contents = level->num_yamyam_contents;
2637 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2638 for (y = 0; y < 3; y++)
2639 for (x = 0; x < 3; x++)
2640 content_array[i][x][y] = level->yamyam_content[i][x][y];
2642 else if (element == EL_BD_AMOEBA)
2648 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2649 for (y = 0; y < 3; y++)
2650 for (x = 0; x < 3; x++)
2651 content_array[i][x][y] = EL_EMPTY;
2652 content_array[0][0][0] = level->amoeba_content;
2656 /* chunk header already written -- write empty chunk data */
2657 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
2659 Error(ERR_WARN, "cannot save content for element '%d'", element);
2663 putFile16BitBE(file, element);
2664 putFile8Bit(file, num_contents);
2665 putFile8Bit(file, content_xsize);
2666 putFile8Bit(file, content_ysize);
2668 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2670 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2671 for (y = 0; y < 3; y++)
2672 for (x = 0; x < 3; x++)
2673 putFile16BitBE(file, content_array[i][x][y]);
2676 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
2679 int envelope_nr = element - EL_ENVELOPE_1;
2680 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
2682 putFile16BitBE(file, element);
2683 putFile16BitBE(file, envelope_len);
2684 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
2685 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
2687 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2689 for (i = 0; i < envelope_len; i++)
2690 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
2694 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
2695 int num_changed_custom_elements)
2699 putFile16BitBE(file, num_changed_custom_elements);
2701 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2703 int element = EL_CUSTOM_START + i;
2705 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
2707 if (check < num_changed_custom_elements)
2709 putFile16BitBE(file, element);
2710 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2717 if (check != num_changed_custom_elements) /* should not happen */
2718 Error(ERR_WARN, "inconsistent number of custom element properties");
2723 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
2724 int num_changed_custom_elements)
2728 putFile16BitBE(file, num_changed_custom_elements);
2730 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2732 int element = EL_CUSTOM_START + i;
2734 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
2736 if (check < num_changed_custom_elements)
2738 putFile16BitBE(file, element);
2739 putFile16BitBE(file, element_info[element].change->target_element);
2746 if (check != num_changed_custom_elements) /* should not happen */
2747 Error(ERR_WARN, "inconsistent number of custom target elements");
2752 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
2753 int num_changed_custom_elements)
2755 int i, j, x, y, check = 0;
2757 putFile16BitBE(file, num_changed_custom_elements);
2759 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2761 int element = EL_CUSTOM_START + i;
2763 if (element_info[element].modified_settings)
2765 if (check < num_changed_custom_elements)
2767 putFile16BitBE(file, element);
2769 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2770 putFile8Bit(file, element_info[element].description[j]);
2772 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2774 /* some free bytes for future properties and padding */
2775 WriteUnusedBytesToFile(file, 7);
2777 putFile8Bit(file, element_info[element].use_gfx_element);
2778 putFile16BitBE(file, element_info[element].gfx_element);
2780 putFile8Bit(file, element_info[element].collect_score);
2781 putFile8Bit(file, element_info[element].collect_count);
2783 putFile16BitBE(file, element_info[element].push_delay_fixed);
2784 putFile16BitBE(file, element_info[element].push_delay_random);
2785 putFile16BitBE(file, element_info[element].move_delay_fixed);
2786 putFile16BitBE(file, element_info[element].move_delay_random);
2788 putFile16BitBE(file, element_info[element].move_pattern);
2789 putFile8Bit(file, element_info[element].move_direction_initial);
2790 putFile8Bit(file, element_info[element].move_stepsize);
2792 for (y = 0; y < 3; y++)
2793 for (x = 0; x < 3; x++)
2794 putFile16BitBE(file, element_info[element].content[x][y]);
2796 putFile32BitBE(file, element_info[element].change->events);
2798 putFile16BitBE(file, element_info[element].change->target_element);
2800 putFile16BitBE(file, element_info[element].change->delay_fixed);
2801 putFile16BitBE(file, element_info[element].change->delay_random);
2802 putFile16BitBE(file, element_info[element].change->delay_frames);
2804 putFile16BitBE(file, element_info[element].change->trigger_element);
2806 putFile8Bit(file, element_info[element].change->explode);
2807 putFile8Bit(file, element_info[element].change->use_target_content);
2808 putFile8Bit(file, element_info[element].change->only_if_complete);
2809 putFile8Bit(file, element_info[element].change->use_random_replace);
2811 putFile8Bit(file, element_info[element].change->random_percentage);
2812 putFile8Bit(file, element_info[element].change->replace_when);
2814 for (y = 0; y < 3; y++)
2815 for (x = 0; x < 3; x++)
2816 putFile16BitBE(file, element_info[element].change->content[x][y]);
2818 putFile8Bit(file, element_info[element].slippery_type);
2820 /* some free bytes for future properties and padding */
2821 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
2828 if (check != num_changed_custom_elements) /* should not happen */
2829 Error(ERR_WARN, "inconsistent number of custom element properties");
2833 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2835 struct ElementInfo *ei = &element_info[element];
2838 putFile16BitBE(file, element);
2840 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2841 putFile8Bit(file, ei->description[i]);
2843 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2844 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
2846 putFile8Bit(file, ei->num_change_pages);
2848 /* some free bytes for future base property values and padding */
2849 WriteUnusedBytesToFile(file, 5);
2851 /* write custom property values */
2853 putFile8Bit(file, ei->use_gfx_element);
2854 putFile16BitBE(file, ei->gfx_element);
2856 putFile8Bit(file, ei->collect_score);
2857 putFile8Bit(file, ei->collect_count);
2859 putFile8Bit(file, ei->drop_delay_fixed);
2860 putFile8Bit(file, ei->push_delay_fixed);
2861 putFile8Bit(file, ei->drop_delay_random);
2862 putFile8Bit(file, ei->push_delay_random);
2863 putFile16BitBE(file, ei->move_delay_fixed);
2864 putFile16BitBE(file, ei->move_delay_random);
2866 /* bits 0 - 15 of "move_pattern" ... */
2867 putFile16BitBE(file, ei->move_pattern & 0xffff);
2868 putFile8Bit(file, ei->move_direction_initial);
2869 putFile8Bit(file, ei->move_stepsize);
2871 putFile8Bit(file, ei->slippery_type);
2873 for (y = 0; y < 3; y++)
2874 for (x = 0; x < 3; x++)
2875 putFile16BitBE(file, ei->content[x][y]);
2877 putFile16BitBE(file, ei->move_enter_element);
2878 putFile16BitBE(file, ei->move_leave_element);
2879 putFile8Bit(file, ei->move_leave_type);
2881 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2882 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
2884 putFile8Bit(file, ei->access_direction);
2886 putFile8Bit(file, ei->explosion_delay);
2887 putFile8Bit(file, ei->ignition_delay);
2888 putFile8Bit(file, ei->explosion_type);
2890 /* some free bytes for future custom property values and padding */
2891 WriteUnusedBytesToFile(file, 1);
2893 /* write change property values */
2895 for (i = 0; i < ei->num_change_pages; i++)
2897 struct ElementChangeInfo *change = &ei->change_page[i];
2899 putFile32BitBE(file, change->events);
2901 putFile16BitBE(file, change->target_element);
2903 putFile16BitBE(file, change->delay_fixed);
2904 putFile16BitBE(file, change->delay_random);
2905 putFile16BitBE(file, change->delay_frames);
2907 putFile16BitBE(file, change->trigger_element);
2909 putFile8Bit(file, change->explode);
2910 putFile8Bit(file, change->use_target_content);
2911 putFile8Bit(file, change->only_if_complete);
2912 putFile8Bit(file, change->use_random_replace);
2914 putFile8Bit(file, change->random_percentage);
2915 putFile8Bit(file, change->replace_when);
2917 for (y = 0; y < 3; y++)
2918 for (x = 0; x < 3; x++)
2919 putFile16BitBE(file, change->target_content[x][y]);
2921 putFile8Bit(file, change->can_change);
2923 putFile8Bit(file, change->trigger_side);
2926 putFile8Bit(file, change->trigger_player);
2927 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
2928 log_2(change->trigger_page)));
2930 /* some free bytes for future change property values and padding */
2931 WriteUnusedBytesToFile(file, 6);
2935 /* some free bytes for future change property values and padding */
2936 WriteUnusedBytesToFile(file, 8);
2941 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2943 struct ElementInfo *ei = &element_info[element];
2944 struct ElementGroupInfo *group = ei->group;
2947 putFile16BitBE(file, element);
2949 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2950 putFile8Bit(file, ei->description[i]);
2952 putFile8Bit(file, group->num_elements);
2954 putFile8Bit(file, ei->use_gfx_element);
2955 putFile16BitBE(file, ei->gfx_element);
2957 putFile8Bit(file, group->choice_mode);
2959 /* some free bytes for future values and padding */
2960 WriteUnusedBytesToFile(file, 3);
2962 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2963 putFile16BitBE(file, group->element[i]);
2966 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2968 int body_chunk_size;
2972 if (!(file = fopen(filename, MODE_WRITE)))
2974 Error(ERR_WARN, "cannot save level file '%s'", filename);
2978 level->file_version = FILE_VERSION_ACTUAL;
2979 level->game_version = GAME_VERSION_ACTUAL;
2981 /* check level field for 16-bit elements */
2982 level->encoding_16bit_field = FALSE;
2983 for (y = 0; y < level->fieldy; y++)
2984 for (x = 0; x < level->fieldx; x++)
2985 if (level->field[x][y] > 255)
2986 level->encoding_16bit_field = TRUE;
2988 /* check yamyam content for 16-bit elements */
2989 level->encoding_16bit_yamyam = FALSE;
2990 for (i = 0; i < level->num_yamyam_contents; i++)
2991 for (y = 0; y < 3; y++)
2992 for (x = 0; x < 3; x++)
2993 if (level->yamyam_content[i][x][y] > 255)
2994 level->encoding_16bit_yamyam = TRUE;
2996 /* check amoeba content for 16-bit elements */
2997 level->encoding_16bit_amoeba = FALSE;
2998 if (level->amoeba_content > 255)
2999 level->encoding_16bit_amoeba = TRUE;
3001 /* calculate size of "BODY" chunk */
3003 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
3005 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3006 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
3008 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3009 SaveLevel_VERS(file, level);
3011 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
3012 SaveLevel_HEAD(file, level);
3014 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
3015 SaveLevel_AUTH(file, level);
3017 putFileChunkBE(file, "BODY", body_chunk_size);
3018 SaveLevel_BODY(file, level);
3020 if (level->encoding_16bit_yamyam ||
3021 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
3023 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3024 SaveLevel_CNT2(file, level, EL_YAMYAM);
3027 if (level->encoding_16bit_amoeba)
3029 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
3030 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
3033 /* check for envelope content */
3034 for (i = 0; i < 4; i++)
3036 if (strlen(level->envelope_text[i]) > 0)
3038 int envelope_len = strlen(level->envelope_text[i]) + 1;
3040 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
3041 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
3045 /* check for non-default custom elements (unless using template level) */
3046 if (!level->use_custom_template)
3048 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3050 int element = EL_CUSTOM_START + i;
3052 if (element_info[element].modified_settings)
3054 int num_change_pages = element_info[element].num_change_pages;
3056 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
3057 SaveLevel_CUS4(file, level, element);
3062 /* check for non-default group elements (unless using template level) */
3063 if (!level->use_custom_template)
3065 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
3067 int element = EL_GROUP_START + i;
3069 if (element_info[element].modified_settings)
3071 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
3072 SaveLevel_GRP1(file, level, element);
3079 SetFilePermissions(filename, PERMS_PRIVATE);
3082 void SaveLevel(int nr)
3084 char *filename = getDefaultLevelFilename(nr);
3086 SaveLevelFromFilename(&level, filename);
3089 void SaveLevelTemplate()
3091 char *filename = getDefaultLevelFilename(-1);
3093 SaveLevelFromFilename(&level, filename);
3096 void DumpLevel(struct LevelInfo *level)
3098 if (level->no_valid_file)
3100 Error(ERR_WARN, "cannot dump -- no valid level file found");
3105 printf_line("-", 79);
3106 printf("Level xxx (file version %08d, game version %08d)\n",
3107 level->file_version, level->game_version);
3108 printf_line("-", 79);
3110 printf("Level author: '%s'\n", level->author);
3111 printf("Level title: '%s'\n", level->name);
3113 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
3115 printf("Level time: %d seconds\n", level->time);
3116 printf("Gems needed: %d\n", level->gems_needed);
3118 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
3119 printf("Time for wheel: %d seconds\n", level->time_wheel);
3120 printf("Time for light: %d seconds\n", level->time_light);
3121 printf("Time for timegate: %d seconds\n", level->time_timegate);
3123 printf("Amoeba speed: %d\n", level->amoeba_speed);
3125 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
3126 printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no"));
3127 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
3128 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
3129 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
3130 printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
3131 printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
3133 printf_line("-", 79);
3137 /* ========================================================================= */
3138 /* tape file functions */
3139 /* ========================================================================= */
3141 static void setTapeInfoToDefaults()
3145 /* always start with reliable default values (empty tape) */
3148 /* default values (also for pre-1.2 tapes) with only the first player */
3149 tape.player_participates[0] = TRUE;
3150 for (i = 1; i < MAX_PLAYERS; i++)
3151 tape.player_participates[i] = FALSE;
3153 /* at least one (default: the first) player participates in every tape */
3154 tape.num_participating_players = 1;
3156 tape.level_nr = level_nr;
3158 tape.changed = FALSE;
3160 tape.recording = FALSE;
3161 tape.playing = FALSE;
3162 tape.pausing = FALSE;
3164 tape.no_valid_file = FALSE;
3167 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
3169 tape->file_version = getFileVersion(file);
3170 tape->game_version = getFileVersion(file);
3175 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
3179 tape->random_seed = getFile32BitBE(file);
3180 tape->date = getFile32BitBE(file);
3181 tape->length = getFile32BitBE(file);
3183 /* read header fields that are new since version 1.2 */
3184 if (tape->file_version >= FILE_VERSION_1_2)
3186 byte store_participating_players = getFile8Bit(file);
3189 /* since version 1.2, tapes store which players participate in the tape */
3190 tape->num_participating_players = 0;
3191 for (i = 0; i < MAX_PLAYERS; i++)
3193 tape->player_participates[i] = FALSE;
3195 if (store_participating_players & (1 << i))
3197 tape->player_participates[i] = TRUE;
3198 tape->num_participating_players++;
3202 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
3204 engine_version = getFileVersion(file);
3205 if (engine_version > 0)
3206 tape->engine_version = engine_version;
3208 tape->engine_version = tape->game_version;
3214 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
3216 int level_identifier_size;
3219 level_identifier_size = getFile16BitBE(file);
3221 tape->level_identifier =
3222 checked_realloc(tape->level_identifier, level_identifier_size);
3224 for (i = 0; i < level_identifier_size; i++)
3225 tape->level_identifier[i] = getFile8Bit(file);
3227 tape->level_nr = getFile16BitBE(file);
3229 chunk_size = 2 + level_identifier_size + 2;
3234 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
3237 int chunk_size_expected =
3238 (tape->num_participating_players + 1) * tape->length;
3240 if (chunk_size_expected != chunk_size)
3242 ReadUnusedBytesFromFile(file, chunk_size);
3243 return chunk_size_expected;
3246 for (i = 0; i < tape->length; i++)
3248 if (i >= MAX_TAPELEN)
3251 for (j = 0; j < MAX_PLAYERS; j++)
3253 tape->pos[i].action[j] = MV_NO_MOVING;
3255 if (tape->player_participates[j])
3256 tape->pos[i].action[j] = getFile8Bit(file);
3259 tape->pos[i].delay = getFile8Bit(file);
3261 if (tape->file_version == FILE_VERSION_1_0)
3263 /* eliminate possible diagonal moves in old tapes */
3264 /* this is only for backward compatibility */
3266 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
3267 byte action = tape->pos[i].action[0];
3268 int k, num_moves = 0;
3270 for (k = 0; k<4; k++)
3272 if (action & joy_dir[k])
3274 tape->pos[i + num_moves].action[0] = joy_dir[k];
3276 tape->pos[i + num_moves].delay = 0;
3285 tape->length += num_moves;
3288 else if (tape->file_version < FILE_VERSION_2_0)
3290 /* convert pre-2.0 tapes to new tape format */
3292 if (tape->pos[i].delay > 1)
3295 tape->pos[i + 1] = tape->pos[i];
3296 tape->pos[i + 1].delay = 1;
3299 for (j = 0; j < MAX_PLAYERS; j++)
3300 tape->pos[i].action[j] = MV_NO_MOVING;
3301 tape->pos[i].delay--;
3312 if (i != tape->length)
3313 chunk_size = (tape->num_participating_players + 1) * i;
3318 void LoadTapeFromFilename(char *filename)
3320 char cookie[MAX_LINE_LEN];
3321 char chunk_name[CHUNK_ID_LEN + 1];
3325 /* always start with reliable default values */
3326 setTapeInfoToDefaults();
3328 if (!(file = fopen(filename, MODE_READ)))
3330 tape.no_valid_file = TRUE;
3333 Error(ERR_WARN, "cannot read tape '%s' -- using empty tape", filename);
3339 getFileChunkBE(file, chunk_name, NULL);
3340 if (strcmp(chunk_name, "RND1") == 0)
3342 getFile32BitBE(file); /* not used */
3344 getFileChunkBE(file, chunk_name, NULL);
3345 if (strcmp(chunk_name, "TAPE") != 0)
3347 tape.no_valid_file = TRUE;
3349 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3354 else /* check for pre-2.0 file format with cookie string */
3356 strcpy(cookie, chunk_name);
3357 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
3358 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3359 cookie[strlen(cookie) - 1] = '\0';
3361 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
3363 tape.no_valid_file = TRUE;
3365 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
3370 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
3372 tape.no_valid_file = TRUE;
3374 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
3379 /* pre-2.0 tape files have no game version, so use file version here */
3380 tape.game_version = tape.file_version;
3383 if (tape.file_version < FILE_VERSION_1_2)
3385 /* tape files from versions before 1.2.0 without chunk structure */
3386 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
3387 LoadTape_BODY(file, 2 * tape.length, &tape);
3395 int (*loader)(FILE *, int, struct TapeInfo *);
3399 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
3400 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
3401 { "INFO", -1, LoadTape_INFO },
3402 { "BODY", -1, LoadTape_BODY },
3406 while (getFileChunkBE(file, chunk_name, &chunk_size))
3410 while (chunk_info[i].name != NULL &&
3411 strcmp(chunk_name, chunk_info[i].name) != 0)
3414 if (chunk_info[i].name == NULL)
3416 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
3417 chunk_name, filename);
3418 ReadUnusedBytesFromFile(file, chunk_size);
3420 else if (chunk_info[i].size != -1 &&
3421 chunk_info[i].size != chunk_size)
3423 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3424 chunk_size, chunk_name, filename);
3425 ReadUnusedBytesFromFile(file, chunk_size);
3429 /* call function to load this tape chunk */
3430 int chunk_size_expected =
3431 (chunk_info[i].loader)(file, chunk_size, &tape);
3433 /* the size of some chunks cannot be checked before reading other
3434 chunks first (like "HEAD" and "BODY") that contain some header
3435 information, so check them here */
3436 if (chunk_size_expected != chunk_size)
3438 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
3439 chunk_size, chunk_name, filename);
3447 tape.length_seconds = GetTapeLength();
3450 printf("::: tape game version: %d\n", tape.game_version);
3451 printf("::: tape engine version: %d\n", tape.engine_version);
3455 void LoadTape(int nr)
3457 char *filename = getTapeFilename(nr);
3459 LoadTapeFromFilename(filename);
3462 void LoadSolutionTape(int nr)
3464 char *filename = getSolutionTapeFilename(nr);
3466 LoadTapeFromFilename(filename);
3469 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
3471 putFileVersion(file, tape->file_version);
3472 putFileVersion(file, tape->game_version);
3475 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
3478 byte store_participating_players = 0;
3480 /* set bits for participating players for compact storage */
3481 for (i = 0; i < MAX_PLAYERS; i++)
3482 if (tape->player_participates[i])
3483 store_participating_players |= (1 << i);
3485 putFile32BitBE(file, tape->random_seed);
3486 putFile32BitBE(file, tape->date);
3487 putFile32BitBE(file, tape->length);
3489 putFile8Bit(file, store_participating_players);
3491 /* unused bytes not at the end here for 4-byte alignment of engine_version */
3492 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3494 putFileVersion(file, tape->engine_version);
3497 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3499 int level_identifier_size = strlen(tape->level_identifier) + 1;
3502 putFile16BitBE(file, level_identifier_size);
3504 for (i = 0; i < level_identifier_size; i++)
3505 putFile8Bit(file, tape->level_identifier[i]);
3507 putFile16BitBE(file, tape->level_nr);
3510 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3514 for (i = 0; i < tape->length; i++)
3516 for (j = 0; j < MAX_PLAYERS; j++)
3517 if (tape->player_participates[j])
3518 putFile8Bit(file, tape->pos[i].action[j]);
3520 putFile8Bit(file, tape->pos[i].delay);
3524 void SaveTape(int nr)
3526 char *filename = getTapeFilename(nr);
3528 boolean new_tape = TRUE;
3529 int num_participating_players = 0;
3530 int info_chunk_size;
3531 int body_chunk_size;
3534 InitTapeDirectory(leveldir_current->subdir);
3536 /* if a tape still exists, ask to overwrite it */
3537 if (access(filename, F_OK) == 0)
3540 if (!Request("Replace old tape ?", REQ_ASK))
3544 if (!(file = fopen(filename, MODE_WRITE)))
3546 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
3550 tape.file_version = FILE_VERSION_ACTUAL;
3551 tape.game_version = GAME_VERSION_ACTUAL;
3553 /* count number of participating players */
3554 for (i = 0; i < MAX_PLAYERS; i++)
3555 if (tape.player_participates[i])
3556 num_participating_players++;
3558 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
3559 body_chunk_size = (num_participating_players + 1) * tape.length;
3561 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3562 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
3564 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3565 SaveTape_VERS(file, &tape);
3567 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
3568 SaveTape_HEAD(file, &tape);
3570 putFileChunkBE(file, "INFO", info_chunk_size);
3571 SaveTape_INFO(file, &tape);
3573 putFileChunkBE(file, "BODY", body_chunk_size);
3574 SaveTape_BODY(file, &tape);
3578 SetFilePermissions(filename, PERMS_PRIVATE);
3580 tape.changed = FALSE;
3583 Request("tape saved !", REQ_CONFIRM);
3586 void DumpTape(struct TapeInfo *tape)
3591 if (tape->no_valid_file)
3593 Error(ERR_WARN, "cannot dump -- no valid tape file found");
3598 if (TAPE_IS_EMPTY(*tape))
3600 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
3606 printf_line("-", 79);
3607 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
3608 tape->level_nr, tape->file_version, tape->game_version);
3609 printf("Level series identifier: '%s'\n", tape->level_identifier);
3610 printf_line("-", 79);
3612 for (i = 0; i < tape->length; i++)
3614 if (i >= MAX_TAPELEN)
3617 printf("%03d: ", i);
3619 for (j = 0; j < MAX_PLAYERS; j++)
3621 if (tape->player_participates[j])
3623 int action = tape->pos[i].action[j];
3625 printf("%d:%02x ", j, action);
3626 printf("[%c%c%c%c|%c%c] - ",
3627 (action & JOY_LEFT ? '<' : ' '),
3628 (action & JOY_RIGHT ? '>' : ' '),
3629 (action & JOY_UP ? '^' : ' '),
3630 (action & JOY_DOWN ? 'v' : ' '),
3631 (action & JOY_BUTTON_1 ? '1' : ' '),
3632 (action & JOY_BUTTON_2 ? '2' : ' '));
3636 printf("(%03d)\n", tape->pos[i].delay);
3639 printf_line("-", 79);
3643 /* ========================================================================= */
3644 /* score file functions */
3645 /* ========================================================================= */
3647 void LoadScore(int nr)
3650 char *filename = getScoreFilename(nr);
3651 char cookie[MAX_LINE_LEN];
3652 char line[MAX_LINE_LEN];
3656 /* always start with reliable default values */
3657 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3659 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
3660 highscore[i].Score = 0;
3663 if (!(file = fopen(filename, MODE_READ)))
3666 /* check file identifier */
3667 fgets(cookie, MAX_LINE_LEN, file);
3668 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3669 cookie[strlen(cookie) - 1] = '\0';
3671 if (!checkCookieString(cookie, SCORE_COOKIE))
3673 Error(ERR_WARN, "unknown format of score file '%s'", filename);
3678 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3680 fscanf(file, "%d", &highscore[i].Score);
3681 fgets(line, MAX_LINE_LEN, file);
3683 if (line[strlen(line) - 1] == '\n')
3684 line[strlen(line) - 1] = '\0';
3686 for (line_ptr = line; *line_ptr; line_ptr++)
3688 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3690 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
3691 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
3700 void SaveScore(int nr)
3703 char *filename = getScoreFilename(nr);
3706 InitScoreDirectory(leveldir_current->subdir);
3708 if (!(file = fopen(filename, MODE_WRITE)))
3710 Error(ERR_WARN, "cannot save score for level %d", nr);
3714 fprintf(file, "%s\n\n", SCORE_COOKIE);
3716 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3717 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
3721 SetFilePermissions(filename, PERMS_PUBLIC);
3725 /* ========================================================================= */
3726 /* setup file functions */
3727 /* ========================================================================= */
3729 #define TOKEN_STR_PLAYER_PREFIX "player_"
3732 #define SETUP_TOKEN_PLAYER_NAME 0
3733 #define SETUP_TOKEN_SOUND 1
3734 #define SETUP_TOKEN_SOUND_LOOPS 2
3735 #define SETUP_TOKEN_SOUND_MUSIC 3
3736 #define SETUP_TOKEN_SOUND_SIMPLE 4
3737 #define SETUP_TOKEN_TOONS 5
3738 #define SETUP_TOKEN_SCROLL_DELAY 6
3739 #define SETUP_TOKEN_SOFT_SCROLLING 7
3740 #define SETUP_TOKEN_FADING 8
3741 #define SETUP_TOKEN_AUTORECORD 9
3742 #define SETUP_TOKEN_QUICK_DOORS 10
3743 #define SETUP_TOKEN_TEAM_MODE 11
3744 #define SETUP_TOKEN_HANDICAP 12
3745 #define SETUP_TOKEN_TIME_LIMIT 13
3746 #define SETUP_TOKEN_FULLSCREEN 14
3747 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
3748 #define SETUP_TOKEN_GRAPHICS_SET 16
3749 #define SETUP_TOKEN_SOUNDS_SET 17
3750 #define SETUP_TOKEN_MUSIC_SET 18
3751 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
3752 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
3753 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
3755 #define NUM_GLOBAL_SETUP_TOKENS 22
3758 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
3759 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
3760 #define SETUP_TOKEN_EDITOR_EL_MORE 2
3761 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
3762 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
3763 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
3764 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
3765 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
3766 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
3767 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
3768 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
3769 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
3771 #define NUM_EDITOR_SETUP_TOKENS 12
3773 /* shortcut setup */
3774 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
3775 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
3776 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
3778 #define NUM_SHORTCUT_SETUP_TOKENS 3
3781 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
3782 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
3783 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
3784 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
3785 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
3786 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
3787 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
3788 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
3789 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
3790 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
3791 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
3792 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
3793 #define SETUP_TOKEN_PLAYER_KEY_UP 12
3794 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
3795 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
3796 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
3798 #define NUM_PLAYER_SETUP_TOKENS 16
3801 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
3802 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
3804 #define NUM_SYSTEM_SETUP_TOKENS 2
3807 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
3809 #define NUM_OPTIONS_SETUP_TOKENS 1
3812 static struct SetupInfo si;
3813 static struct SetupEditorInfo sei;
3814 static struct SetupShortcutInfo ssi;
3815 static struct SetupInputInfo sii;
3816 static struct SetupSystemInfo syi;
3817 static struct OptionInfo soi;
3819 static struct TokenInfo global_setup_tokens[] =
3821 { TYPE_STRING, &si.player_name, "player_name" },
3822 { TYPE_SWITCH, &si.sound, "sound" },
3823 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
3824 { TYPE_SWITCH, &si.sound_music, "background_music" },
3825 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
3826 { TYPE_SWITCH, &si.toons, "toons" },
3827 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
3828 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
3829 { TYPE_SWITCH, &si.fading, "screen_fading" },
3830 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
3831 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
3832 { TYPE_SWITCH, &si.team_mode, "team_mode" },
3833 { TYPE_SWITCH, &si.handicap, "handicap" },
3834 { TYPE_SWITCH, &si.time_limit, "time_limit" },
3835 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
3836 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
3837 { TYPE_STRING, &si.graphics_set, "graphics_set" },
3838 { TYPE_STRING, &si.sounds_set, "sounds_set" },
3839 { TYPE_STRING, &si.music_set, "music_set" },
3840 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
3841 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
3842 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
3845 static struct TokenInfo editor_setup_tokens[] =
3847 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
3848 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
3849 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
3850 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
3851 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
3852 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
3853 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
3854 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
3855 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
3856 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
3857 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
3858 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
3861 static struct TokenInfo shortcut_setup_tokens[] =
3863 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
3864 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
3865 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
3868 static struct TokenInfo player_setup_tokens[] =
3870 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
3871 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
3872 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
3873 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
3874 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
3875 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
3876 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
3877 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
3878 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
3879 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
3880 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
3881 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
3882 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
3883 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
3884 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
3885 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
3888 static struct TokenInfo system_setup_tokens[] =
3890 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
3891 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
3894 static struct TokenInfo options_setup_tokens[] =
3896 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
3899 static char *get_corrected_login_name(char *login_name)
3901 /* needed because player name must be a fixed length string */
3902 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3904 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3905 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3907 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
3908 if (strchr(login_name_new, ' '))
3909 *strchr(login_name_new, ' ') = '\0';
3911 return login_name_new;
3914 static void setSetupInfoToDefaults(struct SetupInfo *si)
3918 si->player_name = get_corrected_login_name(getLoginName());
3921 si->sound_loops = TRUE;
3922 si->sound_music = TRUE;
3923 si->sound_simple = TRUE;
3925 si->double_buffering = TRUE;
3926 si->direct_draw = !si->double_buffering;
3927 si->scroll_delay = TRUE;
3928 si->soft_scrolling = TRUE;
3930 si->autorecord = TRUE;
3931 si->quick_doors = FALSE;
3932 si->team_mode = FALSE;
3933 si->handicap = TRUE;
3934 si->time_limit = TRUE;
3935 si->fullscreen = FALSE;
3936 si->ask_on_escape = TRUE;
3938 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3939 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3940 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3941 si->override_level_graphics = FALSE;
3942 si->override_level_sounds = FALSE;
3943 si->override_level_music = FALSE;
3945 si->editor.el_boulderdash = TRUE;
3946 si->editor.el_emerald_mine = TRUE;
3947 si->editor.el_more = TRUE;
3948 si->editor.el_sokoban = TRUE;
3949 si->editor.el_supaplex = TRUE;
3950 si->editor.el_diamond_caves = TRUE;
3951 si->editor.el_dx_boulderdash = TRUE;
3952 si->editor.el_chars = TRUE;
3953 si->editor.el_custom = TRUE;
3954 si->editor.el_custom_more = FALSE;
3956 si->editor.el_headlines = TRUE;
3957 si->editor.el_user_defined = FALSE;
3959 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3960 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3961 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3963 for (i = 0; i < MAX_PLAYERS; i++)
3965 si->input[i].use_joystick = FALSE;
3966 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3967 si->input[i].joy.xleft = JOYSTICK_XLEFT;
3968 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3969 si->input[i].joy.xright = JOYSTICK_XRIGHT;
3970 si->input[i].joy.yupper = JOYSTICK_YUPPER;
3971 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3972 si->input[i].joy.ylower = JOYSTICK_YLOWER;
3973 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
3974 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
3975 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
3976 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3977 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
3978 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
3979 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
3980 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
3983 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3984 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3986 si->options.verbose = FALSE;
3989 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3993 if (!setup_file_hash)
3998 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3999 setSetupInfo(global_setup_tokens, i,
4000 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
4005 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4006 setSetupInfo(editor_setup_tokens, i,
4007 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
4010 /* shortcut setup */
4011 ssi = setup.shortcut;
4012 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4013 setSetupInfo(shortcut_setup_tokens, i,
4014 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
4015 setup.shortcut = ssi;
4018 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4022 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4024 sii = setup.input[pnr];
4025 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4027 char full_token[100];
4029 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
4030 setSetupInfo(player_setup_tokens, i,
4031 getHashEntry(setup_file_hash, full_token));
4033 setup.input[pnr] = sii;
4038 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4039 setSetupInfo(system_setup_tokens, i,
4040 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
4044 soi = setup.options;
4045 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4046 setSetupInfo(options_setup_tokens, i,
4047 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
4048 setup.options = soi;
4053 char *filename = getSetupFilename();
4054 SetupFileHash *setup_file_hash = NULL;
4056 /* always start with reliable default values */
4057 setSetupInfoToDefaults(&setup);
4059 setup_file_hash = loadSetupFileHash(filename);
4061 if (setup_file_hash)
4063 char *player_name_new;
4065 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
4066 decodeSetupFileHash(setup_file_hash);
4068 setup.direct_draw = !setup.double_buffering;
4070 freeSetupFileHash(setup_file_hash);
4072 /* needed to work around problems with fixed length strings */
4073 player_name_new = get_corrected_login_name(setup.player_name);
4074 free(setup.player_name);
4075 setup.player_name = player_name_new;
4078 Error(ERR_WARN, "using default setup values");
4083 char *filename = getSetupFilename();
4087 InitUserDataDirectory();
4089 if (!(file = fopen(filename, MODE_WRITE)))
4091 Error(ERR_WARN, "cannot write setup file '%s'", filename);
4095 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
4096 getCookie("SETUP")));
4097 fprintf(file, "\n");
4101 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
4103 /* just to make things nicer :) */
4104 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
4105 i == SETUP_TOKEN_GRAPHICS_SET)
4106 fprintf(file, "\n");
4108 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
4113 fprintf(file, "\n");
4114 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
4115 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
4117 /* shortcut setup */
4118 ssi = setup.shortcut;
4119 fprintf(file, "\n");
4120 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
4121 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
4124 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
4128 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
4129 fprintf(file, "\n");
4131 sii = setup.input[pnr];
4132 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
4133 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
4138 fprintf(file, "\n");
4139 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
4140 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
4143 soi = setup.options;
4144 fprintf(file, "\n");
4145 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
4146 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
4150 SetFilePermissions(filename, PERMS_PRIVATE);
4153 void LoadCustomElementDescriptions()
4155 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4156 SetupFileHash *setup_file_hash;
4159 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4161 if (element_info[i].custom_description != NULL)
4163 free(element_info[i].custom_description);
4164 element_info[i].custom_description = NULL;
4168 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4171 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4173 char *token = getStringCat2(element_info[i].token_name, ".name");
4174 char *value = getHashEntry(setup_file_hash, token);
4177 element_info[i].custom_description = getStringCopy(value);
4182 freeSetupFileHash(setup_file_hash);
4185 void LoadSpecialMenuDesignSettings()
4187 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
4188 SetupFileHash *setup_file_hash;
4191 /* always start with reliable default values from default config */
4192 for (i = 0; image_config_vars[i].token != NULL; i++)
4193 for (j = 0; image_config[j].token != NULL; j++)
4194 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
4195 *image_config_vars[i].value =
4196 get_auto_parameter_value(image_config_vars[i].token,
4197 image_config[j].value);
4199 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
4202 /* special case: initialize with default values that may be overwritten */
4203 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
4205 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
4206 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
4207 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
4209 if (value_x != NULL)
4210 menu.draw_xoffset[i] = get_integer_from_string(value_x);
4211 if (value_y != NULL)
4212 menu.draw_yoffset[i] = get_integer_from_string(value_y);
4213 if (list_size != NULL)
4214 menu.list_size[i] = get_integer_from_string(list_size);
4217 /* read (and overwrite with) values that may be specified in config file */
4218 for (i = 0; image_config_vars[i].token != NULL; i++)
4220 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
4223 *image_config_vars[i].value =
4224 get_auto_parameter_value(image_config_vars[i].token, value);
4227 freeSetupFileHash(setup_file_hash);
4230 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
4232 char *filename = getEditorSetupFilename();
4233 SetupFileList *setup_file_list, *list;
4234 SetupFileHash *element_hash;
4235 int num_unknown_tokens = 0;
4238 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
4241 element_hash = newSetupFileHash();
4243 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
4244 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4246 /* determined size may be larger than needed (due to unknown elements) */
4248 for (list = setup_file_list; list != NULL; list = list->next)
4251 /* add space for up to 3 more elements for padding that may be needed */
4254 *elements = checked_malloc(*num_elements * sizeof(int));
4257 for (list = setup_file_list; list != NULL; list = list->next)
4259 char *value = getHashEntry(element_hash, list->token);
4263 (*elements)[(*num_elements)++] = atoi(value);
4267 if (num_unknown_tokens == 0)
4269 Error(ERR_RETURN_LINE, "-");
4270 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4271 Error(ERR_RETURN, "- config file: '%s'", filename);
4273 num_unknown_tokens++;
4276 Error(ERR_RETURN, "- token: '%s'", list->token);
4280 if (num_unknown_tokens > 0)
4281 Error(ERR_RETURN_LINE, "-");
4283 while (*num_elements % 4) /* pad with empty elements, if needed */
4284 (*elements)[(*num_elements)++] = EL_EMPTY;
4286 freeSetupFileList(setup_file_list);
4287 freeSetupFileHash(element_hash);
4291 for (i = 0; i < *num_elements; i++)
4292 printf("editor: element '%s' [%d]\n",
4293 element_info[(*elements)[i]].token_name, (*elements)[i]);
4297 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
4300 SetupFileHash *setup_file_hash = NULL;
4301 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
4302 char *filename_music, *filename_prefix, *filename_info;
4308 token_to_value_ptr[] =
4310 { "title_header", &tmp_music_file_info.title_header },
4311 { "artist_header", &tmp_music_file_info.artist_header },
4312 { "album_header", &tmp_music_file_info.album_header },
4313 { "year_header", &tmp_music_file_info.year_header },
4315 { "title", &tmp_music_file_info.title },
4316 { "artist", &tmp_music_file_info.artist },
4317 { "album", &tmp_music_file_info.album },
4318 { "year", &tmp_music_file_info.year },
4324 filename_music = (is_sound ? getCustomSoundFilename(basename) :
4325 getCustomMusicFilename(basename));
4327 if (filename_music == NULL)
4330 /* ---------- try to replace file extension ---------- */
4332 filename_prefix = getStringCopy(filename_music);
4333 if (strrchr(filename_prefix, '.') != NULL)
4334 *strrchr(filename_prefix, '.') = '\0';
4335 filename_info = getStringCat2(filename_prefix, ".txt");
4338 printf("trying to load file '%s'...\n", filename_info);
4341 if (fileExists(filename_info))
4342 setup_file_hash = loadSetupFileHash(filename_info);
4344 free(filename_prefix);
4345 free(filename_info);
4347 if (setup_file_hash == NULL)
4349 /* ---------- try to add file extension ---------- */
4351 filename_prefix = getStringCopy(filename_music);
4352 filename_info = getStringCat2(filename_prefix, ".txt");
4355 printf("trying to load file '%s'...\n", filename_info);
4358 if (fileExists(filename_info))
4359 setup_file_hash = loadSetupFileHash(filename_info);
4361 free(filename_prefix);
4362 free(filename_info);
4365 if (setup_file_hash == NULL)
4368 /* ---------- music file info found ---------- */
4370 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
4372 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
4374 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
4376 *token_to_value_ptr[i].value_ptr =
4377 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
4380 tmp_music_file_info.basename = getStringCopy(basename);
4381 tmp_music_file_info.music = music;
4382 tmp_music_file_info.is_sound = is_sound;
4384 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
4385 *new_music_file_info = tmp_music_file_info;
4387 return new_music_file_info;
4390 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
4392 return get_music_file_info_ext(basename, music, FALSE);
4395 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
4397 return get_music_file_info_ext(basename, sound, TRUE);
4400 static boolean music_info_listed_ext(struct MusicFileInfo *list,
4401 char *basename, boolean is_sound)
4403 for (; list != NULL; list = list->next)
4404 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
4410 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
4412 return music_info_listed_ext(list, basename, FALSE);
4415 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
4417 return music_info_listed_ext(list, basename, TRUE);
4420 void LoadMusicInfo()
4422 char *music_directory = getCustomMusicDirectory();
4423 int num_music = getMusicListSize();
4424 int num_music_noconf = 0;
4425 int num_sounds = getSoundListSize();
4427 struct dirent *dir_entry;
4428 struct FileInfo *music, *sound;
4429 struct MusicFileInfo *next, **new;
4432 while (music_file_info != NULL)
4434 next = music_file_info->next;
4436 checked_free(music_file_info->basename);
4438 checked_free(music_file_info->title_header);
4439 checked_free(music_file_info->artist_header);
4440 checked_free(music_file_info->album_header);
4441 checked_free(music_file_info->year_header);
4443 checked_free(music_file_info->title);
4444 checked_free(music_file_info->artist);
4445 checked_free(music_file_info->album);
4446 checked_free(music_file_info->year);
4448 free(music_file_info);
4450 music_file_info = next;
4453 new = &music_file_info;
4456 printf("::: num_music == %d\n", num_music);
4459 for (i = 0; i < num_music; i++)
4461 music = getMusicListEntry(i);
4464 printf("::: %d [%08x]\n", i, music->filename);
4467 if (music->filename == NULL)
4470 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
4473 /* a configured file may be not recognized as music */
4474 if (!FileIsMusic(music->filename))
4478 printf("::: -> '%s' (configured)\n", music->filename);
4481 if (!music_info_listed(music_file_info, music->filename))
4483 *new = get_music_file_info(music->filename, i);
4485 new = &(*new)->next;
4489 if ((dir = opendir(music_directory)) == NULL)
4491 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4495 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
4497 char *basename = dir_entry->d_name;
4498 boolean music_already_used = FALSE;
4501 /* skip all music files that are configured in music config file */
4502 for (i = 0; i < num_music; i++)
4504 music = getMusicListEntry(i);
4506 if (music->filename == NULL)
4509 if (strcmp(basename, music->filename) == 0)
4511 music_already_used = TRUE;
4516 if (music_already_used)
4519 if (!FileIsMusic(basename))
4523 printf("::: -> '%s' (found in directory)\n", basename);
4526 if (!music_info_listed(music_file_info, basename))
4528 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
4530 new = &(*new)->next;
4538 for (i = 0; i < num_sounds; i++)
4540 sound = getSoundListEntry(i);
4542 if (sound->filename == NULL)
4545 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
4548 /* a configured file may be not recognized as sound */
4549 if (!FileIsSound(sound->filename))
4553 printf("::: -> '%s' (configured)\n", sound->filename);
4556 if (!sound_info_listed(music_file_info, sound->filename))
4558 *new = get_sound_file_info(sound->filename, i);
4560 new = &(*new)->next;
4566 for (next = music_file_info; next != NULL; next = next->next)
4567 printf("::: title == '%s'\n", next->title);
4571 void add_helpanim_entry(int element, int action, int direction, int delay,
4572 int *num_list_entries)
4574 struct HelpAnimInfo *new_list_entry;
4575 (*num_list_entries)++;
4578 checked_realloc(helpanim_info,
4579 *num_list_entries * sizeof(struct HelpAnimInfo));
4580 new_list_entry = &helpanim_info[*num_list_entries - 1];
4582 new_list_entry->element = element;
4583 new_list_entry->action = action;
4584 new_list_entry->direction = direction;
4585 new_list_entry->delay = delay;
4588 void print_unknown_token(char *filename, char *token, int token_nr)
4592 Error(ERR_RETURN_LINE, "-");
4593 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4594 Error(ERR_RETURN, "- config file: '%s'", filename);
4597 Error(ERR_RETURN, "- token: '%s'", token);
4600 void print_unknown_token_end(int token_nr)
4603 Error(ERR_RETURN_LINE, "-");
4606 void LoadHelpAnimInfo()
4608 char *filename = getHelpAnimFilename();
4609 SetupFileList *setup_file_list = NULL, *list;
4610 SetupFileHash *element_hash, *action_hash, *direction_hash;
4611 int num_list_entries = 0;
4612 int num_unknown_tokens = 0;
4615 if (fileExists(filename))
4616 setup_file_list = loadSetupFileList(filename);
4618 if (setup_file_list == NULL)
4620 /* use reliable default values from static configuration */
4621 SetupFileList *insert_ptr;
4623 insert_ptr = setup_file_list =
4624 newSetupFileList(helpanim_config[0].token,
4625 helpanim_config[0].value);
4627 for (i = 1; helpanim_config[i].token; i++)
4628 insert_ptr = addListEntry(insert_ptr,
4629 helpanim_config[i].token,
4630 helpanim_config[i].value);
4633 element_hash = newSetupFileHash();
4634 action_hash = newSetupFileHash();
4635 direction_hash = newSetupFileHash();
4637 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4638 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4640 for (i = 0; i < NUM_ACTIONS; i++)
4641 setHashEntry(action_hash, element_action_info[i].suffix,
4642 i_to_a(element_action_info[i].value));
4644 /* do not store direction index (bit) here, but direction value! */
4645 for (i = 0; i < NUM_DIRECTIONS; i++)
4646 setHashEntry(direction_hash, element_direction_info[i].suffix,
4647 i_to_a(1 << element_direction_info[i].value));
4649 for (list = setup_file_list; list != NULL; list = list->next)
4651 char *element_token, *action_token, *direction_token;
4652 char *element_value, *action_value, *direction_value;
4653 int delay = atoi(list->value);
4655 if (strcmp(list->token, "end") == 0)
4657 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4662 /* first try to break element into element/action/direction parts;
4663 if this does not work, also accept combined "element[.act][.dir]"
4664 elements (like "dynamite.active"), which are unique elements */
4666 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
4668 element_value = getHashEntry(element_hash, list->token);
4669 if (element_value != NULL) /* element found */
4670 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4674 /* no further suffixes found -- this is not an element */
4675 print_unknown_token(filename, list->token, num_unknown_tokens++);
4681 /* token has format "<prefix>.<something>" */
4683 action_token = strchr(list->token, '.'); /* suffix may be action ... */
4684 direction_token = action_token; /* ... or direction */
4686 element_token = getStringCopy(list->token);
4687 *strchr(element_token, '.') = '\0';
4689 element_value = getHashEntry(element_hash, element_token);
4691 if (element_value == NULL) /* this is no element */
4693 element_value = getHashEntry(element_hash, list->token);
4694 if (element_value != NULL) /* combined element found */
4695 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4698 print_unknown_token(filename, list->token, num_unknown_tokens++);
4700 free(element_token);
4705 action_value = getHashEntry(action_hash, action_token);
4707 if (action_value != NULL) /* action found */
4709 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
4712 free(element_token);
4717 direction_value = getHashEntry(direction_hash, direction_token);
4719 if (direction_value != NULL) /* direction found */
4721 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
4724 free(element_token);
4729 if (strchr(action_token + 1, '.') == NULL)
4731 /* no further suffixes found -- this is not an action nor direction */
4733 element_value = getHashEntry(element_hash, list->token);
4734 if (element_value != NULL) /* combined element found */
4735 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4738 print_unknown_token(filename, list->token, num_unknown_tokens++);
4740 free(element_token);
4745 /* token has format "<prefix>.<suffix>.<something>" */
4747 direction_token = strchr(action_token + 1, '.');
4749 action_token = getStringCopy(action_token);
4750 *strchr(action_token + 1, '.') = '\0';
4752 action_value = getHashEntry(action_hash, action_token);
4754 if (action_value == NULL) /* this is no action */
4756 element_value = getHashEntry(element_hash, list->token);
4757 if (element_value != NULL) /* combined element found */
4758 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4761 print_unknown_token(filename, list->token, num_unknown_tokens++);
4763 free(element_token);
4769 direction_value = getHashEntry(direction_hash, direction_token);
4771 if (direction_value != NULL) /* direction found */
4773 add_helpanim_entry(atoi(element_value), atoi(action_value),
4774 atoi(direction_value), delay, &num_list_entries);
4776 free(element_token);
4782 /* this is no direction */
4784 element_value = getHashEntry(element_hash, list->token);
4785 if (element_value != NULL) /* combined element found */
4786 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4789 print_unknown_token(filename, list->token, num_unknown_tokens++);
4791 free(element_token);
4795 print_unknown_token_end(num_unknown_tokens);
4797 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4798 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
4800 freeSetupFileList(setup_file_list);
4801 freeSetupFileHash(element_hash);
4802 freeSetupFileHash(action_hash);
4803 freeSetupFileHash(direction_hash);
4807 for (i = 0; i < num_list_entries; i++)
4808 printf("::: %d, %d, %d => %d\n",
4809 helpanim_info[i].element,
4810 helpanim_info[i].action,
4811 helpanim_info[i].direction,
4812 helpanim_info[i].delay);
4816 void LoadHelpTextInfo()
4818 char *filename = getHelpTextFilename();
4821 if (helptext_info != NULL)
4823 freeSetupFileHash(helptext_info);
4824 helptext_info = NULL;
4827 if (fileExists(filename))
4828 helptext_info = loadSetupFileHash(filename);
4830 if (helptext_info == NULL)
4832 /* use reliable default values from static configuration */
4833 helptext_info = newSetupFileHash();
4835 for (i = 0; helptext_config[i].token; i++)
4836 setHashEntry(helptext_info,
4837 helptext_config[i].token,
4838 helptext_config[i].value);
4843 BEGIN_HASH_ITERATION(helptext_info, itr)
4845 printf("::: '%s' => '%s'\n",
4846 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
4848 END_HASH_ITERATION(hash, itr)
4853 /* ------------------------------------------------------------------------- *
4855 * ------------------------------------------------------------------------- */
4857 #define MAX_NUM_CONVERT_LEVELS 1000
4859 void ConvertLevels()
4861 static LevelDirTree *convert_leveldir = NULL;
4862 static int convert_level_nr = -1;
4863 static int num_levels_handled = 0;
4864 static int num_levels_converted = 0;
4865 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
4868 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
4869 global.convert_leveldir);
4871 if (convert_leveldir == NULL)
4872 Error(ERR_EXIT, "no such level identifier: '%s'",
4873 global.convert_leveldir);
4875 leveldir_current = convert_leveldir;
4877 if (global.convert_level_nr != -1)
4879 convert_leveldir->first_level = global.convert_level_nr;
4880 convert_leveldir->last_level = global.convert_level_nr;
4883 convert_level_nr = convert_leveldir->first_level;
4885 printf_line("=", 79);
4886 printf("Converting levels\n");
4887 printf_line("-", 79);
4888 printf("Level series identifier: '%s'\n", convert_leveldir->identifier);
4889 printf("Level series name: '%s'\n", convert_leveldir->name);
4890 printf("Level series author: '%s'\n", convert_leveldir->author);
4891 printf("Number of levels: %d\n", convert_leveldir->levels);
4892 printf_line("=", 79);
4895 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
4896 levels_failed[i] = FALSE;
4898 while (convert_level_nr <= convert_leveldir->last_level)
4900 char *level_filename;
4903 level_nr = convert_level_nr++;
4905 printf("Level %03d: ", level_nr);
4907 LoadLevel(level_nr);
4908 if (level.no_valid_file)
4910 printf("(no level)\n");
4914 printf("converting level ... ");
4916 level_filename = getDefaultLevelFilename(level_nr);
4917 new_level = !fileExists(level_filename);
4921 SaveLevel(level_nr);
4923 num_levels_converted++;
4925 printf("converted.\n");
4929 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
4930 levels_failed[level_nr] = TRUE;
4932 printf("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
4935 num_levels_handled++;
4939 printf_line("=", 79);
4940 printf("Number of levels handled: %d\n", num_levels_handled);
4941 printf("Number of levels converted: %d (%d%%)\n", num_levels_converted,
4942 (num_levels_handled ?
4943 num_levels_converted * 100 / num_levels_handled : 0));
4944 printf_line("-", 79);
4945 printf("Summary (for automatic parsing by scripts):\n");
4946 printf("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
4947 convert_leveldir->identifier, num_levels_converted,
4949 (num_levels_handled ?
4950 num_levels_converted * 100 / num_levels_handled : 0));
4952 if (num_levels_handled != num_levels_converted)
4954 printf(", FAILED:");
4955 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
4956 if (levels_failed[i])
4961 printf_line("=", 79);