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].score = 0;
120 element_info[element].gem_count = 0;
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;
176 BorderElement = EL_STEELWALL;
178 level->no_level_file = FALSE;
180 if (leveldir_current == NULL) /* only when dumping level */
183 /* try to determine better author name than 'anonymous' */
184 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
186 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
187 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
191 switch (LEVELCLASS(leveldir_current))
193 case LEVELCLASS_TUTORIAL:
194 strcpy(level->author, PROGRAM_AUTHOR_STRING);
197 case LEVELCLASS_CONTRIBUTION:
198 strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
199 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
202 case LEVELCLASS_USER:
203 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
204 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
208 /* keep default value */
214 static void ActivateLevelTemplate()
216 /* Currently there is no special action needed to activate the template
217 data, because 'element_info' and 'Properties' overwrite the original
218 level data, while all other variables do not change. */
221 boolean LevelFileExists(int level_nr)
223 char *filename = getLevelFilename(level_nr);
225 return (access(filename, F_OK) == 0);
228 static int checkLevelElement(int element)
230 if (element >= NUM_FILE_ELEMENTS)
232 Error(ERR_WARN, "invalid level element %d", element);
233 element = EL_CHAR_QUESTION;
235 else if (element == EL_PLAYER_OBSOLETE)
236 element = EL_PLAYER_1;
237 else if (element == EL_KEY_OBSOLETE)
243 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
245 level->file_version = getFileVersion(file);
246 level->game_version = getFileVersion(file);
251 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
255 level->fieldx = getFile8Bit(file);
256 level->fieldy = getFile8Bit(file);
258 level->time = getFile16BitBE(file);
259 level->gems_needed = getFile16BitBE(file);
261 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
262 level->name[i] = getFile8Bit(file);
263 level->name[MAX_LEVEL_NAME_LEN] = 0;
265 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
266 level->score[i] = getFile8Bit(file);
268 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
269 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
272 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
274 level->amoeba_speed = getFile8Bit(file);
275 level->time_magic_wall = getFile8Bit(file);
276 level->time_wheel = getFile8Bit(file);
277 level->amoeba_content = checkLevelElement(getFile8Bit(file));
278 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
279 level->gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
280 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
281 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
283 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
285 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
290 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
294 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
295 level->author[i] = getFile8Bit(file);
296 level->author[MAX_LEVEL_NAME_LEN] = 0;
301 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
304 int chunk_size_expected = level->fieldx * level->fieldy;
306 /* Note: "chunk_size" was wrong before version 2.0 when elements are
307 stored with 16-bit encoding (and should be twice as big then).
308 Even worse, playfield data was stored 16-bit when only yamyam content
309 contained 16-bit elements and vice versa. */
311 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
312 chunk_size_expected *= 2;
314 if (chunk_size_expected != chunk_size)
316 ReadUnusedBytesFromFile(file, chunk_size);
317 return chunk_size_expected;
320 for(y=0; y<level->fieldy; y++)
321 for(x=0; x<level->fieldx; x++)
323 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
328 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
332 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
333 int chunk_size_expected = header_size + content_size;
335 /* Note: "chunk_size" was wrong before version 2.0 when elements are
336 stored with 16-bit encoding (and should be twice as big then).
337 Even worse, playfield data was stored 16-bit when only yamyam content
338 contained 16-bit elements and vice versa. */
340 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
341 chunk_size_expected += content_size;
343 if (chunk_size_expected != chunk_size)
345 ReadUnusedBytesFromFile(file, chunk_size);
346 return chunk_size_expected;
350 level->num_yamyam_contents = getFile8Bit(file);
354 /* correct invalid number of content fields -- should never happen */
355 if (level->num_yamyam_contents < 1 ||
356 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
357 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
359 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
362 level->yamyam_content[i][x][y] =
363 checkLevelElement(level->encoding_16bit_field ?
364 getFile16BitBE(file) : getFile8Bit(file));
368 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
372 int num_contents, content_xsize, content_ysize;
373 int content_array[MAX_ELEMENT_CONTENTS][3][3];
375 element = checkLevelElement(getFile16BitBE(file));
376 num_contents = getFile8Bit(file);
377 content_xsize = getFile8Bit(file);
378 content_ysize = getFile8Bit(file);
379 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
381 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
384 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
386 /* correct invalid number of content fields -- should never happen */
387 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
388 num_contents = STD_ELEMENT_CONTENTS;
390 if (element == EL_YAMYAM)
392 level->num_yamyam_contents = num_contents;
394 for(i=0; i<num_contents; i++)
397 level->yamyam_content[i][x][y] = content_array[i][x][y];
399 else if (element == EL_BD_AMOEBA)
401 level->amoeba_content = content_array[0][0][0];
405 Error(ERR_WARN, "cannot load content for element '%d'", element);
411 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
413 int num_changed_custom_elements = getFile16BitBE(file);
414 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
417 if (chunk_size_expected != chunk_size)
419 ReadUnusedBytesFromFile(file, chunk_size - 2);
420 return chunk_size_expected;
423 for (i=0; i < num_changed_custom_elements; i++)
425 int element = getFile16BitBE(file);
426 int properties = getFile32BitBE(file);
428 if (IS_CUSTOM_ELEMENT(element))
429 Properties[element][EP_BITFIELD_BASE] = properties;
431 Error(ERR_WARN, "invalid custom element number %d", element);
437 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
439 int num_changed_custom_elements = getFile16BitBE(file);
440 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
443 if (chunk_size_expected != chunk_size)
445 ReadUnusedBytesFromFile(file, chunk_size - 2);
446 return chunk_size_expected;
449 for (i=0; i < num_changed_custom_elements; i++)
451 int element = getFile16BitBE(file);
452 int custom_target_element = getFile16BitBE(file);
454 if (IS_CUSTOM_ELEMENT(element))
455 element_info[element].change.target_element = custom_target_element;
457 Error(ERR_WARN, "invalid custom element number %d", element);
463 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
465 int num_changed_custom_elements = getFile16BitBE(file);
466 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
469 if (chunk_size_expected != chunk_size)
471 ReadUnusedBytesFromFile(file, chunk_size - 2);
472 return chunk_size_expected;
475 for (i=0; i < num_changed_custom_elements; i++)
477 int element = getFile16BitBE(file);
479 if (!IS_CUSTOM_ELEMENT(element))
481 Error(ERR_WARN, "invalid custom element number %d", element);
483 element = EL_DEFAULT; /* dummy element used for artwork config */
486 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
487 element_info[element].description[j] = getFile8Bit(file);
488 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
490 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
492 /* some free bytes for future properties and padding */
493 ReadUnusedBytesFromFile(file, 7);
495 element_info[element].use_gfx_element = getFile8Bit(file);
496 element_info[element].gfx_element =
497 checkLevelElement(getFile16BitBE(file));
499 element_info[element].score = getFile8Bit(file);
500 element_info[element].gem_count = getFile8Bit(file);
502 element_info[element].push_delay_fixed = getFile16BitBE(file);
503 element_info[element].push_delay_random = getFile16BitBE(file);
504 element_info[element].move_delay_fixed = getFile16BitBE(file);
505 element_info[element].move_delay_random = getFile16BitBE(file);
507 element_info[element].move_pattern = getFile16BitBE(file);
508 element_info[element].move_direction_initial = getFile8Bit(file);
509 element_info[element].move_stepsize = getFile8Bit(file);
513 element_info[element].content[x][y] =
514 checkLevelElement(getFile16BitBE(file));
516 element_info[element].change.events = getFile32BitBE(file);
518 element_info[element].change.target_element =
519 checkLevelElement(getFile16BitBE(file));
521 element_info[element].change.delay_fixed = getFile16BitBE(file);
522 element_info[element].change.delay_random = getFile16BitBE(file);
523 element_info[element].change.delay_frames = getFile16BitBE(file);
525 element_info[element].change.trigger_element =
526 checkLevelElement(getFile16BitBE(file));
528 element_info[element].change.explode = getFile8Bit(file);
529 element_info[element].change.use_content = getFile8Bit(file);
530 element_info[element].change.only_complete = getFile8Bit(file);
531 element_info[element].change.use_random_change = getFile8Bit(file);
533 element_info[element].change.random = getFile8Bit(file);
534 element_info[element].change.power = getFile8Bit(file);
538 element_info[element].change.content[x][y] =
539 checkLevelElement(getFile16BitBE(file));
541 element_info[element].slippery_type = getFile8Bit(file);
543 /* some free bytes for future properties and padding */
544 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
550 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
552 char cookie[MAX_LINE_LEN];
553 char chunk_name[CHUNK_ID_LEN + 1];
557 /* always start with reliable default values */
558 setLevelInfoToDefaults(level);
560 if (!(file = fopen(filename, MODE_READ)))
562 level->no_level_file = TRUE;
564 if (level != &level_template)
565 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
570 getFileChunkBE(file, chunk_name, NULL);
571 if (strcmp(chunk_name, "RND1") == 0)
573 getFile32BitBE(file); /* not used */
575 getFileChunkBE(file, chunk_name, NULL);
576 if (strcmp(chunk_name, "CAVE") != 0)
578 Error(ERR_WARN, "unknown format of level file '%s'", filename);
583 else /* check for pre-2.0 file format with cookie string */
585 strcpy(cookie, chunk_name);
586 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
587 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
588 cookie[strlen(cookie) - 1] = '\0';
590 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
592 Error(ERR_WARN, "unknown format of level file '%s'", filename);
597 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
599 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
604 /* pre-2.0 level files have no game version, so use file version here */
605 level->game_version = level->file_version;
608 if (level->file_version < FILE_VERSION_1_2)
610 /* level files from versions before 1.2.0 without chunk structure */
611 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
612 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
620 int (*loader)(FILE *, int, struct LevelInfo *);
624 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
625 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
626 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
627 { "BODY", -1, LoadLevel_BODY },
628 { "CONT", -1, LoadLevel_CONT },
629 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
630 { "CUS1", -1, LoadLevel_CUS1 },
631 { "CUS2", -1, LoadLevel_CUS2 },
632 { "CUS3", -1, LoadLevel_CUS3 },
636 while (getFileChunkBE(file, chunk_name, &chunk_size))
640 while (chunk_info[i].name != NULL &&
641 strcmp(chunk_name, chunk_info[i].name) != 0)
644 if (chunk_info[i].name == NULL)
646 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
647 chunk_name, filename);
648 ReadUnusedBytesFromFile(file, chunk_size);
650 else if (chunk_info[i].size != -1 &&
651 chunk_info[i].size != chunk_size)
653 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
654 chunk_size, chunk_name, filename);
655 ReadUnusedBytesFromFile(file, chunk_size);
659 /* call function to load this level chunk */
660 int chunk_size_expected =
661 (chunk_info[i].loader)(file, chunk_size, level);
663 /* the size of some chunks cannot be checked before reading other
664 chunks first (like "HEAD" and "BODY") that contain some header
665 information, so check them here */
666 if (chunk_size_expected != chunk_size)
668 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
669 chunk_size, chunk_name, filename);
678 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
682 if (leveldir_current == NULL) /* only when dumping level */
685 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
686 IS_LEVELCLASS_USER(leveldir_current))
688 /* For user contributed and private levels, use the version of
689 the game engine the levels were created for.
690 Since 2.0.1, the game engine version is now directly stored
691 in the level file (chunk "VERS"), so there is no need anymore
692 to set the game version from the file version (except for old,
693 pre-2.0 levels, where the game version is still taken from the
694 file format version used to store the level -- see above). */
696 /* do some special adjustments to support older level versions */
697 if (level->file_version == FILE_VERSION_1_0)
699 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
700 Error(ERR_WARN, "using high speed movement for player");
702 /* player was faster than monsters in (pre-)1.0 levels */
703 level->double_speed = TRUE;
706 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
707 if (level->game_version == VERSION_IDENT(2,0,1))
708 level->em_slippery_gems = TRUE;
712 /* Always use the latest version of the game engine for all but
713 user contributed and private levels; this allows for actual
714 corrections in the game engine to take effect for existing,
715 converted levels (from "classic" or other existing games) to
716 make the game emulation more accurate, while (hopefully) not
717 breaking existing levels created from other players. */
719 level->game_version = GAME_VERSION_ACTUAL;
721 /* Set special EM style gems behaviour: EM style gems slip down from
722 normal, steel and growing wall. As this is a more fundamental change,
723 it seems better to set the default behaviour to "off" (as it is more
724 natural) and make it configurable in the level editor (as a property
725 of gem style elements). Already existing converted levels (neither
726 private nor contributed levels) are changed to the new behaviour. */
728 if (level->file_version < FILE_VERSION_2_0)
729 level->em_slippery_gems = TRUE;
732 /* map elements which have changed in newer versions */
733 if (level->game_version <= VERSION_IDENT(2,2,0))
735 /* map game font elements */
736 for(y=0; y<level->fieldy; y++)
738 for(x=0; x<level->fieldx; x++)
740 int element = level->field[x][y];
742 if (element == EL_CHAR('['))
743 element = EL_CHAR_AUMLAUT;
744 else if (element == EL_CHAR('\\'))
745 element = EL_CHAR_OUMLAUT;
746 else if (element == EL_CHAR(']'))
747 element = EL_CHAR_UUMLAUT;
748 else if (element == EL_CHAR('^'))
749 element = EL_CHAR_COPYRIGHT;
751 level->field[x][y] = element;
756 /* copy elements to runtime playfield array */
757 for(x=0; x<MAX_LEV_FIELDX; x++)
758 for(y=0; y<MAX_LEV_FIELDY; y++)
759 Feld[x][y] = level->field[x][y];
761 /* initialize level size variables for faster access */
762 lev_fieldx = level->fieldx;
763 lev_fieldy = level->fieldy;
765 /* determine border element for this level */
768 /* initialize element properties for level editor etc. */
769 InitElementPropertiesEngine(level->game_version);
772 void LoadLevelTemplate(int level_nr)
774 char *filename = getLevelFilename(level_nr);
776 LoadLevelFromFilename(&level_template, filename);
778 ActivateLevelTemplate();
781 void LoadLevel(int level_nr)
783 char *filename = getLevelFilename(level_nr);
785 LoadLevelFromFilename(&level, filename);
787 if (level.use_custom_template)
788 LoadLevelTemplate(-1);
790 LoadLevel_InitLevel(&level, filename);
793 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
795 putFileVersion(file, level->file_version);
796 putFileVersion(file, level->game_version);
799 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
803 putFile8Bit(file, level->fieldx);
804 putFile8Bit(file, level->fieldy);
806 putFile16BitBE(file, level->time);
807 putFile16BitBE(file, level->gems_needed);
809 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
810 putFile8Bit(file, level->name[i]);
812 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
813 putFile8Bit(file, level->score[i]);
815 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
818 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
819 level->yamyam_content[i][x][y]));
820 putFile8Bit(file, level->amoeba_speed);
821 putFile8Bit(file, level->time_magic_wall);
822 putFile8Bit(file, level->time_wheel);
823 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
824 level->amoeba_content));
825 putFile8Bit(file, (level->double_speed ? 1 : 0));
826 putFile8Bit(file, (level->gravity ? 1 : 0));
827 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
828 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
830 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
832 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
835 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
839 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
840 putFile8Bit(file, level->author[i]);
843 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
847 for(y=0; y<level->fieldy; y++)
848 for(x=0; x<level->fieldx; x++)
849 if (level->encoding_16bit_field)
850 putFile16BitBE(file, level->field[x][y]);
852 putFile8Bit(file, level->field[x][y]);
856 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
860 putFile8Bit(file, EL_YAMYAM);
861 putFile8Bit(file, level->num_yamyam_contents);
862 putFile8Bit(file, 0);
863 putFile8Bit(file, 0);
865 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
868 if (level->encoding_16bit_field)
869 putFile16BitBE(file, level->yamyam_content[i][x][y]);
871 putFile8Bit(file, level->yamyam_content[i][x][y]);
875 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
878 int num_contents, content_xsize, content_ysize;
879 int content_array[MAX_ELEMENT_CONTENTS][3][3];
881 if (element == EL_YAMYAM)
883 num_contents = level->num_yamyam_contents;
887 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
890 content_array[i][x][y] = level->yamyam_content[i][x][y];
892 else if (element == EL_BD_AMOEBA)
898 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
901 content_array[i][x][y] = EL_EMPTY;
902 content_array[0][0][0] = level->amoeba_content;
906 /* chunk header already written -- write empty chunk data */
907 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
909 Error(ERR_WARN, "cannot save content for element '%d'", element);
913 putFile16BitBE(file, element);
914 putFile8Bit(file, num_contents);
915 putFile8Bit(file, content_xsize);
916 putFile8Bit(file, content_ysize);
918 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
920 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
923 putFile16BitBE(file, content_array[i][x][y]);
927 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
928 int num_changed_custom_elements)
932 putFile16BitBE(file, num_changed_custom_elements);
934 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
936 int element = EL_CUSTOM_START + i;
938 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
940 if (check < num_changed_custom_elements)
942 putFile16BitBE(file, element);
943 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
950 if (check != num_changed_custom_elements) /* should not happen */
951 Error(ERR_WARN, "inconsistent number of custom element properties");
956 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
957 int num_changed_custom_elements)
961 putFile16BitBE(file, num_changed_custom_elements);
963 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
965 int element = EL_CUSTOM_START + i;
967 if (element_info[element].change.target_element != EL_EMPTY_SPACE)
969 if (check < num_changed_custom_elements)
971 putFile16BitBE(file, element);
972 putFile16BitBE(file, element_info[element].change.target_element);
979 if (check != num_changed_custom_elements) /* should not happen */
980 Error(ERR_WARN, "inconsistent number of custom target elements");
984 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
985 int num_changed_custom_elements)
987 int i, j, x, y, check = 0;
989 putFile16BitBE(file, num_changed_custom_elements);
991 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
993 int element = EL_CUSTOM_START + i;
995 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
997 if (check < num_changed_custom_elements)
999 putFile16BitBE(file, element);
1001 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1002 putFile8Bit(file, element_info[element].description[j]);
1004 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1006 /* some free bytes for future properties and padding */
1007 WriteUnusedBytesToFile(file, 7);
1009 putFile8Bit(file, element_info[element].use_gfx_element);
1010 putFile16BitBE(file, element_info[element].gfx_element);
1012 putFile8Bit(file, element_info[element].score);
1013 putFile8Bit(file, element_info[element].gem_count);
1015 putFile16BitBE(file, element_info[element].push_delay_fixed);
1016 putFile16BitBE(file, element_info[element].push_delay_random);
1017 putFile16BitBE(file, element_info[element].move_delay_fixed);
1018 putFile16BitBE(file, element_info[element].move_delay_random);
1020 putFile16BitBE(file, element_info[element].move_pattern);
1021 putFile8Bit(file, element_info[element].move_direction_initial);
1022 putFile8Bit(file, element_info[element].move_stepsize);
1026 putFile16BitBE(file, element_info[element].content[x][y]);
1028 putFile32BitBE(file, element_info[element].change.events);
1030 putFile16BitBE(file, element_info[element].change.target_element);
1032 putFile16BitBE(file, element_info[element].change.delay_fixed);
1033 putFile16BitBE(file, element_info[element].change.delay_random);
1034 putFile16BitBE(file, element_info[element].change.delay_frames);
1036 putFile16BitBE(file, element_info[element].change.trigger_element);
1038 putFile8Bit(file, element_info[element].change.explode);
1039 putFile8Bit(file, element_info[element].change.use_content);
1040 putFile8Bit(file, element_info[element].change.only_complete);
1041 putFile8Bit(file, element_info[element].change.use_random_change);
1043 putFile8Bit(file, element_info[element].change.random);
1044 putFile8Bit(file, element_info[element].change.power);
1048 putFile16BitBE(file, element_info[element].change.content[x][y]);
1050 putFile8Bit(file, element_info[element].slippery_type);
1052 /* some free bytes for future properties and padding */
1053 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1060 if (check != num_changed_custom_elements) /* should not happen */
1061 Error(ERR_WARN, "inconsistent number of custom element properties");
1064 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1066 int body_chunk_size;
1067 int num_changed_custom_elements = 0;
1068 int level_chunk_CUS3_size;
1072 if (!(file = fopen(filename, MODE_WRITE)))
1074 Error(ERR_WARN, "cannot save level file '%s'", filename);
1078 level->file_version = FILE_VERSION_ACTUAL;
1079 level->game_version = GAME_VERSION_ACTUAL;
1081 /* check level field for 16-bit elements */
1082 level->encoding_16bit_field = FALSE;
1083 for(y=0; y<level->fieldy; y++)
1084 for(x=0; x<level->fieldx; x++)
1085 if (level->field[x][y] > 255)
1086 level->encoding_16bit_field = TRUE;
1088 /* check yamyam content for 16-bit elements */
1089 level->encoding_16bit_yamyam = FALSE;
1090 for(i=0; i<level->num_yamyam_contents; i++)
1093 if (level->yamyam_content[i][x][y] > 255)
1094 level->encoding_16bit_yamyam = TRUE;
1096 /* check amoeba content for 16-bit elements */
1097 level->encoding_16bit_amoeba = FALSE;
1098 if (level->amoeba_content > 255)
1099 level->encoding_16bit_amoeba = TRUE;
1101 /* calculate size of "BODY" chunk */
1103 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1105 /* check for non-standard custom elements and calculate "CUS3" chunk size */
1106 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1107 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1108 num_changed_custom_elements++;
1109 level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1111 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1112 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1114 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1115 SaveLevel_VERS(file, level);
1117 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1118 SaveLevel_HEAD(file, level);
1120 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1121 SaveLevel_AUTH(file, level);
1123 putFileChunkBE(file, "BODY", body_chunk_size);
1124 SaveLevel_BODY(file, level);
1126 if (level->encoding_16bit_yamyam ||
1127 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1129 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1130 SaveLevel_CNT2(file, level, EL_YAMYAM);
1133 if (level->encoding_16bit_amoeba)
1135 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1136 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1139 if (num_changed_custom_elements > 0 && !level->use_custom_template)
1141 putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1142 SaveLevel_CUS3(file, level, num_changed_custom_elements);
1147 SetFilePermissions(filename, PERMS_PRIVATE);
1150 void SaveLevel(int level_nr)
1152 char *filename = getLevelFilename(level_nr);
1154 SaveLevelFromFilename(&level, filename);
1157 void SaveLevelTemplate()
1159 char *filename = getLevelFilename(-1);
1161 SaveLevelFromFilename(&level, filename);
1164 void DumpLevel(struct LevelInfo *level)
1166 printf_line("-", 79);
1167 printf("Level xxx (file version %08d, game version %08d)\n",
1168 level->file_version, level->game_version);
1169 printf_line("-", 79);
1171 printf("Level Author: '%s'\n", level->author);
1172 printf("Level Title: '%s'\n", level->name);
1174 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1176 printf("Level Time: %d seconds\n", level->time);
1177 printf("Gems needed: %d\n", level->gems_needed);
1179 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1180 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1181 printf("Time for Light: %d seconds\n", level->time_light);
1182 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1184 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1186 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1187 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1188 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1190 printf_line("-", 79);
1194 /* ========================================================================= */
1195 /* tape file functions */
1196 /* ========================================================================= */
1198 static void setTapeInfoToDefaults()
1202 /* always start with reliable default values (empty tape) */
1205 /* default values (also for pre-1.2 tapes) with only the first player */
1206 tape.player_participates[0] = TRUE;
1207 for(i=1; i<MAX_PLAYERS; i++)
1208 tape.player_participates[i] = FALSE;
1210 /* at least one (default: the first) player participates in every tape */
1211 tape.num_participating_players = 1;
1213 tape.level_nr = level_nr;
1215 tape.changed = FALSE;
1217 tape.recording = FALSE;
1218 tape.playing = FALSE;
1219 tape.pausing = FALSE;
1222 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1224 tape->file_version = getFileVersion(file);
1225 tape->game_version = getFileVersion(file);
1230 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1234 tape->random_seed = getFile32BitBE(file);
1235 tape->date = getFile32BitBE(file);
1236 tape->length = getFile32BitBE(file);
1238 /* read header fields that are new since version 1.2 */
1239 if (tape->file_version >= FILE_VERSION_1_2)
1241 byte store_participating_players = getFile8Bit(file);
1244 /* since version 1.2, tapes store which players participate in the tape */
1245 tape->num_participating_players = 0;
1246 for(i=0; i<MAX_PLAYERS; i++)
1248 tape->player_participates[i] = FALSE;
1250 if (store_participating_players & (1 << i))
1252 tape->player_participates[i] = TRUE;
1253 tape->num_participating_players++;
1257 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1259 engine_version = getFileVersion(file);
1260 if (engine_version > 0)
1261 tape->engine_version = engine_version;
1267 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1269 int level_identifier_size;
1272 level_identifier_size = getFile16BitBE(file);
1274 tape->level_identifier =
1275 checked_realloc(tape->level_identifier, level_identifier_size);
1277 for(i=0; i < level_identifier_size; i++)
1278 tape->level_identifier[i] = getFile8Bit(file);
1280 tape->level_nr = getFile16BitBE(file);
1282 chunk_size = 2 + level_identifier_size + 2;
1287 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1290 int chunk_size_expected =
1291 (tape->num_participating_players + 1) * tape->length;
1293 if (chunk_size_expected != chunk_size)
1295 ReadUnusedBytesFromFile(file, chunk_size);
1296 return chunk_size_expected;
1299 for(i=0; i<tape->length; i++)
1301 if (i >= MAX_TAPELEN)
1304 for(j=0; j<MAX_PLAYERS; j++)
1306 tape->pos[i].action[j] = MV_NO_MOVING;
1308 if (tape->player_participates[j])
1309 tape->pos[i].action[j] = getFile8Bit(file);
1312 tape->pos[i].delay = getFile8Bit(file);
1314 if (tape->file_version == FILE_VERSION_1_0)
1316 /* eliminate possible diagonal moves in old tapes */
1317 /* this is only for backward compatibility */
1319 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1320 byte action = tape->pos[i].action[0];
1321 int k, num_moves = 0;
1325 if (action & joy_dir[k])
1327 tape->pos[i + num_moves].action[0] = joy_dir[k];
1329 tape->pos[i + num_moves].delay = 0;
1338 tape->length += num_moves;
1341 else if (tape->file_version < FILE_VERSION_2_0)
1343 /* convert pre-2.0 tapes to new tape format */
1345 if (tape->pos[i].delay > 1)
1348 tape->pos[i + 1] = tape->pos[i];
1349 tape->pos[i + 1].delay = 1;
1352 for(j=0; j<MAX_PLAYERS; j++)
1353 tape->pos[i].action[j] = MV_NO_MOVING;
1354 tape->pos[i].delay--;
1365 if (i != tape->length)
1366 chunk_size = (tape->num_participating_players + 1) * i;
1371 void LoadTapeFromFilename(char *filename)
1373 char cookie[MAX_LINE_LEN];
1374 char chunk_name[CHUNK_ID_LEN + 1];
1378 /* always start with reliable default values */
1379 setTapeInfoToDefaults();
1381 if (!(file = fopen(filename, MODE_READ)))
1384 getFileChunkBE(file, chunk_name, NULL);
1385 if (strcmp(chunk_name, "RND1") == 0)
1387 getFile32BitBE(file); /* not used */
1389 getFileChunkBE(file, chunk_name, NULL);
1390 if (strcmp(chunk_name, "TAPE") != 0)
1392 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1397 else /* check for pre-2.0 file format with cookie string */
1399 strcpy(cookie, chunk_name);
1400 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1401 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1402 cookie[strlen(cookie) - 1] = '\0';
1404 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1406 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1411 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1413 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1418 /* pre-2.0 tape files have no game version, so use file version here */
1419 tape.game_version = tape.file_version;
1422 if (tape.file_version < FILE_VERSION_1_2)
1424 /* tape files from versions before 1.2.0 without chunk structure */
1425 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1426 LoadTape_BODY(file, 2 * tape.length, &tape);
1434 int (*loader)(FILE *, int, struct TapeInfo *);
1438 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1439 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1440 { "INFO", -1, LoadTape_INFO },
1441 { "BODY", -1, LoadTape_BODY },
1445 while (getFileChunkBE(file, chunk_name, &chunk_size))
1449 while (chunk_info[i].name != NULL &&
1450 strcmp(chunk_name, chunk_info[i].name) != 0)
1453 if (chunk_info[i].name == NULL)
1455 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1456 chunk_name, filename);
1457 ReadUnusedBytesFromFile(file, chunk_size);
1459 else if (chunk_info[i].size != -1 &&
1460 chunk_info[i].size != chunk_size)
1462 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1463 chunk_size, chunk_name, filename);
1464 ReadUnusedBytesFromFile(file, chunk_size);
1468 /* call function to load this tape chunk */
1469 int chunk_size_expected =
1470 (chunk_info[i].loader)(file, chunk_size, &tape);
1472 /* the size of some chunks cannot be checked before reading other
1473 chunks first (like "HEAD" and "BODY") that contain some header
1474 information, so check them here */
1475 if (chunk_size_expected != chunk_size)
1477 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1478 chunk_size, chunk_name, filename);
1486 tape.length_seconds = GetTapeLength();
1489 printf("tape version: %d\n", tape.game_version);
1493 void LoadTape(int level_nr)
1495 char *filename = getTapeFilename(level_nr);
1497 LoadTapeFromFilename(filename);
1500 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1502 putFileVersion(file, tape->file_version);
1503 putFileVersion(file, tape->game_version);
1506 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1509 byte store_participating_players = 0;
1511 /* set bits for participating players for compact storage */
1512 for(i=0; i<MAX_PLAYERS; i++)
1513 if (tape->player_participates[i])
1514 store_participating_players |= (1 << i);
1516 putFile32BitBE(file, tape->random_seed);
1517 putFile32BitBE(file, tape->date);
1518 putFile32BitBE(file, tape->length);
1520 putFile8Bit(file, store_participating_players);
1522 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1523 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1525 putFileVersion(file, tape->engine_version);
1528 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1530 int level_identifier_size = strlen(tape->level_identifier) + 1;
1533 putFile16BitBE(file, level_identifier_size);
1535 for(i=0; i < level_identifier_size; i++)
1536 putFile8Bit(file, tape->level_identifier[i]);
1538 putFile16BitBE(file, tape->level_nr);
1541 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1545 for(i=0; i<tape->length; i++)
1547 for(j=0; j<MAX_PLAYERS; j++)
1548 if (tape->player_participates[j])
1549 putFile8Bit(file, tape->pos[i].action[j]);
1551 putFile8Bit(file, tape->pos[i].delay);
1555 void SaveTape(int level_nr)
1557 char *filename = getTapeFilename(level_nr);
1559 boolean new_tape = TRUE;
1560 int num_participating_players = 0;
1561 int info_chunk_size;
1562 int body_chunk_size;
1565 InitTapeDirectory(leveldir_current->filename);
1567 /* if a tape still exists, ask to overwrite it */
1568 if (access(filename, F_OK) == 0)
1571 if (!Request("Replace old tape ?", REQ_ASK))
1575 if (!(file = fopen(filename, MODE_WRITE)))
1577 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1581 tape.file_version = FILE_VERSION_ACTUAL;
1582 tape.game_version = GAME_VERSION_ACTUAL;
1584 /* count number of participating players */
1585 for(i=0; i<MAX_PLAYERS; i++)
1586 if (tape.player_participates[i])
1587 num_participating_players++;
1589 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1590 body_chunk_size = (num_participating_players + 1) * tape.length;
1592 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1593 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1595 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1596 SaveTape_VERS(file, &tape);
1598 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1599 SaveTape_HEAD(file, &tape);
1601 putFileChunkBE(file, "INFO", info_chunk_size);
1602 SaveTape_INFO(file, &tape);
1604 putFileChunkBE(file, "BODY", body_chunk_size);
1605 SaveTape_BODY(file, &tape);
1609 SetFilePermissions(filename, PERMS_PRIVATE);
1611 tape.changed = FALSE;
1614 Request("tape saved !", REQ_CONFIRM);
1617 void DumpTape(struct TapeInfo *tape)
1621 if (TAPE_IS_EMPTY(*tape))
1623 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1627 printf_line("-", 79);
1628 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1629 tape->level_nr, tape->file_version, tape->game_version);
1630 printf("Level series identifier: '%s'\n", tape->level_identifier);
1631 printf_line("-", 79);
1633 for(i=0; i<tape->length; i++)
1635 if (i >= MAX_TAPELEN)
1638 printf("%03d: ", i);
1640 for(j=0; j<MAX_PLAYERS; j++)
1642 if (tape->player_participates[j])
1644 int action = tape->pos[i].action[j];
1646 printf("%d:%02x ", j, action);
1647 printf("[%c%c%c%c|%c%c] - ",
1648 (action & JOY_LEFT ? '<' : ' '),
1649 (action & JOY_RIGHT ? '>' : ' '),
1650 (action & JOY_UP ? '^' : ' '),
1651 (action & JOY_DOWN ? 'v' : ' '),
1652 (action & JOY_BUTTON_1 ? '1' : ' '),
1653 (action & JOY_BUTTON_2 ? '2' : ' '));
1657 printf("(%03d)\n", tape->pos[i].delay);
1660 printf_line("-", 79);
1664 /* ========================================================================= */
1665 /* score file functions */
1666 /* ========================================================================= */
1668 void LoadScore(int level_nr)
1671 char *filename = getScoreFilename(level_nr);
1672 char cookie[MAX_LINE_LEN];
1673 char line[MAX_LINE_LEN];
1677 /* always start with reliable default values */
1678 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1680 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1681 highscore[i].Score = 0;
1684 if (!(file = fopen(filename, MODE_READ)))
1687 /* check file identifier */
1688 fgets(cookie, MAX_LINE_LEN, file);
1689 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1690 cookie[strlen(cookie) - 1] = '\0';
1692 if (!checkCookieString(cookie, SCORE_COOKIE))
1694 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1699 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1701 fscanf(file, "%d", &highscore[i].Score);
1702 fgets(line, MAX_LINE_LEN, file);
1704 if (line[strlen(line) - 1] == '\n')
1705 line[strlen(line) - 1] = '\0';
1707 for (line_ptr = line; *line_ptr; line_ptr++)
1709 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1711 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1712 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1721 void SaveScore(int level_nr)
1724 char *filename = getScoreFilename(level_nr);
1727 InitScoreDirectory(leveldir_current->filename);
1729 if (!(file = fopen(filename, MODE_WRITE)))
1731 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1735 fprintf(file, "%s\n\n", SCORE_COOKIE);
1737 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1738 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1742 SetFilePermissions(filename, PERMS_PUBLIC);
1746 /* ========================================================================= */
1747 /* setup file functions */
1748 /* ========================================================================= */
1750 #define TOKEN_STR_PLAYER_PREFIX "player_"
1753 #define SETUP_TOKEN_PLAYER_NAME 0
1754 #define SETUP_TOKEN_SOUND 1
1755 #define SETUP_TOKEN_SOUND_LOOPS 2
1756 #define SETUP_TOKEN_SOUND_MUSIC 3
1757 #define SETUP_TOKEN_SOUND_SIMPLE 4
1758 #define SETUP_TOKEN_TOONS 5
1759 #define SETUP_TOKEN_SCROLL_DELAY 6
1760 #define SETUP_TOKEN_SOFT_SCROLLING 7
1761 #define SETUP_TOKEN_FADING 8
1762 #define SETUP_TOKEN_AUTORECORD 9
1763 #define SETUP_TOKEN_QUICK_DOORS 10
1764 #define SETUP_TOKEN_TEAM_MODE 11
1765 #define SETUP_TOKEN_HANDICAP 12
1766 #define SETUP_TOKEN_TIME_LIMIT 13
1767 #define SETUP_TOKEN_FULLSCREEN 14
1768 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1769 #define SETUP_TOKEN_GRAPHICS_SET 16
1770 #define SETUP_TOKEN_SOUNDS_SET 17
1771 #define SETUP_TOKEN_MUSIC_SET 18
1772 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1773 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1774 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1776 #define NUM_GLOBAL_SETUP_TOKENS 22
1779 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1780 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1781 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1782 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1783 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1784 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1785 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1786 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1787 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1789 #define NUM_EDITOR_SETUP_TOKENS 9
1791 /* shortcut setup */
1792 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1793 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1794 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1796 #define NUM_SHORTCUT_SETUP_TOKENS 3
1799 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1800 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1801 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1802 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1803 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1804 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1805 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1806 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1807 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1808 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1809 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1810 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1811 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1812 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1813 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1814 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1816 #define NUM_PLAYER_SETUP_TOKENS 16
1819 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1820 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1822 #define NUM_SYSTEM_SETUP_TOKENS 2
1825 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1827 #define NUM_OPTIONS_SETUP_TOKENS 1
1830 static struct SetupInfo si;
1831 static struct SetupEditorInfo sei;
1832 static struct SetupShortcutInfo ssi;
1833 static struct SetupInputInfo sii;
1834 static struct SetupSystemInfo syi;
1835 static struct OptionInfo soi;
1837 static struct TokenInfo global_setup_tokens[] =
1839 { TYPE_STRING, &si.player_name, "player_name" },
1840 { TYPE_SWITCH, &si.sound, "sound" },
1841 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1842 { TYPE_SWITCH, &si.sound_music, "background_music" },
1843 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1844 { TYPE_SWITCH, &si.toons, "toons" },
1845 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1846 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1847 { TYPE_SWITCH, &si.fading, "screen_fading" },
1848 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1849 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1850 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1851 { TYPE_SWITCH, &si.handicap, "handicap" },
1852 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1853 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1854 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1855 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1856 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1857 { TYPE_STRING, &si.music_set, "music_set" },
1858 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1859 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1860 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1863 static struct TokenInfo editor_setup_tokens[] =
1865 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1866 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1867 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1868 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1869 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1870 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1871 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1872 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1873 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1876 static struct TokenInfo shortcut_setup_tokens[] =
1878 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1879 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1880 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1883 static struct TokenInfo player_setup_tokens[] =
1885 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1886 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1887 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1888 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1889 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1890 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1891 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1892 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1893 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1894 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1895 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1896 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1897 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1898 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1899 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1900 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1903 static struct TokenInfo system_setup_tokens[] =
1905 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1906 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1909 static struct TokenInfo options_setup_tokens[] =
1911 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1914 static char *get_corrected_login_name(char *login_name)
1916 /* needed because player name must be a fixed length string */
1917 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1919 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1920 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1922 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1923 if (strchr(login_name_new, ' '))
1924 *strchr(login_name_new, ' ') = '\0';
1926 return login_name_new;
1929 static void setSetupInfoToDefaults(struct SetupInfo *si)
1933 si->player_name = get_corrected_login_name(getLoginName());
1936 si->sound_loops = TRUE;
1937 si->sound_music = TRUE;
1938 si->sound_simple = TRUE;
1940 si->double_buffering = TRUE;
1941 si->direct_draw = !si->double_buffering;
1942 si->scroll_delay = TRUE;
1943 si->soft_scrolling = TRUE;
1945 si->autorecord = TRUE;
1946 si->quick_doors = FALSE;
1947 si->team_mode = FALSE;
1948 si->handicap = TRUE;
1949 si->time_limit = TRUE;
1950 si->fullscreen = FALSE;
1951 si->ask_on_escape = TRUE;
1953 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1954 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1955 si->music_set = getStringCopy(MUSIC_SUBDIR);
1956 si->override_level_graphics = FALSE;
1957 si->override_level_sounds = FALSE;
1958 si->override_level_music = FALSE;
1960 si->editor.el_boulderdash = TRUE;
1961 si->editor.el_emerald_mine = TRUE;
1962 si->editor.el_more = TRUE;
1963 si->editor.el_sokoban = TRUE;
1964 si->editor.el_supaplex = TRUE;
1965 si->editor.el_diamond_caves = TRUE;
1966 si->editor.el_dx_boulderdash = TRUE;
1967 si->editor.el_chars = TRUE;
1968 si->editor.el_custom = TRUE;
1970 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1971 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1972 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1974 for (i=0; i<MAX_PLAYERS; i++)
1976 si->input[i].use_joystick = FALSE;
1977 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1978 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1979 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1980 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1981 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1982 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1983 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1984 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1985 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1986 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1987 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1988 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1989 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1990 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1991 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1994 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1995 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1997 si->options.verbose = FALSE;
2000 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2004 if (!setup_file_hash)
2009 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2010 setSetupInfo(global_setup_tokens, i,
2011 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2016 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2017 setSetupInfo(editor_setup_tokens, i,
2018 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2021 /* shortcut setup */
2022 ssi = setup.shortcut;
2023 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2024 setSetupInfo(shortcut_setup_tokens, i,
2025 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2026 setup.shortcut = ssi;
2029 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2033 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2035 sii = setup.input[pnr];
2036 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2038 char full_token[100];
2040 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2041 setSetupInfo(player_setup_tokens, i,
2042 getHashEntry(setup_file_hash, full_token));
2044 setup.input[pnr] = sii;
2049 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2050 setSetupInfo(system_setup_tokens, i,
2051 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2055 soi = setup.options;
2056 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2057 setSetupInfo(options_setup_tokens, i,
2058 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2059 setup.options = soi;
2064 char *filename = getSetupFilename();
2065 SetupFileHash *setup_file_hash = NULL;
2067 /* always start with reliable default values */
2068 setSetupInfoToDefaults(&setup);
2070 setup_file_hash = loadSetupFileHash(filename);
2072 if (setup_file_hash)
2074 char *player_name_new;
2076 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2077 decodeSetupFileHash(setup_file_hash);
2079 setup.direct_draw = !setup.double_buffering;
2081 freeSetupFileHash(setup_file_hash);
2083 /* needed to work around problems with fixed length strings */
2084 player_name_new = get_corrected_login_name(setup.player_name);
2085 free(setup.player_name);
2086 setup.player_name = player_name_new;
2089 Error(ERR_WARN, "using default setup values");
2094 char *filename = getSetupFilename();
2098 InitUserDataDirectory();
2100 if (!(file = fopen(filename, MODE_WRITE)))
2102 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2106 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2107 getCookie("SETUP")));
2108 fprintf(file, "\n");
2112 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2114 /* just to make things nicer :) */
2115 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2116 i == SETUP_TOKEN_GRAPHICS_SET)
2117 fprintf(file, "\n");
2119 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2124 fprintf(file, "\n");
2125 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2126 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2128 /* shortcut setup */
2129 ssi = setup.shortcut;
2130 fprintf(file, "\n");
2131 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2132 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2135 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2139 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2140 fprintf(file, "\n");
2142 sii = setup.input[pnr];
2143 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2144 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2149 fprintf(file, "\n");
2150 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2151 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2154 soi = setup.options;
2155 fprintf(file, "\n");
2156 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2157 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2161 SetFilePermissions(filename, PERMS_PRIVATE);
2164 void LoadCustomElementDescriptions()
2166 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2167 SetupFileHash *setup_file_hash;
2170 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2172 if (element_info[i].custom_description != NULL)
2174 free(element_info[i].custom_description);
2175 element_info[i].custom_description = NULL;
2179 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2182 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2184 char *token = getStringCat2(element_info[i].token_name, ".name");
2185 char *value = getHashEntry(setup_file_hash, token);
2188 element_info[i].custom_description = getStringCopy(value);
2193 freeSetupFileHash(setup_file_hash);
2196 void LoadSpecialMenuDesignSettings()
2198 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2199 SetupFileHash *setup_file_hash;
2202 /* always start with reliable default values from default config */
2203 for (i=0; image_config_vars[i].token != NULL; i++)
2204 for (j=0; image_config[j].token != NULL; j++)
2205 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2206 *image_config_vars[i].value =
2207 get_integer_from_string(image_config[j].value);
2209 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2212 /* special case: initialize with default values that may be overwritten */
2213 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2215 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2216 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2217 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2219 if (value_x != NULL)
2220 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2221 if (value_y != NULL)
2222 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2223 if (list_size != NULL)
2224 menu.list_size[i] = get_integer_from_string(list_size);
2227 /* read (and overwrite with) values that may be specified in config file */
2228 for (i=0; image_config_vars[i].token != NULL; i++)
2230 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2233 *image_config_vars[i].value = get_integer_from_string(value);
2236 freeSetupFileHash(setup_file_hash);