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 11 /* 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;
95 change->sides = CH_SIDE_ANY;
97 change->target_element = EL_EMPTY_SPACE;
99 change->delay_fixed = 0;
100 change->delay_random = 0;
101 change->delay_frames = 1;
103 change->trigger_element = EL_EMPTY_SPACE;
105 change->explode = FALSE;
106 change->use_content = FALSE;
107 change->only_complete = FALSE;
108 change->use_random_change = FALSE;
109 change->random = 100;
110 change->power = CP_NON_DESTRUCTIVE;
112 for (x = 0; x < 3; x++)
113 for (y = 0; y < 3; y++)
114 change->content[x][y] = EL_EMPTY_SPACE;
116 change->direct_action = 0;
117 change->other_action = 0;
119 change->pre_change_function = NULL;
120 change->change_function = NULL;
121 change->post_change_function = NULL;
124 static void setLevelInfoToDefaults(struct LevelInfo *level)
128 level->file_version = FILE_VERSION_ACTUAL;
129 level->game_version = GAME_VERSION_ACTUAL;
131 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
132 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
133 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
135 level->fieldx = STD_LEV_FIELDX;
136 level->fieldy = STD_LEV_FIELDY;
138 for (x = 0; x < MAX_LEV_FIELDX; x++)
139 for (y = 0; y < MAX_LEV_FIELDY; y++)
140 level->field[x][y] = EL_SAND;
143 level->gems_needed = 0;
144 level->amoeba_speed = 10;
145 level->time_magic_wall = 10;
146 level->time_wheel = 10;
147 level->time_light = 10;
148 level->time_timegate = 10;
149 level->amoeba_content = EL_DIAMOND;
150 level->double_speed = FALSE;
151 level->initial_gravity = FALSE;
152 level->em_slippery_gems = FALSE;
153 level->block_last_field = FALSE;
154 level->sp_block_last_field = TRUE;
156 level->use_custom_template = FALSE;
158 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
159 level->name[i] = '\0';
160 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
161 level->author[i] = '\0';
163 strcpy(level->name, NAMELESS_LEVEL_NAME);
164 strcpy(level->author, ANONYMOUS_NAME);
166 for (i = 0; i < 4; i++)
168 level->envelope_text[i][0] = '\0';
169 level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
170 level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
173 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
174 level->score[i] = 10;
176 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
177 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
178 for (x = 0; x < 3; x++)
179 for (y = 0; y < 3; y++)
180 level->yamyam_content[i][x][y] =
181 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
183 level->field[0][0] = EL_PLAYER_1;
184 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
186 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
190 setElementChangePages(&element_info[element], 1);
191 setElementChangeInfoToDefaults(element_info[element].change);
193 if (IS_CUSTOM_ELEMENT(element) || IS_GROUP_ELEMENT(element))
195 for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
196 element_info[element].description[j] = '\0';
198 if (element_info[element].custom_description != NULL)
199 strncpy(element_info[element].description,
200 element_info[element].custom_description,MAX_ELEMENT_NAME_LEN);
202 strcpy(element_info[element].description,
203 element_info[element].editor_description);
205 element_info[element].use_gfx_element = FALSE;
206 element_info[element].gfx_element = EL_EMPTY_SPACE;
209 if (IS_CUSTOM_ELEMENT(element))
211 element_info[element].collect_score = 10; /* special default */
212 element_info[element].collect_count = 1; /* special default */
214 element_info[element].push_delay_fixed = -1; /* initialize later */
215 element_info[element].push_delay_random = -1; /* initialize later */
216 element_info[element].move_delay_fixed = 0;
217 element_info[element].move_delay_random = 0;
219 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
220 element_info[element].move_direction_initial = MV_AUTOMATIC;
221 element_info[element].move_stepsize = TILEX / 8;
222 element_info[element].move_enter_element = EL_EMPTY_SPACE;
223 element_info[element].move_leave_element = EL_EMPTY_SPACE;
224 element_info[element].move_leave_type = LEAVE_TYPE_UNLIMITED;
226 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
228 for (x = 0; x < 3; x++)
229 for (y = 0; y < 3; y++)
230 element_info[element].content[x][y] = EL_EMPTY_SPACE;
232 element_info[element].access_type = 0;
233 element_info[element].access_layer = 0;
234 element_info[element].walk_to_action = 0;
235 element_info[element].smash_targets = 0;
236 element_info[element].deadliness = 0;
237 element_info[element].consistency = 0;
239 element_info[element].can_explode_by_fire = FALSE;
240 element_info[element].can_explode_smashed = FALSE;
241 element_info[element].can_explode_impact = FALSE;
243 element_info[element].current_change_page = 0;
245 /* start with no properties at all */
246 for (j = 0; j < NUM_EP_BITFIELDS; j++)
247 Properties[element][j] = EP_BITMASK_DEFAULT;
249 element_info[element].modified_settings = FALSE;
251 else if (IS_GROUP_ELEMENT(element) || element == EL_INTERNAL_EDITOR)
253 /* initialize memory for list of elements in group */
254 if (element_info[element].group == NULL)
255 element_info[element].group =
256 checked_malloc(sizeof(struct ElementGroupInfo));
258 for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
259 element_info[element].group->element[j] = EL_EMPTY_SPACE;
261 /* default: only one element in group */
262 element_info[element].group->num_elements = 1;
264 element_info[element].group->choice_mode = ANIM_RANDOM;
268 BorderElement = EL_STEELWALL;
270 level->no_level_file = FALSE;
272 if (leveldir_current == NULL) /* only when dumping level */
275 /* try to determine better author name than 'anonymous' */
276 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
278 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
279 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
283 switch (LEVELCLASS(leveldir_current))
285 case LEVELCLASS_TUTORIAL:
286 strcpy(level->author, PROGRAM_AUTHOR_STRING);
289 case LEVELCLASS_CONTRIB:
290 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
291 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
294 case LEVELCLASS_PRIVATE:
295 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
296 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
300 /* keep default value */
306 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
308 level_file_info->nr = 0;
309 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
310 level_file_info->packed = FALSE;
311 level_file_info->basename = NULL;
312 level_file_info->filename = NULL;
315 static void ActivateLevelTemplate()
317 /* Currently there is no special action needed to activate the template
318 data, because 'element_info' and 'Properties' overwrite the original
319 level data, while all other variables do not change. */
322 static char *getLevelFilenameFromBasename(char *basename)
324 static char *filename = NULL;
326 checked_free(filename);
328 filename = getPath2(getCurrentLevelDir(), basename);
333 static int getFileTypeFromBasename(char *basename)
335 static char *filename = NULL;
336 struct stat file_status;
338 /* ---------- try to determine file type from filename ---------- */
340 /* check for typical filename of a Supaplex level package file */
341 if (strlen(basename) == 10 && (strncmp(basename, "levels.d", 8) == 0 ||
342 strncmp(basename, "LEVELS.D", 8) == 0))
343 return LEVEL_FILE_TYPE_SP;
345 /* ---------- try to determine file type from filesize ---------- */
347 checked_free(filename);
348 filename = getPath2(getCurrentLevelDir(), basename);
350 if (stat(filename, &file_status) == 0)
352 /* check for typical filesize of a Supaplex level package file */
353 if (file_status.st_size == 170496)
354 return LEVEL_FILE_TYPE_SP;
357 return LEVEL_FILE_TYPE_UNKNOWN;
360 static char *getSingleLevelBasename(int nr, int type)
362 static char basename[MAX_FILENAME_LEN];
363 char *level_filename = getStringCopy(leveldir_current->level_filename);
365 if (level_filename == NULL)
366 level_filename = getStringCat2("%03d.", LEVELFILE_EXTENSION);
370 case LEVEL_FILE_TYPE_RND:
372 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
374 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
377 case LEVEL_FILE_TYPE_EM:
378 sprintf(basename, "%d", nr);
381 case LEVEL_FILE_TYPE_UNKNOWN:
383 sprintf(basename, level_filename, nr);
387 free(level_filename);
392 static char *getPackedLevelBasename(int type)
394 static char basename[MAX_FILENAME_LEN];
395 char *directory = getCurrentLevelDir();
397 struct dirent *dir_entry;
399 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
401 if ((dir = opendir(directory)) == NULL)
403 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
408 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
410 char *entry_basename = dir_entry->d_name;
411 int entry_type = getFileTypeFromBasename(entry_basename);
413 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
415 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
418 strcpy(basename, entry_basename);
430 static char *getSingleLevelFilename(int nr, int type)
432 return getLevelFilenameFromBasename(getSingleLevelBasename(nr, type));
436 static char *getPackedLevelFilename(int type)
438 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
442 char *getDefaultLevelFilename(int nr)
444 return getSingleLevelFilename(nr, LEVEL_FILE_TYPE_RND);
447 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
452 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
453 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
456 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
461 lfi->basename = getPackedLevelBasename(lfi->type);
462 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
465 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
467 /* special case: level number is negative => check for level template file */
470 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
475 if (leveldir_current->level_filename != NULL)
477 /* check for file name/pattern specified in "levelinfo.conf" */
478 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
479 if (fileExists(lfi->filename))
483 /* check for native Rocks'n'Diamonds level file */
484 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_RND);
485 if (fileExists(lfi->filename))
488 /* check for classic Emerald Mine level file */
489 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_EM);
490 if (fileExists(lfi->filename))
493 /* check for various packed level file formats */
494 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
495 if (fileExists(lfi->filename))
498 /* no known level file found -- try to use default values */
499 setLevelFileInfo_SingleLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
502 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
504 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
505 lfi->type = getFileTypeFromBasename(lfi->basename);
508 static struct LevelFileInfo *getLevelFileInfo(int nr)
510 static struct LevelFileInfo level_file_info;
512 /* always start with reliable default values */
513 setFileInfoToDefaults(&level_file_info);
515 level_file_info.nr = nr; /* set requested level number */
517 determineLevelFileInfo_Filename(&level_file_info);
518 determineLevelFileInfo_Filetype(&level_file_info);
520 return &level_file_info;
524 /* ------------------------------------------------------------------------- */
525 /* functions for loading R'n'D level */
526 /* ------------------------------------------------------------------------- */
528 int getMappedElement(int element)
530 /* map some (historic, now obsolete) elements */
535 case EL_PLAYER_OBSOLETE:
536 element = EL_PLAYER_1;
539 case EL_KEY_OBSOLETE:
542 case EL_EM_KEY_1_FILE_OBSOLETE:
543 element = EL_EM_KEY_1;
546 case EL_EM_KEY_2_FILE_OBSOLETE:
547 element = EL_EM_KEY_2;
550 case EL_EM_KEY_3_FILE_OBSOLETE:
551 element = EL_EM_KEY_3;
554 case EL_EM_KEY_4_FILE_OBSOLETE:
555 element = EL_EM_KEY_4;
558 case EL_ENVELOPE_OBSOLETE:
559 element = EL_ENVELOPE_1;
567 if (element >= NUM_FILE_ELEMENTS)
569 Error(ERR_WARN, "invalid level element %d", element);
571 element = EL_CHAR_QUESTION;
576 if (element >= NUM_FILE_ELEMENTS)
578 Error(ERR_WARN, "invalid level element %d", element);
580 element = EL_CHAR_QUESTION;
582 else if (element == EL_PLAYER_OBSOLETE)
583 element = EL_PLAYER_1;
584 else if (element == EL_KEY_OBSOLETE)
591 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
593 level->file_version = getFileVersion(file);
594 level->game_version = getFileVersion(file);
599 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
603 level->fieldx = getFile8Bit(file);
604 level->fieldy = getFile8Bit(file);
606 level->time = getFile16BitBE(file);
607 level->gems_needed = getFile16BitBE(file);
609 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
610 level->name[i] = getFile8Bit(file);
611 level->name[MAX_LEVEL_NAME_LEN] = 0;
613 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
614 level->score[i] = getFile8Bit(file);
616 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
617 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
618 for (y = 0; y < 3; y++)
619 for (x = 0; x < 3; x++)
620 level->yamyam_content[i][x][y] = getMappedElement(getFile8Bit(file));
622 level->amoeba_speed = getFile8Bit(file);
623 level->time_magic_wall = getFile8Bit(file);
624 level->time_wheel = getFile8Bit(file);
625 level->amoeba_content = getMappedElement(getFile8Bit(file));
626 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
627 level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
628 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
629 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
630 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
631 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
633 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
635 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
640 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
644 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
645 level->author[i] = getFile8Bit(file);
646 level->author[MAX_LEVEL_NAME_LEN] = 0;
651 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
654 int chunk_size_expected = level->fieldx * level->fieldy;
656 /* Note: "chunk_size" was wrong before version 2.0 when elements are
657 stored with 16-bit encoding (and should be twice as big then).
658 Even worse, playfield data was stored 16-bit when only yamyam content
659 contained 16-bit elements and vice versa. */
661 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
662 chunk_size_expected *= 2;
664 if (chunk_size_expected != chunk_size)
666 ReadUnusedBytesFromFile(file, chunk_size);
667 return chunk_size_expected;
670 for (y = 0; y < level->fieldy; y++)
671 for (x = 0; x < level->fieldx; x++)
673 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
678 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
682 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
683 int chunk_size_expected = header_size + content_size;
685 /* Note: "chunk_size" was wrong before version 2.0 when elements are
686 stored with 16-bit encoding (and should be twice as big then).
687 Even worse, playfield data was stored 16-bit when only yamyam content
688 contained 16-bit elements and vice versa. */
690 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
691 chunk_size_expected += content_size;
693 if (chunk_size_expected != chunk_size)
695 ReadUnusedBytesFromFile(file, chunk_size);
696 return chunk_size_expected;
700 level->num_yamyam_contents = getFile8Bit(file);
704 /* correct invalid number of content fields -- should never happen */
705 if (level->num_yamyam_contents < 1 ||
706 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
707 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
709 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
710 for (y = 0; y < 3; y++)
711 for (x = 0; x < 3; x++)
712 level->yamyam_content[i][x][y] =
713 getMappedElement(level->encoding_16bit_field ?
714 getFile16BitBE(file) : getFile8Bit(file));
718 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
722 int num_contents, content_xsize, content_ysize;
723 int content_array[MAX_ELEMENT_CONTENTS][3][3];
725 element = getMappedElement(getFile16BitBE(file));
726 num_contents = getFile8Bit(file);
727 content_xsize = getFile8Bit(file);
728 content_ysize = getFile8Bit(file);
730 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
732 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
733 for (y = 0; y < 3; y++)
734 for (x = 0; x < 3; x++)
735 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
737 /* correct invalid number of content fields -- should never happen */
738 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
739 num_contents = STD_ELEMENT_CONTENTS;
741 if (element == EL_YAMYAM)
743 level->num_yamyam_contents = num_contents;
745 for (i = 0; i < num_contents; i++)
746 for (y = 0; y < 3; y++)
747 for (x = 0; x < 3; x++)
748 level->yamyam_content[i][x][y] = content_array[i][x][y];
750 else if (element == EL_BD_AMOEBA)
752 level->amoeba_content = content_array[0][0][0];
756 Error(ERR_WARN, "cannot load content for element '%d'", element);
762 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
768 int chunk_size_expected;
770 element = getMappedElement(getFile16BitBE(file));
771 if (!IS_ENVELOPE(element))
772 element = EL_ENVELOPE_1;
774 envelope_nr = element - EL_ENVELOPE_1;
776 envelope_len = getFile16BitBE(file);
778 level->envelope_xsize[envelope_nr] = getFile8Bit(file);
779 level->envelope_ysize[envelope_nr] = getFile8Bit(file);
781 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
783 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
784 if (chunk_size_expected != chunk_size)
786 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
787 return chunk_size_expected;
790 for (i = 0; i < envelope_len; i++)
791 level->envelope_text[envelope_nr][i] = getFile8Bit(file);
796 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
798 int num_changed_custom_elements = getFile16BitBE(file);
799 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
802 if (chunk_size_expected != chunk_size)
804 ReadUnusedBytesFromFile(file, chunk_size - 2);
805 return chunk_size_expected;
808 for (i = 0; i < num_changed_custom_elements; i++)
810 int element = getFile16BitBE(file);
811 int properties = getFile32BitBE(file);
813 if (IS_CUSTOM_ELEMENT(element))
814 Properties[element][EP_BITFIELD_BASE] = properties;
816 Error(ERR_WARN, "invalid custom element number %d", element);
822 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
824 int num_changed_custom_elements = getFile16BitBE(file);
825 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
828 if (chunk_size_expected != chunk_size)
830 ReadUnusedBytesFromFile(file, chunk_size - 2);
831 return chunk_size_expected;
834 for (i = 0; i < num_changed_custom_elements; i++)
836 int element = getFile16BitBE(file);
837 int custom_target_element = getFile16BitBE(file);
839 if (IS_CUSTOM_ELEMENT(element))
840 element_info[element].change->target_element = custom_target_element;
842 Error(ERR_WARN, "invalid custom element number %d", element);
848 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
850 int num_changed_custom_elements = getFile16BitBE(file);
851 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
854 if (chunk_size_expected != chunk_size)
856 ReadUnusedBytesFromFile(file, chunk_size - 2);
857 return chunk_size_expected;
860 for (i = 0; i < num_changed_custom_elements; i++)
862 int element = getFile16BitBE(file);
864 if (!IS_CUSTOM_ELEMENT(element))
866 Error(ERR_WARN, "invalid custom element number %d", element);
868 element = EL_INTERNAL_DUMMY;
871 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
872 element_info[element].description[j] = getFile8Bit(file);
873 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
875 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
877 /* some free bytes for future properties and padding */
878 ReadUnusedBytesFromFile(file, 7);
880 element_info[element].use_gfx_element = getFile8Bit(file);
881 element_info[element].gfx_element =
882 getMappedElement(getFile16BitBE(file));
884 element_info[element].collect_score = getFile8Bit(file);
885 element_info[element].collect_count = getFile8Bit(file);
887 element_info[element].push_delay_fixed = getFile16BitBE(file);
888 element_info[element].push_delay_random = getFile16BitBE(file);
889 element_info[element].move_delay_fixed = getFile16BitBE(file);
890 element_info[element].move_delay_random = getFile16BitBE(file);
892 element_info[element].move_pattern = getFile16BitBE(file);
893 element_info[element].move_direction_initial = getFile8Bit(file);
894 element_info[element].move_stepsize = getFile8Bit(file);
896 for (y = 0; y < 3; y++)
897 for (x = 0; x < 3; x++)
898 element_info[element].content[x][y] =
899 getMappedElement(getFile16BitBE(file));
901 element_info[element].change->events = getFile32BitBE(file);
903 element_info[element].change->target_element =
904 getMappedElement(getFile16BitBE(file));
906 element_info[element].change->delay_fixed = getFile16BitBE(file);
907 element_info[element].change->delay_random = getFile16BitBE(file);
908 element_info[element].change->delay_frames = getFile16BitBE(file);
910 element_info[element].change->trigger_element =
911 getMappedElement(getFile16BitBE(file));
913 element_info[element].change->explode = getFile8Bit(file);
914 element_info[element].change->use_content = getFile8Bit(file);
915 element_info[element].change->only_complete = getFile8Bit(file);
916 element_info[element].change->use_random_change = getFile8Bit(file);
918 element_info[element].change->random = getFile8Bit(file);
919 element_info[element].change->power = getFile8Bit(file);
921 for (y = 0; y < 3; y++)
922 for (x = 0; x < 3; x++)
923 element_info[element].change->content[x][y] =
924 getMappedElement(getFile16BitBE(file));
926 element_info[element].slippery_type = getFile8Bit(file);
928 /* some free bytes for future properties and padding */
929 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
931 /* mark that this custom element has been modified */
932 element_info[element].modified_settings = TRUE;
938 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
940 struct ElementInfo *ei;
941 int chunk_size_expected;
945 element = getFile16BitBE(file);
947 if (!IS_CUSTOM_ELEMENT(element))
949 Error(ERR_WARN, "invalid custom element number %d", element);
951 ReadUnusedBytesFromFile(file, chunk_size - 2);
955 ei = &element_info[element];
957 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
958 ei->description[i] = getFile8Bit(file);
959 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
961 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
962 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
964 ei->num_change_pages = getFile8Bit(file);
966 /* some free bytes for future base property values and padding */
967 ReadUnusedBytesFromFile(file, 5);
969 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
970 if (chunk_size_expected != chunk_size)
972 ReadUnusedBytesFromFile(file, chunk_size - 48);
973 return chunk_size_expected;
976 /* read custom property values */
978 ei->use_gfx_element = getFile8Bit(file);
979 ei->gfx_element = getMappedElement(getFile16BitBE(file));
981 ei->collect_score = getFile8Bit(file);
982 ei->collect_count = getFile8Bit(file);
984 ei->push_delay_fixed = getFile16BitBE(file);
985 ei->push_delay_random = getFile16BitBE(file);
986 ei->move_delay_fixed = getFile16BitBE(file);
987 ei->move_delay_random = getFile16BitBE(file);
989 ei->move_pattern = getFile16BitBE(file);
990 ei->move_direction_initial = getFile8Bit(file);
991 ei->move_stepsize = getFile8Bit(file);
993 ei->slippery_type = getFile8Bit(file);
995 for (y = 0; y < 3; y++)
996 for (x = 0; x < 3; x++)
997 ei->content[x][y] = getMappedElement(getFile16BitBE(file));
999 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
1000 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
1001 ei->move_leave_type = getFile8Bit(file);
1003 /* some free bytes for future custom property values and padding */
1004 ReadUnusedBytesFromFile(file, 7);
1006 /* read change property values */
1008 setElementChangePages(ei, ei->num_change_pages);
1010 for (i = 0; i < ei->num_change_pages; i++)
1012 struct ElementChangeInfo *change = &ei->change_page[i];
1014 /* always start with reliable default values */
1015 setElementChangeInfoToDefaults(change);
1017 change->events = getFile32BitBE(file);
1019 change->target_element = getMappedElement(getFile16BitBE(file));
1021 change->delay_fixed = getFile16BitBE(file);
1022 change->delay_random = getFile16BitBE(file);
1023 change->delay_frames = getFile16BitBE(file);
1025 change->trigger_element = getMappedElement(getFile16BitBE(file));
1027 change->explode = getFile8Bit(file);
1028 change->use_content = getFile8Bit(file);
1029 change->only_complete = getFile8Bit(file);
1030 change->use_random_change = getFile8Bit(file);
1032 change->random = getFile8Bit(file);
1033 change->power = getFile8Bit(file);
1035 for (y = 0; y < 3; y++)
1036 for (x = 0; x < 3; x++)
1037 change->content[x][y] = getMappedElement(getFile16BitBE(file));
1039 change->can_change = getFile8Bit(file);
1041 change->sides = getFile8Bit(file);
1043 if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
1044 change->sides = CH_SIDE_ANY;
1046 /* some free bytes for future change property values and padding */
1047 ReadUnusedBytesFromFile(file, 8);
1050 /* mark this custom element as modified */
1051 ei->modified_settings = TRUE;
1056 static int LoadLevel_GRP1(FILE *file, int chunk_size, struct LevelInfo *level)
1058 struct ElementInfo *ei;
1059 struct ElementGroupInfo *group;
1063 element = getFile16BitBE(file);
1065 if (!IS_GROUP_ELEMENT(element))
1067 Error(ERR_WARN, "invalid group element number %d", element);
1069 ReadUnusedBytesFromFile(file, chunk_size - 2);
1073 ei = &element_info[element];
1075 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
1076 ei->description[i] = getFile8Bit(file);
1077 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
1079 group = element_info[element].group;
1081 group->num_elements = getFile8Bit(file);
1083 ei->use_gfx_element = getFile8Bit(file);
1084 ei->gfx_element = getMappedElement(getFile16BitBE(file));
1086 group->choice_mode = getFile8Bit(file);
1088 /* some free bytes for future values and padding */
1089 ReadUnusedBytesFromFile(file, 3);
1091 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
1092 group->element[i] = getMappedElement(getFile16BitBE(file));
1094 /* mark this group element as modified */
1095 element_info[element].modified_settings = TRUE;
1100 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
1101 struct LevelFileInfo *level_file_info)
1103 char *filename = level_file_info->filename;
1104 char cookie[MAX_LINE_LEN];
1105 char chunk_name[CHUNK_ID_LEN + 1];
1109 if (!(file = fopen(filename, MODE_READ)))
1111 level->no_level_file = TRUE;
1113 if (level != &level_template)
1114 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1119 getFileChunkBE(file, chunk_name, NULL);
1120 if (strcmp(chunk_name, "RND1") == 0)
1122 getFile32BitBE(file); /* not used */
1124 getFileChunkBE(file, chunk_name, NULL);
1125 if (strcmp(chunk_name, "CAVE") != 0)
1127 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1132 else /* check for pre-2.0 file format with cookie string */
1134 strcpy(cookie, chunk_name);
1135 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1136 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1137 cookie[strlen(cookie) - 1] = '\0';
1139 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
1141 Error(ERR_WARN, "unknown format of level file '%s'", filename);
1146 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
1148 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1153 /* pre-2.0 level files have no game version, so use file version here */
1154 level->game_version = level->file_version;
1157 if (level->file_version < FILE_VERSION_1_2)
1159 /* level files from versions before 1.2.0 without chunk structure */
1160 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
1161 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
1169 int (*loader)(FILE *, int, struct LevelInfo *);
1173 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1174 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1175 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1176 { "BODY", -1, LoadLevel_BODY },
1177 { "CONT", -1, LoadLevel_CONT },
1178 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1179 { "CNT3", -1, LoadLevel_CNT3 },
1180 { "CUS1", -1, LoadLevel_CUS1 },
1181 { "CUS2", -1, LoadLevel_CUS2 },
1182 { "CUS3", -1, LoadLevel_CUS3 },
1183 { "CUS4", -1, LoadLevel_CUS4 },
1184 { "GRP1", -1, LoadLevel_GRP1 },
1188 while (getFileChunkBE(file, chunk_name, &chunk_size))
1192 while (chunk_info[i].name != NULL &&
1193 strcmp(chunk_name, chunk_info[i].name) != 0)
1196 if (chunk_info[i].name == NULL)
1198 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1199 chunk_name, filename);
1200 ReadUnusedBytesFromFile(file, chunk_size);
1202 else if (chunk_info[i].size != -1 &&
1203 chunk_info[i].size != chunk_size)
1205 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1206 chunk_size, chunk_name, filename);
1207 ReadUnusedBytesFromFile(file, chunk_size);
1211 /* call function to load this level chunk */
1212 int chunk_size_expected =
1213 (chunk_info[i].loader)(file, chunk_size, level);
1215 /* the size of some chunks cannot be checked before reading other
1216 chunks first (like "HEAD" and "BODY") that contain some header
1217 information, so check them here */
1218 if (chunk_size_expected != chunk_size)
1220 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1221 chunk_size, chunk_name, filename);
1230 /* ------------------------------------------------------------------------- */
1231 /* functions for loading EM level */
1232 /* ------------------------------------------------------------------------- */
1234 static int map_em_element_yam(int element)
1238 case 0x00: return EL_EMPTY;
1239 case 0x01: return EL_EMERALD;
1240 case 0x02: return EL_DIAMOND;
1241 case 0x03: return EL_ROCK;
1242 case 0x04: return EL_ROBOT;
1243 case 0x05: return EL_SPACESHIP_UP;
1244 case 0x06: return EL_BOMB;
1245 case 0x07: return EL_BUG_UP;
1246 case 0x08: return EL_AMOEBA_DROP;
1247 case 0x09: return EL_NUT;
1248 case 0x0a: return EL_YAMYAM;
1249 case 0x0b: return EL_QUICKSAND_FULL;
1250 case 0x0c: return EL_SAND;
1251 case 0x0d: return EL_WALL_SLIPPERY;
1252 case 0x0e: return EL_STEELWALL;
1253 case 0x0f: return EL_WALL;
1254 case 0x10: return EL_EM_KEY_1;
1255 case 0x11: return EL_EM_KEY_2;
1256 case 0x12: return EL_EM_KEY_4;
1257 case 0x13: return EL_EM_KEY_3;
1258 case 0x14: return EL_MAGIC_WALL;
1259 case 0x15: return EL_ROBOT_WHEEL;
1260 case 0x16: return EL_DYNAMITE;
1262 case 0x17: return EL_EM_KEY_1; /* EMC */
1263 case 0x18: return EL_BUG_UP; /* EMC */
1264 case 0x1a: return EL_DIAMOND; /* EMC */
1265 case 0x1b: return EL_EMERALD; /* EMC */
1266 case 0x25: return EL_NUT; /* EMC */
1267 case 0x80: return EL_EMPTY; /* EMC */
1268 case 0x85: return EL_EM_KEY_1; /* EMC */
1269 case 0x86: return EL_EM_KEY_2; /* EMC */
1270 case 0x87: return EL_EM_KEY_4; /* EMC */
1271 case 0x88: return EL_EM_KEY_3; /* EMC */
1272 case 0x94: return EL_QUICKSAND_EMPTY; /* EMC */
1273 case 0x9a: return EL_AMOEBA_WET; /* EMC */
1274 case 0xaf: return EL_DYNAMITE; /* EMC */
1275 case 0xbd: return EL_SAND; /* EMC */
1278 Error(ERR_WARN, "invalid level element %d", element);
1279 return EL_CHAR_QUESTION;
1283 static int map_em_element_field(int element)
1285 if (element >= 0xc8 && element <= 0xe1)
1286 return EL_CHAR_A + (element - 0xc8);
1287 else if (element >= 0xe2 && element <= 0xeb)
1288 return EL_CHAR_0 + (element - 0xe2);
1292 case 0x00: return EL_ROCK;
1293 case 0x02: return EL_DIAMOND;
1294 case 0x03: return EL_DIAMOND;
1295 case 0x04: return EL_ROBOT;
1296 case 0x05: return EL_ROBOT; /* EMC */
1297 case 0x08: return EL_SPACESHIP_UP;
1298 case 0x09: return EL_SPACESHIP_RIGHT;
1299 case 0x0a: return EL_SPACESHIP_DOWN;
1300 case 0x0b: return EL_SPACESHIP_LEFT;
1301 case 0x0c: return EL_SPACESHIP_UP;
1302 case 0x0d: return EL_SPACESHIP_RIGHT;
1303 case 0x0e: return EL_SPACESHIP_DOWN;
1304 case 0x0f: return EL_SPACESHIP_LEFT;
1305 case 0x10: return EL_BOMB;
1306 case 0x12: return EL_EMERALD;
1307 case 0x13: return EL_EMERALD;
1308 case 0x14: return EL_BUG_UP;
1309 case 0x15: return EL_BUG_RIGHT;
1310 case 0x16: return EL_BUG_DOWN;
1311 case 0x17: return EL_BUG_LEFT;
1312 case 0x18: return EL_BUG_UP;
1313 case 0x19: return EL_BUG_RIGHT;
1314 case 0x1a: return EL_BUG_DOWN;
1315 case 0x1b: return EL_BUG_LEFT;
1316 case 0x1c: return EL_AMOEBA_DROP;
1317 case 0x20: return EL_ROCK;
1318 case 0x24: return EL_MAGIC_WALL;
1319 case 0x25: return EL_NUT;
1321 /* looks like magic wheel, but is _always_ activated */
1322 case 0x28: return EL_ROBOT_WHEEL; /* EMC */
1324 case 0x29: return EL_YAMYAM;
1325 case 0x2a: return EL_YAMYAM;
1326 case 0x2b: return EL_YAMYAM; /* EMC */
1327 case 0x2c: return EL_YAMYAM; /* EMC */
1328 case 0x2d: return EL_QUICKSAND_FULL;
1329 case 0x39: return EL_EXPANDABLE_WALL_HORIZONTAL; /* EMC */
1330 case 0x3a: return EL_EXPANDABLE_WALL_VERTICAL; /* EMC */
1331 case 0x3b: return EL_DYNAMITE_ACTIVE;
1332 case 0x3c: return EL_DYNAMITE_ACTIVE;
1333 case 0x3d: return EL_DYNAMITE_ACTIVE;
1334 case 0x3e: return EL_DYNAMITE_ACTIVE;
1335 case 0x3f: return EL_ACID_POOL_BOTTOM;
1336 case 0x40: return EL_EXIT_OPEN;
1337 case 0x41: return EL_EXIT_OPEN;
1338 case 0x42: return EL_EXIT_OPEN;
1339 case 0x43: return EL_BALLOON;
1340 case 0x4e: return EL_INVISIBLE_WALL;
1341 case 0x65: return EL_ACID; /* EMC */
1342 case 0x73: return EL_SAND; /* EMC */
1343 case 0x74: return EL_STEELWALL;
1344 case 0x7b: return EL_ACID;
1345 case 0x80: return EL_EMPTY;
1346 case 0x81: return EL_WALL_SLIPPERY;
1347 case 0x82: return EL_SAND;
1348 case 0x83: return EL_STEELWALL;
1349 case 0x84: return EL_WALL;
1350 case 0x85: return EL_EM_KEY_1;
1351 case 0x86: return EL_EM_KEY_2;
1352 case 0x87: return EL_EM_KEY_4;
1353 case 0x88: return EL_EM_KEY_3;
1354 case 0x89: return EL_EM_GATE_1;
1355 case 0x8a: return EL_EM_GATE_2;
1356 case 0x8b: return EL_EM_GATE_4;
1357 case 0x8c: return EL_EM_GATE_3;
1358 case 0x8d: return EL_INVISIBLE_WALL; /* EMC */
1359 case 0x8e: return EL_EM_GATE_1_GRAY;
1360 case 0x8f: return EL_EM_GATE_2_GRAY;
1361 case 0x90: return EL_EM_GATE_4_GRAY;
1362 case 0x91: return EL_EM_GATE_3_GRAY;
1363 case 0x92: return EL_MAGIC_WALL;
1364 case 0x94: return EL_QUICKSAND_EMPTY;
1365 case 0x95: return EL_ACID_POOL_TOPLEFT;
1366 case 0x96: return EL_ACID_POOL_TOPRIGHT;
1367 case 0x97: return EL_ACID_POOL_BOTTOMLEFT;
1368 case 0x98: return EL_ACID_POOL_BOTTOMRIGHT;
1369 case 0x99: return EL_ACID;
1370 case 0x9a: return EL_AMOEBA_DEAD;
1371 case 0x9b: return EL_AMOEBA_DEAD;
1372 case 0x9c: return EL_AMOEBA_DEAD;
1373 case 0x9d: return EL_AMOEBA_DEAD;
1374 case 0x9e: return EL_EXIT_CLOSED;
1375 case 0x9f: return EL_CHAR_LESS; /* EMC */
1376 case 0x93: return EL_ROBOT_WHEEL;
1378 /* looks like normal dust, but behaves like wall */
1379 case 0xa0: return EL_WALL; /* EMC */
1381 case 0xa8: return EL_EMC_WALL_1; /* EMC */
1382 case 0xa9: return EL_EMC_WALL_2; /* EMC */
1383 case 0xaa: return EL_EMC_WALL_3; /* EMC */
1384 case 0xab: return EL_EMC_WALL_7; /* EMC */
1385 case 0xae: return EL_CHAR_MINUS; /* EMC */
1386 case 0xaf: return EL_DYNAMITE;
1387 case 0xb0: return EL_EMC_STEELWALL_1; /* EMC */
1388 case 0xb1: return EL_EMC_WALL_8; /* EMC */
1390 /* (exact steel wall) */
1391 case 0xb3: return EL_STEELWALL; /* EMC */
1393 case 0xb4: return EL_WALL_SLIPPERY; /* EMC */
1394 case 0xb5: return EL_EMC_WALL_6; /* EMC */
1395 case 0xb6: return EL_EMC_WALL_5; /* EMC */
1396 case 0xb7: return EL_EMC_WALL_4; /* EMC */
1397 case 0xb8: return EL_BALLOON_SWITCH_ANY; /* EMC */
1398 case 0xb9: return EL_BALLOON_SWITCH_RIGHT; /* EMC */
1399 case 0xba: return EL_BALLOON_SWITCH_DOWN; /* EMC */
1400 case 0xbb: return EL_BALLOON_SWITCH_LEFT; /* EMC */
1401 case 0xbc: return EL_BALLOON_SWITCH_UP; /* EMC */
1402 case 0xbd: return EL_SAND; /* EMC */
1403 case 0xec: return EL_CHAR_PERIOD;
1404 case 0xed: return EL_CHAR_EXCLAM;
1405 case 0xee: return EL_CHAR_COLON;
1406 case 0xef: return EL_CHAR_QUESTION;
1407 case 0xf0: return EL_CHAR_GREATER;
1408 case 0xf1: return EL_CHAR_COPYRIGHT;
1409 case 0xfe: return EL_PLAYER_1;
1410 case 0xff: return EL_PLAYER_2;
1413 Error(ERR_WARN, "invalid level element %d", element);
1414 return EL_CHAR_QUESTION;
1418 #define EM_LEVEL_SIZE 2106
1419 #define EM_LEVEL_XSIZE 64
1420 #define EM_LEVEL_YSIZE 32
1422 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
1423 struct LevelFileInfo *level_file_info)
1425 char *filename = level_file_info->filename;
1427 unsigned char leveldata[EM_LEVEL_SIZE];
1428 unsigned char *header = &leveldata[EM_LEVEL_XSIZE * EM_LEVEL_YSIZE];
1429 unsigned char code0 = 0x65;
1430 unsigned char code1 = 0x11;
1431 boolean level_is_crypted = FALSE;
1432 int nr = level_file_info->nr;
1435 if (!(file = fopen(filename, MODE_READ)))
1437 level->no_level_file = TRUE;
1439 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1444 for(i = 0; i < EM_LEVEL_SIZE; i++)
1445 leveldata[i] = fgetc(file);
1449 /* check if level data is crypted by testing against known starting bytes
1450 of the few existing crypted level files (from Emerald Mine 1 + 2) */
1452 if ((leveldata[0] == 0xf1 ||
1453 leveldata[0] == 0xf5) && leveldata[2] == 0xe7 && leveldata[3] == 0xee)
1455 level_is_crypted = TRUE;
1457 if (leveldata[0] == 0xf5) /* error in crypted Emerald Mine 2 levels */
1458 leveldata[0] = 0xf1;
1461 if (level_is_crypted) /* decode crypted level data */
1463 for(i = 0; i < EM_LEVEL_SIZE; i++)
1465 leveldata[i] ^= code0;
1466 leveldata[i] -= code1;
1468 code0 = (code0 + 7) & 0xff;
1472 level->fieldx = EM_LEVEL_XSIZE;
1473 level->fieldy = EM_LEVEL_YSIZE;
1475 level->time = header[46] * 10;
1476 level->gems_needed = header[47];
1478 /* The original Emerald Mine levels have their level number stored
1479 at the second byte of the level file...
1480 Do not trust this information at other level files, e.g. EMC,
1481 but correct it anyway (normally the first row is completely
1482 steel wall, so the correction does not hurt anyway). */
1484 if (leveldata[1] == nr)
1485 leveldata[1] = leveldata[2]; /* correct level number field */
1487 sprintf(level->name, "Level %d", nr); /* set level name */
1489 level->score[SC_EMERALD] = header[36];
1490 level->score[SC_DIAMOND] = header[37];
1491 level->score[SC_ROBOT] = header[38];
1492 level->score[SC_SPACESHIP] = header[39];
1493 level->score[SC_BUG] = header[40];
1494 level->score[SC_YAMYAM] = header[41];
1495 level->score[SC_NUT] = header[42];
1496 level->score[SC_DYNAMITE] = header[43];
1497 level->score[SC_TIME_BONUS] = header[44];
1499 level->num_yamyam_contents = 4;
1501 for(i = 0; i < level->num_yamyam_contents; i++)
1502 for(y = 0; y < 3; y++)
1503 for(x = 0; x < 3; x++)
1504 level->yamyam_content[i][x][y] =
1505 map_em_element_yam(header[i * 9 + y * 3 + x]);
1507 level->amoeba_speed = (header[52] * 256 + header[53]) % 256;
1508 level->time_magic_wall = (header[54] * 256 + header[55]) * 16 / 100;
1509 level->time_wheel = (header[56] * 256 + header[57]) * 16 / 100;
1510 level->amoeba_content = EL_DIAMOND;
1512 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
1514 int new_element = map_em_element_field(leveldata[y * EM_LEVEL_XSIZE + x]);
1516 if (new_element == EL_AMOEBA_DEAD && level->amoeba_speed)
1517 new_element = EL_AMOEBA_WET;
1519 level->field[x][y] = new_element;
1522 x = (header[48] * 256 + header[49]) % EM_LEVEL_XSIZE;
1523 y = (header[48] * 256 + header[49]) / EM_LEVEL_XSIZE;
1524 level->field[x][y] = EL_PLAYER_1;
1526 x = (header[50] * 256 + header[51]) % EM_LEVEL_XSIZE;
1527 y = (header[50] * 256 + header[51]) / EM_LEVEL_XSIZE;
1528 level->field[x][y] = EL_PLAYER_2;
1531 /* ------------------------------------------------------------------------- */
1532 /* functions for loading SP level */
1533 /* ------------------------------------------------------------------------- */
1535 #define NUM_SUPAPLEX_LEVELS_PER_PACKAGE 111
1536 #define SP_LEVEL_SIZE 1536
1537 #define SP_LEVEL_XSIZE 60
1538 #define SP_LEVEL_YSIZE 24
1539 #define SP_LEVEL_NAME_LEN 23
1541 static void LoadLevelFromFileStream_SP(FILE *file, struct LevelInfo *level,
1546 /* read level body (width * height == 60 * 24 tiles == 1440 bytes) */
1547 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1549 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1551 int element_old = fgetc(file);
1554 if (element_old <= 0x27)
1555 element_new = getMappedElement(EL_SP_START + element_old);
1556 else if (element_old == 0x28)
1557 element_new = EL_INVISIBLE_WALL;
1560 Error(ERR_WARN, "in level %d, at position %d, %d:", nr, x, y);
1561 Error(ERR_WARN, "invalid level element %d", element_old);
1563 element_new = EL_CHAR_QUESTION;
1566 level->field[x][y] = element_new;
1570 ReadUnusedBytesFromFile(file, 4);
1572 /* Initial gravitation: 1 == "on", anything else (0) == "off" */
1573 level->initial_gravity = (fgetc(file) == 1 ? TRUE : FALSE);
1575 ReadUnusedBytesFromFile(file, 1);
1577 /* level title in uppercase letters, padded with dashes ("-") (23 bytes) */
1578 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
1579 level->name[i] = fgetc(file);
1580 level->name[SP_LEVEL_NAME_LEN] = '\0';
1582 /* initial "freeze zonks": 2 == "on", anything else (0) == "off" */
1583 ReadUnusedBytesFromFile(file, 1); /* !!! NOT SUPPORTED YET !!! */
1585 /* number of infotrons needed; 0 means that Supaplex will count the total
1586 amount of infotrons in the level and use the low byte of that number.
1587 (a multiple of 256 infotrons will result in "0 infotrons needed"!) */
1588 level->gems_needed = fgetc(file);
1590 /* information about special gravity port entries */
1591 ReadUnusedBytesFromFile(file, 65); /* !!! NOT SUPPORTED YET !!! */
1593 level->fieldx = SP_LEVEL_XSIZE;
1594 level->fieldy = SP_LEVEL_YSIZE;
1596 level->time = 0; /* no time limit */
1597 level->amoeba_speed = 0;
1598 level->time_magic_wall = 0;
1599 level->time_wheel = 0;
1600 level->amoeba_content = EL_EMPTY;
1602 for(i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
1603 level->score[i] = 0; /* !!! CORRECT THIS !!! */
1605 /* there are no yamyams in supaplex levels */
1606 for(i = 0; i < level->num_yamyam_contents; i++)
1607 for(y = 0; y < 3; y++)
1608 for(x = 0; x < 3; x++)
1609 level->yamyam_content[i][x][y] = EL_EMPTY;
1612 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
1613 struct LevelFileInfo *level_file_info)
1615 char *filename = level_file_info->filename;
1617 int nr = level_file_info->nr - leveldir_current->first_level;
1619 char name_first, name_last;
1620 struct LevelInfo multipart_level;
1621 int multipart_xpos, multipart_ypos;
1622 boolean is_multipart_level;
1623 boolean is_first_part;
1624 boolean reading_multipart_level = FALSE;
1625 boolean use_empty_level = FALSE;
1627 if (!(file = fopen(filename, MODE_READ)))
1629 level->no_level_file = TRUE;
1631 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
1636 /* position file stream to the requested level inside the level package */
1637 if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
1639 level->no_level_file = TRUE;
1641 Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
1646 /* there exist Supaplex level package files with multi-part levels which
1647 can be detected as follows: instead of leading and trailing dashes ('-')
1648 to pad the level name, they have leading and trailing numbers which are
1649 the x and y coordinations of the current part of the multi-part level;
1650 if there are '?' characters instead of numbers on the left or right side
1651 of the level name, the multi-part level consists of only horizontal or
1654 for (l = nr; l < NUM_SUPAPLEX_LEVELS_PER_PACKAGE; l++)
1656 LoadLevelFromFileStream_SP(file, level, l);
1658 /* check if this level is a part of a bigger multi-part level */
1660 name_first = level->name[0];
1661 name_last = level->name[SP_LEVEL_NAME_LEN - 1];
1663 is_multipart_level =
1664 ((name_first == '?' || (name_first >= '0' && name_first <= '9')) &&
1665 (name_last == '?' || (name_last >= '0' && name_last <= '9')));
1668 ((name_first == '?' || name_first == '1') &&
1669 (name_last == '?' || name_last == '1'));
1671 /* correct leading multipart level meta information in level name */
1672 for (i = 0; i < SP_LEVEL_NAME_LEN && level->name[i] == name_first; i++)
1673 level->name[i] = '-';
1675 /* correct trailing multipart level meta information in level name */
1676 for (i = SP_LEVEL_NAME_LEN - 1; i>=0 && level->name[i] == name_last; i--)
1677 level->name[i] = '-';
1679 /* ---------- check for normal single level ---------- */
1681 if (!reading_multipart_level && !is_multipart_level)
1683 /* the current level is simply a normal single-part level, and we are
1684 not reading a multi-part level yet, so return the level as it is */
1689 /* ---------- check for empty level (unused multi-part) ---------- */
1691 if (!reading_multipart_level && is_multipart_level && !is_first_part)
1693 /* this is a part of a multi-part level, but not the first part
1694 (and we are not already reading parts of a multi-part level);
1695 in this case, use an empty level instead of the single part */
1697 use_empty_level = TRUE;
1702 /* ---------- check for finished multi-part level ---------- */
1704 if (reading_multipart_level &&
1705 (!is_multipart_level ||
1706 strcmp(level->name, multipart_level.name) != 0))
1708 /* we are already reading parts of a multi-part level, but this level is
1709 either not a multi-part level, or a part of a different multi-part
1710 level; in both cases, the multi-part level seems to be complete */
1715 /* ---------- here we have one part of a multi-part level ---------- */
1717 reading_multipart_level = TRUE;
1719 if (is_first_part) /* start with first part of new multi-part level */
1721 /* copy level info structure from first part */
1722 multipart_level = *level;
1724 /* clear playfield of new multi-part level */
1725 for (y = 0; y < MAX_LEV_FIELDY; y++)
1726 for (x = 0; x < MAX_LEV_FIELDX; x++)
1727 multipart_level.field[x][y] = EL_EMPTY;
1730 if (name_first == '?')
1732 if (name_last == '?')
1735 multipart_xpos = (int)(name_first - '0');
1736 multipart_ypos = (int)(name_last - '0');
1739 printf("----------> part (%d/%d) of multi-part level '%s'\n",
1740 multipart_xpos, multipart_ypos, multipart_level.name);
1743 if (multipart_xpos * SP_LEVEL_XSIZE > MAX_LEV_FIELDX ||
1744 multipart_ypos * SP_LEVEL_YSIZE > MAX_LEV_FIELDY)
1746 Error(ERR_WARN, "multi-part level is too big -- ignoring part of it");
1751 multipart_level.fieldx = MAX(multipart_level.fieldx,
1752 multipart_xpos * SP_LEVEL_XSIZE);
1753 multipart_level.fieldy = MAX(multipart_level.fieldy,
1754 multipart_ypos * SP_LEVEL_YSIZE);
1756 /* copy level part at the right position of multi-part level */
1757 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1759 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1761 int start_x = (multipart_xpos - 1) * SP_LEVEL_XSIZE;
1762 int start_y = (multipart_ypos - 1) * SP_LEVEL_YSIZE;
1764 multipart_level.field[start_x + x][start_y + y] = level->field[x][y];
1771 if (use_empty_level)
1773 setLevelInfoToDefaults(level);
1775 level->fieldx = SP_LEVEL_XSIZE;
1776 level->fieldy = SP_LEVEL_YSIZE;
1778 for (y = 0; y < SP_LEVEL_YSIZE; y++)
1779 for (x = 0; x < SP_LEVEL_XSIZE; x++)
1780 level->field[x][y] = EL_EMPTY;
1782 strcpy(level->name, "-------- EMPTY --------");
1784 Error(ERR_WARN, "single part of multi-part level -- using empty level");
1787 if (reading_multipart_level)
1788 *level = multipart_level;
1791 /* ------------------------------------------------------------------------- */
1792 /* functions for loading generic level */
1793 /* ------------------------------------------------------------------------- */
1795 void LoadLevelFromFileInfo(struct LevelInfo *level,
1796 struct LevelFileInfo *level_file_info)
1798 /* always start with reliable default values */
1799 setLevelInfoToDefaults(level);
1801 switch (level_file_info->type)
1803 case LEVEL_FILE_TYPE_RND:
1804 LoadLevelFromFileInfo_RND(level, level_file_info);
1807 case LEVEL_FILE_TYPE_EM:
1808 LoadLevelFromFileInfo_EM(level, level_file_info);
1811 case LEVEL_FILE_TYPE_SP:
1812 LoadLevelFromFileInfo_SP(level, level_file_info);
1816 LoadLevelFromFileInfo_RND(level, level_file_info);
1821 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
1823 static struct LevelFileInfo level_file_info;
1825 /* always start with reliable default values */
1826 setFileInfoToDefaults(&level_file_info);
1828 level_file_info.nr = 0; /* unknown level number */
1829 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
1830 level_file_info.filename = filename;
1832 LoadLevelFromFileInfo(level, &level_file_info);
1835 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
1837 if (leveldir_current == NULL) /* only when dumping level */
1841 printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
1844 /* determine correct game engine version of current level */
1846 if (!leveldir_current->latest_engine)
1848 if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
1849 IS_LEVELCLASS_PRIVATE(leveldir_current) ||
1850 IS_LEVELCLASS_UNDEFINED(leveldir_current))
1854 printf("\n::: This level is private or contributed: '%s'\n", filename);
1858 printf("\n::: Use the stored game engine version for this level\n");
1861 /* For all levels which are not forced to use the latest game engine
1862 version (normally user contributed, private and undefined levels),
1863 use the version of the game engine the levels were created for.
1865 Since 2.0.1, the game engine version is now directly stored
1866 in the level file (chunk "VERS"), so there is no need anymore
1867 to set the game version from the file version (except for old,
1868 pre-2.0 levels, where the game version is still taken from the
1869 file format version used to store the level -- see above). */
1871 /* do some special adjustments to support older level versions */
1872 if (level->file_version == FILE_VERSION_1_0)
1874 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
1875 Error(ERR_WARN, "using high speed movement for player");
1877 /* player was faster than monsters in (pre-)1.0 levels */
1878 level->double_speed = TRUE;
1881 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1882 if (level->game_version == VERSION_IDENT(2,0,1,0))
1883 level->em_slippery_gems = TRUE;
1888 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1889 leveldir_current->sort_priority, filename);
1893 printf("\n::: Use latest game engine version for this level.\n");
1896 /* For all levels which are forced to use the latest game engine version
1897 (normally all but user contributed, private and undefined levels), set
1898 the game engine version to the actual version; this allows for actual
1899 corrections in the game engine to take effect for existing, converted
1900 levels (from "classic" or other existing games) to make the emulation
1901 of the corresponding game more accurate, while (hopefully) not breaking
1902 existing levels created from other players. */
1905 printf("::: changing engine from %d to %d\n",
1906 level->game_version, GAME_VERSION_ACTUAL);
1909 level->game_version = GAME_VERSION_ACTUAL;
1911 /* Set special EM style gems behaviour: EM style gems slip down from
1912 normal, steel and growing wall. As this is a more fundamental change,
1913 it seems better to set the default behaviour to "off" (as it is more
1914 natural) and make it configurable in the level editor (as a property
1915 of gem style elements). Already existing converted levels (neither
1916 private nor contributed levels) are changed to the new behaviour. */
1918 if (level->file_version < FILE_VERSION_2_0)
1919 level->em_slippery_gems = TRUE;
1923 printf("::: => %d\n", level->game_version);
1927 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
1931 /* map custom element change events that have changed in newer versions
1932 (these following values were accidentally changed in version 3.0.1) */
1933 if (level->game_version <= VERSION_IDENT(3,0,0,0))
1935 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1937 int element = EL_CUSTOM_START + i;
1939 /* order of checking and copying events to be mapped is important */
1940 for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER_OBSOLETE; j--)
1942 if (HAS_CHANGE_EVENT(element, j - 2))
1944 SET_CHANGE_EVENT(element, j - 2, FALSE);
1945 SET_CHANGE_EVENT(element, j, TRUE);
1949 /* order of checking and copying events to be mapped is important */
1950 for (j = CE_OTHER_GETS_COLLECTED; j >= CE_HITTING_SOMETHING; j--)
1952 if (HAS_CHANGE_EVENT(element, j - 1))
1954 SET_CHANGE_EVENT(element, j - 1, FALSE);
1955 SET_CHANGE_EVENT(element, j, TRUE);
1961 /* some custom element change events get mapped since version 3.0.3 */
1962 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1964 int element = EL_CUSTOM_START + i;
1966 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE) ||
1967 HAS_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE))
1969 SET_CHANGE_EVENT(element, CE_BY_PLAYER_OBSOLETE, FALSE);
1970 SET_CHANGE_EVENT(element, CE_BY_COLLISION_OBSOLETE, FALSE);
1972 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
1976 /* initialize "can_change" field for old levels with only one change page */
1977 if (level->game_version <= VERSION_IDENT(3,0,2,0))
1979 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1981 int element = EL_CUSTOM_START + i;
1983 if (CAN_CHANGE(element))
1984 element_info[element].change->can_change = TRUE;
1989 /* set default push delay values (corrected since version 3.0.7-1) */
1990 if (level->game_version < VERSION_IDENT(3,0,7,1))
1992 game.default_push_delay_fixed = 2;
1993 game.default_push_delay_random = 8;
1997 game.default_push_delay_fixed = 8;
1998 game.default_push_delay_random = 8;
2001 /* set uninitialized push delay values of custom elements in older levels */
2002 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2004 int element = EL_CUSTOM_START + i;
2006 if (element_info[element].push_delay_fixed == -1)
2007 element_info[element].push_delay_fixed = game.default_push_delay_fixed;
2008 if (element_info[element].push_delay_random == -1)
2009 element_info[element].push_delay_random = game.default_push_delay_random;
2013 /* initialize element properties for level editor etc. */
2014 InitElementPropertiesEngine(level->game_version);
2017 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
2021 /* map elements that have changed in newer versions */
2022 for (y = 0; y < level->fieldy; y++)
2024 for (x = 0; x < level->fieldx; x++)
2026 int element = level->field[x][y];
2028 if (level->game_version <= VERSION_IDENT(2,2,0,0))
2030 /* map game font elements */
2031 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2032 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2033 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2034 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2037 if (level->game_version < VERSION_IDENT(3,0,0,0))
2039 /* map Supaplex gravity tube elements */
2040 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2041 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2042 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2043 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2047 level->field[x][y] = element;
2051 /* copy elements to runtime playfield array */
2052 for (x = 0; x < MAX_LEV_FIELDX; x++)
2053 for (y = 0; y < MAX_LEV_FIELDY; y++)
2054 Feld[x][y] = level->field[x][y];
2056 /* initialize level size variables for faster access */
2057 lev_fieldx = level->fieldx;
2058 lev_fieldy = level->fieldy;
2060 /* determine border element for this level */
2064 void LoadLevelTemplate(int nr)
2067 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2068 char *filename = level_file_info->filename;
2070 LoadLevelFromFileInfo(&level_template, level_file_info);
2072 char *filename = getDefaultLevelFilename(nr);
2074 LoadLevelFromFilename_RND(&level_template, filename);
2077 LoadLevel_InitVersion(&level, filename);
2078 LoadLevel_InitElements(&level, filename);
2080 ActivateLevelTemplate();
2083 void LoadLevel(int nr)
2086 struct LevelFileInfo *level_file_info = getLevelFileInfo(nr);
2087 char *filename = level_file_info->filename;
2089 LoadLevelFromFileInfo(&level, level_file_info);
2091 char *filename = getLevelFilename(nr);
2093 LoadLevelFromFilename_RND(&level, filename);
2096 if (level.use_custom_template)
2097 LoadLevelTemplate(-1);
2100 LoadLevel_InitVersion(&level, filename);
2101 LoadLevel_InitElements(&level, filename);
2102 LoadLevel_InitPlayfield(&level, filename);
2104 LoadLevel_InitLevel(&level, filename);
2108 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
2110 putFileVersion(file, level->file_version);
2111 putFileVersion(file, level->game_version);
2114 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
2118 putFile8Bit(file, level->fieldx);
2119 putFile8Bit(file, level->fieldy);
2121 putFile16BitBE(file, level->time);
2122 putFile16BitBE(file, level->gems_needed);
2124 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2125 putFile8Bit(file, level->name[i]);
2127 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2128 putFile8Bit(file, level->score[i]);
2130 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2131 for (y = 0; y < 3; y++)
2132 for (x = 0; x < 3; x++)
2133 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
2134 level->yamyam_content[i][x][y]));
2135 putFile8Bit(file, level->amoeba_speed);
2136 putFile8Bit(file, level->time_magic_wall);
2137 putFile8Bit(file, level->time_wheel);
2138 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
2139 level->amoeba_content));
2140 putFile8Bit(file, (level->double_speed ? 1 : 0));
2141 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
2142 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
2143 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
2144 putFile8Bit(file, (level->block_last_field ? 1 : 0));
2145 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
2147 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
2149 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
2152 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
2156 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2157 putFile8Bit(file, level->author[i]);
2160 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
2164 for (y = 0; y < level->fieldy; y++)
2165 for (x = 0; x < level->fieldx; x++)
2166 if (level->encoding_16bit_field)
2167 putFile16BitBE(file, level->field[x][y]);
2169 putFile8Bit(file, level->field[x][y]);
2173 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
2177 putFile8Bit(file, EL_YAMYAM);
2178 putFile8Bit(file, level->num_yamyam_contents);
2179 putFile8Bit(file, 0);
2180 putFile8Bit(file, 0);
2182 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2183 for (y = 0; y < 3; y++)
2184 for (x = 0; x < 3; x++)
2185 if (level->encoding_16bit_field)
2186 putFile16BitBE(file, level->yamyam_content[i][x][y]);
2188 putFile8Bit(file, level->yamyam_content[i][x][y]);
2192 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
2195 int num_contents, content_xsize, content_ysize;
2196 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2198 if (element == EL_YAMYAM)
2200 num_contents = level->num_yamyam_contents;
2204 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2205 for (y = 0; y < 3; y++)
2206 for (x = 0; x < 3; x++)
2207 content_array[i][x][y] = level->yamyam_content[i][x][y];
2209 else if (element == EL_BD_AMOEBA)
2215 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2216 for (y = 0; y < 3; y++)
2217 for (x = 0; x < 3; x++)
2218 content_array[i][x][y] = EL_EMPTY;
2219 content_array[0][0][0] = level->amoeba_content;
2223 /* chunk header already written -- write empty chunk data */
2224 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
2226 Error(ERR_WARN, "cannot save content for element '%d'", element);
2230 putFile16BitBE(file, element);
2231 putFile8Bit(file, num_contents);
2232 putFile8Bit(file, content_xsize);
2233 putFile8Bit(file, content_ysize);
2235 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2237 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2238 for (y = 0; y < 3; y++)
2239 for (x = 0; x < 3; x++)
2240 putFile16BitBE(file, content_array[i][x][y]);
2243 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
2246 int envelope_nr = element - EL_ENVELOPE_1;
2247 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
2249 putFile16BitBE(file, element);
2250 putFile16BitBE(file, envelope_len);
2251 putFile8Bit(file, level->envelope_xsize[envelope_nr]);
2252 putFile8Bit(file, level->envelope_ysize[envelope_nr]);
2254 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2256 for (i = 0; i < envelope_len; i++)
2257 putFile8Bit(file, level->envelope_text[envelope_nr][i]);
2261 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
2262 int num_changed_custom_elements)
2266 putFile16BitBE(file, num_changed_custom_elements);
2268 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2270 int element = EL_CUSTOM_START + i;
2272 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
2274 if (check < num_changed_custom_elements)
2276 putFile16BitBE(file, element);
2277 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2284 if (check != num_changed_custom_elements) /* should not happen */
2285 Error(ERR_WARN, "inconsistent number of custom element properties");
2290 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
2291 int num_changed_custom_elements)
2295 putFile16BitBE(file, num_changed_custom_elements);
2297 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2299 int element = EL_CUSTOM_START + i;
2301 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
2303 if (check < num_changed_custom_elements)
2305 putFile16BitBE(file, element);
2306 putFile16BitBE(file, element_info[element].change->target_element);
2313 if (check != num_changed_custom_elements) /* should not happen */
2314 Error(ERR_WARN, "inconsistent number of custom target elements");
2319 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
2320 int num_changed_custom_elements)
2322 int i, j, x, y, check = 0;
2324 putFile16BitBE(file, num_changed_custom_elements);
2326 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2328 int element = EL_CUSTOM_START + i;
2330 if (element_info[element].modified_settings)
2332 if (check < num_changed_custom_elements)
2334 putFile16BitBE(file, element);
2336 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2337 putFile8Bit(file, element_info[element].description[j]);
2339 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2341 /* some free bytes for future properties and padding */
2342 WriteUnusedBytesToFile(file, 7);
2344 putFile8Bit(file, element_info[element].use_gfx_element);
2345 putFile16BitBE(file, element_info[element].gfx_element);
2347 putFile8Bit(file, element_info[element].collect_score);
2348 putFile8Bit(file, element_info[element].collect_count);
2350 putFile16BitBE(file, element_info[element].push_delay_fixed);
2351 putFile16BitBE(file, element_info[element].push_delay_random);
2352 putFile16BitBE(file, element_info[element].move_delay_fixed);
2353 putFile16BitBE(file, element_info[element].move_delay_random);
2355 putFile16BitBE(file, element_info[element].move_pattern);
2356 putFile8Bit(file, element_info[element].move_direction_initial);
2357 putFile8Bit(file, element_info[element].move_stepsize);
2359 for (y = 0; y < 3; y++)
2360 for (x = 0; x < 3; x++)
2361 putFile16BitBE(file, element_info[element].content[x][y]);
2363 putFile32BitBE(file, element_info[element].change->events);
2365 putFile16BitBE(file, element_info[element].change->target_element);
2367 putFile16BitBE(file, element_info[element].change->delay_fixed);
2368 putFile16BitBE(file, element_info[element].change->delay_random);
2369 putFile16BitBE(file, element_info[element].change->delay_frames);
2371 putFile16BitBE(file, element_info[element].change->trigger_element);
2373 putFile8Bit(file, element_info[element].change->explode);
2374 putFile8Bit(file, element_info[element].change->use_content);
2375 putFile8Bit(file, element_info[element].change->only_complete);
2376 putFile8Bit(file, element_info[element].change->use_random_change);
2378 putFile8Bit(file, element_info[element].change->random);
2379 putFile8Bit(file, element_info[element].change->power);
2381 for (y = 0; y < 3; y++)
2382 for (x = 0; x < 3; x++)
2383 putFile16BitBE(file, element_info[element].change->content[x][y]);
2385 putFile8Bit(file, element_info[element].slippery_type);
2387 /* some free bytes for future properties and padding */
2388 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
2395 if (check != num_changed_custom_elements) /* should not happen */
2396 Error(ERR_WARN, "inconsistent number of custom element properties");
2400 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
2402 struct ElementInfo *ei = &element_info[element];
2405 putFile16BitBE(file, element);
2407 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2408 putFile8Bit(file, ei->description[i]);
2410 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
2411 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
2413 putFile8Bit(file, ei->num_change_pages);
2415 /* some free bytes for future base property values and padding */
2416 WriteUnusedBytesToFile(file, 5);
2418 /* write custom property values */
2420 putFile8Bit(file, ei->use_gfx_element);
2421 putFile16BitBE(file, ei->gfx_element);
2423 putFile8Bit(file, ei->collect_score);
2424 putFile8Bit(file, ei->collect_count);
2426 putFile16BitBE(file, ei->push_delay_fixed);
2427 putFile16BitBE(file, ei->push_delay_random);
2428 putFile16BitBE(file, ei->move_delay_fixed);
2429 putFile16BitBE(file, ei->move_delay_random);
2431 putFile16BitBE(file, ei->move_pattern);
2432 putFile8Bit(file, ei->move_direction_initial);
2433 putFile8Bit(file, ei->move_stepsize);
2435 putFile8Bit(file, ei->slippery_type);
2437 for (y = 0; y < 3; y++)
2438 for (x = 0; x < 3; x++)
2439 putFile16BitBE(file, ei->content[x][y]);
2441 putFile16BitBE(file, ei->move_enter_element);
2442 putFile16BitBE(file, ei->move_leave_element);
2443 putFile8Bit(file, ei->move_leave_type);
2445 /* some free bytes for future custom property values and padding */
2446 WriteUnusedBytesToFile(file, 7);
2448 /* write change property values */
2450 for (i = 0; i < ei->num_change_pages; i++)
2452 struct ElementChangeInfo *change = &ei->change_page[i];
2454 putFile32BitBE(file, change->events);
2456 putFile16BitBE(file, change->target_element);
2458 putFile16BitBE(file, change->delay_fixed);
2459 putFile16BitBE(file, change->delay_random);
2460 putFile16BitBE(file, change->delay_frames);
2462 putFile16BitBE(file, change->trigger_element);
2464 putFile8Bit(file, change->explode);
2465 putFile8Bit(file, change->use_content);
2466 putFile8Bit(file, change->only_complete);
2467 putFile8Bit(file, change->use_random_change);
2469 putFile8Bit(file, change->random);
2470 putFile8Bit(file, change->power);
2472 for (y = 0; y < 3; y++)
2473 for (x = 0; x < 3; x++)
2474 putFile16BitBE(file, change->content[x][y]);
2476 putFile8Bit(file, change->can_change);
2478 putFile8Bit(file, change->sides);
2480 /* some free bytes for future change property values and padding */
2481 WriteUnusedBytesToFile(file, 8);
2485 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
2487 struct ElementInfo *ei = &element_info[element];
2488 struct ElementGroupInfo *group = ei->group;
2491 putFile16BitBE(file, element);
2493 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2494 putFile8Bit(file, ei->description[i]);
2496 putFile8Bit(file, group->num_elements);
2498 putFile8Bit(file, ei->use_gfx_element);
2499 putFile16BitBE(file, ei->gfx_element);
2501 putFile8Bit(file, group->choice_mode);
2503 /* some free bytes for future values and padding */
2504 WriteUnusedBytesToFile(file, 3);
2506 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2507 putFile16BitBE(file, group->element[i]);
2510 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
2512 int body_chunk_size;
2516 if (!(file = fopen(filename, MODE_WRITE)))
2518 Error(ERR_WARN, "cannot save level file '%s'", filename);
2522 level->file_version = FILE_VERSION_ACTUAL;
2523 level->game_version = GAME_VERSION_ACTUAL;
2525 /* check level field for 16-bit elements */
2526 level->encoding_16bit_field = FALSE;
2527 for (y = 0; y < level->fieldy; y++)
2528 for (x = 0; x < level->fieldx; x++)
2529 if (level->field[x][y] > 255)
2530 level->encoding_16bit_field = TRUE;
2532 /* check yamyam content for 16-bit elements */
2533 level->encoding_16bit_yamyam = FALSE;
2534 for (i = 0; i < level->num_yamyam_contents; i++)
2535 for (y = 0; y < 3; y++)
2536 for (x = 0; x < 3; x++)
2537 if (level->yamyam_content[i][x][y] > 255)
2538 level->encoding_16bit_yamyam = TRUE;
2540 /* check amoeba content for 16-bit elements */
2541 level->encoding_16bit_amoeba = FALSE;
2542 if (level->amoeba_content > 255)
2543 level->encoding_16bit_amoeba = TRUE;
2545 /* calculate size of "BODY" chunk */
2547 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
2549 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2550 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
2552 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2553 SaveLevel_VERS(file, level);
2555 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
2556 SaveLevel_HEAD(file, level);
2558 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
2559 SaveLevel_AUTH(file, level);
2561 putFileChunkBE(file, "BODY", body_chunk_size);
2562 SaveLevel_BODY(file, level);
2564 if (level->encoding_16bit_yamyam ||
2565 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
2567 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2568 SaveLevel_CNT2(file, level, EL_YAMYAM);
2571 if (level->encoding_16bit_amoeba)
2573 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
2574 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
2577 /* check for envelope content */
2578 for (i = 0; i < 4; i++)
2580 if (strlen(level->envelope_text[i]) > 0)
2582 int envelope_len = strlen(level->envelope_text[i]) + 1;
2584 putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_SIZE(envelope_len));
2585 SaveLevel_CNT3(file, level, EL_ENVELOPE_1 + i);
2589 /* check for non-default custom elements (unless using template level) */
2590 if (!level->use_custom_template)
2592 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2594 int element = EL_CUSTOM_START + i;
2596 if (element_info[element].modified_settings)
2598 int num_change_pages = element_info[element].num_change_pages;
2600 putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
2601 SaveLevel_CUS4(file, level, element);
2606 /* check for non-default group elements (unless using template level) */
2607 if (!level->use_custom_template)
2609 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2611 int element = EL_GROUP_START + i;
2613 if (element_info[element].modified_settings)
2615 putFileChunkBE(file, "GRP1", LEVEL_CHUNK_GRP1_SIZE);
2616 SaveLevel_GRP1(file, level, element);
2623 SetFilePermissions(filename, PERMS_PRIVATE);
2626 void SaveLevel(int nr)
2628 char *filename = getDefaultLevelFilename(nr);
2630 SaveLevelFromFilename(&level, filename);
2633 void SaveLevelTemplate()
2635 char *filename = getDefaultLevelFilename(-1);
2637 SaveLevelFromFilename(&level, filename);
2640 void DumpLevel(struct LevelInfo *level)
2642 printf_line("-", 79);
2643 printf("Level xxx (file version %08d, game version %08d)\n",
2644 level->file_version, level->game_version);
2645 printf_line("-", 79);
2647 printf("Level author: '%s'\n", level->author);
2648 printf("Level title: '%s'\n", level->name);
2650 printf("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
2652 printf("Level time: %d seconds\n", level->time);
2653 printf("Gems needed: %d\n", level->gems_needed);
2655 printf("Time for magic wall: %d seconds\n", level->time_magic_wall);
2656 printf("Time for wheel: %d seconds\n", level->time_wheel);
2657 printf("Time for light: %d seconds\n", level->time_light);
2658 printf("Time for timegate: %d seconds\n", level->time_timegate);
2660 printf("Amoeba speed: %d\n", level->amoeba_speed);
2662 printf("Initial gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
2663 printf("Double speed movement: %s\n", (level->double_speed ? "yes" : "no"));
2664 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
2665 printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
2666 printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
2668 printf_line("-", 79);
2672 /* ========================================================================= */
2673 /* tape file functions */
2674 /* ========================================================================= */
2676 static void setTapeInfoToDefaults()
2680 /* always start with reliable default values (empty tape) */
2683 /* default values (also for pre-1.2 tapes) with only the first player */
2684 tape.player_participates[0] = TRUE;
2685 for (i = 1; i < MAX_PLAYERS; i++)
2686 tape.player_participates[i] = FALSE;
2688 /* at least one (default: the first) player participates in every tape */
2689 tape.num_participating_players = 1;
2691 tape.level_nr = level_nr;
2693 tape.changed = FALSE;
2695 tape.recording = FALSE;
2696 tape.playing = FALSE;
2697 tape.pausing = FALSE;
2700 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
2702 tape->file_version = getFileVersion(file);
2703 tape->game_version = getFileVersion(file);
2708 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
2712 tape->random_seed = getFile32BitBE(file);
2713 tape->date = getFile32BitBE(file);
2714 tape->length = getFile32BitBE(file);
2716 /* read header fields that are new since version 1.2 */
2717 if (tape->file_version >= FILE_VERSION_1_2)
2719 byte store_participating_players = getFile8Bit(file);
2722 /* since version 1.2, tapes store which players participate in the tape */
2723 tape->num_participating_players = 0;
2724 for (i = 0; i < MAX_PLAYERS; i++)
2726 tape->player_participates[i] = FALSE;
2728 if (store_participating_players & (1 << i))
2730 tape->player_participates[i] = TRUE;
2731 tape->num_participating_players++;
2735 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
2737 engine_version = getFileVersion(file);
2738 if (engine_version > 0)
2739 tape->engine_version = engine_version;
2741 tape->engine_version = tape->game_version;
2747 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
2749 int level_identifier_size;
2752 level_identifier_size = getFile16BitBE(file);
2754 tape->level_identifier =
2755 checked_realloc(tape->level_identifier, level_identifier_size);
2757 for (i = 0; i < level_identifier_size; i++)
2758 tape->level_identifier[i] = getFile8Bit(file);
2760 tape->level_nr = getFile16BitBE(file);
2762 chunk_size = 2 + level_identifier_size + 2;
2767 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
2770 int chunk_size_expected =
2771 (tape->num_participating_players + 1) * tape->length;
2773 if (chunk_size_expected != chunk_size)
2775 ReadUnusedBytesFromFile(file, chunk_size);
2776 return chunk_size_expected;
2779 for (i = 0; i < tape->length; i++)
2781 if (i >= MAX_TAPELEN)
2784 for (j = 0; j < MAX_PLAYERS; j++)
2786 tape->pos[i].action[j] = MV_NO_MOVING;
2788 if (tape->player_participates[j])
2789 tape->pos[i].action[j] = getFile8Bit(file);
2792 tape->pos[i].delay = getFile8Bit(file);
2794 if (tape->file_version == FILE_VERSION_1_0)
2796 /* eliminate possible diagonal moves in old tapes */
2797 /* this is only for backward compatibility */
2799 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
2800 byte action = tape->pos[i].action[0];
2801 int k, num_moves = 0;
2803 for (k = 0; k<4; k++)
2805 if (action & joy_dir[k])
2807 tape->pos[i + num_moves].action[0] = joy_dir[k];
2809 tape->pos[i + num_moves].delay = 0;
2818 tape->length += num_moves;
2821 else if (tape->file_version < FILE_VERSION_2_0)
2823 /* convert pre-2.0 tapes to new tape format */
2825 if (tape->pos[i].delay > 1)
2828 tape->pos[i + 1] = tape->pos[i];
2829 tape->pos[i + 1].delay = 1;
2832 for (j = 0; j < MAX_PLAYERS; j++)
2833 tape->pos[i].action[j] = MV_NO_MOVING;
2834 tape->pos[i].delay--;
2845 if (i != tape->length)
2846 chunk_size = (tape->num_participating_players + 1) * i;
2851 void LoadTapeFromFilename(char *filename)
2853 char cookie[MAX_LINE_LEN];
2854 char chunk_name[CHUNK_ID_LEN + 1];
2858 /* always start with reliable default values */
2859 setTapeInfoToDefaults();
2861 if (!(file = fopen(filename, MODE_READ)))
2864 getFileChunkBE(file, chunk_name, NULL);
2865 if (strcmp(chunk_name, "RND1") == 0)
2867 getFile32BitBE(file); /* not used */
2869 getFileChunkBE(file, chunk_name, NULL);
2870 if (strcmp(chunk_name, "TAPE") != 0)
2872 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2877 else /* check for pre-2.0 file format with cookie string */
2879 strcpy(cookie, chunk_name);
2880 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
2881 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2882 cookie[strlen(cookie) - 1] = '\0';
2884 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
2886 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
2891 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
2893 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
2898 /* pre-2.0 tape files have no game version, so use file version here */
2899 tape.game_version = tape.file_version;
2902 if (tape.file_version < FILE_VERSION_1_2)
2904 /* tape files from versions before 1.2.0 without chunk structure */
2905 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
2906 LoadTape_BODY(file, 2 * tape.length, &tape);
2914 int (*loader)(FILE *, int, struct TapeInfo *);
2918 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
2919 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
2920 { "INFO", -1, LoadTape_INFO },
2921 { "BODY", -1, LoadTape_BODY },
2925 while (getFileChunkBE(file, chunk_name, &chunk_size))
2929 while (chunk_info[i].name != NULL &&
2930 strcmp(chunk_name, chunk_info[i].name) != 0)
2933 if (chunk_info[i].name == NULL)
2935 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
2936 chunk_name, filename);
2937 ReadUnusedBytesFromFile(file, chunk_size);
2939 else if (chunk_info[i].size != -1 &&
2940 chunk_info[i].size != chunk_size)
2942 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2943 chunk_size, chunk_name, filename);
2944 ReadUnusedBytesFromFile(file, chunk_size);
2948 /* call function to load this tape chunk */
2949 int chunk_size_expected =
2950 (chunk_info[i].loader)(file, chunk_size, &tape);
2952 /* the size of some chunks cannot be checked before reading other
2953 chunks first (like "HEAD" and "BODY") that contain some header
2954 information, so check them here */
2955 if (chunk_size_expected != chunk_size)
2957 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2958 chunk_size, chunk_name, filename);
2966 tape.length_seconds = GetTapeLength();
2969 printf("::: tape game version: %d\n", tape.game_version);
2970 printf("::: tape engine version: %d\n", tape.engine_version);
2974 void LoadTape(int nr)
2976 char *filename = getTapeFilename(nr);
2978 LoadTapeFromFilename(filename);
2981 void LoadSolutionTape(int nr)
2983 char *filename = getSolutionTapeFilename(nr);
2985 LoadTapeFromFilename(filename);
2988 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2990 putFileVersion(file, tape->file_version);
2991 putFileVersion(file, tape->game_version);
2994 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2997 byte store_participating_players = 0;
2999 /* set bits for participating players for compact storage */
3000 for (i = 0; i < MAX_PLAYERS; i++)
3001 if (tape->player_participates[i])
3002 store_participating_players |= (1 << i);
3004 putFile32BitBE(file, tape->random_seed);
3005 putFile32BitBE(file, tape->date);
3006 putFile32BitBE(file, tape->length);
3008 putFile8Bit(file, store_participating_players);
3010 /* unused bytes not at the end here for 4-byte alignment of engine_version */
3011 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
3013 putFileVersion(file, tape->engine_version);
3016 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
3018 int level_identifier_size = strlen(tape->level_identifier) + 1;
3021 putFile16BitBE(file, level_identifier_size);
3023 for (i = 0; i < level_identifier_size; i++)
3024 putFile8Bit(file, tape->level_identifier[i]);
3026 putFile16BitBE(file, tape->level_nr);
3029 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
3033 for (i = 0; i < tape->length; i++)
3035 for (j = 0; j < MAX_PLAYERS; j++)
3036 if (tape->player_participates[j])
3037 putFile8Bit(file, tape->pos[i].action[j]);
3039 putFile8Bit(file, tape->pos[i].delay);
3043 void SaveTape(int nr)
3045 char *filename = getTapeFilename(nr);
3047 boolean new_tape = TRUE;
3048 int num_participating_players = 0;
3049 int info_chunk_size;
3050 int body_chunk_size;
3053 InitTapeDirectory(leveldir_current->subdir);
3055 /* if a tape still exists, ask to overwrite it */
3056 if (access(filename, F_OK) == 0)
3059 if (!Request("Replace old tape ?", REQ_ASK))
3063 if (!(file = fopen(filename, MODE_WRITE)))
3065 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
3069 tape.file_version = FILE_VERSION_ACTUAL;
3070 tape.game_version = GAME_VERSION_ACTUAL;
3072 /* count number of participating players */
3073 for (i = 0; i < MAX_PLAYERS; i++)
3074 if (tape.player_participates[i])
3075 num_participating_players++;
3077 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
3078 body_chunk_size = (num_participating_players + 1) * tape.length;
3080 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
3081 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
3083 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
3084 SaveTape_VERS(file, &tape);
3086 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
3087 SaveTape_HEAD(file, &tape);
3089 putFileChunkBE(file, "INFO", info_chunk_size);
3090 SaveTape_INFO(file, &tape);
3092 putFileChunkBE(file, "BODY", body_chunk_size);
3093 SaveTape_BODY(file, &tape);
3097 SetFilePermissions(filename, PERMS_PRIVATE);
3099 tape.changed = FALSE;
3102 Request("tape saved !", REQ_CONFIRM);
3105 void DumpTape(struct TapeInfo *tape)
3109 if (TAPE_IS_EMPTY(*tape))
3111 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
3115 printf_line("-", 79);
3116 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
3117 tape->level_nr, tape->file_version, tape->game_version);
3118 printf("Level series identifier: '%s'\n", tape->level_identifier);
3119 printf_line("-", 79);
3121 for (i = 0; i < tape->length; i++)
3123 if (i >= MAX_TAPELEN)
3126 printf("%03d: ", i);
3128 for (j = 0; j < MAX_PLAYERS; j++)
3130 if (tape->player_participates[j])
3132 int action = tape->pos[i].action[j];
3134 printf("%d:%02x ", j, action);
3135 printf("[%c%c%c%c|%c%c] - ",
3136 (action & JOY_LEFT ? '<' : ' '),
3137 (action & JOY_RIGHT ? '>' : ' '),
3138 (action & JOY_UP ? '^' : ' '),
3139 (action & JOY_DOWN ? 'v' : ' '),
3140 (action & JOY_BUTTON_1 ? '1' : ' '),
3141 (action & JOY_BUTTON_2 ? '2' : ' '));
3145 printf("(%03d)\n", tape->pos[i].delay);
3148 printf_line("-", 79);
3152 /* ========================================================================= */
3153 /* score file functions */
3154 /* ========================================================================= */
3156 void LoadScore(int nr)
3159 char *filename = getScoreFilename(nr);
3160 char cookie[MAX_LINE_LEN];
3161 char line[MAX_LINE_LEN];
3165 /* always start with reliable default values */
3166 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3168 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
3169 highscore[i].Score = 0;
3172 if (!(file = fopen(filename, MODE_READ)))
3175 /* check file identifier */
3176 fgets(cookie, MAX_LINE_LEN, file);
3177 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3178 cookie[strlen(cookie) - 1] = '\0';
3180 if (!checkCookieString(cookie, SCORE_COOKIE))
3182 Error(ERR_WARN, "unknown format of score file '%s'", filename);
3187 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3189 fscanf(file, "%d", &highscore[i].Score);
3190 fgets(line, MAX_LINE_LEN, file);
3192 if (line[strlen(line) - 1] == '\n')
3193 line[strlen(line) - 1] = '\0';
3195 for (line_ptr = line; *line_ptr; line_ptr++)
3197 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
3199 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
3200 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
3209 void SaveScore(int nr)
3212 char *filename = getScoreFilename(nr);
3215 InitScoreDirectory(leveldir_current->subdir);
3217 if (!(file = fopen(filename, MODE_WRITE)))
3219 Error(ERR_WARN, "cannot save score for level %d", nr);
3223 fprintf(file, "%s\n\n", SCORE_COOKIE);
3225 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
3226 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
3230 SetFilePermissions(filename, PERMS_PUBLIC);
3234 /* ========================================================================= */
3235 /* setup file functions */
3236 /* ========================================================================= */
3238 #define TOKEN_STR_PLAYER_PREFIX "player_"
3241 #define SETUP_TOKEN_PLAYER_NAME 0
3242 #define SETUP_TOKEN_SOUND 1
3243 #define SETUP_TOKEN_SOUND_LOOPS 2
3244 #define SETUP_TOKEN_SOUND_MUSIC 3
3245 #define SETUP_TOKEN_SOUND_SIMPLE 4
3246 #define SETUP_TOKEN_TOONS 5
3247 #define SETUP_TOKEN_SCROLL_DELAY 6
3248 #define SETUP_TOKEN_SOFT_SCROLLING 7
3249 #define SETUP_TOKEN_FADING 8
3250 #define SETUP_TOKEN_AUTORECORD 9
3251 #define SETUP_TOKEN_QUICK_DOORS 10
3252 #define SETUP_TOKEN_TEAM_MODE 11
3253 #define SETUP_TOKEN_HANDICAP 12
3254 #define SETUP_TOKEN_TIME_LIMIT 13
3255 #define SETUP_TOKEN_FULLSCREEN 14
3256 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
3257 #define SETUP_TOKEN_GRAPHICS_SET 16
3258 #define SETUP_TOKEN_SOUNDS_SET 17
3259 #define SETUP_TOKEN_MUSIC_SET 18
3260 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
3261 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
3262 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
3264 #define NUM_GLOBAL_SETUP_TOKENS 22
3267 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
3268 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
3269 #define SETUP_TOKEN_EDITOR_EL_MORE 2
3270 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
3271 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
3272 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
3273 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
3274 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
3275 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
3276 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
3277 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
3278 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
3280 #define NUM_EDITOR_SETUP_TOKENS 12
3282 /* shortcut setup */
3283 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
3284 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
3285 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
3287 #define NUM_SHORTCUT_SETUP_TOKENS 3
3290 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
3291 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
3292 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
3293 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
3294 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
3295 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
3296 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
3297 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
3298 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
3299 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
3300 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
3301 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
3302 #define SETUP_TOKEN_PLAYER_KEY_UP 12
3303 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
3304 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
3305 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
3307 #define NUM_PLAYER_SETUP_TOKENS 16
3310 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
3311 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
3313 #define NUM_SYSTEM_SETUP_TOKENS 2
3316 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
3318 #define NUM_OPTIONS_SETUP_TOKENS 1
3321 static struct SetupInfo si;
3322 static struct SetupEditorInfo sei;
3323 static struct SetupShortcutInfo ssi;
3324 static struct SetupInputInfo sii;
3325 static struct SetupSystemInfo syi;
3326 static struct OptionInfo soi;
3328 static struct TokenInfo global_setup_tokens[] =
3330 { TYPE_STRING, &si.player_name, "player_name" },
3331 { TYPE_SWITCH, &si.sound, "sound" },
3332 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
3333 { TYPE_SWITCH, &si.sound_music, "background_music" },
3334 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
3335 { TYPE_SWITCH, &si.toons, "toons" },
3336 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
3337 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
3338 { TYPE_SWITCH, &si.fading, "screen_fading" },
3339 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
3340 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
3341 { TYPE_SWITCH, &si.team_mode, "team_mode" },
3342 { TYPE_SWITCH, &si.handicap, "handicap" },
3343 { TYPE_SWITCH, &si.time_limit, "time_limit" },
3344 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
3345 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
3346 { TYPE_STRING, &si.graphics_set, "graphics_set" },
3347 { TYPE_STRING, &si.sounds_set, "sounds_set" },
3348 { TYPE_STRING, &si.music_set, "music_set" },
3349 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
3350 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
3351 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
3354 static struct TokenInfo editor_setup_tokens[] =
3356 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
3357 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
3358 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
3359 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
3360 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
3361 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
3362 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
3363 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
3364 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
3365 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
3366 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
3367 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
3370 static struct TokenInfo shortcut_setup_tokens[] =
3372 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
3373 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
3374 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
3377 static struct TokenInfo player_setup_tokens[] =
3379 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
3380 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
3381 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
3382 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
3383 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
3384 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
3385 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
3386 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
3387 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
3388 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
3389 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
3390 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
3391 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
3392 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
3393 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
3394 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" }
3397 static struct TokenInfo system_setup_tokens[] =
3399 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
3400 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
3403 static struct TokenInfo options_setup_tokens[] =
3405 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
3408 static char *get_corrected_login_name(char *login_name)
3410 /* needed because player name must be a fixed length string */
3411 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3413 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
3414 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
3416 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
3417 if (strchr(login_name_new, ' '))
3418 *strchr(login_name_new, ' ') = '\0';
3420 return login_name_new;
3423 static void setSetupInfoToDefaults(struct SetupInfo *si)
3427 si->player_name = get_corrected_login_name(getLoginName());
3430 si->sound_loops = TRUE;
3431 si->sound_music = TRUE;
3432 si->sound_simple = TRUE;
3434 si->double_buffering = TRUE;
3435 si->direct_draw = !si->double_buffering;
3436 si->scroll_delay = TRUE;
3437 si->soft_scrolling = TRUE;
3439 si->autorecord = TRUE;
3440 si->quick_doors = FALSE;
3441 si->team_mode = FALSE;
3442 si->handicap = TRUE;
3443 si->time_limit = TRUE;
3444 si->fullscreen = FALSE;
3445 si->ask_on_escape = TRUE;
3447 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
3448 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
3449 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
3450 si->override_level_graphics = FALSE;
3451 si->override_level_sounds = FALSE;
3452 si->override_level_music = FALSE;
3454 si->editor.el_boulderdash = TRUE;
3455 si->editor.el_emerald_mine = TRUE;
3456 si->editor.el_more = TRUE;
3457 si->editor.el_sokoban = TRUE;
3458 si->editor.el_supaplex = TRUE;
3459 si->editor.el_diamond_caves = TRUE;
3460 si->editor.el_dx_boulderdash = TRUE;
3461 si->editor.el_chars = TRUE;
3462 si->editor.el_custom = TRUE;
3463 si->editor.el_custom_more = FALSE;
3465 si->editor.el_headlines = TRUE;
3466 si->editor.el_user_defined = FALSE;
3468 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
3469 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
3470 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
3472 for (i = 0; i < MAX_PLAYERS; i++)
3474 si->input[i].use_joystick = FALSE;
3475 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
3476 si->input[i].joy.xleft = JOYSTICK_XLEFT;
3477 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
3478 si->input[i].joy.xright = JOYSTICK_XRIGHT;
3479 si->input[i].joy.yupper = JOYSTICK_YUPPER;
3480 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
3481 si->input[i].joy.ylower = JOYSTICK_YLOWER;
3482 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
3483 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
3484 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
3485 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
3486 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
3487 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
3488 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
3489 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
3492 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
3493 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
3495 si->options.verbose = FALSE;
3498 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
3502 if (!setup_file_hash)
3507 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3508 setSetupInfo(global_setup_tokens, i,
3509 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
3514 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3515 setSetupInfo(editor_setup_tokens, i,
3516 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
3519 /* shortcut setup */
3520 ssi = setup.shortcut;
3521 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3522 setSetupInfo(shortcut_setup_tokens, i,
3523 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
3524 setup.shortcut = ssi;
3527 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3531 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3533 sii = setup.input[pnr];
3534 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3536 char full_token[100];
3538 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
3539 setSetupInfo(player_setup_tokens, i,
3540 getHashEntry(setup_file_hash, full_token));
3542 setup.input[pnr] = sii;
3547 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3548 setSetupInfo(system_setup_tokens, i,
3549 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
3553 soi = setup.options;
3554 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3555 setSetupInfo(options_setup_tokens, i,
3556 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
3557 setup.options = soi;
3562 char *filename = getSetupFilename();
3563 SetupFileHash *setup_file_hash = NULL;
3565 /* always start with reliable default values */
3566 setSetupInfoToDefaults(&setup);
3568 setup_file_hash = loadSetupFileHash(filename);
3570 if (setup_file_hash)
3572 char *player_name_new;
3574 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
3575 decodeSetupFileHash(setup_file_hash);
3577 setup.direct_draw = !setup.double_buffering;
3579 freeSetupFileHash(setup_file_hash);
3581 /* needed to work around problems with fixed length strings */
3582 player_name_new = get_corrected_login_name(setup.player_name);
3583 free(setup.player_name);
3584 setup.player_name = player_name_new;
3587 Error(ERR_WARN, "using default setup values");
3592 char *filename = getSetupFilename();
3596 InitUserDataDirectory();
3598 if (!(file = fopen(filename, MODE_WRITE)))
3600 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3604 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3605 getCookie("SETUP")));
3606 fprintf(file, "\n");
3610 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
3612 /* just to make things nicer :) */
3613 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
3614 i == SETUP_TOKEN_GRAPHICS_SET)
3615 fprintf(file, "\n");
3617 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
3622 fprintf(file, "\n");
3623 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
3624 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
3626 /* shortcut setup */
3627 ssi = setup.shortcut;
3628 fprintf(file, "\n");
3629 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
3630 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
3633 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
3637 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3638 fprintf(file, "\n");
3640 sii = setup.input[pnr];
3641 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
3642 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
3647 fprintf(file, "\n");
3648 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
3649 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
3652 soi = setup.options;
3653 fprintf(file, "\n");
3654 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
3655 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
3659 SetFilePermissions(filename, PERMS_PRIVATE);
3662 void LoadCustomElementDescriptions()
3664 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3665 SetupFileHash *setup_file_hash;
3668 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3670 if (element_info[i].custom_description != NULL)
3672 free(element_info[i].custom_description);
3673 element_info[i].custom_description = NULL;
3677 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3680 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3682 char *token = getStringCat2(element_info[i].token_name, ".name");
3683 char *value = getHashEntry(setup_file_hash, token);
3686 element_info[i].custom_description = getStringCopy(value);
3691 freeSetupFileHash(setup_file_hash);
3694 void LoadSpecialMenuDesignSettings()
3696 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
3697 SetupFileHash *setup_file_hash;
3700 /* always start with reliable default values from default config */
3701 for (i = 0; image_config_vars[i].token != NULL; i++)
3702 for (j = 0; image_config[j].token != NULL; j++)
3703 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
3704 *image_config_vars[i].value =
3705 get_auto_parameter_value(image_config_vars[i].token,
3706 image_config[j].value);
3708 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
3711 /* special case: initialize with default values that may be overwritten */
3712 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
3714 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
3715 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
3716 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
3718 if (value_x != NULL)
3719 menu.draw_xoffset[i] = get_integer_from_string(value_x);
3720 if (value_y != NULL)
3721 menu.draw_yoffset[i] = get_integer_from_string(value_y);
3722 if (list_size != NULL)
3723 menu.list_size[i] = get_integer_from_string(list_size);
3726 /* read (and overwrite with) values that may be specified in config file */
3727 for (i = 0; image_config_vars[i].token != NULL; i++)
3729 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
3732 *image_config_vars[i].value =
3733 get_auto_parameter_value(image_config_vars[i].token, value);
3736 freeSetupFileHash(setup_file_hash);
3739 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
3741 char *filename = getEditorSetupFilename();
3742 SetupFileList *setup_file_list, *list;
3743 SetupFileHash *element_hash;
3744 int num_unknown_tokens = 0;
3747 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
3750 element_hash = newSetupFileHash();
3752 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
3753 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
3755 /* determined size may be larger than needed (due to unknown elements) */
3757 for (list = setup_file_list; list != NULL; list = list->next)
3760 /* add space for up to 3 more elements for padding that may be needed */
3763 *elements = checked_malloc(*num_elements * sizeof(int));
3766 for (list = setup_file_list; list != NULL; list = list->next)
3768 char *value = getHashEntry(element_hash, list->token);
3772 (*elements)[(*num_elements)++] = atoi(value);
3776 if (num_unknown_tokens == 0)
3778 Error(ERR_RETURN_LINE, "-");
3779 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
3780 Error(ERR_RETURN, "- config file: '%s'", filename);
3782 num_unknown_tokens++;
3785 Error(ERR_RETURN, "- token: '%s'", list->token);
3789 if (num_unknown_tokens > 0)
3790 Error(ERR_RETURN_LINE, "-");
3792 while (*num_elements % 4) /* pad with empty elements, if needed */
3793 (*elements)[(*num_elements)++] = EL_EMPTY;
3795 freeSetupFileList(setup_file_list);
3796 freeSetupFileHash(element_hash);
3800 for (i = 0; i < *num_elements; i++)
3801 printf("editor: element '%s' [%d]\n",
3802 element_info[(*elements)[i]].token_name, (*elements)[i]);
3806 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
3809 SetupFileHash *setup_file_hash = NULL;
3810 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
3811 char *filename_music, *filename_prefix, *filename_info;
3817 token_to_value_ptr[] =
3819 { "title_header", &tmp_music_file_info.title_header },
3820 { "artist_header", &tmp_music_file_info.artist_header },
3821 { "album_header", &tmp_music_file_info.album_header },
3822 { "year_header", &tmp_music_file_info.year_header },
3824 { "title", &tmp_music_file_info.title },
3825 { "artist", &tmp_music_file_info.artist },
3826 { "album", &tmp_music_file_info.album },
3827 { "year", &tmp_music_file_info.year },
3833 filename_music = (is_sound ? getCustomSoundFilename(basename) :
3834 getCustomMusicFilename(basename));
3836 if (filename_music == NULL)
3839 /* ---------- try to replace file extension ---------- */
3841 filename_prefix = getStringCopy(filename_music);
3842 if (strrchr(filename_prefix, '.') != NULL)
3843 *strrchr(filename_prefix, '.') = '\0';
3844 filename_info = getStringCat2(filename_prefix, ".txt");
3847 printf("trying to load file '%s'...\n", filename_info);
3850 if (fileExists(filename_info))
3851 setup_file_hash = loadSetupFileHash(filename_info);
3853 free(filename_prefix);
3854 free(filename_info);
3856 if (setup_file_hash == NULL)
3858 /* ---------- try to add file extension ---------- */
3860 filename_prefix = getStringCopy(filename_music);
3861 filename_info = getStringCat2(filename_prefix, ".txt");
3864 printf("trying to load file '%s'...\n", filename_info);
3867 if (fileExists(filename_info))
3868 setup_file_hash = loadSetupFileHash(filename_info);
3870 free(filename_prefix);
3871 free(filename_info);
3874 if (setup_file_hash == NULL)
3877 /* ---------- music file info found ---------- */
3879 memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
3881 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
3883 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
3885 *token_to_value_ptr[i].value_ptr =
3886 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
3889 tmp_music_file_info.basename = getStringCopy(basename);
3890 tmp_music_file_info.music = music;
3891 tmp_music_file_info.is_sound = is_sound;
3893 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
3894 *new_music_file_info = tmp_music_file_info;
3896 return new_music_file_info;
3899 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
3901 return get_music_file_info_ext(basename, music, FALSE);
3904 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
3906 return get_music_file_info_ext(basename, sound, TRUE);
3909 static boolean music_info_listed_ext(struct MusicFileInfo *list,
3910 char *basename, boolean is_sound)
3912 for (; list != NULL; list = list->next)
3913 if (list->is_sound == is_sound && strcmp(list->basename, basename) == 0)
3919 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
3921 return music_info_listed_ext(list, basename, FALSE);
3924 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
3926 return music_info_listed_ext(list, basename, TRUE);
3929 void LoadMusicInfo()
3931 char *music_directory = getCustomMusicDirectory();
3932 int num_music = getMusicListSize();
3933 int num_music_noconf = 0;
3934 int num_sounds = getSoundListSize();
3936 struct dirent *dir_entry;
3937 struct FileInfo *music, *sound;
3938 struct MusicFileInfo *next, **new;
3941 while (music_file_info != NULL)
3943 next = music_file_info->next;
3945 checked_free(music_file_info->basename);
3947 checked_free(music_file_info->title_header);
3948 checked_free(music_file_info->artist_header);
3949 checked_free(music_file_info->album_header);
3950 checked_free(music_file_info->year_header);
3952 checked_free(music_file_info->title);
3953 checked_free(music_file_info->artist);
3954 checked_free(music_file_info->album);
3955 checked_free(music_file_info->year);
3957 free(music_file_info);
3959 music_file_info = next;
3962 new = &music_file_info;
3965 printf("::: num_music == %d\n", num_music);
3968 for (i = 0; i < num_music; i++)
3970 music = getMusicListEntry(i);
3973 printf("::: %d [%08x]\n", i, music->filename);
3976 if (music->filename == NULL)
3979 if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
3982 /* a configured file may be not recognized as music */
3983 if (!FileIsMusic(music->filename))
3987 printf("::: -> '%s' (configured)\n", music->filename);
3990 if (!music_info_listed(music_file_info, music->filename))
3992 *new = get_music_file_info(music->filename, i);
3994 new = &(*new)->next;
3998 if ((dir = opendir(music_directory)) == NULL)
4000 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
4004 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
4006 char *basename = dir_entry->d_name;
4007 boolean music_already_used = FALSE;
4010 /* skip all music files that are configured in music config file */
4011 for (i = 0; i < num_music; i++)
4013 music = getMusicListEntry(i);
4015 if (music->filename == NULL)
4018 if (strcmp(basename, music->filename) == 0)
4020 music_already_used = TRUE;
4025 if (music_already_used)
4028 if (!FileIsMusic(basename))
4032 printf("::: -> '%s' (found in directory)\n", basename);
4035 if (!music_info_listed(music_file_info, basename))
4037 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
4039 new = &(*new)->next;
4047 for (i = 0; i < num_sounds; i++)
4049 sound = getSoundListEntry(i);
4051 if (sound->filename == NULL)
4054 if (strcmp(sound->filename, UNDEFINED_FILENAME) == 0)
4057 /* a configured file may be not recognized as sound */
4058 if (!FileIsSound(sound->filename))
4062 printf("::: -> '%s' (configured)\n", sound->filename);
4065 if (!sound_info_listed(music_file_info, sound->filename))
4067 *new = get_sound_file_info(sound->filename, i);
4069 new = &(*new)->next;
4075 for (next = music_file_info; next != NULL; next = next->next)
4076 printf("::: title == '%s'\n", next->title);
4080 void add_helpanim_entry(int element, int action, int direction, int delay,
4081 int *num_list_entries)
4083 struct HelpAnimInfo *new_list_entry;
4084 (*num_list_entries)++;
4087 checked_realloc(helpanim_info,
4088 *num_list_entries * sizeof(struct HelpAnimInfo));
4089 new_list_entry = &helpanim_info[*num_list_entries - 1];
4091 new_list_entry->element = element;
4092 new_list_entry->action = action;
4093 new_list_entry->direction = direction;
4094 new_list_entry->delay = delay;
4097 void print_unknown_token(char *filename, char *token, int token_nr)
4101 Error(ERR_RETURN_LINE, "-");
4102 Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
4103 Error(ERR_RETURN, "- config file: '%s'", filename);
4106 Error(ERR_RETURN, "- token: '%s'", token);
4109 void print_unknown_token_end(int token_nr)
4112 Error(ERR_RETURN_LINE, "-");
4115 void LoadHelpAnimInfo()
4117 char *filename = getHelpAnimFilename();
4118 SetupFileList *setup_file_list = NULL, *list;
4119 SetupFileHash *element_hash, *action_hash, *direction_hash;
4120 int num_list_entries = 0;
4121 int num_unknown_tokens = 0;
4124 if (fileExists(filename))
4125 setup_file_list = loadSetupFileList(filename);
4127 if (setup_file_list == NULL)
4129 /* use reliable default values from static configuration */
4130 SetupFileList *insert_ptr;
4132 insert_ptr = setup_file_list =
4133 newSetupFileList(helpanim_config[0].token,
4134 helpanim_config[0].value);
4136 for (i = 1; helpanim_config[i].token; i++)
4137 insert_ptr = addListEntry(insert_ptr,
4138 helpanim_config[i].token,
4139 helpanim_config[i].value);
4142 element_hash = newSetupFileHash();
4143 action_hash = newSetupFileHash();
4144 direction_hash = newSetupFileHash();
4146 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4147 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
4149 for (i = 0; i < NUM_ACTIONS; i++)
4150 setHashEntry(action_hash, element_action_info[i].suffix,
4151 i_to_a(element_action_info[i].value));
4153 /* do not store direction index (bit) here, but direction value! */
4154 for (i = 0; i < NUM_DIRECTIONS; i++)
4155 setHashEntry(direction_hash, element_direction_info[i].suffix,
4156 i_to_a(1 << element_direction_info[i].value));
4158 for (list = setup_file_list; list != NULL; list = list->next)
4160 char *element_token, *action_token, *direction_token;
4161 char *element_value, *action_value, *direction_value;
4162 int delay = atoi(list->value);
4164 if (strcmp(list->token, "end") == 0)
4166 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4171 /* first try to break element into element/action/direction parts;
4172 if this does not work, also accept combined "element[.act][.dir]"
4173 elements (like "dynamite.active"), which are unique elements */
4175 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
4177 element_value = getHashEntry(element_hash, list->token);
4178 if (element_value != NULL) /* element found */
4179 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4183 /* no further suffixes found -- this is not an element */
4184 print_unknown_token(filename, list->token, num_unknown_tokens++);
4190 /* token has format "<prefix>.<something>" */
4192 action_token = strchr(list->token, '.'); /* suffix may be action ... */
4193 direction_token = action_token; /* ... or direction */
4195 element_token = getStringCopy(list->token);
4196 *strchr(element_token, '.') = '\0';
4198 element_value = getHashEntry(element_hash, element_token);
4200 if (element_value == NULL) /* this is no element */
4202 element_value = getHashEntry(element_hash, list->token);
4203 if (element_value != NULL) /* combined element found */
4204 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4207 print_unknown_token(filename, list->token, num_unknown_tokens++);
4209 free(element_token);
4214 action_value = getHashEntry(action_hash, action_token);
4216 if (action_value != NULL) /* action found */
4218 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
4221 free(element_token);
4226 direction_value = getHashEntry(direction_hash, direction_token);
4228 if (direction_value != NULL) /* direction found */
4230 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
4233 free(element_token);
4238 if (strchr(action_token + 1, '.') == NULL)
4240 /* no further suffixes found -- this is not an action nor direction */
4242 element_value = getHashEntry(element_hash, list->token);
4243 if (element_value != NULL) /* combined element found */
4244 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4247 print_unknown_token(filename, list->token, num_unknown_tokens++);
4249 free(element_token);
4254 /* token has format "<prefix>.<suffix>.<something>" */
4256 direction_token = strchr(action_token + 1, '.');
4258 action_token = getStringCopy(action_token);
4259 *strchr(action_token + 1, '.') = '\0';
4261 action_value = getHashEntry(action_hash, action_token);
4263 if (action_value == NULL) /* this is no action */
4265 element_value = getHashEntry(element_hash, list->token);
4266 if (element_value != NULL) /* combined element found */
4267 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4270 print_unknown_token(filename, list->token, num_unknown_tokens++);
4272 free(element_token);
4278 direction_value = getHashEntry(direction_hash, direction_token);
4280 if (direction_value != NULL) /* direction found */
4282 add_helpanim_entry(atoi(element_value), atoi(action_value),
4283 atoi(direction_value), delay, &num_list_entries);
4285 free(element_token);
4291 /* this is no direction */
4293 element_value = getHashEntry(element_hash, list->token);
4294 if (element_value != NULL) /* combined element found */
4295 add_helpanim_entry(atoi(element_value), -1, -1, delay,
4298 print_unknown_token(filename, list->token, num_unknown_tokens++);
4300 free(element_token);
4304 print_unknown_token_end(num_unknown_tokens);
4306 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
4307 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
4309 freeSetupFileList(setup_file_list);
4310 freeSetupFileHash(element_hash);
4311 freeSetupFileHash(action_hash);
4312 freeSetupFileHash(direction_hash);
4316 for (i = 0; i < num_list_entries; i++)
4317 printf("::: %d, %d, %d => %d\n",
4318 helpanim_info[i].element,
4319 helpanim_info[i].action,
4320 helpanim_info[i].direction,
4321 helpanim_info[i].delay);
4325 void LoadHelpTextInfo()
4327 char *filename = getHelpTextFilename();
4330 if (helptext_info != NULL)
4332 freeSetupFileHash(helptext_info);
4333 helptext_info = NULL;
4336 if (fileExists(filename))
4337 helptext_info = loadSetupFileHash(filename);
4339 if (helptext_info == NULL)
4341 /* use reliable default values from static configuration */
4342 helptext_info = newSetupFileHash();
4344 for (i = 0; helptext_config[i].token; i++)
4345 setHashEntry(helptext_info,
4346 helptext_config[i].token,
4347 helptext_config[i].value);
4352 BEGIN_HASH_ITERATION(helptext_info, itr)
4354 printf("::: '%s' => '%s'\n",
4355 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
4357 END_HASH_ITERATION(hash, itr)