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 *eci)
69 eci->events = CE_BITMASK_DEFAULT;
70 eci->target_element = EL_EMPTY_SPACE;
73 eci->delay_random = 0;
74 eci->delay_frames = -1; /* later set to reliable default value */
76 eci->trigger_element = EL_EMPTY_SPACE;
79 eci->use_content = FALSE;
80 eci->only_complete = FALSE;
81 eci->use_random_change = FALSE;
83 eci->power = CP_NON_DESTRUCTIVE;
87 eci->content[x][y] = EL_EMPTY_SPACE;
89 eci->player_action = 0;
90 eci->collide_action = 0;
91 eci->other_action = 0;
93 eci->pre_change_function = NULL;
94 eci->change_function = NULL;
95 eci->post_change_function = NULL;
98 static void setLevelInfoToDefaults(struct LevelInfo *level)
102 level->file_version = FILE_VERSION_ACTUAL;
103 level->game_version = GAME_VERSION_ACTUAL;
105 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
106 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
107 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
109 level->fieldx = STD_LEV_FIELDX;
110 level->fieldy = STD_LEV_FIELDY;
112 for(x=0; x<MAX_LEV_FIELDX; x++)
113 for(y=0; y<MAX_LEV_FIELDY; y++)
114 level->field[x][y] = EL_SAND;
117 level->gems_needed = 0;
118 level->amoeba_speed = 10;
119 level->time_magic_wall = 10;
120 level->time_wheel = 10;
121 level->time_light = 10;
122 level->time_timegate = 10;
123 level->amoeba_content = EL_DIAMOND;
124 level->double_speed = FALSE;
125 level->gravity = FALSE;
126 level->em_slippery_gems = FALSE;
128 level->use_custom_template = FALSE;
130 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
131 level->name[i] = '\0';
132 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
133 level->author[i] = '\0';
135 strcpy(level->name, NAMELESS_LEVEL_NAME);
136 strcpy(level->author, ANONYMOUS_NAME);
138 level->envelope[0] = '\0';
139 level->envelope_xsize = MAX_ENVELOPE_XSIZE;
140 level->envelope_ysize = MAX_ENVELOPE_YSIZE;
142 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
143 level->score[i] = 10;
145 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
146 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
149 level->yamyam_content[i][x][y] =
150 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
152 level->field[0][0] = EL_PLAYER_1;
153 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
155 for (i=0; i < MAX_NUM_ELEMENTS; i++)
157 setElementChangePages(&element_info[i], 1);
158 setElementChangeInfoToDefaults(element_info[i].change);
161 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
163 int element = EL_CUSTOM_START + i;
165 for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
166 element_info[element].description[j] = '\0';
167 if (element_info[element].custom_description != NULL)
168 strncpy(element_info[element].description,
169 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
171 strcpy(element_info[element].description,
172 element_info[element].editor_description);
174 element_info[element].use_gfx_element = FALSE;
175 element_info[element].gfx_element = EL_EMPTY_SPACE;
177 element_info[element].collect_score = 10; /* special default */
178 element_info[element].collect_count = 1; /* special default */
180 element_info[element].push_delay_fixed = 2; /* special default */
181 element_info[element].push_delay_random = 8; /* special default */
182 element_info[element].move_delay_fixed = 0;
183 element_info[element].move_delay_random = 0;
185 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
186 element_info[element].move_direction_initial = MV_NO_MOVING;
187 element_info[element].move_stepsize = TILEX / 8;
189 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
193 element_info[element].content[x][y] = EL_EMPTY_SPACE;
195 element_info[element].access_type = 0;
196 element_info[element].access_layer = 0;
197 element_info[element].walk_to_action = 0;
198 element_info[element].smash_targets = 0;
199 element_info[element].deadliness = 0;
200 element_info[element].consistency = 0;
202 element_info[element].can_explode_by_fire = FALSE;
203 element_info[element].can_explode_smashed = FALSE;
204 element_info[element].can_explode_impact = FALSE;
206 element_info[element].current_change_page = 0;
208 /* start with no properties at all */
209 for (j=0; j < NUM_EP_BITFIELDS; j++)
210 Properties[element][j] = EP_BITMASK_DEFAULT;
212 element_info[element].modified_settings = FALSE;
215 BorderElement = EL_STEELWALL;
217 level->no_level_file = FALSE;
219 if (leveldir_current == NULL) /* only when dumping level */
222 /* try to determine better author name than 'anonymous' */
223 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
225 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
226 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
230 switch (LEVELCLASS(leveldir_current))
232 case LEVELCLASS_TUTORIAL:
233 strcpy(level->author, PROGRAM_AUTHOR_STRING);
236 case LEVELCLASS_CONTRIBUTION:
237 strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
238 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
241 case LEVELCLASS_USER:
242 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
243 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
247 /* keep default value */
253 static void ActivateLevelTemplate()
255 /* Currently there is no special action needed to activate the template
256 data, because 'element_info' and 'Properties' overwrite the original
257 level data, while all other variables do not change. */
260 boolean LevelFileExists(int level_nr)
262 char *filename = getLevelFilename(level_nr);
264 return (access(filename, F_OK) == 0);
267 static int checkLevelElement(int element)
269 if (element >= NUM_FILE_ELEMENTS)
271 Error(ERR_WARN, "invalid level element %d", element);
272 element = EL_CHAR_QUESTION;
274 else if (element == EL_PLAYER_OBSOLETE)
275 element = EL_PLAYER_1;
276 else if (element == EL_KEY_OBSOLETE)
282 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
284 level->file_version = getFileVersion(file);
285 level->game_version = getFileVersion(file);
290 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
294 level->fieldx = getFile8Bit(file);
295 level->fieldy = getFile8Bit(file);
297 level->time = getFile16BitBE(file);
298 level->gems_needed = getFile16BitBE(file);
300 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
301 level->name[i] = getFile8Bit(file);
302 level->name[MAX_LEVEL_NAME_LEN] = 0;
304 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
305 level->score[i] = getFile8Bit(file);
307 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
308 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
311 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
313 level->amoeba_speed = getFile8Bit(file);
314 level->time_magic_wall = getFile8Bit(file);
315 level->time_wheel = getFile8Bit(file);
316 level->amoeba_content = checkLevelElement(getFile8Bit(file));
317 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
318 level->gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
319 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
320 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
322 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
324 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
329 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
333 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
334 level->author[i] = getFile8Bit(file);
335 level->author[MAX_LEVEL_NAME_LEN] = 0;
340 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
343 int chunk_size_expected = level->fieldx * level->fieldy;
345 /* Note: "chunk_size" was wrong before version 2.0 when elements are
346 stored with 16-bit encoding (and should be twice as big then).
347 Even worse, playfield data was stored 16-bit when only yamyam content
348 contained 16-bit elements and vice versa. */
350 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
351 chunk_size_expected *= 2;
353 if (chunk_size_expected != chunk_size)
355 ReadUnusedBytesFromFile(file, chunk_size);
356 return chunk_size_expected;
359 for(y=0; y<level->fieldy; y++)
360 for(x=0; x<level->fieldx; x++)
362 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
367 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
371 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
372 int chunk_size_expected = header_size + content_size;
374 /* Note: "chunk_size" was wrong before version 2.0 when elements are
375 stored with 16-bit encoding (and should be twice as big then).
376 Even worse, playfield data was stored 16-bit when only yamyam content
377 contained 16-bit elements and vice versa. */
379 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
380 chunk_size_expected += content_size;
382 if (chunk_size_expected != chunk_size)
384 ReadUnusedBytesFromFile(file, chunk_size);
385 return chunk_size_expected;
389 level->num_yamyam_contents = getFile8Bit(file);
393 /* correct invalid number of content fields -- should never happen */
394 if (level->num_yamyam_contents < 1 ||
395 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
396 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
398 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
401 level->yamyam_content[i][x][y] =
402 checkLevelElement(level->encoding_16bit_field ?
403 getFile16BitBE(file) : getFile8Bit(file));
407 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
411 int num_contents, content_xsize, content_ysize;
412 int content_array[MAX_ELEMENT_CONTENTS][3][3];
414 element = checkLevelElement(getFile16BitBE(file));
415 num_contents = getFile8Bit(file);
416 content_xsize = getFile8Bit(file);
417 content_ysize = getFile8Bit(file);
418 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
420 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
423 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
425 /* correct invalid number of content fields -- should never happen */
426 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
427 num_contents = STD_ELEMENT_CONTENTS;
429 if (element == EL_YAMYAM)
431 level->num_yamyam_contents = num_contents;
433 for(i=0; i<num_contents; i++)
436 level->yamyam_content[i][x][y] = content_array[i][x][y];
438 else if (element == EL_BD_AMOEBA)
440 level->amoeba_content = content_array[0][0][0];
444 Error(ERR_WARN, "cannot load content for element '%d'", element);
450 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
452 int num_changed_custom_elements = getFile16BitBE(file);
453 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
456 if (chunk_size_expected != chunk_size)
458 ReadUnusedBytesFromFile(file, chunk_size - 2);
459 return chunk_size_expected;
462 for (i=0; i < num_changed_custom_elements; i++)
464 int element = getFile16BitBE(file);
465 int properties = getFile32BitBE(file);
467 if (IS_CUSTOM_ELEMENT(element))
468 Properties[element][EP_BITFIELD_BASE] = properties;
470 Error(ERR_WARN, "invalid custom element number %d", element);
476 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
478 int num_changed_custom_elements = getFile16BitBE(file);
479 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
482 if (chunk_size_expected != chunk_size)
484 ReadUnusedBytesFromFile(file, chunk_size - 2);
485 return chunk_size_expected;
488 for (i=0; i < num_changed_custom_elements; i++)
490 int element = getFile16BitBE(file);
491 int custom_target_element = getFile16BitBE(file);
493 if (IS_CUSTOM_ELEMENT(element))
494 element_info[element].change->target_element = custom_target_element;
496 Error(ERR_WARN, "invalid custom element number %d", element);
502 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
504 int num_changed_custom_elements = getFile16BitBE(file);
505 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
508 if (chunk_size_expected != chunk_size)
510 ReadUnusedBytesFromFile(file, chunk_size - 2);
511 return chunk_size_expected;
514 for (i=0; i < num_changed_custom_elements; i++)
516 int element = getFile16BitBE(file);
518 if (!IS_CUSTOM_ELEMENT(element))
520 Error(ERR_WARN, "invalid custom element number %d", element);
522 element = EL_DEFAULT; /* dummy element used for artwork config */
525 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
526 element_info[element].description[j] = getFile8Bit(file);
527 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
529 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
531 /* some free bytes for future properties and padding */
532 ReadUnusedBytesFromFile(file, 7);
534 element_info[element].use_gfx_element = getFile8Bit(file);
535 element_info[element].gfx_element =
536 checkLevelElement(getFile16BitBE(file));
538 element_info[element].collect_score = getFile8Bit(file);
539 element_info[element].collect_count = getFile8Bit(file);
541 element_info[element].push_delay_fixed = getFile16BitBE(file);
542 element_info[element].push_delay_random = getFile16BitBE(file);
543 element_info[element].move_delay_fixed = getFile16BitBE(file);
544 element_info[element].move_delay_random = getFile16BitBE(file);
546 element_info[element].move_pattern = getFile16BitBE(file);
547 element_info[element].move_direction_initial = getFile8Bit(file);
548 element_info[element].move_stepsize = getFile8Bit(file);
552 element_info[element].content[x][y] =
553 checkLevelElement(getFile16BitBE(file));
555 element_info[element].change->events = getFile32BitBE(file);
557 element_info[element].change->target_element =
558 checkLevelElement(getFile16BitBE(file));
560 element_info[element].change->delay_fixed = getFile16BitBE(file);
561 element_info[element].change->delay_random = getFile16BitBE(file);
562 element_info[element].change->delay_frames = getFile16BitBE(file);
564 element_info[element].change->trigger_element =
565 checkLevelElement(getFile16BitBE(file));
567 element_info[element].change->explode = getFile8Bit(file);
568 element_info[element].change->use_content = getFile8Bit(file);
569 element_info[element].change->only_complete = getFile8Bit(file);
570 element_info[element].change->use_random_change = getFile8Bit(file);
572 element_info[element].change->random = getFile8Bit(file);
573 element_info[element].change->power = getFile8Bit(file);
577 element_info[element].change->content[x][y] =
578 checkLevelElement(getFile16BitBE(file));
580 element_info[element].slippery_type = getFile8Bit(file);
582 /* some free bytes for future properties and padding */
583 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
585 /* mark that this custom element has been modified */
586 element_info[element].modified_settings = TRUE;
592 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
594 char cookie[MAX_LINE_LEN];
595 char chunk_name[CHUNK_ID_LEN + 1];
599 /* always start with reliable default values */
600 setLevelInfoToDefaults(level);
602 if (!(file = fopen(filename, MODE_READ)))
604 level->no_level_file = TRUE;
606 if (level != &level_template)
607 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
612 getFileChunkBE(file, chunk_name, NULL);
613 if (strcmp(chunk_name, "RND1") == 0)
615 getFile32BitBE(file); /* not used */
617 getFileChunkBE(file, chunk_name, NULL);
618 if (strcmp(chunk_name, "CAVE") != 0)
620 Error(ERR_WARN, "unknown format of level file '%s'", filename);
625 else /* check for pre-2.0 file format with cookie string */
627 strcpy(cookie, chunk_name);
628 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
629 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
630 cookie[strlen(cookie) - 1] = '\0';
632 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
634 Error(ERR_WARN, "unknown format of level file '%s'", filename);
639 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
641 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
646 /* pre-2.0 level files have no game version, so use file version here */
647 level->game_version = level->file_version;
650 if (level->file_version < FILE_VERSION_1_2)
652 /* level files from versions before 1.2.0 without chunk structure */
653 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
654 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
662 int (*loader)(FILE *, int, struct LevelInfo *);
666 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
667 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
668 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
669 { "BODY", -1, LoadLevel_BODY },
670 { "CONT", -1, LoadLevel_CONT },
671 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
672 { "CUS1", -1, LoadLevel_CUS1 },
673 { "CUS2", -1, LoadLevel_CUS2 },
674 { "CUS3", -1, LoadLevel_CUS3 },
678 while (getFileChunkBE(file, chunk_name, &chunk_size))
682 while (chunk_info[i].name != NULL &&
683 strcmp(chunk_name, chunk_info[i].name) != 0)
686 if (chunk_info[i].name == NULL)
688 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
689 chunk_name, filename);
690 ReadUnusedBytesFromFile(file, chunk_size);
692 else if (chunk_info[i].size != -1 &&
693 chunk_info[i].size != chunk_size)
695 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
696 chunk_size, chunk_name, filename);
697 ReadUnusedBytesFromFile(file, chunk_size);
701 /* call function to load this level chunk */
702 int chunk_size_expected =
703 (chunk_info[i].loader)(file, chunk_size, level);
705 /* the size of some chunks cannot be checked before reading other
706 chunks first (like "HEAD" and "BODY") that contain some header
707 information, so check them here */
708 if (chunk_size_expected != chunk_size)
710 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
711 chunk_size, chunk_name, filename);
720 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
724 if (leveldir_current == NULL) /* only when dumping level */
727 /* determine correct game engine version of current level */
728 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
729 IS_LEVELCLASS_USER(leveldir_current))
732 printf("\n::: This level is private or contributed: '%s'\n", filename);
735 /* For user contributed and private levels, use the version of
736 the game engine the levels were created for.
737 Since 2.0.1, the game engine version is now directly stored
738 in the level file (chunk "VERS"), so there is no need anymore
739 to set the game version from the file version (except for old,
740 pre-2.0 levels, where the game version is still taken from the
741 file format version used to store the level -- see above). */
743 /* do some special adjustments to support older level versions */
744 if (level->file_version == FILE_VERSION_1_0)
746 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
747 Error(ERR_WARN, "using high speed movement for player");
749 /* player was faster than monsters in (pre-)1.0 levels */
750 level->double_speed = TRUE;
753 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
754 if (level->game_version == VERSION_IDENT(2,0,1))
755 level->em_slippery_gems = TRUE;
760 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
761 leveldir_current->sort_priority, filename);
764 /* Always use the latest version of the game engine for all but
765 user contributed and private levels; this allows for actual
766 corrections in the game engine to take effect for existing,
767 converted levels (from "classic" or other existing games) to
768 make the game emulation more accurate, while (hopefully) not
769 breaking existing levels created from other players. */
771 level->game_version = GAME_VERSION_ACTUAL;
773 /* Set special EM style gems behaviour: EM style gems slip down from
774 normal, steel and growing wall. As this is a more fundamental change,
775 it seems better to set the default behaviour to "off" (as it is more
776 natural) and make it configurable in the level editor (as a property
777 of gem style elements). Already existing converted levels (neither
778 private nor contributed levels) are changed to the new behaviour. */
780 if (level->file_version < FILE_VERSION_2_0)
781 level->em_slippery_gems = TRUE;
784 /* map elements that have changed in newer versions */
785 for(y=0; y<level->fieldy; y++)
787 for(x=0; x<level->fieldx; x++)
789 int element = level->field[x][y];
791 if (level->game_version <= VERSION_IDENT(2,2,0))
793 /* map game font elements */
794 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
795 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
796 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
797 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
800 if (level->game_version < VERSION_IDENT(3,0,0))
802 /* map Supaplex gravity tube elements */
803 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
804 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
805 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
806 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
810 level->field[x][y] = element;
814 /* map custom element change events that have changed in newer versions
815 (these following values have accidentally changed in version 3.0.1) */
816 if (level->game_version <= VERSION_IDENT(3,0,0))
818 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
820 int element = EL_CUSTOM_START + i;
822 /* order of checking events to be mapped is important */
823 for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
825 if (HAS_CHANGE_EVENT(element, j - 2))
827 SET_CHANGE_EVENT(element, j - 2, FALSE);
828 SET_CHANGE_EVENT(element, j, TRUE);
832 /* order of checking events to be mapped is important */
833 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
835 if (HAS_CHANGE_EVENT(element, j - 1))
837 SET_CHANGE_EVENT(element, j - 1, FALSE);
838 SET_CHANGE_EVENT(element, j, TRUE);
844 /* copy elements to runtime playfield array */
845 for(x=0; x<MAX_LEV_FIELDX; x++)
846 for(y=0; y<MAX_LEV_FIELDY; y++)
847 Feld[x][y] = level->field[x][y];
849 /* initialize level size variables for faster access */
850 lev_fieldx = level->fieldx;
851 lev_fieldy = level->fieldy;
853 /* determine border element for this level */
856 /* initialize element properties for level editor etc. */
857 InitElementPropertiesEngine(level->game_version);
860 void LoadLevelTemplate(int level_nr)
862 char *filename = getLevelFilename(level_nr);
864 LoadLevelFromFilename(&level_template, filename);
866 ActivateLevelTemplate();
869 void LoadLevel(int level_nr)
871 char *filename = getLevelFilename(level_nr);
873 LoadLevelFromFilename(&level, filename);
875 if (level.use_custom_template)
876 LoadLevelTemplate(-1);
878 LoadLevel_InitLevel(&level, filename);
881 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
883 putFileVersion(file, level->file_version);
884 putFileVersion(file, level->game_version);
887 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
891 putFile8Bit(file, level->fieldx);
892 putFile8Bit(file, level->fieldy);
894 putFile16BitBE(file, level->time);
895 putFile16BitBE(file, level->gems_needed);
897 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
898 putFile8Bit(file, level->name[i]);
900 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
901 putFile8Bit(file, level->score[i]);
903 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
906 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
907 level->yamyam_content[i][x][y]));
908 putFile8Bit(file, level->amoeba_speed);
909 putFile8Bit(file, level->time_magic_wall);
910 putFile8Bit(file, level->time_wheel);
911 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
912 level->amoeba_content));
913 putFile8Bit(file, (level->double_speed ? 1 : 0));
914 putFile8Bit(file, (level->gravity ? 1 : 0));
915 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
916 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
918 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
920 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
923 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
927 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
928 putFile8Bit(file, level->author[i]);
931 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
935 for(y=0; y<level->fieldy; y++)
936 for(x=0; x<level->fieldx; x++)
937 if (level->encoding_16bit_field)
938 putFile16BitBE(file, level->field[x][y]);
940 putFile8Bit(file, level->field[x][y]);
944 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
948 putFile8Bit(file, EL_YAMYAM);
949 putFile8Bit(file, level->num_yamyam_contents);
950 putFile8Bit(file, 0);
951 putFile8Bit(file, 0);
953 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
956 if (level->encoding_16bit_field)
957 putFile16BitBE(file, level->yamyam_content[i][x][y]);
959 putFile8Bit(file, level->yamyam_content[i][x][y]);
963 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
966 int num_contents, content_xsize, content_ysize;
967 int content_array[MAX_ELEMENT_CONTENTS][3][3];
969 if (element == EL_YAMYAM)
971 num_contents = level->num_yamyam_contents;
975 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
978 content_array[i][x][y] = level->yamyam_content[i][x][y];
980 else if (element == EL_BD_AMOEBA)
986 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
989 content_array[i][x][y] = EL_EMPTY;
990 content_array[0][0][0] = level->amoeba_content;
994 /* chunk header already written -- write empty chunk data */
995 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
997 Error(ERR_WARN, "cannot save content for element '%d'", element);
1001 putFile16BitBE(file, element);
1002 putFile8Bit(file, num_contents);
1003 putFile8Bit(file, content_xsize);
1004 putFile8Bit(file, content_ysize);
1006 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1008 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1011 putFile16BitBE(file, content_array[i][x][y]);
1015 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1016 int num_changed_custom_elements)
1020 putFile16BitBE(file, num_changed_custom_elements);
1022 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1024 int element = EL_CUSTOM_START + i;
1026 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1028 if (check < num_changed_custom_elements)
1030 putFile16BitBE(file, element);
1031 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1038 if (check != num_changed_custom_elements) /* should not happen */
1039 Error(ERR_WARN, "inconsistent number of custom element properties");
1044 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1045 int num_changed_custom_elements)
1049 putFile16BitBE(file, num_changed_custom_elements);
1051 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1053 int element = EL_CUSTOM_START + i;
1055 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1057 if (check < num_changed_custom_elements)
1059 putFile16BitBE(file, element);
1060 putFile16BitBE(file, element_info[element].change->target_element);
1067 if (check != num_changed_custom_elements) /* should not happen */
1068 Error(ERR_WARN, "inconsistent number of custom target elements");
1072 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1073 int num_changed_custom_elements)
1075 int i, j, x, y, check = 0;
1077 putFile16BitBE(file, num_changed_custom_elements);
1079 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1081 int element = EL_CUSTOM_START + i;
1083 if (element_info[element].modified_settings)
1085 if (check < num_changed_custom_elements)
1087 putFile16BitBE(file, element);
1089 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1090 putFile8Bit(file, element_info[element].description[j]);
1092 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1094 /* some free bytes for future properties and padding */
1095 WriteUnusedBytesToFile(file, 7);
1097 putFile8Bit(file, element_info[element].use_gfx_element);
1098 putFile16BitBE(file, element_info[element].gfx_element);
1100 putFile8Bit(file, element_info[element].collect_score);
1101 putFile8Bit(file, element_info[element].collect_count);
1103 putFile16BitBE(file, element_info[element].push_delay_fixed);
1104 putFile16BitBE(file, element_info[element].push_delay_random);
1105 putFile16BitBE(file, element_info[element].move_delay_fixed);
1106 putFile16BitBE(file, element_info[element].move_delay_random);
1108 putFile16BitBE(file, element_info[element].move_pattern);
1109 putFile8Bit(file, element_info[element].move_direction_initial);
1110 putFile8Bit(file, element_info[element].move_stepsize);
1114 putFile16BitBE(file, element_info[element].content[x][y]);
1116 putFile32BitBE(file, element_info[element].change->events);
1118 putFile16BitBE(file, element_info[element].change->target_element);
1120 putFile16BitBE(file, element_info[element].change->delay_fixed);
1121 putFile16BitBE(file, element_info[element].change->delay_random);
1122 putFile16BitBE(file, element_info[element].change->delay_frames);
1124 putFile16BitBE(file, element_info[element].change->trigger_element);
1126 putFile8Bit(file, element_info[element].change->explode);
1127 putFile8Bit(file, element_info[element].change->use_content);
1128 putFile8Bit(file, element_info[element].change->only_complete);
1129 putFile8Bit(file, element_info[element].change->use_random_change);
1131 putFile8Bit(file, element_info[element].change->random);
1132 putFile8Bit(file, element_info[element].change->power);
1136 putFile16BitBE(file, element_info[element].change->content[x][y]);
1138 putFile8Bit(file, element_info[element].slippery_type);
1140 /* some free bytes for future properties and padding */
1141 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1148 if (check != num_changed_custom_elements) /* should not happen */
1149 Error(ERR_WARN, "inconsistent number of custom element properties");
1152 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1154 int body_chunk_size;
1155 int num_changed_custom_elements = 0;
1156 int level_chunk_CUS3_size;
1160 if (!(file = fopen(filename, MODE_WRITE)))
1162 Error(ERR_WARN, "cannot save level file '%s'", filename);
1166 level->file_version = FILE_VERSION_ACTUAL;
1167 level->game_version = GAME_VERSION_ACTUAL;
1169 /* check level field for 16-bit elements */
1170 level->encoding_16bit_field = FALSE;
1171 for(y=0; y<level->fieldy; y++)
1172 for(x=0; x<level->fieldx; x++)
1173 if (level->field[x][y] > 255)
1174 level->encoding_16bit_field = TRUE;
1176 /* check yamyam content for 16-bit elements */
1177 level->encoding_16bit_yamyam = FALSE;
1178 for(i=0; i<level->num_yamyam_contents; i++)
1181 if (level->yamyam_content[i][x][y] > 255)
1182 level->encoding_16bit_yamyam = TRUE;
1184 /* check amoeba content for 16-bit elements */
1185 level->encoding_16bit_amoeba = FALSE;
1186 if (level->amoeba_content > 255)
1187 level->encoding_16bit_amoeba = TRUE;
1189 /* calculate size of "BODY" chunk */
1191 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1193 /* check for non-standard custom elements and calculate "CUS3" chunk size */
1194 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1195 if (element_info[EL_CUSTOM_START + i].modified_settings)
1196 num_changed_custom_elements++;
1197 level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1199 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1200 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1202 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1203 SaveLevel_VERS(file, level);
1205 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1206 SaveLevel_HEAD(file, level);
1208 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1209 SaveLevel_AUTH(file, level);
1211 putFileChunkBE(file, "BODY", body_chunk_size);
1212 SaveLevel_BODY(file, level);
1214 if (level->encoding_16bit_yamyam ||
1215 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1217 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1218 SaveLevel_CNT2(file, level, EL_YAMYAM);
1221 if (level->encoding_16bit_amoeba)
1223 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1224 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1227 if (num_changed_custom_elements > 0 && !level->use_custom_template)
1229 putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1230 SaveLevel_CUS3(file, level, num_changed_custom_elements);
1235 SetFilePermissions(filename, PERMS_PRIVATE);
1238 void SaveLevel(int level_nr)
1240 char *filename = getLevelFilename(level_nr);
1242 SaveLevelFromFilename(&level, filename);
1245 void SaveLevelTemplate()
1247 char *filename = getLevelFilename(-1);
1249 SaveLevelFromFilename(&level, filename);
1252 void DumpLevel(struct LevelInfo *level)
1254 printf_line("-", 79);
1255 printf("Level xxx (file version %08d, game version %08d)\n",
1256 level->file_version, level->game_version);
1257 printf_line("-", 79);
1259 printf("Level Author: '%s'\n", level->author);
1260 printf("Level Title: '%s'\n", level->name);
1262 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1264 printf("Level Time: %d seconds\n", level->time);
1265 printf("Gems needed: %d\n", level->gems_needed);
1267 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1268 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1269 printf("Time for Light: %d seconds\n", level->time_light);
1270 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1272 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1274 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1275 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1276 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1278 printf_line("-", 79);
1282 /* ========================================================================= */
1283 /* tape file functions */
1284 /* ========================================================================= */
1286 static void setTapeInfoToDefaults()
1290 /* always start with reliable default values (empty tape) */
1293 /* default values (also for pre-1.2 tapes) with only the first player */
1294 tape.player_participates[0] = TRUE;
1295 for(i=1; i<MAX_PLAYERS; i++)
1296 tape.player_participates[i] = FALSE;
1298 /* at least one (default: the first) player participates in every tape */
1299 tape.num_participating_players = 1;
1301 tape.level_nr = level_nr;
1303 tape.changed = FALSE;
1305 tape.recording = FALSE;
1306 tape.playing = FALSE;
1307 tape.pausing = FALSE;
1310 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1312 tape->file_version = getFileVersion(file);
1313 tape->game_version = getFileVersion(file);
1318 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1322 tape->random_seed = getFile32BitBE(file);
1323 tape->date = getFile32BitBE(file);
1324 tape->length = getFile32BitBE(file);
1326 /* read header fields that are new since version 1.2 */
1327 if (tape->file_version >= FILE_VERSION_1_2)
1329 byte store_participating_players = getFile8Bit(file);
1332 /* since version 1.2, tapes store which players participate in the tape */
1333 tape->num_participating_players = 0;
1334 for(i=0; i<MAX_PLAYERS; i++)
1336 tape->player_participates[i] = FALSE;
1338 if (store_participating_players & (1 << i))
1340 tape->player_participates[i] = TRUE;
1341 tape->num_participating_players++;
1345 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1347 engine_version = getFileVersion(file);
1348 if (engine_version > 0)
1349 tape->engine_version = engine_version;
1351 tape->engine_version = tape->game_version;
1357 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1359 int level_identifier_size;
1362 level_identifier_size = getFile16BitBE(file);
1364 tape->level_identifier =
1365 checked_realloc(tape->level_identifier, level_identifier_size);
1367 for(i=0; i < level_identifier_size; i++)
1368 tape->level_identifier[i] = getFile8Bit(file);
1370 tape->level_nr = getFile16BitBE(file);
1372 chunk_size = 2 + level_identifier_size + 2;
1377 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1380 int chunk_size_expected =
1381 (tape->num_participating_players + 1) * tape->length;
1383 if (chunk_size_expected != chunk_size)
1385 ReadUnusedBytesFromFile(file, chunk_size);
1386 return chunk_size_expected;
1389 for(i=0; i<tape->length; i++)
1391 if (i >= MAX_TAPELEN)
1394 for(j=0; j<MAX_PLAYERS; j++)
1396 tape->pos[i].action[j] = MV_NO_MOVING;
1398 if (tape->player_participates[j])
1399 tape->pos[i].action[j] = getFile8Bit(file);
1402 tape->pos[i].delay = getFile8Bit(file);
1404 if (tape->file_version == FILE_VERSION_1_0)
1406 /* eliminate possible diagonal moves in old tapes */
1407 /* this is only for backward compatibility */
1409 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1410 byte action = tape->pos[i].action[0];
1411 int k, num_moves = 0;
1415 if (action & joy_dir[k])
1417 tape->pos[i + num_moves].action[0] = joy_dir[k];
1419 tape->pos[i + num_moves].delay = 0;
1428 tape->length += num_moves;
1431 else if (tape->file_version < FILE_VERSION_2_0)
1433 /* convert pre-2.0 tapes to new tape format */
1435 if (tape->pos[i].delay > 1)
1438 tape->pos[i + 1] = tape->pos[i];
1439 tape->pos[i + 1].delay = 1;
1442 for(j=0; j<MAX_PLAYERS; j++)
1443 tape->pos[i].action[j] = MV_NO_MOVING;
1444 tape->pos[i].delay--;
1455 if (i != tape->length)
1456 chunk_size = (tape->num_participating_players + 1) * i;
1461 void LoadTapeFromFilename(char *filename)
1463 char cookie[MAX_LINE_LEN];
1464 char chunk_name[CHUNK_ID_LEN + 1];
1468 /* always start with reliable default values */
1469 setTapeInfoToDefaults();
1471 if (!(file = fopen(filename, MODE_READ)))
1474 getFileChunkBE(file, chunk_name, NULL);
1475 if (strcmp(chunk_name, "RND1") == 0)
1477 getFile32BitBE(file); /* not used */
1479 getFileChunkBE(file, chunk_name, NULL);
1480 if (strcmp(chunk_name, "TAPE") != 0)
1482 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1487 else /* check for pre-2.0 file format with cookie string */
1489 strcpy(cookie, chunk_name);
1490 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1491 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1492 cookie[strlen(cookie) - 1] = '\0';
1494 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1496 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1501 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1503 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1508 /* pre-2.0 tape files have no game version, so use file version here */
1509 tape.game_version = tape.file_version;
1512 if (tape.file_version < FILE_VERSION_1_2)
1514 /* tape files from versions before 1.2.0 without chunk structure */
1515 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1516 LoadTape_BODY(file, 2 * tape.length, &tape);
1524 int (*loader)(FILE *, int, struct TapeInfo *);
1528 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1529 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1530 { "INFO", -1, LoadTape_INFO },
1531 { "BODY", -1, LoadTape_BODY },
1535 while (getFileChunkBE(file, chunk_name, &chunk_size))
1539 while (chunk_info[i].name != NULL &&
1540 strcmp(chunk_name, chunk_info[i].name) != 0)
1543 if (chunk_info[i].name == NULL)
1545 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1546 chunk_name, filename);
1547 ReadUnusedBytesFromFile(file, chunk_size);
1549 else if (chunk_info[i].size != -1 &&
1550 chunk_info[i].size != chunk_size)
1552 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1553 chunk_size, chunk_name, filename);
1554 ReadUnusedBytesFromFile(file, chunk_size);
1558 /* call function to load this tape chunk */
1559 int chunk_size_expected =
1560 (chunk_info[i].loader)(file, chunk_size, &tape);
1562 /* the size of some chunks cannot be checked before reading other
1563 chunks first (like "HEAD" and "BODY") that contain some header
1564 information, so check them here */
1565 if (chunk_size_expected != chunk_size)
1567 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1568 chunk_size, chunk_name, filename);
1576 tape.length_seconds = GetTapeLength();
1579 printf("tape game version: %d\n", tape.game_version);
1580 printf("tape engine version: %d\n", tape.engine_version);
1584 void LoadTape(int level_nr)
1586 char *filename = getTapeFilename(level_nr);
1588 LoadTapeFromFilename(filename);
1591 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1593 putFileVersion(file, tape->file_version);
1594 putFileVersion(file, tape->game_version);
1597 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1600 byte store_participating_players = 0;
1602 /* set bits for participating players for compact storage */
1603 for(i=0; i<MAX_PLAYERS; i++)
1604 if (tape->player_participates[i])
1605 store_participating_players |= (1 << i);
1607 putFile32BitBE(file, tape->random_seed);
1608 putFile32BitBE(file, tape->date);
1609 putFile32BitBE(file, tape->length);
1611 putFile8Bit(file, store_participating_players);
1613 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1614 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1616 putFileVersion(file, tape->engine_version);
1619 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1621 int level_identifier_size = strlen(tape->level_identifier) + 1;
1624 putFile16BitBE(file, level_identifier_size);
1626 for(i=0; i < level_identifier_size; i++)
1627 putFile8Bit(file, tape->level_identifier[i]);
1629 putFile16BitBE(file, tape->level_nr);
1632 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1636 for(i=0; i<tape->length; i++)
1638 for(j=0; j<MAX_PLAYERS; j++)
1639 if (tape->player_participates[j])
1640 putFile8Bit(file, tape->pos[i].action[j]);
1642 putFile8Bit(file, tape->pos[i].delay);
1646 void SaveTape(int level_nr)
1648 char *filename = getTapeFilename(level_nr);
1650 boolean new_tape = TRUE;
1651 int num_participating_players = 0;
1652 int info_chunk_size;
1653 int body_chunk_size;
1656 InitTapeDirectory(leveldir_current->filename);
1658 /* if a tape still exists, ask to overwrite it */
1659 if (access(filename, F_OK) == 0)
1662 if (!Request("Replace old tape ?", REQ_ASK))
1666 if (!(file = fopen(filename, MODE_WRITE)))
1668 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1672 tape.file_version = FILE_VERSION_ACTUAL;
1673 tape.game_version = GAME_VERSION_ACTUAL;
1675 /* count number of participating players */
1676 for(i=0; i<MAX_PLAYERS; i++)
1677 if (tape.player_participates[i])
1678 num_participating_players++;
1680 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1681 body_chunk_size = (num_participating_players + 1) * tape.length;
1683 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1684 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1686 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1687 SaveTape_VERS(file, &tape);
1689 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1690 SaveTape_HEAD(file, &tape);
1692 putFileChunkBE(file, "INFO", info_chunk_size);
1693 SaveTape_INFO(file, &tape);
1695 putFileChunkBE(file, "BODY", body_chunk_size);
1696 SaveTape_BODY(file, &tape);
1700 SetFilePermissions(filename, PERMS_PRIVATE);
1702 tape.changed = FALSE;
1705 Request("tape saved !", REQ_CONFIRM);
1708 void DumpTape(struct TapeInfo *tape)
1712 if (TAPE_IS_EMPTY(*tape))
1714 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1718 printf_line("-", 79);
1719 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1720 tape->level_nr, tape->file_version, tape->game_version);
1721 printf("Level series identifier: '%s'\n", tape->level_identifier);
1722 printf_line("-", 79);
1724 for(i=0; i<tape->length; i++)
1726 if (i >= MAX_TAPELEN)
1729 printf("%03d: ", i);
1731 for(j=0; j<MAX_PLAYERS; j++)
1733 if (tape->player_participates[j])
1735 int action = tape->pos[i].action[j];
1737 printf("%d:%02x ", j, action);
1738 printf("[%c%c%c%c|%c%c] - ",
1739 (action & JOY_LEFT ? '<' : ' '),
1740 (action & JOY_RIGHT ? '>' : ' '),
1741 (action & JOY_UP ? '^' : ' '),
1742 (action & JOY_DOWN ? 'v' : ' '),
1743 (action & JOY_BUTTON_1 ? '1' : ' '),
1744 (action & JOY_BUTTON_2 ? '2' : ' '));
1748 printf("(%03d)\n", tape->pos[i].delay);
1751 printf_line("-", 79);
1755 /* ========================================================================= */
1756 /* score file functions */
1757 /* ========================================================================= */
1759 void LoadScore(int level_nr)
1762 char *filename = getScoreFilename(level_nr);
1763 char cookie[MAX_LINE_LEN];
1764 char line[MAX_LINE_LEN];
1768 /* always start with reliable default values */
1769 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1771 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1772 highscore[i].Score = 0;
1775 if (!(file = fopen(filename, MODE_READ)))
1778 /* check file identifier */
1779 fgets(cookie, MAX_LINE_LEN, file);
1780 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1781 cookie[strlen(cookie) - 1] = '\0';
1783 if (!checkCookieString(cookie, SCORE_COOKIE))
1785 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1790 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1792 fscanf(file, "%d", &highscore[i].Score);
1793 fgets(line, MAX_LINE_LEN, file);
1795 if (line[strlen(line) - 1] == '\n')
1796 line[strlen(line) - 1] = '\0';
1798 for (line_ptr = line; *line_ptr; line_ptr++)
1800 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1802 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1803 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1812 void SaveScore(int level_nr)
1815 char *filename = getScoreFilename(level_nr);
1818 InitScoreDirectory(leveldir_current->filename);
1820 if (!(file = fopen(filename, MODE_WRITE)))
1822 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1826 fprintf(file, "%s\n\n", SCORE_COOKIE);
1828 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1829 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1833 SetFilePermissions(filename, PERMS_PUBLIC);
1837 /* ========================================================================= */
1838 /* setup file functions */
1839 /* ========================================================================= */
1841 #define TOKEN_STR_PLAYER_PREFIX "player_"
1844 #define SETUP_TOKEN_PLAYER_NAME 0
1845 #define SETUP_TOKEN_SOUND 1
1846 #define SETUP_TOKEN_SOUND_LOOPS 2
1847 #define SETUP_TOKEN_SOUND_MUSIC 3
1848 #define SETUP_TOKEN_SOUND_SIMPLE 4
1849 #define SETUP_TOKEN_TOONS 5
1850 #define SETUP_TOKEN_SCROLL_DELAY 6
1851 #define SETUP_TOKEN_SOFT_SCROLLING 7
1852 #define SETUP_TOKEN_FADING 8
1853 #define SETUP_TOKEN_AUTORECORD 9
1854 #define SETUP_TOKEN_QUICK_DOORS 10
1855 #define SETUP_TOKEN_TEAM_MODE 11
1856 #define SETUP_TOKEN_HANDICAP 12
1857 #define SETUP_TOKEN_TIME_LIMIT 13
1858 #define SETUP_TOKEN_FULLSCREEN 14
1859 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1860 #define SETUP_TOKEN_GRAPHICS_SET 16
1861 #define SETUP_TOKEN_SOUNDS_SET 17
1862 #define SETUP_TOKEN_MUSIC_SET 18
1863 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1864 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1865 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1867 #define NUM_GLOBAL_SETUP_TOKENS 22
1870 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1871 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1872 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1873 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1874 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1875 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1876 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1877 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1878 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1880 #define NUM_EDITOR_SETUP_TOKENS 9
1882 /* shortcut setup */
1883 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1884 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1885 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1887 #define NUM_SHORTCUT_SETUP_TOKENS 3
1890 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1891 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1892 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1893 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1894 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1895 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1896 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1897 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1898 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1899 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1900 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1901 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1902 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1903 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1904 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1905 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1907 #define NUM_PLAYER_SETUP_TOKENS 16
1910 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1911 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1913 #define NUM_SYSTEM_SETUP_TOKENS 2
1916 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1918 #define NUM_OPTIONS_SETUP_TOKENS 1
1921 static struct SetupInfo si;
1922 static struct SetupEditorInfo sei;
1923 static struct SetupShortcutInfo ssi;
1924 static struct SetupInputInfo sii;
1925 static struct SetupSystemInfo syi;
1926 static struct OptionInfo soi;
1928 static struct TokenInfo global_setup_tokens[] =
1930 { TYPE_STRING, &si.player_name, "player_name" },
1931 { TYPE_SWITCH, &si.sound, "sound" },
1932 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1933 { TYPE_SWITCH, &si.sound_music, "background_music" },
1934 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1935 { TYPE_SWITCH, &si.toons, "toons" },
1936 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1937 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1938 { TYPE_SWITCH, &si.fading, "screen_fading" },
1939 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1940 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1941 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1942 { TYPE_SWITCH, &si.handicap, "handicap" },
1943 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1944 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1945 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1946 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1947 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1948 { TYPE_STRING, &si.music_set, "music_set" },
1949 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1950 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1951 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1954 static struct TokenInfo editor_setup_tokens[] =
1956 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1957 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1958 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1959 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1960 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1961 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1962 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1963 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1964 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1965 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
1968 static struct TokenInfo shortcut_setup_tokens[] =
1970 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1971 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1972 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1975 static struct TokenInfo player_setup_tokens[] =
1977 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1978 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1979 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1980 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1981 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1982 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1983 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1984 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1985 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1986 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1987 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1988 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1989 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1990 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1991 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1992 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1995 static struct TokenInfo system_setup_tokens[] =
1997 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1998 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2001 static struct TokenInfo options_setup_tokens[] =
2003 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
2006 static char *get_corrected_login_name(char *login_name)
2008 /* needed because player name must be a fixed length string */
2009 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2011 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2012 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2014 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
2015 if (strchr(login_name_new, ' '))
2016 *strchr(login_name_new, ' ') = '\0';
2018 return login_name_new;
2021 static void setSetupInfoToDefaults(struct SetupInfo *si)
2025 si->player_name = get_corrected_login_name(getLoginName());
2028 si->sound_loops = TRUE;
2029 si->sound_music = TRUE;
2030 si->sound_simple = TRUE;
2032 si->double_buffering = TRUE;
2033 si->direct_draw = !si->double_buffering;
2034 si->scroll_delay = TRUE;
2035 si->soft_scrolling = TRUE;
2037 si->autorecord = TRUE;
2038 si->quick_doors = FALSE;
2039 si->team_mode = FALSE;
2040 si->handicap = TRUE;
2041 si->time_limit = TRUE;
2042 si->fullscreen = FALSE;
2043 si->ask_on_escape = TRUE;
2045 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2046 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2047 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2048 si->override_level_graphics = FALSE;
2049 si->override_level_sounds = FALSE;
2050 si->override_level_music = FALSE;
2052 si->editor.el_boulderdash = TRUE;
2053 si->editor.el_emerald_mine = TRUE;
2054 si->editor.el_more = TRUE;
2055 si->editor.el_sokoban = TRUE;
2056 si->editor.el_supaplex = TRUE;
2057 si->editor.el_diamond_caves = TRUE;
2058 si->editor.el_dx_boulderdash = TRUE;
2059 si->editor.el_chars = TRUE;
2060 si->editor.el_custom = TRUE;
2061 si->editor.el_custom_more = FALSE;
2063 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2064 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2065 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2067 for (i=0; i<MAX_PLAYERS; i++)
2069 si->input[i].use_joystick = FALSE;
2070 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2071 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2072 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2073 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2074 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2075 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2076 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2077 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2078 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2079 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2080 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2081 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2082 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2083 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2084 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2087 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2088 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2090 si->options.verbose = FALSE;
2093 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2097 if (!setup_file_hash)
2102 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2103 setSetupInfo(global_setup_tokens, i,
2104 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2109 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2110 setSetupInfo(editor_setup_tokens, i,
2111 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2114 /* shortcut setup */
2115 ssi = setup.shortcut;
2116 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2117 setSetupInfo(shortcut_setup_tokens, i,
2118 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2119 setup.shortcut = ssi;
2122 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2126 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2128 sii = setup.input[pnr];
2129 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2131 char full_token[100];
2133 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2134 setSetupInfo(player_setup_tokens, i,
2135 getHashEntry(setup_file_hash, full_token));
2137 setup.input[pnr] = sii;
2142 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2143 setSetupInfo(system_setup_tokens, i,
2144 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2148 soi = setup.options;
2149 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2150 setSetupInfo(options_setup_tokens, i,
2151 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2152 setup.options = soi;
2157 char *filename = getSetupFilename();
2158 SetupFileHash *setup_file_hash = NULL;
2160 /* always start with reliable default values */
2161 setSetupInfoToDefaults(&setup);
2163 setup_file_hash = loadSetupFileHash(filename);
2165 if (setup_file_hash)
2167 char *player_name_new;
2169 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2170 decodeSetupFileHash(setup_file_hash);
2172 setup.direct_draw = !setup.double_buffering;
2174 freeSetupFileHash(setup_file_hash);
2176 /* needed to work around problems with fixed length strings */
2177 player_name_new = get_corrected_login_name(setup.player_name);
2178 free(setup.player_name);
2179 setup.player_name = player_name_new;
2182 Error(ERR_WARN, "using default setup values");
2187 char *filename = getSetupFilename();
2191 InitUserDataDirectory();
2193 if (!(file = fopen(filename, MODE_WRITE)))
2195 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2199 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2200 getCookie("SETUP")));
2201 fprintf(file, "\n");
2205 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2207 /* just to make things nicer :) */
2208 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2209 i == SETUP_TOKEN_GRAPHICS_SET)
2210 fprintf(file, "\n");
2212 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2217 fprintf(file, "\n");
2218 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2219 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2221 /* shortcut setup */
2222 ssi = setup.shortcut;
2223 fprintf(file, "\n");
2224 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2225 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2228 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2232 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2233 fprintf(file, "\n");
2235 sii = setup.input[pnr];
2236 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2237 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2242 fprintf(file, "\n");
2243 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2244 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2247 soi = setup.options;
2248 fprintf(file, "\n");
2249 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2250 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2254 SetFilePermissions(filename, PERMS_PRIVATE);
2257 void LoadCustomElementDescriptions()
2259 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2260 SetupFileHash *setup_file_hash;
2263 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2265 if (element_info[i].custom_description != NULL)
2267 free(element_info[i].custom_description);
2268 element_info[i].custom_description = NULL;
2272 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2275 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2277 char *token = getStringCat2(element_info[i].token_name, ".name");
2278 char *value = getHashEntry(setup_file_hash, token);
2281 element_info[i].custom_description = getStringCopy(value);
2286 freeSetupFileHash(setup_file_hash);
2289 void LoadSpecialMenuDesignSettings()
2291 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2292 SetupFileHash *setup_file_hash;
2295 /* always start with reliable default values from default config */
2296 for (i=0; image_config_vars[i].token != NULL; i++)
2297 for (j=0; image_config[j].token != NULL; j++)
2298 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2299 *image_config_vars[i].value =
2300 get_integer_from_string(image_config[j].value);
2302 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2305 /* special case: initialize with default values that may be overwritten */
2306 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2308 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2309 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2310 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2312 if (value_x != NULL)
2313 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2314 if (value_y != NULL)
2315 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2316 if (list_size != NULL)
2317 menu.list_size[i] = get_integer_from_string(list_size);
2320 /* read (and overwrite with) values that may be specified in config file */
2321 for (i=0; image_config_vars[i].token != NULL; i++)
2323 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2326 *image_config_vars[i].value = get_integer_from_string(value);
2329 freeSetupFileHash(setup_file_hash);