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->player_action = 0;
92 change->collide_action = 0;
93 change->other_action = 0;
95 change->pre_change_function = NULL;
96 change->change_function = NULL;
97 change->post_change_function = NULL;
100 static void setLevelInfoToDefaults(struct LevelInfo *level)
104 level->file_version = FILE_VERSION_ACTUAL;
105 level->game_version = GAME_VERSION_ACTUAL;
107 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
108 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
109 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
111 level->fieldx = STD_LEV_FIELDX;
112 level->fieldy = STD_LEV_FIELDY;
114 for(x=0; x<MAX_LEV_FIELDX; x++)
115 for(y=0; y<MAX_LEV_FIELDY; y++)
116 level->field[x][y] = EL_SAND;
119 level->gems_needed = 0;
120 level->amoeba_speed = 10;
121 level->time_magic_wall = 10;
122 level->time_wheel = 10;
123 level->time_light = 10;
124 level->time_timegate = 10;
125 level->amoeba_content = EL_DIAMOND;
126 level->double_speed = FALSE;
127 level->gravity = FALSE;
128 level->em_slippery_gems = FALSE;
130 level->use_custom_template = FALSE;
132 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
133 level->name[i] = '\0';
134 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
135 level->author[i] = '\0';
137 strcpy(level->name, NAMELESS_LEVEL_NAME);
138 strcpy(level->author, ANONYMOUS_NAME);
140 level->envelope[0] = '\0';
141 level->envelope_xsize = MAX_ENVELOPE_XSIZE;
142 level->envelope_ysize = MAX_ENVELOPE_YSIZE;
144 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
145 level->score[i] = 10;
147 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
148 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
151 level->yamyam_content[i][x][y] =
152 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
154 level->field[0][0] = EL_PLAYER_1;
155 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
157 for (i=0; i < MAX_NUM_ELEMENTS; i++)
159 setElementChangePages(&element_info[i], 1);
160 setElementChangeInfoToDefaults(element_info[i].change);
163 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
165 int element = EL_CUSTOM_START + i;
167 for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
168 element_info[element].description[j] = '\0';
169 if (element_info[element].custom_description != NULL)
170 strncpy(element_info[element].description,
171 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
173 strcpy(element_info[element].description,
174 element_info[element].editor_description);
176 element_info[element].use_gfx_element = FALSE;
177 element_info[element].gfx_element = EL_EMPTY_SPACE;
179 element_info[element].collect_score = 10; /* special default */
180 element_info[element].collect_count = 1; /* special default */
182 element_info[element].push_delay_fixed = 2; /* special default */
183 element_info[element].push_delay_random = 8; /* special default */
184 element_info[element].move_delay_fixed = 0;
185 element_info[element].move_delay_random = 0;
187 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
188 element_info[element].move_direction_initial = MV_NO_MOVING;
189 element_info[element].move_stepsize = TILEX / 8;
191 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
195 element_info[element].content[x][y] = EL_EMPTY_SPACE;
197 element_info[element].access_type = 0;
198 element_info[element].access_layer = 0;
199 element_info[element].walk_to_action = 0;
200 element_info[element].smash_targets = 0;
201 element_info[element].deadliness = 0;
202 element_info[element].consistency = 0;
204 element_info[element].can_explode_by_fire = FALSE;
205 element_info[element].can_explode_smashed = FALSE;
206 element_info[element].can_explode_impact = FALSE;
208 element_info[element].current_change_page = 0;
210 /* start with no properties at all */
211 for (j=0; j < NUM_EP_BITFIELDS; j++)
212 Properties[element][j] = EP_BITMASK_DEFAULT;
214 element_info[element].modified_settings = FALSE;
217 BorderElement = EL_STEELWALL;
219 level->no_level_file = FALSE;
221 if (leveldir_current == NULL) /* only when dumping level */
224 /* try to determine better author name than 'anonymous' */
225 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
227 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
228 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
232 switch (LEVELCLASS(leveldir_current))
234 case LEVELCLASS_TUTORIAL:
235 strcpy(level->author, PROGRAM_AUTHOR_STRING);
238 case LEVELCLASS_CONTRIBUTION:
239 strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
240 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
243 case LEVELCLASS_USER:
244 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
245 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
249 /* keep default value */
255 static void ActivateLevelTemplate()
257 /* Currently there is no special action needed to activate the template
258 data, because 'element_info' and 'Properties' overwrite the original
259 level data, while all other variables do not change. */
262 boolean LevelFileExists(int level_nr)
264 char *filename = getLevelFilename(level_nr);
266 return (access(filename, F_OK) == 0);
269 static int checkLevelElement(int element)
271 if (element >= NUM_FILE_ELEMENTS)
273 Error(ERR_WARN, "invalid level element %d", element);
274 element = EL_CHAR_QUESTION;
276 else if (element == EL_PLAYER_OBSOLETE)
277 element = EL_PLAYER_1;
278 else if (element == EL_KEY_OBSOLETE)
284 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
286 level->file_version = getFileVersion(file);
287 level->game_version = getFileVersion(file);
292 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
296 level->fieldx = getFile8Bit(file);
297 level->fieldy = getFile8Bit(file);
299 level->time = getFile16BitBE(file);
300 level->gems_needed = getFile16BitBE(file);
302 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
303 level->name[i] = getFile8Bit(file);
304 level->name[MAX_LEVEL_NAME_LEN] = 0;
306 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
307 level->score[i] = getFile8Bit(file);
309 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
310 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
313 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
315 level->amoeba_speed = getFile8Bit(file);
316 level->time_magic_wall = getFile8Bit(file);
317 level->time_wheel = getFile8Bit(file);
318 level->amoeba_content = checkLevelElement(getFile8Bit(file));
319 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
320 level->gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
321 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
322 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
324 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
326 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
331 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
335 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
336 level->author[i] = getFile8Bit(file);
337 level->author[MAX_LEVEL_NAME_LEN] = 0;
342 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
345 int chunk_size_expected = level->fieldx * level->fieldy;
347 /* Note: "chunk_size" was wrong before version 2.0 when elements are
348 stored with 16-bit encoding (and should be twice as big then).
349 Even worse, playfield data was stored 16-bit when only yamyam content
350 contained 16-bit elements and vice versa. */
352 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
353 chunk_size_expected *= 2;
355 if (chunk_size_expected != chunk_size)
357 ReadUnusedBytesFromFile(file, chunk_size);
358 return chunk_size_expected;
361 for(y=0; y<level->fieldy; y++)
362 for(x=0; x<level->fieldx; x++)
364 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
369 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
373 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
374 int chunk_size_expected = header_size + content_size;
376 /* Note: "chunk_size" was wrong before version 2.0 when elements are
377 stored with 16-bit encoding (and should be twice as big then).
378 Even worse, playfield data was stored 16-bit when only yamyam content
379 contained 16-bit elements and vice versa. */
381 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
382 chunk_size_expected += content_size;
384 if (chunk_size_expected != chunk_size)
386 ReadUnusedBytesFromFile(file, chunk_size);
387 return chunk_size_expected;
391 level->num_yamyam_contents = getFile8Bit(file);
395 /* correct invalid number of content fields -- should never happen */
396 if (level->num_yamyam_contents < 1 ||
397 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
398 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
400 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
403 level->yamyam_content[i][x][y] =
404 checkLevelElement(level->encoding_16bit_field ?
405 getFile16BitBE(file) : getFile8Bit(file));
409 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
413 int num_contents, content_xsize, content_ysize;
414 int content_array[MAX_ELEMENT_CONTENTS][3][3];
416 element = checkLevelElement(getFile16BitBE(file));
417 num_contents = getFile8Bit(file);
418 content_xsize = getFile8Bit(file);
419 content_ysize = getFile8Bit(file);
420 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
422 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
425 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
427 /* correct invalid number of content fields -- should never happen */
428 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
429 num_contents = STD_ELEMENT_CONTENTS;
431 if (element == EL_YAMYAM)
433 level->num_yamyam_contents = num_contents;
435 for(i=0; i<num_contents; i++)
438 level->yamyam_content[i][x][y] = content_array[i][x][y];
440 else if (element == EL_BD_AMOEBA)
442 level->amoeba_content = content_array[0][0][0];
446 Error(ERR_WARN, "cannot load content for element '%d'", element);
452 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
454 int num_changed_custom_elements = getFile16BitBE(file);
455 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
458 if (chunk_size_expected != chunk_size)
460 ReadUnusedBytesFromFile(file, chunk_size - 2);
461 return chunk_size_expected;
464 for (i=0; i < num_changed_custom_elements; i++)
466 int element = getFile16BitBE(file);
467 int properties = getFile32BitBE(file);
469 if (IS_CUSTOM_ELEMENT(element))
470 Properties[element][EP_BITFIELD_BASE] = properties;
472 Error(ERR_WARN, "invalid custom element number %d", element);
478 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
480 int num_changed_custom_elements = getFile16BitBE(file);
481 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
484 if (chunk_size_expected != chunk_size)
486 ReadUnusedBytesFromFile(file, chunk_size - 2);
487 return chunk_size_expected;
490 for (i=0; i < num_changed_custom_elements; i++)
492 int element = getFile16BitBE(file);
493 int custom_target_element = getFile16BitBE(file);
495 if (IS_CUSTOM_ELEMENT(element))
496 element_info[element].change->target_element = custom_target_element;
498 Error(ERR_WARN, "invalid custom element number %d", element);
504 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
506 int num_changed_custom_elements = getFile16BitBE(file);
507 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
510 if (chunk_size_expected != chunk_size)
512 ReadUnusedBytesFromFile(file, chunk_size - 2);
513 return chunk_size_expected;
516 for (i=0; i < num_changed_custom_elements; i++)
518 int element = getFile16BitBE(file);
520 if (!IS_CUSTOM_ELEMENT(element))
522 Error(ERR_WARN, "invalid custom element number %d", element);
524 element = EL_DEFAULT; /* dummy element used for artwork config */
527 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
528 element_info[element].description[j] = getFile8Bit(file);
529 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
531 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
533 /* some free bytes for future properties and padding */
534 ReadUnusedBytesFromFile(file, 7);
536 element_info[element].use_gfx_element = getFile8Bit(file);
537 element_info[element].gfx_element =
538 checkLevelElement(getFile16BitBE(file));
540 element_info[element].collect_score = getFile8Bit(file);
541 element_info[element].collect_count = getFile8Bit(file);
543 element_info[element].push_delay_fixed = getFile16BitBE(file);
544 element_info[element].push_delay_random = getFile16BitBE(file);
545 element_info[element].move_delay_fixed = getFile16BitBE(file);
546 element_info[element].move_delay_random = getFile16BitBE(file);
548 element_info[element].move_pattern = getFile16BitBE(file);
549 element_info[element].move_direction_initial = getFile8Bit(file);
550 element_info[element].move_stepsize = getFile8Bit(file);
554 element_info[element].content[x][y] =
555 checkLevelElement(getFile16BitBE(file));
557 element_info[element].change->events = getFile32BitBE(file);
559 element_info[element].change->target_element =
560 checkLevelElement(getFile16BitBE(file));
562 element_info[element].change->delay_fixed = getFile16BitBE(file);
563 element_info[element].change->delay_random = getFile16BitBE(file);
564 element_info[element].change->delay_frames = getFile16BitBE(file);
566 element_info[element].change->trigger_element =
567 checkLevelElement(getFile16BitBE(file));
569 element_info[element].change->explode = getFile8Bit(file);
570 element_info[element].change->use_content = getFile8Bit(file);
571 element_info[element].change->only_complete = getFile8Bit(file);
572 element_info[element].change->use_random_change = getFile8Bit(file);
574 element_info[element].change->random = getFile8Bit(file);
575 element_info[element].change->power = getFile8Bit(file);
579 element_info[element].change->content[x][y] =
580 checkLevelElement(getFile16BitBE(file));
582 element_info[element].slippery_type = getFile8Bit(file);
584 /* some free bytes for future properties and padding */
585 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
587 /* mark that this custom element has been modified */
588 element_info[element].modified_settings = TRUE;
594 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
596 char cookie[MAX_LINE_LEN];
597 char chunk_name[CHUNK_ID_LEN + 1];
601 /* always start with reliable default values */
602 setLevelInfoToDefaults(level);
604 if (!(file = fopen(filename, MODE_READ)))
606 level->no_level_file = TRUE;
608 if (level != &level_template)
609 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
614 getFileChunkBE(file, chunk_name, NULL);
615 if (strcmp(chunk_name, "RND1") == 0)
617 getFile32BitBE(file); /* not used */
619 getFileChunkBE(file, chunk_name, NULL);
620 if (strcmp(chunk_name, "CAVE") != 0)
622 Error(ERR_WARN, "unknown format of level file '%s'", filename);
627 else /* check for pre-2.0 file format with cookie string */
629 strcpy(cookie, chunk_name);
630 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
631 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
632 cookie[strlen(cookie) - 1] = '\0';
634 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
636 Error(ERR_WARN, "unknown format of level file '%s'", filename);
641 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
643 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
648 /* pre-2.0 level files have no game version, so use file version here */
649 level->game_version = level->file_version;
652 if (level->file_version < FILE_VERSION_1_2)
654 /* level files from versions before 1.2.0 without chunk structure */
655 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
656 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
664 int (*loader)(FILE *, int, struct LevelInfo *);
668 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
669 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
670 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
671 { "BODY", -1, LoadLevel_BODY },
672 { "CONT", -1, LoadLevel_CONT },
673 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
674 { "CUS1", -1, LoadLevel_CUS1 },
675 { "CUS2", -1, LoadLevel_CUS2 },
676 { "CUS3", -1, LoadLevel_CUS3 },
680 while (getFileChunkBE(file, chunk_name, &chunk_size))
684 while (chunk_info[i].name != NULL &&
685 strcmp(chunk_name, chunk_info[i].name) != 0)
688 if (chunk_info[i].name == NULL)
690 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
691 chunk_name, filename);
692 ReadUnusedBytesFromFile(file, chunk_size);
694 else if (chunk_info[i].size != -1 &&
695 chunk_info[i].size != chunk_size)
697 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
698 chunk_size, chunk_name, filename);
699 ReadUnusedBytesFromFile(file, chunk_size);
703 /* call function to load this level chunk */
704 int chunk_size_expected =
705 (chunk_info[i].loader)(file, chunk_size, level);
707 /* the size of some chunks cannot be checked before reading other
708 chunks first (like "HEAD" and "BODY") that contain some header
709 information, so check them here */
710 if (chunk_size_expected != chunk_size)
712 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
713 chunk_size, chunk_name, filename);
722 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
726 if (leveldir_current == NULL) /* only when dumping level */
729 /* determine correct game engine version of current level */
730 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
731 IS_LEVELCLASS_USER(leveldir_current))
734 printf("\n::: This level is private or contributed: '%s'\n", filename);
737 /* For user contributed and private levels, use the version of
738 the game engine the levels were created for.
739 Since 2.0.1, the game engine version is now directly stored
740 in the level file (chunk "VERS"), so there is no need anymore
741 to set the game version from the file version (except for old,
742 pre-2.0 levels, where the game version is still taken from the
743 file format version used to store the level -- see above). */
745 /* do some special adjustments to support older level versions */
746 if (level->file_version == FILE_VERSION_1_0)
748 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
749 Error(ERR_WARN, "using high speed movement for player");
751 /* player was faster than monsters in (pre-)1.0 levels */
752 level->double_speed = TRUE;
755 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
756 if (level->game_version == VERSION_IDENT(2,0,1))
757 level->em_slippery_gems = TRUE;
762 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
763 leveldir_current->sort_priority, filename);
766 /* Always use the latest version of the game engine for all but
767 user contributed and private levels; this allows for actual
768 corrections in the game engine to take effect for existing,
769 converted levels (from "classic" or other existing games) to
770 make the game emulation more accurate, while (hopefully) not
771 breaking existing levels created from other players. */
773 level->game_version = GAME_VERSION_ACTUAL;
775 /* Set special EM style gems behaviour: EM style gems slip down from
776 normal, steel and growing wall. As this is a more fundamental change,
777 it seems better to set the default behaviour to "off" (as it is more
778 natural) and make it configurable in the level editor (as a property
779 of gem style elements). Already existing converted levels (neither
780 private nor contributed levels) are changed to the new behaviour. */
782 if (level->file_version < FILE_VERSION_2_0)
783 level->em_slippery_gems = TRUE;
786 /* map elements that have changed in newer versions */
787 for(y=0; y<level->fieldy; y++)
789 for(x=0; x<level->fieldx; x++)
791 int element = level->field[x][y];
793 if (level->game_version <= VERSION_IDENT(2,2,0))
795 /* map game font elements */
796 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
797 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
798 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
799 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
802 if (level->game_version < VERSION_IDENT(3,0,0))
804 /* map Supaplex gravity tube elements */
805 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
806 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
807 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
808 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
812 level->field[x][y] = element;
816 /* map custom element change events that have changed in newer versions
817 (these following values have accidentally changed in version 3.0.1) */
818 if (level->game_version <= VERSION_IDENT(3,0,0))
820 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
822 int element = EL_CUSTOM_START + i;
824 /* order of checking events to be mapped is important */
825 for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
827 if (HAS_CHANGE_EVENT(element, j - 2))
829 SET_CHANGE_EVENT(element, j - 2, FALSE);
830 SET_CHANGE_EVENT(element, j, TRUE);
834 /* order of checking events to be mapped is important */
835 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
837 if (HAS_CHANGE_EVENT(element, j - 1))
839 SET_CHANGE_EVENT(element, j - 1, FALSE);
840 SET_CHANGE_EVENT(element, j, TRUE);
846 /* initialize "can_change" field for old levels with only one change page */
847 if (level->game_version <= VERSION_IDENT(3,0,2))
849 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
851 int element = EL_CUSTOM_START + i;
853 if (CAN_CHANGE(element))
854 element_info[element].change->can_change = TRUE;
858 /* copy elements to runtime playfield array */
859 for(x=0; x<MAX_LEV_FIELDX; x++)
860 for(y=0; y<MAX_LEV_FIELDY; y++)
861 Feld[x][y] = level->field[x][y];
863 /* initialize level size variables for faster access */
864 lev_fieldx = level->fieldx;
865 lev_fieldy = level->fieldy;
867 /* determine border element for this level */
870 /* initialize element properties for level editor etc. */
871 InitElementPropertiesEngine(level->game_version);
874 void LoadLevelTemplate(int level_nr)
876 char *filename = getLevelFilename(level_nr);
878 LoadLevelFromFilename(&level_template, filename);
880 ActivateLevelTemplate();
883 void LoadLevel(int level_nr)
885 char *filename = getLevelFilename(level_nr);
887 LoadLevelFromFilename(&level, filename);
889 if (level.use_custom_template)
890 LoadLevelTemplate(-1);
892 LoadLevel_InitLevel(&level, filename);
895 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
897 putFileVersion(file, level->file_version);
898 putFileVersion(file, level->game_version);
901 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
905 putFile8Bit(file, level->fieldx);
906 putFile8Bit(file, level->fieldy);
908 putFile16BitBE(file, level->time);
909 putFile16BitBE(file, level->gems_needed);
911 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
912 putFile8Bit(file, level->name[i]);
914 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
915 putFile8Bit(file, level->score[i]);
917 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
920 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
921 level->yamyam_content[i][x][y]));
922 putFile8Bit(file, level->amoeba_speed);
923 putFile8Bit(file, level->time_magic_wall);
924 putFile8Bit(file, level->time_wheel);
925 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
926 level->amoeba_content));
927 putFile8Bit(file, (level->double_speed ? 1 : 0));
928 putFile8Bit(file, (level->gravity ? 1 : 0));
929 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
930 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
932 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
934 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
937 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
941 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
942 putFile8Bit(file, level->author[i]);
945 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
949 for(y=0; y<level->fieldy; y++)
950 for(x=0; x<level->fieldx; x++)
951 if (level->encoding_16bit_field)
952 putFile16BitBE(file, level->field[x][y]);
954 putFile8Bit(file, level->field[x][y]);
958 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
962 putFile8Bit(file, EL_YAMYAM);
963 putFile8Bit(file, level->num_yamyam_contents);
964 putFile8Bit(file, 0);
965 putFile8Bit(file, 0);
967 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
970 if (level->encoding_16bit_field)
971 putFile16BitBE(file, level->yamyam_content[i][x][y]);
973 putFile8Bit(file, level->yamyam_content[i][x][y]);
977 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
980 int num_contents, content_xsize, content_ysize;
981 int content_array[MAX_ELEMENT_CONTENTS][3][3];
983 if (element == EL_YAMYAM)
985 num_contents = level->num_yamyam_contents;
989 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
992 content_array[i][x][y] = level->yamyam_content[i][x][y];
994 else if (element == EL_BD_AMOEBA)
1000 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1003 content_array[i][x][y] = EL_EMPTY;
1004 content_array[0][0][0] = level->amoeba_content;
1008 /* chunk header already written -- write empty chunk data */
1009 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1011 Error(ERR_WARN, "cannot save content for element '%d'", element);
1015 putFile16BitBE(file, element);
1016 putFile8Bit(file, num_contents);
1017 putFile8Bit(file, content_xsize);
1018 putFile8Bit(file, content_ysize);
1020 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1022 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1025 putFile16BitBE(file, content_array[i][x][y]);
1029 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1030 int num_changed_custom_elements)
1034 putFile16BitBE(file, num_changed_custom_elements);
1036 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1038 int element = EL_CUSTOM_START + i;
1040 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1042 if (check < num_changed_custom_elements)
1044 putFile16BitBE(file, element);
1045 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1052 if (check != num_changed_custom_elements) /* should not happen */
1053 Error(ERR_WARN, "inconsistent number of custom element properties");
1058 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1059 int num_changed_custom_elements)
1063 putFile16BitBE(file, num_changed_custom_elements);
1065 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1067 int element = EL_CUSTOM_START + i;
1069 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1071 if (check < num_changed_custom_elements)
1073 putFile16BitBE(file, element);
1074 putFile16BitBE(file, element_info[element].change->target_element);
1081 if (check != num_changed_custom_elements) /* should not happen */
1082 Error(ERR_WARN, "inconsistent number of custom target elements");
1086 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1087 int num_changed_custom_elements)
1089 int i, j, x, y, check = 0;
1091 putFile16BitBE(file, num_changed_custom_elements);
1093 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1095 int element = EL_CUSTOM_START + i;
1097 if (element_info[element].modified_settings)
1099 if (check < num_changed_custom_elements)
1101 putFile16BitBE(file, element);
1103 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1104 putFile8Bit(file, element_info[element].description[j]);
1106 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1108 /* some free bytes for future properties and padding */
1109 WriteUnusedBytesToFile(file, 7);
1111 putFile8Bit(file, element_info[element].use_gfx_element);
1112 putFile16BitBE(file, element_info[element].gfx_element);
1114 putFile8Bit(file, element_info[element].collect_score);
1115 putFile8Bit(file, element_info[element].collect_count);
1117 putFile16BitBE(file, element_info[element].push_delay_fixed);
1118 putFile16BitBE(file, element_info[element].push_delay_random);
1119 putFile16BitBE(file, element_info[element].move_delay_fixed);
1120 putFile16BitBE(file, element_info[element].move_delay_random);
1122 putFile16BitBE(file, element_info[element].move_pattern);
1123 putFile8Bit(file, element_info[element].move_direction_initial);
1124 putFile8Bit(file, element_info[element].move_stepsize);
1128 putFile16BitBE(file, element_info[element].content[x][y]);
1130 putFile32BitBE(file, element_info[element].change->events);
1132 putFile16BitBE(file, element_info[element].change->target_element);
1134 putFile16BitBE(file, element_info[element].change->delay_fixed);
1135 putFile16BitBE(file, element_info[element].change->delay_random);
1136 putFile16BitBE(file, element_info[element].change->delay_frames);
1138 putFile16BitBE(file, element_info[element].change->trigger_element);
1140 putFile8Bit(file, element_info[element].change->explode);
1141 putFile8Bit(file, element_info[element].change->use_content);
1142 putFile8Bit(file, element_info[element].change->only_complete);
1143 putFile8Bit(file, element_info[element].change->use_random_change);
1145 putFile8Bit(file, element_info[element].change->random);
1146 putFile8Bit(file, element_info[element].change->power);
1150 putFile16BitBE(file, element_info[element].change->content[x][y]);
1152 putFile8Bit(file, element_info[element].slippery_type);
1154 /* some free bytes for future properties and padding */
1155 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1162 if (check != num_changed_custom_elements) /* should not happen */
1163 Error(ERR_WARN, "inconsistent number of custom element properties");
1166 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1168 int body_chunk_size;
1169 int num_changed_custom_elements = 0;
1170 int level_chunk_CUS3_size;
1174 if (!(file = fopen(filename, MODE_WRITE)))
1176 Error(ERR_WARN, "cannot save level file '%s'", filename);
1180 level->file_version = FILE_VERSION_ACTUAL;
1181 level->game_version = GAME_VERSION_ACTUAL;
1183 /* check level field for 16-bit elements */
1184 level->encoding_16bit_field = FALSE;
1185 for(y=0; y<level->fieldy; y++)
1186 for(x=0; x<level->fieldx; x++)
1187 if (level->field[x][y] > 255)
1188 level->encoding_16bit_field = TRUE;
1190 /* check yamyam content for 16-bit elements */
1191 level->encoding_16bit_yamyam = FALSE;
1192 for(i=0; i<level->num_yamyam_contents; i++)
1195 if (level->yamyam_content[i][x][y] > 255)
1196 level->encoding_16bit_yamyam = TRUE;
1198 /* check amoeba content for 16-bit elements */
1199 level->encoding_16bit_amoeba = FALSE;
1200 if (level->amoeba_content > 255)
1201 level->encoding_16bit_amoeba = TRUE;
1203 /* calculate size of "BODY" chunk */
1205 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1207 /* check for non-standard custom elements and calculate "CUS3" chunk size */
1208 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1209 if (element_info[EL_CUSTOM_START + i].modified_settings)
1210 num_changed_custom_elements++;
1211 level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1213 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1214 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1216 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1217 SaveLevel_VERS(file, level);
1219 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1220 SaveLevel_HEAD(file, level);
1222 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1223 SaveLevel_AUTH(file, level);
1225 putFileChunkBE(file, "BODY", body_chunk_size);
1226 SaveLevel_BODY(file, level);
1228 if (level->encoding_16bit_yamyam ||
1229 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1231 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1232 SaveLevel_CNT2(file, level, EL_YAMYAM);
1235 if (level->encoding_16bit_amoeba)
1237 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1238 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1241 if (num_changed_custom_elements > 0 && !level->use_custom_template)
1243 putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1244 SaveLevel_CUS3(file, level, num_changed_custom_elements);
1249 SetFilePermissions(filename, PERMS_PRIVATE);
1252 void SaveLevel(int level_nr)
1254 char *filename = getLevelFilename(level_nr);
1256 SaveLevelFromFilename(&level, filename);
1259 void SaveLevelTemplate()
1261 char *filename = getLevelFilename(-1);
1263 SaveLevelFromFilename(&level, filename);
1266 void DumpLevel(struct LevelInfo *level)
1268 printf_line("-", 79);
1269 printf("Level xxx (file version %08d, game version %08d)\n",
1270 level->file_version, level->game_version);
1271 printf_line("-", 79);
1273 printf("Level Author: '%s'\n", level->author);
1274 printf("Level Title: '%s'\n", level->name);
1276 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1278 printf("Level Time: %d seconds\n", level->time);
1279 printf("Gems needed: %d\n", level->gems_needed);
1281 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1282 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1283 printf("Time for Light: %d seconds\n", level->time_light);
1284 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1286 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1288 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1289 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1290 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1292 printf_line("-", 79);
1296 /* ========================================================================= */
1297 /* tape file functions */
1298 /* ========================================================================= */
1300 static void setTapeInfoToDefaults()
1304 /* always start with reliable default values (empty tape) */
1307 /* default values (also for pre-1.2 tapes) with only the first player */
1308 tape.player_participates[0] = TRUE;
1309 for(i=1; i<MAX_PLAYERS; i++)
1310 tape.player_participates[i] = FALSE;
1312 /* at least one (default: the first) player participates in every tape */
1313 tape.num_participating_players = 1;
1315 tape.level_nr = level_nr;
1317 tape.changed = FALSE;
1319 tape.recording = FALSE;
1320 tape.playing = FALSE;
1321 tape.pausing = FALSE;
1324 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1326 tape->file_version = getFileVersion(file);
1327 tape->game_version = getFileVersion(file);
1332 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1336 tape->random_seed = getFile32BitBE(file);
1337 tape->date = getFile32BitBE(file);
1338 tape->length = getFile32BitBE(file);
1340 /* read header fields that are new since version 1.2 */
1341 if (tape->file_version >= FILE_VERSION_1_2)
1343 byte store_participating_players = getFile8Bit(file);
1346 /* since version 1.2, tapes store which players participate in the tape */
1347 tape->num_participating_players = 0;
1348 for(i=0; i<MAX_PLAYERS; i++)
1350 tape->player_participates[i] = FALSE;
1352 if (store_participating_players & (1 << i))
1354 tape->player_participates[i] = TRUE;
1355 tape->num_participating_players++;
1359 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1361 engine_version = getFileVersion(file);
1362 if (engine_version > 0)
1363 tape->engine_version = engine_version;
1365 tape->engine_version = tape->game_version;
1371 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1373 int level_identifier_size;
1376 level_identifier_size = getFile16BitBE(file);
1378 tape->level_identifier =
1379 checked_realloc(tape->level_identifier, level_identifier_size);
1381 for(i=0; i < level_identifier_size; i++)
1382 tape->level_identifier[i] = getFile8Bit(file);
1384 tape->level_nr = getFile16BitBE(file);
1386 chunk_size = 2 + level_identifier_size + 2;
1391 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1394 int chunk_size_expected =
1395 (tape->num_participating_players + 1) * tape->length;
1397 if (chunk_size_expected != chunk_size)
1399 ReadUnusedBytesFromFile(file, chunk_size);
1400 return chunk_size_expected;
1403 for(i=0; i<tape->length; i++)
1405 if (i >= MAX_TAPELEN)
1408 for(j=0; j<MAX_PLAYERS; j++)
1410 tape->pos[i].action[j] = MV_NO_MOVING;
1412 if (tape->player_participates[j])
1413 tape->pos[i].action[j] = getFile8Bit(file);
1416 tape->pos[i].delay = getFile8Bit(file);
1418 if (tape->file_version == FILE_VERSION_1_0)
1420 /* eliminate possible diagonal moves in old tapes */
1421 /* this is only for backward compatibility */
1423 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1424 byte action = tape->pos[i].action[0];
1425 int k, num_moves = 0;
1429 if (action & joy_dir[k])
1431 tape->pos[i + num_moves].action[0] = joy_dir[k];
1433 tape->pos[i + num_moves].delay = 0;
1442 tape->length += num_moves;
1445 else if (tape->file_version < FILE_VERSION_2_0)
1447 /* convert pre-2.0 tapes to new tape format */
1449 if (tape->pos[i].delay > 1)
1452 tape->pos[i + 1] = tape->pos[i];
1453 tape->pos[i + 1].delay = 1;
1456 for(j=0; j<MAX_PLAYERS; j++)
1457 tape->pos[i].action[j] = MV_NO_MOVING;
1458 tape->pos[i].delay--;
1469 if (i != tape->length)
1470 chunk_size = (tape->num_participating_players + 1) * i;
1475 void LoadTapeFromFilename(char *filename)
1477 char cookie[MAX_LINE_LEN];
1478 char chunk_name[CHUNK_ID_LEN + 1];
1482 /* always start with reliable default values */
1483 setTapeInfoToDefaults();
1485 if (!(file = fopen(filename, MODE_READ)))
1488 getFileChunkBE(file, chunk_name, NULL);
1489 if (strcmp(chunk_name, "RND1") == 0)
1491 getFile32BitBE(file); /* not used */
1493 getFileChunkBE(file, chunk_name, NULL);
1494 if (strcmp(chunk_name, "TAPE") != 0)
1496 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1501 else /* check for pre-2.0 file format with cookie string */
1503 strcpy(cookie, chunk_name);
1504 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1505 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1506 cookie[strlen(cookie) - 1] = '\0';
1508 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1510 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1515 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1517 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1522 /* pre-2.0 tape files have no game version, so use file version here */
1523 tape.game_version = tape.file_version;
1526 if (tape.file_version < FILE_VERSION_1_2)
1528 /* tape files from versions before 1.2.0 without chunk structure */
1529 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1530 LoadTape_BODY(file, 2 * tape.length, &tape);
1538 int (*loader)(FILE *, int, struct TapeInfo *);
1542 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1543 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1544 { "INFO", -1, LoadTape_INFO },
1545 { "BODY", -1, LoadTape_BODY },
1549 while (getFileChunkBE(file, chunk_name, &chunk_size))
1553 while (chunk_info[i].name != NULL &&
1554 strcmp(chunk_name, chunk_info[i].name) != 0)
1557 if (chunk_info[i].name == NULL)
1559 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1560 chunk_name, filename);
1561 ReadUnusedBytesFromFile(file, chunk_size);
1563 else if (chunk_info[i].size != -1 &&
1564 chunk_info[i].size != chunk_size)
1566 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1567 chunk_size, chunk_name, filename);
1568 ReadUnusedBytesFromFile(file, chunk_size);
1572 /* call function to load this tape chunk */
1573 int chunk_size_expected =
1574 (chunk_info[i].loader)(file, chunk_size, &tape);
1576 /* the size of some chunks cannot be checked before reading other
1577 chunks first (like "HEAD" and "BODY") that contain some header
1578 information, so check them here */
1579 if (chunk_size_expected != chunk_size)
1581 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1582 chunk_size, chunk_name, filename);
1590 tape.length_seconds = GetTapeLength();
1593 printf("tape game version: %d\n", tape.game_version);
1594 printf("tape engine version: %d\n", tape.engine_version);
1598 void LoadTape(int level_nr)
1600 char *filename = getTapeFilename(level_nr);
1602 LoadTapeFromFilename(filename);
1605 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1607 putFileVersion(file, tape->file_version);
1608 putFileVersion(file, tape->game_version);
1611 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1614 byte store_participating_players = 0;
1616 /* set bits for participating players for compact storage */
1617 for(i=0; i<MAX_PLAYERS; i++)
1618 if (tape->player_participates[i])
1619 store_participating_players |= (1 << i);
1621 putFile32BitBE(file, tape->random_seed);
1622 putFile32BitBE(file, tape->date);
1623 putFile32BitBE(file, tape->length);
1625 putFile8Bit(file, store_participating_players);
1627 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1628 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1630 putFileVersion(file, tape->engine_version);
1633 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1635 int level_identifier_size = strlen(tape->level_identifier) + 1;
1638 putFile16BitBE(file, level_identifier_size);
1640 for(i=0; i < level_identifier_size; i++)
1641 putFile8Bit(file, tape->level_identifier[i]);
1643 putFile16BitBE(file, tape->level_nr);
1646 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1650 for(i=0; i<tape->length; i++)
1652 for(j=0; j<MAX_PLAYERS; j++)
1653 if (tape->player_participates[j])
1654 putFile8Bit(file, tape->pos[i].action[j]);
1656 putFile8Bit(file, tape->pos[i].delay);
1660 void SaveTape(int level_nr)
1662 char *filename = getTapeFilename(level_nr);
1664 boolean new_tape = TRUE;
1665 int num_participating_players = 0;
1666 int info_chunk_size;
1667 int body_chunk_size;
1670 InitTapeDirectory(leveldir_current->filename);
1672 /* if a tape still exists, ask to overwrite it */
1673 if (access(filename, F_OK) == 0)
1676 if (!Request("Replace old tape ?", REQ_ASK))
1680 if (!(file = fopen(filename, MODE_WRITE)))
1682 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1686 tape.file_version = FILE_VERSION_ACTUAL;
1687 tape.game_version = GAME_VERSION_ACTUAL;
1689 /* count number of participating players */
1690 for(i=0; i<MAX_PLAYERS; i++)
1691 if (tape.player_participates[i])
1692 num_participating_players++;
1694 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1695 body_chunk_size = (num_participating_players + 1) * tape.length;
1697 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1698 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1700 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1701 SaveTape_VERS(file, &tape);
1703 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1704 SaveTape_HEAD(file, &tape);
1706 putFileChunkBE(file, "INFO", info_chunk_size);
1707 SaveTape_INFO(file, &tape);
1709 putFileChunkBE(file, "BODY", body_chunk_size);
1710 SaveTape_BODY(file, &tape);
1714 SetFilePermissions(filename, PERMS_PRIVATE);
1716 tape.changed = FALSE;
1719 Request("tape saved !", REQ_CONFIRM);
1722 void DumpTape(struct TapeInfo *tape)
1726 if (TAPE_IS_EMPTY(*tape))
1728 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1732 printf_line("-", 79);
1733 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1734 tape->level_nr, tape->file_version, tape->game_version);
1735 printf("Level series identifier: '%s'\n", tape->level_identifier);
1736 printf_line("-", 79);
1738 for(i=0; i<tape->length; i++)
1740 if (i >= MAX_TAPELEN)
1743 printf("%03d: ", i);
1745 for(j=0; j<MAX_PLAYERS; j++)
1747 if (tape->player_participates[j])
1749 int action = tape->pos[i].action[j];
1751 printf("%d:%02x ", j, action);
1752 printf("[%c%c%c%c|%c%c] - ",
1753 (action & JOY_LEFT ? '<' : ' '),
1754 (action & JOY_RIGHT ? '>' : ' '),
1755 (action & JOY_UP ? '^' : ' '),
1756 (action & JOY_DOWN ? 'v' : ' '),
1757 (action & JOY_BUTTON_1 ? '1' : ' '),
1758 (action & JOY_BUTTON_2 ? '2' : ' '));
1762 printf("(%03d)\n", tape->pos[i].delay);
1765 printf_line("-", 79);
1769 /* ========================================================================= */
1770 /* score file functions */
1771 /* ========================================================================= */
1773 void LoadScore(int level_nr)
1776 char *filename = getScoreFilename(level_nr);
1777 char cookie[MAX_LINE_LEN];
1778 char line[MAX_LINE_LEN];
1782 /* always start with reliable default values */
1783 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1785 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1786 highscore[i].Score = 0;
1789 if (!(file = fopen(filename, MODE_READ)))
1792 /* check file identifier */
1793 fgets(cookie, MAX_LINE_LEN, file);
1794 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1795 cookie[strlen(cookie) - 1] = '\0';
1797 if (!checkCookieString(cookie, SCORE_COOKIE))
1799 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1804 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1806 fscanf(file, "%d", &highscore[i].Score);
1807 fgets(line, MAX_LINE_LEN, file);
1809 if (line[strlen(line) - 1] == '\n')
1810 line[strlen(line) - 1] = '\0';
1812 for (line_ptr = line; *line_ptr; line_ptr++)
1814 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1816 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1817 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1826 void SaveScore(int level_nr)
1829 char *filename = getScoreFilename(level_nr);
1832 InitScoreDirectory(leveldir_current->filename);
1834 if (!(file = fopen(filename, MODE_WRITE)))
1836 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1840 fprintf(file, "%s\n\n", SCORE_COOKIE);
1842 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1843 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1847 SetFilePermissions(filename, PERMS_PUBLIC);
1851 /* ========================================================================= */
1852 /* setup file functions */
1853 /* ========================================================================= */
1855 #define TOKEN_STR_PLAYER_PREFIX "player_"
1858 #define SETUP_TOKEN_PLAYER_NAME 0
1859 #define SETUP_TOKEN_SOUND 1
1860 #define SETUP_TOKEN_SOUND_LOOPS 2
1861 #define SETUP_TOKEN_SOUND_MUSIC 3
1862 #define SETUP_TOKEN_SOUND_SIMPLE 4
1863 #define SETUP_TOKEN_TOONS 5
1864 #define SETUP_TOKEN_SCROLL_DELAY 6
1865 #define SETUP_TOKEN_SOFT_SCROLLING 7
1866 #define SETUP_TOKEN_FADING 8
1867 #define SETUP_TOKEN_AUTORECORD 9
1868 #define SETUP_TOKEN_QUICK_DOORS 10
1869 #define SETUP_TOKEN_TEAM_MODE 11
1870 #define SETUP_TOKEN_HANDICAP 12
1871 #define SETUP_TOKEN_TIME_LIMIT 13
1872 #define SETUP_TOKEN_FULLSCREEN 14
1873 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1874 #define SETUP_TOKEN_GRAPHICS_SET 16
1875 #define SETUP_TOKEN_SOUNDS_SET 17
1876 #define SETUP_TOKEN_MUSIC_SET 18
1877 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1878 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1879 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1881 #define NUM_GLOBAL_SETUP_TOKENS 22
1884 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1885 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1886 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1887 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1888 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1889 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1890 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1891 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1892 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1894 #define NUM_EDITOR_SETUP_TOKENS 9
1896 /* shortcut setup */
1897 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1898 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1899 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1901 #define NUM_SHORTCUT_SETUP_TOKENS 3
1904 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1905 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1906 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1907 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1908 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1909 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1910 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1911 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1912 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1913 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1914 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1915 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1916 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1917 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1918 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1919 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1921 #define NUM_PLAYER_SETUP_TOKENS 16
1924 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1925 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1927 #define NUM_SYSTEM_SETUP_TOKENS 2
1930 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1932 #define NUM_OPTIONS_SETUP_TOKENS 1
1935 static struct SetupInfo si;
1936 static struct SetupEditorInfo sei;
1937 static struct SetupShortcutInfo ssi;
1938 static struct SetupInputInfo sii;
1939 static struct SetupSystemInfo syi;
1940 static struct OptionInfo soi;
1942 static struct TokenInfo global_setup_tokens[] =
1944 { TYPE_STRING, &si.player_name, "player_name" },
1945 { TYPE_SWITCH, &si.sound, "sound" },
1946 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1947 { TYPE_SWITCH, &si.sound_music, "background_music" },
1948 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1949 { TYPE_SWITCH, &si.toons, "toons" },
1950 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1951 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1952 { TYPE_SWITCH, &si.fading, "screen_fading" },
1953 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1954 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1955 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1956 { TYPE_SWITCH, &si.handicap, "handicap" },
1957 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1958 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1959 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1960 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1961 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1962 { TYPE_STRING, &si.music_set, "music_set" },
1963 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1964 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1965 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1968 static struct TokenInfo editor_setup_tokens[] =
1970 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1971 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1972 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1973 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1974 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1975 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1976 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1977 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1978 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1979 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
1982 static struct TokenInfo shortcut_setup_tokens[] =
1984 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1985 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1986 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1989 static struct TokenInfo player_setup_tokens[] =
1991 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1992 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1993 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1994 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1995 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1996 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1997 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1998 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1999 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2000 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2001 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
2002 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
2003 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
2004 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
2005 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
2006 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
2009 static struct TokenInfo system_setup_tokens[] =
2011 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
2012 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2015 static struct TokenInfo options_setup_tokens[] =
2017 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2020 static char *get_corrected_login_name(char *login_name)
2022 /* needed because player name must be a fixed length string */
2023 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2025 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2026 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2028 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2029 if (strchr(login_name_new, ' '))
2030 *strchr(login_name_new, ' ') = '\0';
2032 return login_name_new;
2035 static void setSetupInfoToDefaults(struct SetupInfo *si)
2039 si->player_name = get_corrected_login_name(getLoginName());
2042 si->sound_loops = TRUE;
2043 si->sound_music = TRUE;
2044 si->sound_simple = TRUE;
2046 si->double_buffering = TRUE;
2047 si->direct_draw = !si->double_buffering;
2048 si->scroll_delay = TRUE;
2049 si->soft_scrolling = TRUE;
2051 si->autorecord = TRUE;
2052 si->quick_doors = FALSE;
2053 si->team_mode = FALSE;
2054 si->handicap = TRUE;
2055 si->time_limit = TRUE;
2056 si->fullscreen = FALSE;
2057 si->ask_on_escape = TRUE;
2059 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2060 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2061 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2062 si->override_level_graphics = FALSE;
2063 si->override_level_sounds = FALSE;
2064 si->override_level_music = FALSE;
2066 si->editor.el_boulderdash = TRUE;
2067 si->editor.el_emerald_mine = TRUE;
2068 si->editor.el_more = TRUE;
2069 si->editor.el_sokoban = TRUE;
2070 si->editor.el_supaplex = TRUE;
2071 si->editor.el_diamond_caves = TRUE;
2072 si->editor.el_dx_boulderdash = TRUE;
2073 si->editor.el_chars = TRUE;
2074 si->editor.el_custom = TRUE;
2075 si->editor.el_custom_more = FALSE;
2077 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2078 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2079 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2081 for (i=0; i<MAX_PLAYERS; i++)
2083 si->input[i].use_joystick = FALSE;
2084 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2085 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2086 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2087 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2088 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2089 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2090 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2091 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2092 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2093 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2094 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2095 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2096 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2097 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2098 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2101 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2102 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2104 si->options.verbose = FALSE;
2107 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2111 if (!setup_file_hash)
2116 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2117 setSetupInfo(global_setup_tokens, i,
2118 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2123 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2124 setSetupInfo(editor_setup_tokens, i,
2125 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2128 /* shortcut setup */
2129 ssi = setup.shortcut;
2130 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2131 setSetupInfo(shortcut_setup_tokens, i,
2132 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2133 setup.shortcut = ssi;
2136 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2140 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2142 sii = setup.input[pnr];
2143 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2145 char full_token[100];
2147 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2148 setSetupInfo(player_setup_tokens, i,
2149 getHashEntry(setup_file_hash, full_token));
2151 setup.input[pnr] = sii;
2156 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2157 setSetupInfo(system_setup_tokens, i,
2158 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2162 soi = setup.options;
2163 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2164 setSetupInfo(options_setup_tokens, i,
2165 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2166 setup.options = soi;
2171 char *filename = getSetupFilename();
2172 SetupFileHash *setup_file_hash = NULL;
2174 /* always start with reliable default values */
2175 setSetupInfoToDefaults(&setup);
2177 setup_file_hash = loadSetupFileHash(filename);
2179 if (setup_file_hash)
2181 char *player_name_new;
2183 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2184 decodeSetupFileHash(setup_file_hash);
2186 setup.direct_draw = !setup.double_buffering;
2188 freeSetupFileHash(setup_file_hash);
2190 /* needed to work around problems with fixed length strings */
2191 player_name_new = get_corrected_login_name(setup.player_name);
2192 free(setup.player_name);
2193 setup.player_name = player_name_new;
2196 Error(ERR_WARN, "using default setup values");
2201 char *filename = getSetupFilename();
2205 InitUserDataDirectory();
2207 if (!(file = fopen(filename, MODE_WRITE)))
2209 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2213 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2214 getCookie("SETUP")));
2215 fprintf(file, "\n");
2219 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2221 /* just to make things nicer :) */
2222 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2223 i == SETUP_TOKEN_GRAPHICS_SET)
2224 fprintf(file, "\n");
2226 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2231 fprintf(file, "\n");
2232 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2233 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2235 /* shortcut setup */
2236 ssi = setup.shortcut;
2237 fprintf(file, "\n");
2238 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2239 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2242 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2246 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2247 fprintf(file, "\n");
2249 sii = setup.input[pnr];
2250 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2251 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2256 fprintf(file, "\n");
2257 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2258 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2261 soi = setup.options;
2262 fprintf(file, "\n");
2263 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2264 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2268 SetFilePermissions(filename, PERMS_PRIVATE);
2271 void LoadCustomElementDescriptions()
2273 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2274 SetupFileHash *setup_file_hash;
2277 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2279 if (element_info[i].custom_description != NULL)
2281 free(element_info[i].custom_description);
2282 element_info[i].custom_description = NULL;
2286 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2289 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2291 char *token = getStringCat2(element_info[i].token_name, ".name");
2292 char *value = getHashEntry(setup_file_hash, token);
2295 element_info[i].custom_description = getStringCopy(value);
2300 freeSetupFileHash(setup_file_hash);
2303 void LoadSpecialMenuDesignSettings()
2305 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2306 SetupFileHash *setup_file_hash;
2309 /* always start with reliable default values from default config */
2310 for (i=0; image_config_vars[i].token != NULL; i++)
2311 for (j=0; image_config[j].token != NULL; j++)
2312 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2313 *image_config_vars[i].value =
2314 get_integer_from_string(image_config[j].value);
2316 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2319 /* special case: initialize with default values that may be overwritten */
2320 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2322 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2323 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2324 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2326 if (value_x != NULL)
2327 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2328 if (value_y != NULL)
2329 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2330 if (list_size != NULL)
2331 menu.list_size[i] = get_integer_from_string(list_size);
2334 /* read (and overwrite with) values that may be specified in config file */
2335 for (i=0; image_config_vars[i].token != NULL; i++)
2337 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2340 *image_config_vars[i].value = get_integer_from_string(value);
2343 freeSetupFileHash(setup_file_hash);