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 static void setLevelInfoToDefaults(struct LevelInfo *level)
54 level->file_version = FILE_VERSION_ACTUAL;
55 level->game_version = GAME_VERSION_ACTUAL;
57 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
58 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
59 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
61 level->fieldx = STD_LEV_FIELDX;
62 level->fieldy = STD_LEV_FIELDY;
64 for(x=0; x<MAX_LEV_FIELDX; x++)
65 for(y=0; y<MAX_LEV_FIELDY; y++)
66 level->field[x][y] = EL_SAND;
69 level->gems_needed = 0;
70 level->amoeba_speed = 10;
71 level->time_magic_wall = 10;
72 level->time_wheel = 10;
73 level->time_light = 10;
74 level->time_timegate = 10;
75 level->amoeba_content = EL_DIAMOND;
76 level->double_speed = FALSE;
77 level->gravity = FALSE;
78 level->em_slippery_gems = FALSE;
80 level->use_custom_template = FALSE;
82 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
83 level->name[i] = '\0';
84 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
85 level->author[i] = '\0';
87 strcpy(level->name, NAMELESS_LEVEL_NAME);
88 strcpy(level->author, ANONYMOUS_NAME);
90 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
93 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
94 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
97 level->yamyam_content[i][x][y] =
98 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
100 level->field[0][0] = EL_PLAYER_1;
101 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
103 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
105 int element = EL_CUSTOM_START + i;
107 for(j=0; j<MAX_ELEMENT_NAME_LEN + 1; j++)
108 element_info[element].description[j] = '\0';
109 if (element_info[element].custom_description != NULL)
110 strncpy(element_info[element].description,
111 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
113 strcpy(element_info[element].description,
114 element_info[element].editor_description);
116 element_info[element].use_gfx_element = FALSE;
117 element_info[element].gfx_element = EL_EMPTY_SPACE;
119 element_info[element].collect_score = 10; /* special default */
120 element_info[element].collect_count = 1; /* special default */
122 element_info[element].push_delay_fixed = 2; /* special default */
123 element_info[element].push_delay_random = 8; /* special default */
124 element_info[element].move_delay_fixed = 0;
125 element_info[element].move_delay_random = 0;
127 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
128 element_info[element].move_direction_initial = MV_NO_MOVING;
129 element_info[element].move_stepsize = TILEX / 8;
131 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
135 element_info[element].content[x][y] = EL_EMPTY_SPACE;
137 element_info[element].change.events = CE_BITMASK_DEFAULT;
138 element_info[element].change.target_element = EL_EMPTY_SPACE;
140 element_info[element].change.delay_fixed = 0;
141 element_info[element].change.delay_random = 0;
142 element_info[element].change.delay_frames = -1; /* use default */
144 element_info[element].change.trigger_element = EL_EMPTY_SPACE;
146 element_info[element].change.explode = FALSE;
147 element_info[element].change.use_content = FALSE;
148 element_info[element].change.only_complete = FALSE;
149 element_info[element].change.use_random_change = FALSE;
150 element_info[element].change.random = 0;
151 element_info[element].change.power = CP_NON_DESTRUCTIVE;
155 element_info[element].change.content[x][y] = EL_EMPTY_SPACE;
157 element_info[element].access_type = 0;
158 element_info[element].access_layer = 0;
159 element_info[element].walk_to_action = 0;
160 element_info[element].smash_targets = 0;
161 element_info[element].deadliness = 0;
162 element_info[element].consistency = 0;
163 element_info[element].change_player_action = 0;
164 element_info[element].change_collide_action = 0;
165 element_info[element].change_other_action = 0;
167 element_info[element].can_explode_by_fire = FALSE;
168 element_info[element].can_explode_smashed = FALSE;
169 element_info[element].can_explode_impact = FALSE;
171 /* start with no properties at all */
172 for (j=0; j < NUM_EP_BITFIELDS; j++)
173 Properties[element][j] = EP_BITMASK_DEFAULT;
175 element_info[element].modified_settings = FALSE;
178 BorderElement = EL_STEELWALL;
180 level->no_level_file = FALSE;
182 if (leveldir_current == NULL) /* only when dumping level */
185 /* try to determine better author name than 'anonymous' */
186 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
188 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
189 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
193 switch (LEVELCLASS(leveldir_current))
195 case LEVELCLASS_TUTORIAL:
196 strcpy(level->author, PROGRAM_AUTHOR_STRING);
199 case LEVELCLASS_CONTRIBUTION:
200 strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
201 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
204 case LEVELCLASS_USER:
205 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
206 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
210 /* keep default value */
216 static void ActivateLevelTemplate()
218 /* Currently there is no special action needed to activate the template
219 data, because 'element_info' and 'Properties' overwrite the original
220 level data, while all other variables do not change. */
223 boolean LevelFileExists(int level_nr)
225 char *filename = getLevelFilename(level_nr);
227 return (access(filename, F_OK) == 0);
230 static int checkLevelElement(int element)
232 if (element >= NUM_FILE_ELEMENTS)
234 Error(ERR_WARN, "invalid level element %d", element);
235 element = EL_CHAR_QUESTION;
237 else if (element == EL_PLAYER_OBSOLETE)
238 element = EL_PLAYER_1;
239 else if (element == EL_KEY_OBSOLETE)
245 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
247 level->file_version = getFileVersion(file);
248 level->game_version = getFileVersion(file);
253 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
257 level->fieldx = getFile8Bit(file);
258 level->fieldy = getFile8Bit(file);
260 level->time = getFile16BitBE(file);
261 level->gems_needed = getFile16BitBE(file);
263 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
264 level->name[i] = getFile8Bit(file);
265 level->name[MAX_LEVEL_NAME_LEN] = 0;
267 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
268 level->score[i] = getFile8Bit(file);
270 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
271 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
274 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
276 level->amoeba_speed = getFile8Bit(file);
277 level->time_magic_wall = getFile8Bit(file);
278 level->time_wheel = getFile8Bit(file);
279 level->amoeba_content = checkLevelElement(getFile8Bit(file));
280 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
281 level->gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
282 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
283 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
285 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
287 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
292 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
296 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
297 level->author[i] = getFile8Bit(file);
298 level->author[MAX_LEVEL_NAME_LEN] = 0;
303 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
306 int chunk_size_expected = level->fieldx * level->fieldy;
308 /* Note: "chunk_size" was wrong before version 2.0 when elements are
309 stored with 16-bit encoding (and should be twice as big then).
310 Even worse, playfield data was stored 16-bit when only yamyam content
311 contained 16-bit elements and vice versa. */
313 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
314 chunk_size_expected *= 2;
316 if (chunk_size_expected != chunk_size)
318 ReadUnusedBytesFromFile(file, chunk_size);
319 return chunk_size_expected;
322 for(y=0; y<level->fieldy; y++)
323 for(x=0; x<level->fieldx; x++)
325 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
330 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
334 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
335 int chunk_size_expected = header_size + content_size;
337 /* Note: "chunk_size" was wrong before version 2.0 when elements are
338 stored with 16-bit encoding (and should be twice as big then).
339 Even worse, playfield data was stored 16-bit when only yamyam content
340 contained 16-bit elements and vice versa. */
342 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
343 chunk_size_expected += content_size;
345 if (chunk_size_expected != chunk_size)
347 ReadUnusedBytesFromFile(file, chunk_size);
348 return chunk_size_expected;
352 level->num_yamyam_contents = getFile8Bit(file);
356 /* correct invalid number of content fields -- should never happen */
357 if (level->num_yamyam_contents < 1 ||
358 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
359 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
361 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
364 level->yamyam_content[i][x][y] =
365 checkLevelElement(level->encoding_16bit_field ?
366 getFile16BitBE(file) : getFile8Bit(file));
370 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
374 int num_contents, content_xsize, content_ysize;
375 int content_array[MAX_ELEMENT_CONTENTS][3][3];
377 element = checkLevelElement(getFile16BitBE(file));
378 num_contents = getFile8Bit(file);
379 content_xsize = getFile8Bit(file);
380 content_ysize = getFile8Bit(file);
381 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
383 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
386 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
388 /* correct invalid number of content fields -- should never happen */
389 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
390 num_contents = STD_ELEMENT_CONTENTS;
392 if (element == EL_YAMYAM)
394 level->num_yamyam_contents = num_contents;
396 for(i=0; i<num_contents; i++)
399 level->yamyam_content[i][x][y] = content_array[i][x][y];
401 else if (element == EL_BD_AMOEBA)
403 level->amoeba_content = content_array[0][0][0];
407 Error(ERR_WARN, "cannot load content for element '%d'", element);
413 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
415 int num_changed_custom_elements = getFile16BitBE(file);
416 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
419 if (chunk_size_expected != chunk_size)
421 ReadUnusedBytesFromFile(file, chunk_size - 2);
422 return chunk_size_expected;
425 for (i=0; i < num_changed_custom_elements; i++)
427 int element = getFile16BitBE(file);
428 int properties = getFile32BitBE(file);
430 if (IS_CUSTOM_ELEMENT(element))
431 Properties[element][EP_BITFIELD_BASE] = properties;
433 Error(ERR_WARN, "invalid custom element number %d", element);
439 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
441 int num_changed_custom_elements = getFile16BitBE(file);
442 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
445 if (chunk_size_expected != chunk_size)
447 ReadUnusedBytesFromFile(file, chunk_size - 2);
448 return chunk_size_expected;
451 for (i=0; i < num_changed_custom_elements; i++)
453 int element = getFile16BitBE(file);
454 int custom_target_element = getFile16BitBE(file);
456 if (IS_CUSTOM_ELEMENT(element))
457 element_info[element].change.target_element = custom_target_element;
459 Error(ERR_WARN, "invalid custom element number %d", element);
465 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
467 int num_changed_custom_elements = getFile16BitBE(file);
468 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
471 if (chunk_size_expected != chunk_size)
473 ReadUnusedBytesFromFile(file, chunk_size - 2);
474 return chunk_size_expected;
477 for (i=0; i < num_changed_custom_elements; i++)
479 int element = getFile16BitBE(file);
481 if (!IS_CUSTOM_ELEMENT(element))
483 Error(ERR_WARN, "invalid custom element number %d", element);
485 element = EL_DEFAULT; /* dummy element used for artwork config */
488 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
489 element_info[element].description[j] = getFile8Bit(file);
490 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
492 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
494 /* some free bytes for future properties and padding */
495 ReadUnusedBytesFromFile(file, 7);
497 element_info[element].use_gfx_element = getFile8Bit(file);
498 element_info[element].gfx_element =
499 checkLevelElement(getFile16BitBE(file));
501 element_info[element].collect_score = getFile8Bit(file);
502 element_info[element].collect_count = getFile8Bit(file);
504 element_info[element].push_delay_fixed = getFile16BitBE(file);
505 element_info[element].push_delay_random = getFile16BitBE(file);
506 element_info[element].move_delay_fixed = getFile16BitBE(file);
507 element_info[element].move_delay_random = getFile16BitBE(file);
509 element_info[element].move_pattern = getFile16BitBE(file);
510 element_info[element].move_direction_initial = getFile8Bit(file);
511 element_info[element].move_stepsize = getFile8Bit(file);
515 element_info[element].content[x][y] =
516 checkLevelElement(getFile16BitBE(file));
518 element_info[element].change.events = getFile32BitBE(file);
520 element_info[element].change.target_element =
521 checkLevelElement(getFile16BitBE(file));
523 element_info[element].change.delay_fixed = getFile16BitBE(file);
524 element_info[element].change.delay_random = getFile16BitBE(file);
525 element_info[element].change.delay_frames = getFile16BitBE(file);
527 element_info[element].change.trigger_element =
528 checkLevelElement(getFile16BitBE(file));
530 element_info[element].change.explode = getFile8Bit(file);
531 element_info[element].change.use_content = getFile8Bit(file);
532 element_info[element].change.only_complete = getFile8Bit(file);
533 element_info[element].change.use_random_change = getFile8Bit(file);
535 element_info[element].change.random = getFile8Bit(file);
536 element_info[element].change.power = getFile8Bit(file);
540 element_info[element].change.content[x][y] =
541 checkLevelElement(getFile16BitBE(file));
543 element_info[element].slippery_type = getFile8Bit(file);
545 /* some free bytes for future properties and padding */
546 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
548 /* mark that this custom element has been modified */
549 element_info[element].modified_settings = TRUE;
555 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
557 char cookie[MAX_LINE_LEN];
558 char chunk_name[CHUNK_ID_LEN + 1];
562 /* always start with reliable default values */
563 setLevelInfoToDefaults(level);
565 if (!(file = fopen(filename, MODE_READ)))
567 level->no_level_file = TRUE;
569 if (level != &level_template)
570 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
575 getFileChunkBE(file, chunk_name, NULL);
576 if (strcmp(chunk_name, "RND1") == 0)
578 getFile32BitBE(file); /* not used */
580 getFileChunkBE(file, chunk_name, NULL);
581 if (strcmp(chunk_name, "CAVE") != 0)
583 Error(ERR_WARN, "unknown format of level file '%s'", filename);
588 else /* check for pre-2.0 file format with cookie string */
590 strcpy(cookie, chunk_name);
591 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
592 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
593 cookie[strlen(cookie) - 1] = '\0';
595 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
597 Error(ERR_WARN, "unknown format of level file '%s'", filename);
602 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
604 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
609 /* pre-2.0 level files have no game version, so use file version here */
610 level->game_version = level->file_version;
613 if (level->file_version < FILE_VERSION_1_2)
615 /* level files from versions before 1.2.0 without chunk structure */
616 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
617 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
625 int (*loader)(FILE *, int, struct LevelInfo *);
629 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
630 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
631 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
632 { "BODY", -1, LoadLevel_BODY },
633 { "CONT", -1, LoadLevel_CONT },
634 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
635 { "CUS1", -1, LoadLevel_CUS1 },
636 { "CUS2", -1, LoadLevel_CUS2 },
637 { "CUS3", -1, LoadLevel_CUS3 },
641 while (getFileChunkBE(file, chunk_name, &chunk_size))
645 while (chunk_info[i].name != NULL &&
646 strcmp(chunk_name, chunk_info[i].name) != 0)
649 if (chunk_info[i].name == NULL)
651 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
652 chunk_name, filename);
653 ReadUnusedBytesFromFile(file, chunk_size);
655 else if (chunk_info[i].size != -1 &&
656 chunk_info[i].size != chunk_size)
658 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
659 chunk_size, chunk_name, filename);
660 ReadUnusedBytesFromFile(file, chunk_size);
664 /* call function to load this level chunk */
665 int chunk_size_expected =
666 (chunk_info[i].loader)(file, chunk_size, level);
668 /* the size of some chunks cannot be checked before reading other
669 chunks first (like "HEAD" and "BODY") that contain some header
670 information, so check them here */
671 if (chunk_size_expected != chunk_size)
673 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
674 chunk_size, chunk_name, filename);
683 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
687 if (leveldir_current == NULL) /* only when dumping level */
690 /* determine correct game engine version of current level */
691 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
692 IS_LEVELCLASS_USER(leveldir_current))
695 printf("\n::: This level is private or contributed: '%s'\n", filename);
698 /* For user contributed and private levels, use the version of
699 the game engine the levels were created for.
700 Since 2.0.1, the game engine version is now directly stored
701 in the level file (chunk "VERS"), so there is no need anymore
702 to set the game version from the file version (except for old,
703 pre-2.0 levels, where the game version is still taken from the
704 file format version used to store the level -- see above). */
706 /* do some special adjustments to support older level versions */
707 if (level->file_version == FILE_VERSION_1_0)
709 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
710 Error(ERR_WARN, "using high speed movement for player");
712 /* player was faster than monsters in (pre-)1.0 levels */
713 level->double_speed = TRUE;
716 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
717 if (level->game_version == VERSION_IDENT(2,0,1))
718 level->em_slippery_gems = TRUE;
723 printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
724 leveldir_current->sort_priority, filename);
727 /* Always use the latest version of the game engine for all but
728 user contributed and private levels; this allows for actual
729 corrections in the game engine to take effect for existing,
730 converted levels (from "classic" or other existing games) to
731 make the game emulation more accurate, while (hopefully) not
732 breaking existing levels created from other players. */
734 level->game_version = GAME_VERSION_ACTUAL;
736 /* Set special EM style gems behaviour: EM style gems slip down from
737 normal, steel and growing wall. As this is a more fundamental change,
738 it seems better to set the default behaviour to "off" (as it is more
739 natural) and make it configurable in the level editor (as a property
740 of gem style elements). Already existing converted levels (neither
741 private nor contributed levels) are changed to the new behaviour. */
743 if (level->file_version < FILE_VERSION_2_0)
744 level->em_slippery_gems = TRUE;
747 /* map elements that have changed in newer versions */
748 for(y=0; y<level->fieldy; y++)
750 for(x=0; x<level->fieldx; x++)
752 int element = level->field[x][y];
754 if (level->game_version <= VERSION_IDENT(2,2,0))
756 /* map game font elements */
757 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
758 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
759 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
760 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
763 if (level->game_version < VERSION_IDENT(3,0,0))
765 /* map Supaplex gravity tube elements */
766 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
767 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
768 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
769 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
773 level->field[x][y] = element;
777 /* map custom element change events that have changed in newer versions
778 (these following values have accidentally changed in version 3.0.1) */
779 if (level->game_version <= VERSION_IDENT(3,0,0))
781 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
783 int element = EL_CUSTOM_START + i;
785 /* order of checking events to be mapped is important */
786 for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
788 if (HAS_CHANGE_EVENT(element, j - 2))
790 SET_CHANGE_EVENT(element, j - 2, FALSE);
791 SET_CHANGE_EVENT(element, j, TRUE);
795 /* order of checking events to be mapped is important */
796 for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
798 if (HAS_CHANGE_EVENT(element, j - 1))
800 SET_CHANGE_EVENT(element, j - 1, FALSE);
801 SET_CHANGE_EVENT(element, j, TRUE);
807 /* copy elements to runtime playfield array */
808 for(x=0; x<MAX_LEV_FIELDX; x++)
809 for(y=0; y<MAX_LEV_FIELDY; y++)
810 Feld[x][y] = level->field[x][y];
812 /* initialize level size variables for faster access */
813 lev_fieldx = level->fieldx;
814 lev_fieldy = level->fieldy;
816 /* determine border element for this level */
819 /* initialize element properties for level editor etc. */
820 InitElementPropertiesEngine(level->game_version);
823 void LoadLevelTemplate(int level_nr)
825 char *filename = getLevelFilename(level_nr);
827 LoadLevelFromFilename(&level_template, filename);
829 ActivateLevelTemplate();
832 void LoadLevel(int level_nr)
834 char *filename = getLevelFilename(level_nr);
836 LoadLevelFromFilename(&level, filename);
838 if (level.use_custom_template)
839 LoadLevelTemplate(-1);
841 LoadLevel_InitLevel(&level, filename);
844 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
846 putFileVersion(file, level->file_version);
847 putFileVersion(file, level->game_version);
850 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
854 putFile8Bit(file, level->fieldx);
855 putFile8Bit(file, level->fieldy);
857 putFile16BitBE(file, level->time);
858 putFile16BitBE(file, level->gems_needed);
860 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
861 putFile8Bit(file, level->name[i]);
863 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
864 putFile8Bit(file, level->score[i]);
866 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
869 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
870 level->yamyam_content[i][x][y]));
871 putFile8Bit(file, level->amoeba_speed);
872 putFile8Bit(file, level->time_magic_wall);
873 putFile8Bit(file, level->time_wheel);
874 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
875 level->amoeba_content));
876 putFile8Bit(file, (level->double_speed ? 1 : 0));
877 putFile8Bit(file, (level->gravity ? 1 : 0));
878 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
879 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
881 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
883 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
886 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
890 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
891 putFile8Bit(file, level->author[i]);
894 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
898 for(y=0; y<level->fieldy; y++)
899 for(x=0; x<level->fieldx; x++)
900 if (level->encoding_16bit_field)
901 putFile16BitBE(file, level->field[x][y]);
903 putFile8Bit(file, level->field[x][y]);
907 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
911 putFile8Bit(file, EL_YAMYAM);
912 putFile8Bit(file, level->num_yamyam_contents);
913 putFile8Bit(file, 0);
914 putFile8Bit(file, 0);
916 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
919 if (level->encoding_16bit_field)
920 putFile16BitBE(file, level->yamyam_content[i][x][y]);
922 putFile8Bit(file, level->yamyam_content[i][x][y]);
926 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
929 int num_contents, content_xsize, content_ysize;
930 int content_array[MAX_ELEMENT_CONTENTS][3][3];
932 if (element == EL_YAMYAM)
934 num_contents = level->num_yamyam_contents;
938 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
941 content_array[i][x][y] = level->yamyam_content[i][x][y];
943 else if (element == EL_BD_AMOEBA)
949 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
952 content_array[i][x][y] = EL_EMPTY;
953 content_array[0][0][0] = level->amoeba_content;
957 /* chunk header already written -- write empty chunk data */
958 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
960 Error(ERR_WARN, "cannot save content for element '%d'", element);
964 putFile16BitBE(file, element);
965 putFile8Bit(file, num_contents);
966 putFile8Bit(file, content_xsize);
967 putFile8Bit(file, content_ysize);
969 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
971 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
974 putFile16BitBE(file, content_array[i][x][y]);
978 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
979 int num_changed_custom_elements)
983 putFile16BitBE(file, num_changed_custom_elements);
985 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
987 int element = EL_CUSTOM_START + i;
989 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
991 if (check < num_changed_custom_elements)
993 putFile16BitBE(file, element);
994 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1001 if (check != num_changed_custom_elements) /* should not happen */
1002 Error(ERR_WARN, "inconsistent number of custom element properties");
1007 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1008 int num_changed_custom_elements)
1012 putFile16BitBE(file, num_changed_custom_elements);
1014 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1016 int element = EL_CUSTOM_START + i;
1018 if (element_info[element].change.target_element != EL_EMPTY_SPACE)
1020 if (check < num_changed_custom_elements)
1022 putFile16BitBE(file, element);
1023 putFile16BitBE(file, element_info[element].change.target_element);
1030 if (check != num_changed_custom_elements) /* should not happen */
1031 Error(ERR_WARN, "inconsistent number of custom target elements");
1035 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1036 int num_changed_custom_elements)
1038 int i, j, x, y, check = 0;
1040 putFile16BitBE(file, num_changed_custom_elements);
1042 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1044 int element = EL_CUSTOM_START + i;
1046 if (element_info[element].modified_settings)
1048 if (check < num_changed_custom_elements)
1050 putFile16BitBE(file, element);
1052 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1053 putFile8Bit(file, element_info[element].description[j]);
1055 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1057 /* some free bytes for future properties and padding */
1058 WriteUnusedBytesToFile(file, 7);
1060 putFile8Bit(file, element_info[element].use_gfx_element);
1061 putFile16BitBE(file, element_info[element].gfx_element);
1063 putFile8Bit(file, element_info[element].collect_score);
1064 putFile8Bit(file, element_info[element].collect_count);
1066 putFile16BitBE(file, element_info[element].push_delay_fixed);
1067 putFile16BitBE(file, element_info[element].push_delay_random);
1068 putFile16BitBE(file, element_info[element].move_delay_fixed);
1069 putFile16BitBE(file, element_info[element].move_delay_random);
1071 putFile16BitBE(file, element_info[element].move_pattern);
1072 putFile8Bit(file, element_info[element].move_direction_initial);
1073 putFile8Bit(file, element_info[element].move_stepsize);
1077 putFile16BitBE(file, element_info[element].content[x][y]);
1079 putFile32BitBE(file, element_info[element].change.events);
1081 putFile16BitBE(file, element_info[element].change.target_element);
1083 putFile16BitBE(file, element_info[element].change.delay_fixed);
1084 putFile16BitBE(file, element_info[element].change.delay_random);
1085 putFile16BitBE(file, element_info[element].change.delay_frames);
1087 putFile16BitBE(file, element_info[element].change.trigger_element);
1089 putFile8Bit(file, element_info[element].change.explode);
1090 putFile8Bit(file, element_info[element].change.use_content);
1091 putFile8Bit(file, element_info[element].change.only_complete);
1092 putFile8Bit(file, element_info[element].change.use_random_change);
1094 putFile8Bit(file, element_info[element].change.random);
1095 putFile8Bit(file, element_info[element].change.power);
1099 putFile16BitBE(file, element_info[element].change.content[x][y]);
1101 putFile8Bit(file, element_info[element].slippery_type);
1103 /* some free bytes for future properties and padding */
1104 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1111 if (check != num_changed_custom_elements) /* should not happen */
1112 Error(ERR_WARN, "inconsistent number of custom element properties");
1115 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1117 int body_chunk_size;
1118 int num_changed_custom_elements = 0;
1119 int level_chunk_CUS3_size;
1123 if (!(file = fopen(filename, MODE_WRITE)))
1125 Error(ERR_WARN, "cannot save level file '%s'", filename);
1129 level->file_version = FILE_VERSION_ACTUAL;
1130 level->game_version = GAME_VERSION_ACTUAL;
1132 /* check level field for 16-bit elements */
1133 level->encoding_16bit_field = FALSE;
1134 for(y=0; y<level->fieldy; y++)
1135 for(x=0; x<level->fieldx; x++)
1136 if (level->field[x][y] > 255)
1137 level->encoding_16bit_field = TRUE;
1139 /* check yamyam content for 16-bit elements */
1140 level->encoding_16bit_yamyam = FALSE;
1141 for(i=0; i<level->num_yamyam_contents; i++)
1144 if (level->yamyam_content[i][x][y] > 255)
1145 level->encoding_16bit_yamyam = TRUE;
1147 /* check amoeba content for 16-bit elements */
1148 level->encoding_16bit_amoeba = FALSE;
1149 if (level->amoeba_content > 255)
1150 level->encoding_16bit_amoeba = TRUE;
1152 /* calculate size of "BODY" chunk */
1154 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1156 /* check for non-standard custom elements and calculate "CUS3" chunk size */
1157 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1158 if (element_info[EL_CUSTOM_START + i].modified_settings)
1159 num_changed_custom_elements++;
1160 level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1162 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1163 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1165 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1166 SaveLevel_VERS(file, level);
1168 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1169 SaveLevel_HEAD(file, level);
1171 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1172 SaveLevel_AUTH(file, level);
1174 putFileChunkBE(file, "BODY", body_chunk_size);
1175 SaveLevel_BODY(file, level);
1177 if (level->encoding_16bit_yamyam ||
1178 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1180 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1181 SaveLevel_CNT2(file, level, EL_YAMYAM);
1184 if (level->encoding_16bit_amoeba)
1186 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1187 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1190 if (num_changed_custom_elements > 0 && !level->use_custom_template)
1192 putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1193 SaveLevel_CUS3(file, level, num_changed_custom_elements);
1198 SetFilePermissions(filename, PERMS_PRIVATE);
1201 void SaveLevel(int level_nr)
1203 char *filename = getLevelFilename(level_nr);
1205 SaveLevelFromFilename(&level, filename);
1208 void SaveLevelTemplate()
1210 char *filename = getLevelFilename(-1);
1212 SaveLevelFromFilename(&level, filename);
1215 void DumpLevel(struct LevelInfo *level)
1217 printf_line("-", 79);
1218 printf("Level xxx (file version %08d, game version %08d)\n",
1219 level->file_version, level->game_version);
1220 printf_line("-", 79);
1222 printf("Level Author: '%s'\n", level->author);
1223 printf("Level Title: '%s'\n", level->name);
1225 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1227 printf("Level Time: %d seconds\n", level->time);
1228 printf("Gems needed: %d\n", level->gems_needed);
1230 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1231 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1232 printf("Time for Light: %d seconds\n", level->time_light);
1233 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1235 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1237 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1238 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1239 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1241 printf_line("-", 79);
1245 /* ========================================================================= */
1246 /* tape file functions */
1247 /* ========================================================================= */
1249 static void setTapeInfoToDefaults()
1253 /* always start with reliable default values (empty tape) */
1256 /* default values (also for pre-1.2 tapes) with only the first player */
1257 tape.player_participates[0] = TRUE;
1258 for(i=1; i<MAX_PLAYERS; i++)
1259 tape.player_participates[i] = FALSE;
1261 /* at least one (default: the first) player participates in every tape */
1262 tape.num_participating_players = 1;
1264 tape.level_nr = level_nr;
1266 tape.changed = FALSE;
1268 tape.recording = FALSE;
1269 tape.playing = FALSE;
1270 tape.pausing = FALSE;
1273 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1275 tape->file_version = getFileVersion(file);
1276 tape->game_version = getFileVersion(file);
1281 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1285 tape->random_seed = getFile32BitBE(file);
1286 tape->date = getFile32BitBE(file);
1287 tape->length = getFile32BitBE(file);
1289 /* read header fields that are new since version 1.2 */
1290 if (tape->file_version >= FILE_VERSION_1_2)
1292 byte store_participating_players = getFile8Bit(file);
1295 /* since version 1.2, tapes store which players participate in the tape */
1296 tape->num_participating_players = 0;
1297 for(i=0; i<MAX_PLAYERS; i++)
1299 tape->player_participates[i] = FALSE;
1301 if (store_participating_players & (1 << i))
1303 tape->player_participates[i] = TRUE;
1304 tape->num_participating_players++;
1308 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1310 engine_version = getFileVersion(file);
1311 if (engine_version > 0)
1312 tape->engine_version = engine_version;
1314 tape->engine_version = tape->game_version;
1320 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1322 int level_identifier_size;
1325 level_identifier_size = getFile16BitBE(file);
1327 tape->level_identifier =
1328 checked_realloc(tape->level_identifier, level_identifier_size);
1330 for(i=0; i < level_identifier_size; i++)
1331 tape->level_identifier[i] = getFile8Bit(file);
1333 tape->level_nr = getFile16BitBE(file);
1335 chunk_size = 2 + level_identifier_size + 2;
1340 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1343 int chunk_size_expected =
1344 (tape->num_participating_players + 1) * tape->length;
1346 if (chunk_size_expected != chunk_size)
1348 ReadUnusedBytesFromFile(file, chunk_size);
1349 return chunk_size_expected;
1352 for(i=0; i<tape->length; i++)
1354 if (i >= MAX_TAPELEN)
1357 for(j=0; j<MAX_PLAYERS; j++)
1359 tape->pos[i].action[j] = MV_NO_MOVING;
1361 if (tape->player_participates[j])
1362 tape->pos[i].action[j] = getFile8Bit(file);
1365 tape->pos[i].delay = getFile8Bit(file);
1367 if (tape->file_version == FILE_VERSION_1_0)
1369 /* eliminate possible diagonal moves in old tapes */
1370 /* this is only for backward compatibility */
1372 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1373 byte action = tape->pos[i].action[0];
1374 int k, num_moves = 0;
1378 if (action & joy_dir[k])
1380 tape->pos[i + num_moves].action[0] = joy_dir[k];
1382 tape->pos[i + num_moves].delay = 0;
1391 tape->length += num_moves;
1394 else if (tape->file_version < FILE_VERSION_2_0)
1396 /* convert pre-2.0 tapes to new tape format */
1398 if (tape->pos[i].delay > 1)
1401 tape->pos[i + 1] = tape->pos[i];
1402 tape->pos[i + 1].delay = 1;
1405 for(j=0; j<MAX_PLAYERS; j++)
1406 tape->pos[i].action[j] = MV_NO_MOVING;
1407 tape->pos[i].delay--;
1418 if (i != tape->length)
1419 chunk_size = (tape->num_participating_players + 1) * i;
1424 void LoadTapeFromFilename(char *filename)
1426 char cookie[MAX_LINE_LEN];
1427 char chunk_name[CHUNK_ID_LEN + 1];
1431 /* always start with reliable default values */
1432 setTapeInfoToDefaults();
1434 if (!(file = fopen(filename, MODE_READ)))
1437 getFileChunkBE(file, chunk_name, NULL);
1438 if (strcmp(chunk_name, "RND1") == 0)
1440 getFile32BitBE(file); /* not used */
1442 getFileChunkBE(file, chunk_name, NULL);
1443 if (strcmp(chunk_name, "TAPE") != 0)
1445 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1450 else /* check for pre-2.0 file format with cookie string */
1452 strcpy(cookie, chunk_name);
1453 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1454 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1455 cookie[strlen(cookie) - 1] = '\0';
1457 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1459 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1464 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1466 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1471 /* pre-2.0 tape files have no game version, so use file version here */
1472 tape.game_version = tape.file_version;
1475 if (tape.file_version < FILE_VERSION_1_2)
1477 /* tape files from versions before 1.2.0 without chunk structure */
1478 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1479 LoadTape_BODY(file, 2 * tape.length, &tape);
1487 int (*loader)(FILE *, int, struct TapeInfo *);
1491 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1492 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1493 { "INFO", -1, LoadTape_INFO },
1494 { "BODY", -1, LoadTape_BODY },
1498 while (getFileChunkBE(file, chunk_name, &chunk_size))
1502 while (chunk_info[i].name != NULL &&
1503 strcmp(chunk_name, chunk_info[i].name) != 0)
1506 if (chunk_info[i].name == NULL)
1508 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1509 chunk_name, filename);
1510 ReadUnusedBytesFromFile(file, chunk_size);
1512 else if (chunk_info[i].size != -1 &&
1513 chunk_info[i].size != chunk_size)
1515 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1516 chunk_size, chunk_name, filename);
1517 ReadUnusedBytesFromFile(file, chunk_size);
1521 /* call function to load this tape chunk */
1522 int chunk_size_expected =
1523 (chunk_info[i].loader)(file, chunk_size, &tape);
1525 /* the size of some chunks cannot be checked before reading other
1526 chunks first (like "HEAD" and "BODY") that contain some header
1527 information, so check them here */
1528 if (chunk_size_expected != chunk_size)
1530 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1531 chunk_size, chunk_name, filename);
1539 tape.length_seconds = GetTapeLength();
1542 printf("tape game version: %d\n", tape.game_version);
1543 printf("tape engine version: %d\n", tape.engine_version);
1547 void LoadTape(int level_nr)
1549 char *filename = getTapeFilename(level_nr);
1551 LoadTapeFromFilename(filename);
1554 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1556 putFileVersion(file, tape->file_version);
1557 putFileVersion(file, tape->game_version);
1560 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1563 byte store_participating_players = 0;
1565 /* set bits for participating players for compact storage */
1566 for(i=0; i<MAX_PLAYERS; i++)
1567 if (tape->player_participates[i])
1568 store_participating_players |= (1 << i);
1570 putFile32BitBE(file, tape->random_seed);
1571 putFile32BitBE(file, tape->date);
1572 putFile32BitBE(file, tape->length);
1574 putFile8Bit(file, store_participating_players);
1576 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1577 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1579 putFileVersion(file, tape->engine_version);
1582 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1584 int level_identifier_size = strlen(tape->level_identifier) + 1;
1587 putFile16BitBE(file, level_identifier_size);
1589 for(i=0; i < level_identifier_size; i++)
1590 putFile8Bit(file, tape->level_identifier[i]);
1592 putFile16BitBE(file, tape->level_nr);
1595 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1599 for(i=0; i<tape->length; i++)
1601 for(j=0; j<MAX_PLAYERS; j++)
1602 if (tape->player_participates[j])
1603 putFile8Bit(file, tape->pos[i].action[j]);
1605 putFile8Bit(file, tape->pos[i].delay);
1609 void SaveTape(int level_nr)
1611 char *filename = getTapeFilename(level_nr);
1613 boolean new_tape = TRUE;
1614 int num_participating_players = 0;
1615 int info_chunk_size;
1616 int body_chunk_size;
1619 InitTapeDirectory(leveldir_current->filename);
1621 /* if a tape still exists, ask to overwrite it */
1622 if (access(filename, F_OK) == 0)
1625 if (!Request("Replace old tape ?", REQ_ASK))
1629 if (!(file = fopen(filename, MODE_WRITE)))
1631 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1635 tape.file_version = FILE_VERSION_ACTUAL;
1636 tape.game_version = GAME_VERSION_ACTUAL;
1638 /* count number of participating players */
1639 for(i=0; i<MAX_PLAYERS; i++)
1640 if (tape.player_participates[i])
1641 num_participating_players++;
1643 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1644 body_chunk_size = (num_participating_players + 1) * tape.length;
1646 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1647 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1649 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1650 SaveTape_VERS(file, &tape);
1652 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1653 SaveTape_HEAD(file, &tape);
1655 putFileChunkBE(file, "INFO", info_chunk_size);
1656 SaveTape_INFO(file, &tape);
1658 putFileChunkBE(file, "BODY", body_chunk_size);
1659 SaveTape_BODY(file, &tape);
1663 SetFilePermissions(filename, PERMS_PRIVATE);
1665 tape.changed = FALSE;
1668 Request("tape saved !", REQ_CONFIRM);
1671 void DumpTape(struct TapeInfo *tape)
1675 if (TAPE_IS_EMPTY(*tape))
1677 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1681 printf_line("-", 79);
1682 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1683 tape->level_nr, tape->file_version, tape->game_version);
1684 printf("Level series identifier: '%s'\n", tape->level_identifier);
1685 printf_line("-", 79);
1687 for(i=0; i<tape->length; i++)
1689 if (i >= MAX_TAPELEN)
1692 printf("%03d: ", i);
1694 for(j=0; j<MAX_PLAYERS; j++)
1696 if (tape->player_participates[j])
1698 int action = tape->pos[i].action[j];
1700 printf("%d:%02x ", j, action);
1701 printf("[%c%c%c%c|%c%c] - ",
1702 (action & JOY_LEFT ? '<' : ' '),
1703 (action & JOY_RIGHT ? '>' : ' '),
1704 (action & JOY_UP ? '^' : ' '),
1705 (action & JOY_DOWN ? 'v' : ' '),
1706 (action & JOY_BUTTON_1 ? '1' : ' '),
1707 (action & JOY_BUTTON_2 ? '2' : ' '));
1711 printf("(%03d)\n", tape->pos[i].delay);
1714 printf_line("-", 79);
1718 /* ========================================================================= */
1719 /* score file functions */
1720 /* ========================================================================= */
1722 void LoadScore(int level_nr)
1725 char *filename = getScoreFilename(level_nr);
1726 char cookie[MAX_LINE_LEN];
1727 char line[MAX_LINE_LEN];
1731 /* always start with reliable default values */
1732 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1734 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1735 highscore[i].Score = 0;
1738 if (!(file = fopen(filename, MODE_READ)))
1741 /* check file identifier */
1742 fgets(cookie, MAX_LINE_LEN, file);
1743 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1744 cookie[strlen(cookie) - 1] = '\0';
1746 if (!checkCookieString(cookie, SCORE_COOKIE))
1748 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1753 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1755 fscanf(file, "%d", &highscore[i].Score);
1756 fgets(line, MAX_LINE_LEN, file);
1758 if (line[strlen(line) - 1] == '\n')
1759 line[strlen(line) - 1] = '\0';
1761 for (line_ptr = line; *line_ptr; line_ptr++)
1763 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1765 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1766 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1775 void SaveScore(int level_nr)
1778 char *filename = getScoreFilename(level_nr);
1781 InitScoreDirectory(leveldir_current->filename);
1783 if (!(file = fopen(filename, MODE_WRITE)))
1785 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1789 fprintf(file, "%s\n\n", SCORE_COOKIE);
1791 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1792 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1796 SetFilePermissions(filename, PERMS_PUBLIC);
1800 /* ========================================================================= */
1801 /* setup file functions */
1802 /* ========================================================================= */
1804 #define TOKEN_STR_PLAYER_PREFIX "player_"
1807 #define SETUP_TOKEN_PLAYER_NAME 0
1808 #define SETUP_TOKEN_SOUND 1
1809 #define SETUP_TOKEN_SOUND_LOOPS 2
1810 #define SETUP_TOKEN_SOUND_MUSIC 3
1811 #define SETUP_TOKEN_SOUND_SIMPLE 4
1812 #define SETUP_TOKEN_TOONS 5
1813 #define SETUP_TOKEN_SCROLL_DELAY 6
1814 #define SETUP_TOKEN_SOFT_SCROLLING 7
1815 #define SETUP_TOKEN_FADING 8
1816 #define SETUP_TOKEN_AUTORECORD 9
1817 #define SETUP_TOKEN_QUICK_DOORS 10
1818 #define SETUP_TOKEN_TEAM_MODE 11
1819 #define SETUP_TOKEN_HANDICAP 12
1820 #define SETUP_TOKEN_TIME_LIMIT 13
1821 #define SETUP_TOKEN_FULLSCREEN 14
1822 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1823 #define SETUP_TOKEN_GRAPHICS_SET 16
1824 #define SETUP_TOKEN_SOUNDS_SET 17
1825 #define SETUP_TOKEN_MUSIC_SET 18
1826 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1827 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1828 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1830 #define NUM_GLOBAL_SETUP_TOKENS 22
1833 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1834 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1835 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1836 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1837 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1838 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1839 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1840 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1841 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1843 #define NUM_EDITOR_SETUP_TOKENS 9
1845 /* shortcut setup */
1846 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1847 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1848 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1850 #define NUM_SHORTCUT_SETUP_TOKENS 3
1853 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1854 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1855 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1856 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1857 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1858 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1859 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1860 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1861 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1862 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1863 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1864 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1865 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1866 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1867 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1868 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1870 #define NUM_PLAYER_SETUP_TOKENS 16
1873 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1874 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1876 #define NUM_SYSTEM_SETUP_TOKENS 2
1879 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1881 #define NUM_OPTIONS_SETUP_TOKENS 1
1884 static struct SetupInfo si;
1885 static struct SetupEditorInfo sei;
1886 static struct SetupShortcutInfo ssi;
1887 static struct SetupInputInfo sii;
1888 static struct SetupSystemInfo syi;
1889 static struct OptionInfo soi;
1891 static struct TokenInfo global_setup_tokens[] =
1893 { TYPE_STRING, &si.player_name, "player_name" },
1894 { TYPE_SWITCH, &si.sound, "sound" },
1895 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1896 { TYPE_SWITCH, &si.sound_music, "background_music" },
1897 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1898 { TYPE_SWITCH, &si.toons, "toons" },
1899 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1900 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1901 { TYPE_SWITCH, &si.fading, "screen_fading" },
1902 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1903 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1904 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1905 { TYPE_SWITCH, &si.handicap, "handicap" },
1906 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1907 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1908 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1909 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1910 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1911 { TYPE_STRING, &si.music_set, "music_set" },
1912 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1913 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1914 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1917 static struct TokenInfo editor_setup_tokens[] =
1919 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1920 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1921 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1922 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1923 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1924 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1925 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1926 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1927 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1928 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
1931 static struct TokenInfo shortcut_setup_tokens[] =
1933 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1934 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1935 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1938 static struct TokenInfo player_setup_tokens[] =
1940 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1941 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1942 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1943 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1944 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1945 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1946 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1947 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1948 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1949 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1950 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1951 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1952 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1953 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1954 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1955 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1958 static struct TokenInfo system_setup_tokens[] =
1960 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1961 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1964 static struct TokenInfo options_setup_tokens[] =
1966 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1969 static char *get_corrected_login_name(char *login_name)
1971 /* needed because player name must be a fixed length string */
1972 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1974 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1975 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1977 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1978 if (strchr(login_name_new, ' '))
1979 *strchr(login_name_new, ' ') = '\0';
1981 return login_name_new;
1984 static void setSetupInfoToDefaults(struct SetupInfo *si)
1988 si->player_name = get_corrected_login_name(getLoginName());
1991 si->sound_loops = TRUE;
1992 si->sound_music = TRUE;
1993 si->sound_simple = TRUE;
1995 si->double_buffering = TRUE;
1996 si->direct_draw = !si->double_buffering;
1997 si->scroll_delay = TRUE;
1998 si->soft_scrolling = TRUE;
2000 si->autorecord = TRUE;
2001 si->quick_doors = FALSE;
2002 si->team_mode = FALSE;
2003 si->handicap = TRUE;
2004 si->time_limit = TRUE;
2005 si->fullscreen = FALSE;
2006 si->ask_on_escape = TRUE;
2008 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2009 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2010 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2011 si->override_level_graphics = FALSE;
2012 si->override_level_sounds = FALSE;
2013 si->override_level_music = FALSE;
2015 si->editor.el_boulderdash = TRUE;
2016 si->editor.el_emerald_mine = TRUE;
2017 si->editor.el_more = TRUE;
2018 si->editor.el_sokoban = TRUE;
2019 si->editor.el_supaplex = TRUE;
2020 si->editor.el_diamond_caves = TRUE;
2021 si->editor.el_dx_boulderdash = TRUE;
2022 si->editor.el_chars = TRUE;
2023 si->editor.el_custom = TRUE;
2024 si->editor.el_custom_more = FALSE;
2026 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2027 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2028 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2030 for (i=0; i<MAX_PLAYERS; i++)
2032 si->input[i].use_joystick = FALSE;
2033 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2034 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2035 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2036 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2037 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2038 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2039 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2040 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2041 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2042 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2043 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2044 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2045 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2046 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2047 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2050 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2051 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2053 si->options.verbose = FALSE;
2056 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2060 if (!setup_file_hash)
2065 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2066 setSetupInfo(global_setup_tokens, i,
2067 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2072 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2073 setSetupInfo(editor_setup_tokens, i,
2074 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2077 /* shortcut setup */
2078 ssi = setup.shortcut;
2079 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2080 setSetupInfo(shortcut_setup_tokens, i,
2081 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2082 setup.shortcut = ssi;
2085 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2089 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2091 sii = setup.input[pnr];
2092 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2094 char full_token[100];
2096 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2097 setSetupInfo(player_setup_tokens, i,
2098 getHashEntry(setup_file_hash, full_token));
2100 setup.input[pnr] = sii;
2105 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2106 setSetupInfo(system_setup_tokens, i,
2107 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2111 soi = setup.options;
2112 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2113 setSetupInfo(options_setup_tokens, i,
2114 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2115 setup.options = soi;
2120 char *filename = getSetupFilename();
2121 SetupFileHash *setup_file_hash = NULL;
2123 /* always start with reliable default values */
2124 setSetupInfoToDefaults(&setup);
2126 setup_file_hash = loadSetupFileHash(filename);
2128 if (setup_file_hash)
2130 char *player_name_new;
2132 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2133 decodeSetupFileHash(setup_file_hash);
2135 setup.direct_draw = !setup.double_buffering;
2137 freeSetupFileHash(setup_file_hash);
2139 /* needed to work around problems with fixed length strings */
2140 player_name_new = get_corrected_login_name(setup.player_name);
2141 free(setup.player_name);
2142 setup.player_name = player_name_new;
2145 Error(ERR_WARN, "using default setup values");
2150 char *filename = getSetupFilename();
2154 InitUserDataDirectory();
2156 if (!(file = fopen(filename, MODE_WRITE)))
2158 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2162 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2163 getCookie("SETUP")));
2164 fprintf(file, "\n");
2168 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2170 /* just to make things nicer :) */
2171 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2172 i == SETUP_TOKEN_GRAPHICS_SET)
2173 fprintf(file, "\n");
2175 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2180 fprintf(file, "\n");
2181 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2182 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2184 /* shortcut setup */
2185 ssi = setup.shortcut;
2186 fprintf(file, "\n");
2187 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2188 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2191 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2195 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2196 fprintf(file, "\n");
2198 sii = setup.input[pnr];
2199 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2200 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2205 fprintf(file, "\n");
2206 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2207 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2210 soi = setup.options;
2211 fprintf(file, "\n");
2212 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2213 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2217 SetFilePermissions(filename, PERMS_PRIVATE);
2220 void LoadCustomElementDescriptions()
2222 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2223 SetupFileHash *setup_file_hash;
2226 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2228 if (element_info[i].custom_description != NULL)
2230 free(element_info[i].custom_description);
2231 element_info[i].custom_description = NULL;
2235 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2238 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2240 char *token = getStringCat2(element_info[i].token_name, ".name");
2241 char *value = getHashEntry(setup_file_hash, token);
2244 element_info[i].custom_description = getStringCopy(value);
2249 freeSetupFileHash(setup_file_hash);
2252 void LoadSpecialMenuDesignSettings()
2254 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2255 SetupFileHash *setup_file_hash;
2258 /* always start with reliable default values from default config */
2259 for (i=0; image_config_vars[i].token != NULL; i++)
2260 for (j=0; image_config[j].token != NULL; j++)
2261 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2262 *image_config_vars[i].value =
2263 get_integer_from_string(image_config[j].value);
2265 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2268 /* special case: initialize with default values that may be overwritten */
2269 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2271 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2272 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2273 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2275 if (value_x != NULL)
2276 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2277 if (value_y != NULL)
2278 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2279 if (list_size != NULL)
2280 menu.list_size[i] = get_integer_from_string(list_size);
2283 /* read (and overwrite with) values that may be specified in config file */
2284 for (i=0; image_config_vars[i].token != NULL; i++)
2286 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2289 *image_config_vars[i].value = get_integer_from_string(value);
2292 freeSetupFileHash(setup_file_hash);