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 ***********************************************************/
17 #include "libgame/libgame.h"
25 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
26 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
27 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
28 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
29 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
30 #define LEVEL_HEADER_UNUSED 13 /* unused level header bytes */
31 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
32 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
33 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
34 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
35 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
36 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
38 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + x * LEVEL_CPART_CUS3_SIZE)
40 /* file identifier strings */
41 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
42 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
43 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
46 /* ========================================================================= */
47 /* level file functions */
48 /* ========================================================================= */
50 void setElementChangePages(struct ElementInfo *ei, int change_pages)
52 int change_page_size = sizeof(struct ElementChangeInfo);
54 ei->num_change_pages = MAX(1, change_pages);
57 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
59 if (ei->current_change_page >= ei->num_change_pages)
60 ei->current_change_page = ei->num_change_pages - 1;
62 ei->change = &ei->change_page[ei->current_change_page];
65 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
69 change->can_change = FALSE;
71 change->events = CE_BITMASK_DEFAULT;
72 change->target_element = EL_EMPTY_SPACE;
74 change->delay_fixed = 0;
75 change->delay_random = 0;
76 change->delay_frames = -1; /* later set to reliable default value */
78 change->trigger_element = EL_EMPTY_SPACE;
80 change->explode = FALSE;
81 change->use_content = FALSE;
82 change->only_complete = FALSE;
83 change->use_random_change = FALSE;
85 change->power = CP_NON_DESTRUCTIVE;
89 change->content[x][y] = EL_EMPTY_SPACE;
91 change->direct_action = 0;
92 change->other_action = 0;
94 change->pre_change_function = NULL;
95 change->change_function = NULL;
96 change->post_change_function = NULL;
99 static void setLevelInfoToDefaults(struct LevelInfo *level)
103 level->file_version = FILE_VERSION_ACTUAL;
104 level->game_version = GAME_VERSION_ACTUAL;
106 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
107 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
108 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
110 level->fieldx = STD_LEV_FIELDX;
111 level->fieldy = STD_LEV_FIELDY;
113 for(x=0; x<MAX_LEV_FIELDX; x++)
114 for(y=0; y<MAX_LEV_FIELDY; y++)
115 level->field[x][y] = EL_SAND;
118 level->gems_needed = 0;
119 level->amoeba_speed = 10;
120 level->time_magic_wall = 10;
121 level->time_wheel = 10;
122 level->time_light = 10;
123 level->time_timegate = 10;
124 level->amoeba_content = EL_DIAMOND;
125 level->double_speed = FALSE;
126 level->gravity = FALSE;
127 level->em_slippery_gems = FALSE;
129 level->use_custom_template = FALSE;
131 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
132 level->name[i] = '\0';
133 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
134 level->author[i] = '\0';
136 strcpy(level->name, NAMELESS_LEVEL_NAME);
137 strcpy(level->author, ANONYMOUS_NAME);
139 level->envelope[0] = '\0';
140 level->envelope_xsize = MAX_ENVELOPE_XSIZE;
141 level->envelope_ysize = MAX_ENVELOPE_YSIZE;
143 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
144 level->score[i] = 10;
146 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
147 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
150 level->yamyam_content[i][x][y] =
151 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
153 level->field[0][0] = EL_PLAYER_1;
154 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
156 for (i=0; i < MAX_NUM_ELEMENTS; i++)
158 setElementChangePages(&element_info[i], 1);
159 setElementChangeInfoToDefaults(element_info[i].change);
162 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
164 int element = EL_CUSTOM_START + i;
166 for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
167 element_info[element].description[j] = '\0';
168 if (element_info[element].custom_description != NULL)
169 strncpy(element_info[element].description,
170 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
172 strcpy(element_info[element].description,
173 element_info[element].editor_description);
175 element_info[element].use_gfx_element = FALSE;
176 element_info[element].gfx_element = EL_EMPTY_SPACE;
178 element_info[element].collect_score = 10; /* special default */
179 element_info[element].collect_count = 1; /* special default */
181 element_info[element].push_delay_fixed = 2; /* special default */
182 element_info[element].push_delay_random = 8; /* special default */
183 element_info[element].move_delay_fixed = 0;
184 element_info[element].move_delay_random = 0;
186 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
187 element_info[element].move_direction_initial = MV_NO_MOVING;
188 element_info[element].move_stepsize = TILEX / 8;
190 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
194 element_info[element].content[x][y] = EL_EMPTY_SPACE;
196 element_info[element].access_type = 0;
197 element_info[element].access_layer = 0;
198 element_info[element].walk_to_action = 0;
199 element_info[element].smash_targets = 0;
200 element_info[element].deadliness = 0;
201 element_info[element].consistency = 0;
203 element_info[element].can_explode_by_fire = FALSE;
204 element_info[element].can_explode_smashed = FALSE;
205 element_info[element].can_explode_impact = FALSE;
207 element_info[element].current_change_page = 0;
209 /* start with no properties at all */
210 for (j=0; j < NUM_EP_BITFIELDS; j++)
211 Properties[element][j] = EP_BITMASK_DEFAULT;
213 element_info[element].modified_settings = FALSE;
216 BorderElement = EL_STEELWALL;
218 level->no_level_file = FALSE;
220 if (leveldir_current == NULL) /* only when dumping level */
223 /* try to determine better author name than 'anonymous' */
224 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
226 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
227 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
231 switch (LEVELCLASS(leveldir_current))
233 case LEVELCLASS_TUTORIAL:
234 strcpy(level->author, PROGRAM_AUTHOR_STRING);
237 case LEVELCLASS_CONTRIBUTION:
238 strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
239 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
242 case LEVELCLASS_USER:
243 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
244 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
248 /* keep default value */
254 static void ActivateLevelTemplate()
256 /* Currently there is no special action needed to activate the template
257 data, because 'element_info' and 'Properties' overwrite the original
258 level data, while all other variables do not change. */
261 boolean LevelFileExists(int level_nr)
263 char *filename = getLevelFilename(level_nr);
265 return (access(filename, F_OK) == 0);
268 static int checkLevelElement(int element)
270 if (element >= NUM_FILE_ELEMENTS)
272 Error(ERR_WARN, "invalid level element %d", element);
273 element = EL_CHAR_QUESTION;
275 else if (element == EL_PLAYER_OBSOLETE)
276 element = EL_PLAYER_1;
277 else if (element == EL_KEY_OBSOLETE)
283 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
285 level->file_version = getFileVersion(file);
286 level->game_version = getFileVersion(file);
291 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
295 level->fieldx = getFile8Bit(file);
296 level->fieldy = getFile8Bit(file);
298 level->time = getFile16BitBE(file);
299 level->gems_needed = getFile16BitBE(file);
301 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
302 level->name[i] = getFile8Bit(file);
303 level->name[MAX_LEVEL_NAME_LEN] = 0;
305 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
306 level->score[i] = getFile8Bit(file);
308 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
309 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
312 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
314 level->amoeba_speed = getFile8Bit(file);
315 level->time_magic_wall = getFile8Bit(file);
316 level->time_wheel = getFile8Bit(file);
317 level->amoeba_content = checkLevelElement(getFile8Bit(file));
318 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
319 level->gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
320 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
321 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
323 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
325 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
330 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
334 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
335 level->author[i] = getFile8Bit(file);
336 level->author[MAX_LEVEL_NAME_LEN] = 0;
341 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
344 int chunk_size_expected = level->fieldx * level->fieldy;
346 /* Note: "chunk_size" was wrong before version 2.0 when elements are
347 stored with 16-bit encoding (and should be twice as big then).
348 Even worse, playfield data was stored 16-bit when only yamyam content
349 contained 16-bit elements and vice versa. */
351 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
352 chunk_size_expected *= 2;
354 if (chunk_size_expected != chunk_size)
356 ReadUnusedBytesFromFile(file, chunk_size);
357 return chunk_size_expected;
360 for(y=0; y<level->fieldy; y++)
361 for(x=0; x<level->fieldx; x++)
363 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
368 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
372 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
373 int chunk_size_expected = header_size + content_size;
375 /* Note: "chunk_size" was wrong before version 2.0 when elements are
376 stored with 16-bit encoding (and should be twice as big then).
377 Even worse, playfield data was stored 16-bit when only yamyam content
378 contained 16-bit elements and vice versa. */
380 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
381 chunk_size_expected += content_size;
383 if (chunk_size_expected != chunk_size)
385 ReadUnusedBytesFromFile(file, chunk_size);
386 return chunk_size_expected;
390 level->num_yamyam_contents = getFile8Bit(file);
394 /* correct invalid number of content fields -- should never happen */
395 if (level->num_yamyam_contents < 1 ||
396 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
397 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
399 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
402 level->yamyam_content[i][x][y] =
403 checkLevelElement(level->encoding_16bit_field ?
404 getFile16BitBE(file) : getFile8Bit(file));
408 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
412 int num_contents, content_xsize, content_ysize;
413 int content_array[MAX_ELEMENT_CONTENTS][3][3];
415 element = checkLevelElement(getFile16BitBE(file));
416 num_contents = getFile8Bit(file);
417 content_xsize = getFile8Bit(file);
418 content_ysize = getFile8Bit(file);
419 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
421 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
424 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
426 /* correct invalid number of content fields -- should never happen */
427 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
428 num_contents = STD_ELEMENT_CONTENTS;
430 if (element == EL_YAMYAM)
432 level->num_yamyam_contents = num_contents;
434 for(i=0; i<num_contents; i++)
437 level->yamyam_content[i][x][y] = content_array[i][x][y];
439 else if (element == EL_BD_AMOEBA)
441 level->amoeba_content = content_array[0][0][0];
445 Error(ERR_WARN, "cannot load content for element '%d'", element);
451 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
453 int num_changed_custom_elements = getFile16BitBE(file);
454 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
457 if (chunk_size_expected != chunk_size)
459 ReadUnusedBytesFromFile(file, chunk_size - 2);
460 return chunk_size_expected;
463 for (i=0; i < num_changed_custom_elements; i++)
465 int element = getFile16BitBE(file);
466 int properties = getFile32BitBE(file);
468 if (IS_CUSTOM_ELEMENT(element))
469 Properties[element][EP_BITFIELD_BASE] = properties;
471 Error(ERR_WARN, "invalid custom element number %d", element);
477 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
479 int num_changed_custom_elements = getFile16BitBE(file);
480 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
483 if (chunk_size_expected != chunk_size)
485 ReadUnusedBytesFromFile(file, chunk_size - 2);
486 return chunk_size_expected;
489 for (i=0; i < num_changed_custom_elements; i++)
491 int element = getFile16BitBE(file);
492 int custom_target_element = getFile16BitBE(file);
494 if (IS_CUSTOM_ELEMENT(element))
495 element_info[element].change->target_element = custom_target_element;
497 Error(ERR_WARN, "invalid custom element number %d", element);
503 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
505 int num_changed_custom_elements = getFile16BitBE(file);
506 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
509 if (chunk_size_expected != chunk_size)
511 ReadUnusedBytesFromFile(file, chunk_size - 2);
512 return chunk_size_expected;
515 for (i=0; i < num_changed_custom_elements; i++)
517 int element = getFile16BitBE(file);
519 if (!IS_CUSTOM_ELEMENT(element))
521 Error(ERR_WARN, "invalid custom element number %d", element);
523 element = EL_DEFAULT; /* dummy element used for artwork config */
526 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
527 element_info[element].description[j] = getFile8Bit(file);
528 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
530 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
532 /* some free bytes for future properties and padding */
533 ReadUnusedBytesFromFile(file, 7);
535 element_info[element].use_gfx_element = getFile8Bit(file);
536 element_info[element].gfx_element =
537 checkLevelElement(getFile16BitBE(file));
539 element_info[element].collect_score = getFile8Bit(file);
540 element_info[element].collect_count = getFile8Bit(file);
542 element_info[element].push_delay_fixed = getFile16BitBE(file);
543 element_info[element].push_delay_random = getFile16BitBE(file);
544 element_info[element].move_delay_fixed = getFile16BitBE(file);
545 element_info[element].move_delay_random = getFile16BitBE(file);
547 element_info[element].move_pattern = getFile16BitBE(file);
548 element_info[element].move_direction_initial = getFile8Bit(file);
549 element_info[element].move_stepsize = getFile8Bit(file);
553 element_info[element].content[x][y] =
554 checkLevelElement(getFile16BitBE(file));
556 element_info[element].change->events = getFile32BitBE(file);
558 element_info[element].change->target_element =
559 checkLevelElement(getFile16BitBE(file));
561 element_info[element].change->delay_fixed = getFile16BitBE(file);
562 element_info[element].change->delay_random = getFile16BitBE(file);
563 element_info[element].change->delay_frames = getFile16BitBE(file);
565 element_info[element].change->trigger_element =
566 checkLevelElement(getFile16BitBE(file));
568 element_info[element].change->explode = getFile8Bit(file);
569 element_info[element].change->use_content = getFile8Bit(file);
570 element_info[element].change->only_complete = getFile8Bit(file);
571 element_info[element].change->use_random_change = getFile8Bit(file);
573 element_info[element].change->random = getFile8Bit(file);
574 element_info[element].change->power = getFile8Bit(file);
578 element_info[element].change->content[x][y] =
579 checkLevelElement(getFile16BitBE(file));
581 element_info[element].slippery_type = getFile8Bit(file);
583 /* some free bytes for future properties and padding */
584 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
586 /* mark that this custom element has been modified */
587 element_info[element].modified_settings = TRUE;
593 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
595 char cookie[MAX_LINE_LEN];
596 char chunk_name[CHUNK_ID_LEN + 1];
600 /* always start with reliable default values */
601 setLevelInfoToDefaults(level);
603 if (!(file = fopen(filename, MODE_READ)))
605 level->no_level_file = TRUE;
607 if (level != &level_template)
608 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
613 getFileChunkBE(file, chunk_name, NULL);
614 if (strcmp(chunk_name, "RND1") == 0)
616 getFile32BitBE(file); /* not used */
618 getFileChunkBE(file, chunk_name, NULL);
619 if (strcmp(chunk_name, "CAVE") != 0)
621 Error(ERR_WARN, "unknown format of level file '%s'", filename);
626 else /* check for pre-2.0 file format with cookie string */
628 strcpy(cookie, chunk_name);
629 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
630 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
631 cookie[strlen(cookie) - 1] = '\0';
633 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
635 Error(ERR_WARN, "unknown format of level file '%s'", filename);
640 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
642 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
647 /* pre-2.0 level files have no game version, so use file version here */
648 level->game_version = level->file_version;
651 if (level->file_version < FILE_VERSION_1_2)
653 /* level files from versions before 1.2.0 without chunk structure */
654 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
655 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
663 int (*loader)(FILE *, int, struct LevelInfo *);
667 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
668 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
669 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
670 { "BODY", -1, LoadLevel_BODY },
671 { "CONT", -1, LoadLevel_CONT },
672 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
673 { "CUS1", -1, LoadLevel_CUS1 },
674 { "CUS2", -1, LoadLevel_CUS2 },
675 { "CUS3", -1, LoadLevel_CUS3 },
679 while (getFileChunkBE(file, chunk_name, &chunk_size))
683 while (chunk_info[i].name != NULL &&
684 strcmp(chunk_name, chunk_info[i].name) != 0)
687 if (chunk_info[i].name == NULL)
689 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
690 chunk_name, filename);
691 ReadUnusedBytesFromFile(file, chunk_size);
693 else if (chunk_info[i].size != -1 &&
694 chunk_info[i].size != chunk_size)
696 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
697 chunk_size, chunk_name, filename);
698 ReadUnusedBytesFromFile(file, chunk_size);
702 /* call function to load this level chunk */
703 int chunk_size_expected =
704 (chunk_info[i].loader)(file, chunk_size, level);
706 /* the size of some chunks cannot be checked before reading other
707 chunks first (like "HEAD" and "BODY") that contain some header
708 information, so check them here */
709 if (chunk_size_expected != chunk_size)
711 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
712 chunk_size, chunk_name, filename);
723 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
725 if (leveldir_current == NULL) /* only when dumping level */
728 /* determine correct game engine version of current level */
729 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
730 IS_LEVELCLASS_USER(leveldir_current))
733 printf("\n::: This level is private or contributed: '%s'\n", filename);
736 /* For user contributed and private levels, use the version of
737 the game engine the levels were created for.
738 Since 2.0.1, the game engine version is now directly stored
739 in the level file (chunk "VERS"), so there is no need anymore
740 to set the game version from the file version (except for old,
741 pre-2.0 levels, where the game version is still taken from the
742 file format version used to store the level -- see above). */
744 /* do some special adjustments to support older level versions */
745 if (level->file_version == FILE_VERSION_1_0)
747 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
748 Error(ERR_WARN, "using high speed movement for player");
750 /* player was faster than monsters in (pre-)1.0 levels */
751 level->double_speed = TRUE;
754 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
755 if (level->game_version == VERSION_IDENT(2,0,1))
756 level->em_slippery_gems = TRUE;
761 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
762 leveldir_current->sort_priority, filename);
765 /* Always use the latest version of the game engine for all but
766 user contributed and private levels; this allows for actual
767 corrections in the game engine to take effect for existing,
768 converted levels (from "classic" or other existing games) to
769 make the game emulation more accurate, while (hopefully) not
770 breaking existing levels created from other players. */
772 level->game_version = GAME_VERSION_ACTUAL;
774 /* Set special EM style gems behaviour: EM style gems slip down from
775 normal, steel and growing wall. As this is a more fundamental change,
776 it seems better to set the default behaviour to "off" (as it is more
777 natural) and make it configurable in the level editor (as a property
778 of gem style elements). Already existing converted levels (neither
779 private nor contributed levels) are changed to the new behaviour. */
781 if (level->file_version < FILE_VERSION_2_0)
782 level->em_slippery_gems = TRUE;
786 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
790 /* map custom element change events that have changed in newer versions
791 (these following values have accidentally changed in version 3.0.1) */
792 if (level->game_version <= VERSION_IDENT(3,0,0))
794 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
796 int element = EL_CUSTOM_START + i;
798 /* order of checking and copying events to be mapped is important */
799 for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
801 if (HAS_CHANGE_EVENT(element, j - 2))
803 SET_CHANGE_EVENT(element, j - 2, FALSE);
804 SET_CHANGE_EVENT(element, j, TRUE);
808 /* order of checking and copying events to be mapped is important */
809 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
811 if (HAS_CHANGE_EVENT(element, j - 1))
813 SET_CHANGE_EVENT(element, j - 1, FALSE);
814 SET_CHANGE_EVENT(element, j, TRUE);
820 /* some custom element change events get mapped since version 3.0.3 */
821 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
823 int element = EL_CUSTOM_START + i;
825 if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
826 HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
828 SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
829 SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
831 SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
835 /* initialize "can_change" field for old levels with only one change page */
836 if (level->game_version <= VERSION_IDENT(3,0,2))
838 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
840 int element = EL_CUSTOM_START + i;
842 if (CAN_CHANGE(element))
843 element_info[element].change->can_change = TRUE;
847 /* initialize element properties for level editor etc. */
848 InitElementPropertiesEngine(level->game_version);
851 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
855 /* map elements that have changed in newer versions */
856 for(y=0; y<level->fieldy; y++)
858 for(x=0; x<level->fieldx; x++)
860 int element = level->field[x][y];
862 if (level->game_version <= VERSION_IDENT(2,2,0))
864 /* map game font elements */
865 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
866 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
867 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
868 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
871 if (level->game_version < VERSION_IDENT(3,0,0))
873 /* map Supaplex gravity tube elements */
874 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
875 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
876 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
877 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
881 level->field[x][y] = element;
885 /* copy elements to runtime playfield array */
886 for(x=0; x<MAX_LEV_FIELDX; x++)
887 for(y=0; y<MAX_LEV_FIELDY; y++)
888 Feld[x][y] = level->field[x][y];
890 /* initialize level size variables for faster access */
891 lev_fieldx = level->fieldx;
892 lev_fieldy = level->fieldy;
894 /* determine border element for this level */
900 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
904 if (leveldir_current == NULL) /* only when dumping level */
907 /* determine correct game engine version of current level */
908 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
909 IS_LEVELCLASS_USER(leveldir_current))
912 printf("\n::: This level is private or contributed: '%s'\n", filename);
915 /* For user contributed and private levels, use the version of
916 the game engine the levels were created for.
917 Since 2.0.1, the game engine version is now directly stored
918 in the level file (chunk "VERS"), so there is no need anymore
919 to set the game version from the file version (except for old,
920 pre-2.0 levels, where the game version is still taken from the
921 file format version used to store the level -- see above). */
923 /* do some special adjustments to support older level versions */
924 if (level->file_version == FILE_VERSION_1_0)
926 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
927 Error(ERR_WARN, "using high speed movement for player");
929 /* player was faster than monsters in (pre-)1.0 levels */
930 level->double_speed = TRUE;
933 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
934 if (level->game_version == VERSION_IDENT(2,0,1))
935 level->em_slippery_gems = TRUE;
940 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
941 leveldir_current->sort_priority, filename);
944 /* Always use the latest version of the game engine for all but
945 user contributed and private levels; this allows for actual
946 corrections in the game engine to take effect for existing,
947 converted levels (from "classic" or other existing games) to
948 make the game emulation more accurate, while (hopefully) not
949 breaking existing levels created from other players. */
951 level->game_version = GAME_VERSION_ACTUAL;
953 /* Set special EM style gems behaviour: EM style gems slip down from
954 normal, steel and growing wall. As this is a more fundamental change,
955 it seems better to set the default behaviour to "off" (as it is more
956 natural) and make it configurable in the level editor (as a property
957 of gem style elements). Already existing converted levels (neither
958 private nor contributed levels) are changed to the new behaviour. */
960 if (level->file_version < FILE_VERSION_2_0)
961 level->em_slippery_gems = TRUE;
964 /* map elements that have changed in newer versions */
965 for(y=0; y<level->fieldy; y++)
967 for(x=0; x<level->fieldx; x++)
969 int element = level->field[x][y];
971 if (level->game_version <= VERSION_IDENT(2,2,0))
973 /* map game font elements */
974 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
975 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
976 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
977 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
980 if (level->game_version < VERSION_IDENT(3,0,0))
982 /* map Supaplex gravity tube elements */
983 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
984 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
985 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
986 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
990 level->field[x][y] = element;
994 /* map custom element change events that have changed in newer versions
995 (these following values have accidentally changed in version 3.0.1) */
996 if (level->game_version <= VERSION_IDENT(3,0,0))
998 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1000 int element = EL_CUSTOM_START + i;
1002 /* order of checking events to be mapped is important */
1003 for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
1005 if (HAS_CHANGE_EVENT(element, j - 2))
1007 SET_CHANGE_EVENT(element, j - 2, FALSE);
1008 SET_CHANGE_EVENT(element, j, TRUE);
1012 /* order of checking events to be mapped is important */
1013 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1015 if (HAS_CHANGE_EVENT(element, j - 1))
1017 SET_CHANGE_EVENT(element, j - 1, FALSE);
1018 SET_CHANGE_EVENT(element, j, TRUE);
1024 /* initialize "can_change" field for old levels with only one change page */
1025 if (level->game_version <= VERSION_IDENT(3,0,2))
1027 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1029 int element = EL_CUSTOM_START + i;
1031 if (CAN_CHANGE(element))
1032 element_info[element].change->can_change = TRUE;
1036 /* copy elements to runtime playfield array */
1037 for(x=0; x<MAX_LEV_FIELDX; x++)
1038 for(y=0; y<MAX_LEV_FIELDY; y++)
1039 Feld[x][y] = level->field[x][y];
1041 /* initialize level size variables for faster access */
1042 lev_fieldx = level->fieldx;
1043 lev_fieldy = level->fieldy;
1045 /* determine border element for this level */
1048 /* initialize element properties for level editor etc. */
1049 InitElementPropertiesEngine(level->game_version);
1054 void LoadLevelTemplate(int level_nr)
1056 char *filename = getLevelFilename(level_nr);
1058 LoadLevelFromFilename(&level_template, filename);
1060 LoadLevel_InitVersion(&level, filename);
1061 LoadLevel_InitElements(&level, filename);
1063 ActivateLevelTemplate();
1066 void LoadLevel(int level_nr)
1068 char *filename = getLevelFilename(level_nr);
1070 LoadLevelFromFilename(&level, filename);
1072 if (level.use_custom_template)
1073 LoadLevelTemplate(-1);
1076 LoadLevel_InitVersion(&level, filename);
1077 LoadLevel_InitElements(&level, filename);
1078 LoadLevel_InitPlayfield(&level, filename);
1080 LoadLevel_InitLevel(&level, filename);
1084 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1086 putFileVersion(file, level->file_version);
1087 putFileVersion(file, level->game_version);
1090 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1094 putFile8Bit(file, level->fieldx);
1095 putFile8Bit(file, level->fieldy);
1097 putFile16BitBE(file, level->time);
1098 putFile16BitBE(file, level->gems_needed);
1100 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1101 putFile8Bit(file, level->name[i]);
1103 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1104 putFile8Bit(file, level->score[i]);
1106 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1109 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1110 level->yamyam_content[i][x][y]));
1111 putFile8Bit(file, level->amoeba_speed);
1112 putFile8Bit(file, level->time_magic_wall);
1113 putFile8Bit(file, level->time_wheel);
1114 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1115 level->amoeba_content));
1116 putFile8Bit(file, (level->double_speed ? 1 : 0));
1117 putFile8Bit(file, (level->gravity ? 1 : 0));
1118 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1119 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1121 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1123 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1126 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1130 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1131 putFile8Bit(file, level->author[i]);
1134 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1138 for(y=0; y<level->fieldy; y++)
1139 for(x=0; x<level->fieldx; x++)
1140 if (level->encoding_16bit_field)
1141 putFile16BitBE(file, level->field[x][y]);
1143 putFile8Bit(file, level->field[x][y]);
1147 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1151 putFile8Bit(file, EL_YAMYAM);
1152 putFile8Bit(file, level->num_yamyam_contents);
1153 putFile8Bit(file, 0);
1154 putFile8Bit(file, 0);
1156 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1159 if (level->encoding_16bit_field)
1160 putFile16BitBE(file, level->yamyam_content[i][x][y]);
1162 putFile8Bit(file, level->yamyam_content[i][x][y]);
1166 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1169 int num_contents, content_xsize, content_ysize;
1170 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1172 if (element == EL_YAMYAM)
1174 num_contents = level->num_yamyam_contents;
1178 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1181 content_array[i][x][y] = level->yamyam_content[i][x][y];
1183 else if (element == EL_BD_AMOEBA)
1189 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1192 content_array[i][x][y] = EL_EMPTY;
1193 content_array[0][0][0] = level->amoeba_content;
1197 /* chunk header already written -- write empty chunk data */
1198 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1200 Error(ERR_WARN, "cannot save content for element '%d'", element);
1204 putFile16BitBE(file, element);
1205 putFile8Bit(file, num_contents);
1206 putFile8Bit(file, content_xsize);
1207 putFile8Bit(file, content_ysize);
1209 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1211 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1214 putFile16BitBE(file, content_array[i][x][y]);
1218 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1219 int num_changed_custom_elements)
1223 putFile16BitBE(file, num_changed_custom_elements);
1225 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1227 int element = EL_CUSTOM_START + i;
1229 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1231 if (check < num_changed_custom_elements)
1233 putFile16BitBE(file, element);
1234 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1241 if (check != num_changed_custom_elements) /* should not happen */
1242 Error(ERR_WARN, "inconsistent number of custom element properties");
1247 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1248 int num_changed_custom_elements)
1252 putFile16BitBE(file, num_changed_custom_elements);
1254 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1256 int element = EL_CUSTOM_START + i;
1258 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1260 if (check < num_changed_custom_elements)
1262 putFile16BitBE(file, element);
1263 putFile16BitBE(file, element_info[element].change->target_element);
1270 if (check != num_changed_custom_elements) /* should not happen */
1271 Error(ERR_WARN, "inconsistent number of custom target elements");
1275 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1276 int num_changed_custom_elements)
1278 int i, j, x, y, check = 0;
1280 putFile16BitBE(file, num_changed_custom_elements);
1282 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1284 int element = EL_CUSTOM_START + i;
1286 if (element_info[element].modified_settings)
1288 if (check < num_changed_custom_elements)
1290 putFile16BitBE(file, element);
1292 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1293 putFile8Bit(file, element_info[element].description[j]);
1295 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1297 /* some free bytes for future properties and padding */
1298 WriteUnusedBytesToFile(file, 7);
1300 putFile8Bit(file, element_info[element].use_gfx_element);
1301 putFile16BitBE(file, element_info[element].gfx_element);
1303 putFile8Bit(file, element_info[element].collect_score);
1304 putFile8Bit(file, element_info[element].collect_count);
1306 putFile16BitBE(file, element_info[element].push_delay_fixed);
1307 putFile16BitBE(file, element_info[element].push_delay_random);
1308 putFile16BitBE(file, element_info[element].move_delay_fixed);
1309 putFile16BitBE(file, element_info[element].move_delay_random);
1311 putFile16BitBE(file, element_info[element].move_pattern);
1312 putFile8Bit(file, element_info[element].move_direction_initial);
1313 putFile8Bit(file, element_info[element].move_stepsize);
1317 putFile16BitBE(file, element_info[element].content[x][y]);
1319 putFile32BitBE(file, element_info[element].change->events);
1321 putFile16BitBE(file, element_info[element].change->target_element);
1323 putFile16BitBE(file, element_info[element].change->delay_fixed);
1324 putFile16BitBE(file, element_info[element].change->delay_random);
1325 putFile16BitBE(file, element_info[element].change->delay_frames);
1327 putFile16BitBE(file, element_info[element].change->trigger_element);
1329 putFile8Bit(file, element_info[element].change->explode);
1330 putFile8Bit(file, element_info[element].change->use_content);
1331 putFile8Bit(file, element_info[element].change->only_complete);
1332 putFile8Bit(file, element_info[element].change->use_random_change);
1334 putFile8Bit(file, element_info[element].change->random);
1335 putFile8Bit(file, element_info[element].change->power);
1339 putFile16BitBE(file, element_info[element].change->content[x][y]);
1341 putFile8Bit(file, element_info[element].slippery_type);
1343 /* some free bytes for future properties and padding */
1344 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1351 if (check != num_changed_custom_elements) /* should not happen */
1352 Error(ERR_WARN, "inconsistent number of custom element properties");
1355 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1357 int body_chunk_size;
1358 int num_changed_custom_elements = 0;
1359 int level_chunk_CUS3_size;
1363 if (!(file = fopen(filename, MODE_WRITE)))
1365 Error(ERR_WARN, "cannot save level file '%s'", filename);
1369 level->file_version = FILE_VERSION_ACTUAL;
1370 level->game_version = GAME_VERSION_ACTUAL;
1372 /* check level field for 16-bit elements */
1373 level->encoding_16bit_field = FALSE;
1374 for(y=0; y<level->fieldy; y++)
1375 for(x=0; x<level->fieldx; x++)
1376 if (level->field[x][y] > 255)
1377 level->encoding_16bit_field = TRUE;
1379 /* check yamyam content for 16-bit elements */
1380 level->encoding_16bit_yamyam = FALSE;
1381 for(i=0; i<level->num_yamyam_contents; i++)
1384 if (level->yamyam_content[i][x][y] > 255)
1385 level->encoding_16bit_yamyam = TRUE;
1387 /* check amoeba content for 16-bit elements */
1388 level->encoding_16bit_amoeba = FALSE;
1389 if (level->amoeba_content > 255)
1390 level->encoding_16bit_amoeba = TRUE;
1392 /* calculate size of "BODY" chunk */
1394 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1396 /* check for non-standard custom elements and calculate "CUS3" chunk size */
1397 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1398 if (element_info[EL_CUSTOM_START + i].modified_settings)
1399 num_changed_custom_elements++;
1400 level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1402 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1403 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1405 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1406 SaveLevel_VERS(file, level);
1408 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1409 SaveLevel_HEAD(file, level);
1411 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1412 SaveLevel_AUTH(file, level);
1414 putFileChunkBE(file, "BODY", body_chunk_size);
1415 SaveLevel_BODY(file, level);
1417 if (level->encoding_16bit_yamyam ||
1418 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1420 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1421 SaveLevel_CNT2(file, level, EL_YAMYAM);
1424 if (level->encoding_16bit_amoeba)
1426 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1427 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1430 if (num_changed_custom_elements > 0 && !level->use_custom_template)
1432 putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1433 SaveLevel_CUS3(file, level, num_changed_custom_elements);
1438 SetFilePermissions(filename, PERMS_PRIVATE);
1441 void SaveLevel(int level_nr)
1443 char *filename = getLevelFilename(level_nr);
1445 SaveLevelFromFilename(&level, filename);
1448 void SaveLevelTemplate()
1450 char *filename = getLevelFilename(-1);
1452 SaveLevelFromFilename(&level, filename);
1455 void DumpLevel(struct LevelInfo *level)
1457 printf_line("-", 79);
1458 printf("Level xxx (file version %08d, game version %08d)\n",
1459 level->file_version, level->game_version);
1460 printf_line("-", 79);
1462 printf("Level Author: '%s'\n", level->author);
1463 printf("Level Title: '%s'\n", level->name);
1465 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1467 printf("Level Time: %d seconds\n", level->time);
1468 printf("Gems needed: %d\n", level->gems_needed);
1470 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1471 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1472 printf("Time for Light: %d seconds\n", level->time_light);
1473 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1475 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1477 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1478 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1479 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1481 printf_line("-", 79);
1485 /* ========================================================================= */
1486 /* tape file functions */
1487 /* ========================================================================= */
1489 static void setTapeInfoToDefaults()
1493 /* always start with reliable default values (empty tape) */
1496 /* default values (also for pre-1.2 tapes) with only the first player */
1497 tape.player_participates[0] = TRUE;
1498 for(i=1; i<MAX_PLAYERS; i++)
1499 tape.player_participates[i] = FALSE;
1501 /* at least one (default: the first) player participates in every tape */
1502 tape.num_participating_players = 1;
1504 tape.level_nr = level_nr;
1506 tape.changed = FALSE;
1508 tape.recording = FALSE;
1509 tape.playing = FALSE;
1510 tape.pausing = FALSE;
1513 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1515 tape->file_version = getFileVersion(file);
1516 tape->game_version = getFileVersion(file);
1521 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1525 tape->random_seed = getFile32BitBE(file);
1526 tape->date = getFile32BitBE(file);
1527 tape->length = getFile32BitBE(file);
1529 /* read header fields that are new since version 1.2 */
1530 if (tape->file_version >= FILE_VERSION_1_2)
1532 byte store_participating_players = getFile8Bit(file);
1535 /* since version 1.2, tapes store which players participate in the tape */
1536 tape->num_participating_players = 0;
1537 for(i=0; i<MAX_PLAYERS; i++)
1539 tape->player_participates[i] = FALSE;
1541 if (store_participating_players & (1 << i))
1543 tape->player_participates[i] = TRUE;
1544 tape->num_participating_players++;
1548 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1550 engine_version = getFileVersion(file);
1551 if (engine_version > 0)
1552 tape->engine_version = engine_version;
1554 tape->engine_version = tape->game_version;
1560 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1562 int level_identifier_size;
1565 level_identifier_size = getFile16BitBE(file);
1567 tape->level_identifier =
1568 checked_realloc(tape->level_identifier, level_identifier_size);
1570 for(i=0; i < level_identifier_size; i++)
1571 tape->level_identifier[i] = getFile8Bit(file);
1573 tape->level_nr = getFile16BitBE(file);
1575 chunk_size = 2 + level_identifier_size + 2;
1580 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1583 int chunk_size_expected =
1584 (tape->num_participating_players + 1) * tape->length;
1586 if (chunk_size_expected != chunk_size)
1588 ReadUnusedBytesFromFile(file, chunk_size);
1589 return chunk_size_expected;
1592 for(i=0; i<tape->length; i++)
1594 if (i >= MAX_TAPELEN)
1597 for(j=0; j<MAX_PLAYERS; j++)
1599 tape->pos[i].action[j] = MV_NO_MOVING;
1601 if (tape->player_participates[j])
1602 tape->pos[i].action[j] = getFile8Bit(file);
1605 tape->pos[i].delay = getFile8Bit(file);
1607 if (tape->file_version == FILE_VERSION_1_0)
1609 /* eliminate possible diagonal moves in old tapes */
1610 /* this is only for backward compatibility */
1612 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1613 byte action = tape->pos[i].action[0];
1614 int k, num_moves = 0;
1618 if (action & joy_dir[k])
1620 tape->pos[i + num_moves].action[0] = joy_dir[k];
1622 tape->pos[i + num_moves].delay = 0;
1631 tape->length += num_moves;
1634 else if (tape->file_version < FILE_VERSION_2_0)
1636 /* convert pre-2.0 tapes to new tape format */
1638 if (tape->pos[i].delay > 1)
1641 tape->pos[i + 1] = tape->pos[i];
1642 tape->pos[i + 1].delay = 1;
1645 for(j=0; j<MAX_PLAYERS; j++)
1646 tape->pos[i].action[j] = MV_NO_MOVING;
1647 tape->pos[i].delay--;
1658 if (i != tape->length)
1659 chunk_size = (tape->num_participating_players + 1) * i;
1664 void LoadTapeFromFilename(char *filename)
1666 char cookie[MAX_LINE_LEN];
1667 char chunk_name[CHUNK_ID_LEN + 1];
1671 /* always start with reliable default values */
1672 setTapeInfoToDefaults();
1674 if (!(file = fopen(filename, MODE_READ)))
1677 getFileChunkBE(file, chunk_name, NULL);
1678 if (strcmp(chunk_name, "RND1") == 0)
1680 getFile32BitBE(file); /* not used */
1682 getFileChunkBE(file, chunk_name, NULL);
1683 if (strcmp(chunk_name, "TAPE") != 0)
1685 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1690 else /* check for pre-2.0 file format with cookie string */
1692 strcpy(cookie, chunk_name);
1693 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1694 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1695 cookie[strlen(cookie) - 1] = '\0';
1697 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1699 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1704 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1706 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1711 /* pre-2.0 tape files have no game version, so use file version here */
1712 tape.game_version = tape.file_version;
1715 if (tape.file_version < FILE_VERSION_1_2)
1717 /* tape files from versions before 1.2.0 without chunk structure */
1718 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1719 LoadTape_BODY(file, 2 * tape.length, &tape);
1727 int (*loader)(FILE *, int, struct TapeInfo *);
1731 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1732 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1733 { "INFO", -1, LoadTape_INFO },
1734 { "BODY", -1, LoadTape_BODY },
1738 while (getFileChunkBE(file, chunk_name, &chunk_size))
1742 while (chunk_info[i].name != NULL &&
1743 strcmp(chunk_name, chunk_info[i].name) != 0)
1746 if (chunk_info[i].name == NULL)
1748 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1749 chunk_name, filename);
1750 ReadUnusedBytesFromFile(file, chunk_size);
1752 else if (chunk_info[i].size != -1 &&
1753 chunk_info[i].size != chunk_size)
1755 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1756 chunk_size, chunk_name, filename);
1757 ReadUnusedBytesFromFile(file, chunk_size);
1761 /* call function to load this tape chunk */
1762 int chunk_size_expected =
1763 (chunk_info[i].loader)(file, chunk_size, &tape);
1765 /* the size of some chunks cannot be checked before reading other
1766 chunks first (like "HEAD" and "BODY") that contain some header
1767 information, so check them here */
1768 if (chunk_size_expected != chunk_size)
1770 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1771 chunk_size, chunk_name, filename);
1779 tape.length_seconds = GetTapeLength();
1782 printf("tape game version: %d\n", tape.game_version);
1783 printf("tape engine version: %d\n", tape.engine_version);
1787 void LoadTape(int level_nr)
1789 char *filename = getTapeFilename(level_nr);
1791 LoadTapeFromFilename(filename);
1794 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1796 putFileVersion(file, tape->file_version);
1797 putFileVersion(file, tape->game_version);
1800 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1803 byte store_participating_players = 0;
1805 /* set bits for participating players for compact storage */
1806 for(i=0; i<MAX_PLAYERS; i++)
1807 if (tape->player_participates[i])
1808 store_participating_players |= (1 << i);
1810 putFile32BitBE(file, tape->random_seed);
1811 putFile32BitBE(file, tape->date);
1812 putFile32BitBE(file, tape->length);
1814 putFile8Bit(file, store_participating_players);
1816 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1817 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1819 putFileVersion(file, tape->engine_version);
1822 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1824 int level_identifier_size = strlen(tape->level_identifier) + 1;
1827 putFile16BitBE(file, level_identifier_size);
1829 for(i=0; i < level_identifier_size; i++)
1830 putFile8Bit(file, tape->level_identifier[i]);
1832 putFile16BitBE(file, tape->level_nr);
1835 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1839 for(i=0; i<tape->length; i++)
1841 for(j=0; j<MAX_PLAYERS; j++)
1842 if (tape->player_participates[j])
1843 putFile8Bit(file, tape->pos[i].action[j]);
1845 putFile8Bit(file, tape->pos[i].delay);
1849 void SaveTape(int level_nr)
1851 char *filename = getTapeFilename(level_nr);
1853 boolean new_tape = TRUE;
1854 int num_participating_players = 0;
1855 int info_chunk_size;
1856 int body_chunk_size;
1859 InitTapeDirectory(leveldir_current->filename);
1861 /* if a tape still exists, ask to overwrite it */
1862 if (access(filename, F_OK) == 0)
1865 if (!Request("Replace old tape ?", REQ_ASK))
1869 if (!(file = fopen(filename, MODE_WRITE)))
1871 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1875 tape.file_version = FILE_VERSION_ACTUAL;
1876 tape.game_version = GAME_VERSION_ACTUAL;
1878 /* count number of participating players */
1879 for(i=0; i<MAX_PLAYERS; i++)
1880 if (tape.player_participates[i])
1881 num_participating_players++;
1883 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1884 body_chunk_size = (num_participating_players + 1) * tape.length;
1886 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1887 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1889 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1890 SaveTape_VERS(file, &tape);
1892 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1893 SaveTape_HEAD(file, &tape);
1895 putFileChunkBE(file, "INFO", info_chunk_size);
1896 SaveTape_INFO(file, &tape);
1898 putFileChunkBE(file, "BODY", body_chunk_size);
1899 SaveTape_BODY(file, &tape);
1903 SetFilePermissions(filename, PERMS_PRIVATE);
1905 tape.changed = FALSE;
1908 Request("tape saved !", REQ_CONFIRM);
1911 void DumpTape(struct TapeInfo *tape)
1915 if (TAPE_IS_EMPTY(*tape))
1917 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1921 printf_line("-", 79);
1922 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1923 tape->level_nr, tape->file_version, tape->game_version);
1924 printf("Level series identifier: '%s'\n", tape->level_identifier);
1925 printf_line("-", 79);
1927 for(i=0; i<tape->length; i++)
1929 if (i >= MAX_TAPELEN)
1932 printf("%03d: ", i);
1934 for(j=0; j<MAX_PLAYERS; j++)
1936 if (tape->player_participates[j])
1938 int action = tape->pos[i].action[j];
1940 printf("%d:%02x ", j, action);
1941 printf("[%c%c%c%c|%c%c] - ",
1942 (action & JOY_LEFT ? '<' : ' '),
1943 (action & JOY_RIGHT ? '>' : ' '),
1944 (action & JOY_UP ? '^' : ' '),
1945 (action & JOY_DOWN ? 'v' : ' '),
1946 (action & JOY_BUTTON_1 ? '1' : ' '),
1947 (action & JOY_BUTTON_2 ? '2' : ' '));
1951 printf("(%03d)\n", tape->pos[i].delay);
1954 printf_line("-", 79);
1958 /* ========================================================================= */
1959 /* score file functions */
1960 /* ========================================================================= */
1962 void LoadScore(int level_nr)
1965 char *filename = getScoreFilename(level_nr);
1966 char cookie[MAX_LINE_LEN];
1967 char line[MAX_LINE_LEN];
1971 /* always start with reliable default values */
1972 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1974 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1975 highscore[i].Score = 0;
1978 if (!(file = fopen(filename, MODE_READ)))
1981 /* check file identifier */
1982 fgets(cookie, MAX_LINE_LEN, file);
1983 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1984 cookie[strlen(cookie) - 1] = '\0';
1986 if (!checkCookieString(cookie, SCORE_COOKIE))
1988 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1993 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1995 fscanf(file, "%d", &highscore[i].Score);
1996 fgets(line, MAX_LINE_LEN, file);
1998 if (line[strlen(line) - 1] == '\n')
1999 line[strlen(line) - 1] = '\0';
2001 for (line_ptr = line; *line_ptr; line_ptr++)
2003 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2005 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2006 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2015 void SaveScore(int level_nr)
2018 char *filename = getScoreFilename(level_nr);
2021 InitScoreDirectory(leveldir_current->filename);
2023 if (!(file = fopen(filename, MODE_WRITE)))
2025 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2029 fprintf(file, "%s\n\n", SCORE_COOKIE);
2031 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2032 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2036 SetFilePermissions(filename, PERMS_PUBLIC);
2040 /* ========================================================================= */
2041 /* setup file functions */
2042 /* ========================================================================= */
2044 #define TOKEN_STR_PLAYER_PREFIX "player_"
2047 #define SETUP_TOKEN_PLAYER_NAME 0
2048 #define SETUP_TOKEN_SOUND 1
2049 #define SETUP_TOKEN_SOUND_LOOPS 2
2050 #define SETUP_TOKEN_SOUND_MUSIC 3
2051 #define SETUP_TOKEN_SOUND_SIMPLE 4
2052 #define SETUP_TOKEN_TOONS 5
2053 #define SETUP_TOKEN_SCROLL_DELAY 6
2054 #define SETUP_TOKEN_SOFT_SCROLLING 7
2055 #define SETUP_TOKEN_FADING 8
2056 #define SETUP_TOKEN_AUTORECORD 9
2057 #define SETUP_TOKEN_QUICK_DOORS 10
2058 #define SETUP_TOKEN_TEAM_MODE 11
2059 #define SETUP_TOKEN_HANDICAP 12
2060 #define SETUP_TOKEN_TIME_LIMIT 13
2061 #define SETUP_TOKEN_FULLSCREEN 14
2062 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
2063 #define SETUP_TOKEN_GRAPHICS_SET 16
2064 #define SETUP_TOKEN_SOUNDS_SET 17
2065 #define SETUP_TOKEN_MUSIC_SET 18
2066 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
2067 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
2068 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
2070 #define NUM_GLOBAL_SETUP_TOKENS 22
2073 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
2074 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
2075 #define SETUP_TOKEN_EDITOR_EL_MORE 2
2076 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
2077 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
2078 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
2079 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
2080 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
2081 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
2082 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
2083 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
2085 #define NUM_EDITOR_SETUP_TOKENS 11
2087 /* shortcut setup */
2088 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
2089 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
2090 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
2092 #define NUM_SHORTCUT_SETUP_TOKENS 3
2095 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
2096 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
2097 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
2098 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
2099 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
2100 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
2101 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
2102 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
2103 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
2104 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
2105 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
2106 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
2107 #define SETUP_TOKEN_PLAYER_KEY_UP 12
2108 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
2109 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
2110 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
2112 #define NUM_PLAYER_SETUP_TOKENS 16
2115 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
2116 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
2118 #define NUM_SYSTEM_SETUP_TOKENS 2
2121 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
2123 #define NUM_OPTIONS_SETUP_TOKENS 1
2126 static struct SetupInfo si;
2127 static struct SetupEditorInfo sei;
2128 static struct SetupShortcutInfo ssi;
2129 static struct SetupInputInfo sii;
2130 static struct SetupSystemInfo syi;
2131 static struct OptionInfo soi;
2133 static struct TokenInfo global_setup_tokens[] =
2135 { TYPE_STRING, &si.player_name, "player_name" },
2136 { TYPE_SWITCH, &si.sound, "sound" },
2137 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2138 { TYPE_SWITCH, &si.sound_music, "background_music" },
2139 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2140 { TYPE_SWITCH, &si.toons, "toons" },
2141 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2142 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2143 { TYPE_SWITCH, &si.fading, "screen_fading" },
2144 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2145 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2146 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2147 { TYPE_SWITCH, &si.handicap, "handicap" },
2148 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2149 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2150 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
2151 { TYPE_STRING, &si.graphics_set, "graphics_set" },
2152 { TYPE_STRING, &si.sounds_set, "sounds_set" },
2153 { TYPE_STRING, &si.music_set, "music_set" },
2154 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2155 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
2156 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
2159 static struct TokenInfo editor_setup_tokens[] =
2161 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
2162 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
2163 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
2164 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
2165 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
2166 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
2167 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
2168 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
2169 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
2170 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
2171 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
2174 static struct TokenInfo shortcut_setup_tokens[] =
2176 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
2177 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
2178 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
2181 static struct TokenInfo player_setup_tokens[] =
2183 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2184 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2185 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2186 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2187 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2188 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2189 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2190 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2191 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2192 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2193 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2194 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2195 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2196 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2197 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2198 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
2201 static struct TokenInfo system_setup_tokens[] =
2203 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2204 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2207 static struct TokenInfo options_setup_tokens[] =
2209 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2212 static char *get_corrected_login_name(char *login_name)
2214 /* needed because player name must be a fixed length string */
2215 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2217 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2218 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2220 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2221 if (strchr(login_name_new, ' '))
2222 *strchr(login_name_new, ' ') = '\0';
2224 return login_name_new;
2227 static void setSetupInfoToDefaults(struct SetupInfo *si)
2231 si->player_name = get_corrected_login_name(getLoginName());
2234 si->sound_loops = TRUE;
2235 si->sound_music = TRUE;
2236 si->sound_simple = TRUE;
2238 si->double_buffering = TRUE;
2239 si->direct_draw = !si->double_buffering;
2240 si->scroll_delay = TRUE;
2241 si->soft_scrolling = TRUE;
2243 si->autorecord = TRUE;
2244 si->quick_doors = FALSE;
2245 si->team_mode = FALSE;
2246 si->handicap = TRUE;
2247 si->time_limit = TRUE;
2248 si->fullscreen = FALSE;
2249 si->ask_on_escape = TRUE;
2251 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2252 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2253 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2254 si->override_level_graphics = FALSE;
2255 si->override_level_sounds = FALSE;
2256 si->override_level_music = FALSE;
2258 si->editor.el_boulderdash = TRUE;
2259 si->editor.el_emerald_mine = TRUE;
2260 si->editor.el_more = TRUE;
2261 si->editor.el_sokoban = TRUE;
2262 si->editor.el_supaplex = TRUE;
2263 si->editor.el_diamond_caves = TRUE;
2264 si->editor.el_dx_boulderdash = TRUE;
2265 si->editor.el_chars = TRUE;
2266 si->editor.el_custom = TRUE;
2267 si->editor.el_custom_more = FALSE;
2269 si->editor.el_headlines = TRUE;
2271 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2272 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2273 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2275 for (i=0; i<MAX_PLAYERS; i++)
2277 si->input[i].use_joystick = FALSE;
2278 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2279 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2280 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2281 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2282 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2283 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2284 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2285 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2286 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2287 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2288 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2289 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2290 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2291 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2292 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2295 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2296 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2298 si->options.verbose = FALSE;
2301 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2305 if (!setup_file_hash)
2310 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2311 setSetupInfo(global_setup_tokens, i,
2312 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2317 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2318 setSetupInfo(editor_setup_tokens, i,
2319 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2322 /* shortcut setup */
2323 ssi = setup.shortcut;
2324 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2325 setSetupInfo(shortcut_setup_tokens, i,
2326 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2327 setup.shortcut = ssi;
2330 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2334 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2336 sii = setup.input[pnr];
2337 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2339 char full_token[100];
2341 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2342 setSetupInfo(player_setup_tokens, i,
2343 getHashEntry(setup_file_hash, full_token));
2345 setup.input[pnr] = sii;
2350 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2351 setSetupInfo(system_setup_tokens, i,
2352 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2356 soi = setup.options;
2357 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2358 setSetupInfo(options_setup_tokens, i,
2359 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2360 setup.options = soi;
2365 char *filename = getSetupFilename();
2366 SetupFileHash *setup_file_hash = NULL;
2368 /* always start with reliable default values */
2369 setSetupInfoToDefaults(&setup);
2371 setup_file_hash = loadSetupFileHash(filename);
2373 if (setup_file_hash)
2375 char *player_name_new;
2377 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2378 decodeSetupFileHash(setup_file_hash);
2380 setup.direct_draw = !setup.double_buffering;
2382 freeSetupFileHash(setup_file_hash);
2384 /* needed to work around problems with fixed length strings */
2385 player_name_new = get_corrected_login_name(setup.player_name);
2386 free(setup.player_name);
2387 setup.player_name = player_name_new;
2390 Error(ERR_WARN, "using default setup values");
2395 char *filename = getSetupFilename();
2399 InitUserDataDirectory();
2401 if (!(file = fopen(filename, MODE_WRITE)))
2403 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2407 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2408 getCookie("SETUP")));
2409 fprintf(file, "\n");
2413 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2415 /* just to make things nicer :) */
2416 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2417 i == SETUP_TOKEN_GRAPHICS_SET)
2418 fprintf(file, "\n");
2420 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2425 fprintf(file, "\n");
2426 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2427 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2429 /* shortcut setup */
2430 ssi = setup.shortcut;
2431 fprintf(file, "\n");
2432 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2433 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2436 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2440 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2441 fprintf(file, "\n");
2443 sii = setup.input[pnr];
2444 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2445 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2450 fprintf(file, "\n");
2451 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2452 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2455 soi = setup.options;
2456 fprintf(file, "\n");
2457 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2458 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2462 SetFilePermissions(filename, PERMS_PRIVATE);
2465 void LoadCustomElementDescriptions()
2467 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2468 SetupFileHash *setup_file_hash;
2471 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2473 if (element_info[i].custom_description != NULL)
2475 free(element_info[i].custom_description);
2476 element_info[i].custom_description = NULL;
2480 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2483 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2485 char *token = getStringCat2(element_info[i].token_name, ".name");
2486 char *value = getHashEntry(setup_file_hash, token);
2489 element_info[i].custom_description = getStringCopy(value);
2494 freeSetupFileHash(setup_file_hash);
2497 void LoadSpecialMenuDesignSettings()
2499 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2500 SetupFileHash *setup_file_hash;
2503 /* always start with reliable default values from default config */
2504 for (i=0; image_config_vars[i].token != NULL; i++)
2505 for (j=0; image_config[j].token != NULL; j++)
2506 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2507 *image_config_vars[i].value =
2508 get_integer_from_string(image_config[j].value);
2510 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2513 /* special case: initialize with default values that may be overwritten */
2514 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2516 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2517 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2518 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2520 if (value_x != NULL)
2521 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2522 if (value_y != NULL)
2523 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2524 if (list_size != NULL)
2525 menu.list_size[i] = get_integer_from_string(list_size);
2528 /* read (and overwrite with) values that may be specified in config file */
2529 for (i=0; image_config_vars[i].token != NULL; i++)
2531 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2534 *image_config_vars[i].value = get_integer_from_string(value);
2537 freeSetupFileHash(setup_file_hash);