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 for(y=0; y<level->fieldy; y++)
735 for(x=0; x<level->fieldx; x++)
737 int element = level->field[x][y];
739 if (level->game_version <= VERSION_IDENT(2,2,0))
741 /* map game font elements */
742 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
743 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
744 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
745 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
747 else if (level->game_version < VERSION_IDENT(3,0,0))
749 /* map Supaplex gravity tube elements */
750 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
751 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
752 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
753 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
757 level->field[x][y] = element;
761 /* copy elements to runtime playfield array */
762 for(x=0; x<MAX_LEV_FIELDX; x++)
763 for(y=0; y<MAX_LEV_FIELDY; y++)
764 Feld[x][y] = level->field[x][y];
766 /* initialize level size variables for faster access */
767 lev_fieldx = level->fieldx;
768 lev_fieldy = level->fieldy;
770 /* determine border element for this level */
773 /* initialize element properties for level editor etc. */
774 InitElementPropertiesEngine(level->game_version);
777 void LoadLevelTemplate(int level_nr)
779 char *filename = getLevelFilename(level_nr);
781 LoadLevelFromFilename(&level_template, filename);
783 ActivateLevelTemplate();
786 void LoadLevel(int level_nr)
788 char *filename = getLevelFilename(level_nr);
790 LoadLevelFromFilename(&level, filename);
792 if (level.use_custom_template)
793 LoadLevelTemplate(-1);
795 LoadLevel_InitLevel(&level, filename);
798 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
800 putFileVersion(file, level->file_version);
801 putFileVersion(file, level->game_version);
804 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
808 putFile8Bit(file, level->fieldx);
809 putFile8Bit(file, level->fieldy);
811 putFile16BitBE(file, level->time);
812 putFile16BitBE(file, level->gems_needed);
814 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
815 putFile8Bit(file, level->name[i]);
817 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
818 putFile8Bit(file, level->score[i]);
820 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
823 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
824 level->yamyam_content[i][x][y]));
825 putFile8Bit(file, level->amoeba_speed);
826 putFile8Bit(file, level->time_magic_wall);
827 putFile8Bit(file, level->time_wheel);
828 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
829 level->amoeba_content));
830 putFile8Bit(file, (level->double_speed ? 1 : 0));
831 putFile8Bit(file, (level->gravity ? 1 : 0));
832 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
833 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
835 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
837 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
840 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
844 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
845 putFile8Bit(file, level->author[i]);
848 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
852 for(y=0; y<level->fieldy; y++)
853 for(x=0; x<level->fieldx; x++)
854 if (level->encoding_16bit_field)
855 putFile16BitBE(file, level->field[x][y]);
857 putFile8Bit(file, level->field[x][y]);
861 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
865 putFile8Bit(file, EL_YAMYAM);
866 putFile8Bit(file, level->num_yamyam_contents);
867 putFile8Bit(file, 0);
868 putFile8Bit(file, 0);
870 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
873 if (level->encoding_16bit_field)
874 putFile16BitBE(file, level->yamyam_content[i][x][y]);
876 putFile8Bit(file, level->yamyam_content[i][x][y]);
880 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
883 int num_contents, content_xsize, content_ysize;
884 int content_array[MAX_ELEMENT_CONTENTS][3][3];
886 if (element == EL_YAMYAM)
888 num_contents = level->num_yamyam_contents;
892 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
895 content_array[i][x][y] = level->yamyam_content[i][x][y];
897 else if (element == EL_BD_AMOEBA)
903 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
906 content_array[i][x][y] = EL_EMPTY;
907 content_array[0][0][0] = level->amoeba_content;
911 /* chunk header already written -- write empty chunk data */
912 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
914 Error(ERR_WARN, "cannot save content for element '%d'", element);
918 putFile16BitBE(file, element);
919 putFile8Bit(file, num_contents);
920 putFile8Bit(file, content_xsize);
921 putFile8Bit(file, content_ysize);
923 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
925 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
928 putFile16BitBE(file, content_array[i][x][y]);
932 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
933 int num_changed_custom_elements)
937 putFile16BitBE(file, num_changed_custom_elements);
939 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
941 int element = EL_CUSTOM_START + i;
943 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
945 if (check < num_changed_custom_elements)
947 putFile16BitBE(file, element);
948 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
955 if (check != num_changed_custom_elements) /* should not happen */
956 Error(ERR_WARN, "inconsistent number of custom element properties");
961 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
962 int num_changed_custom_elements)
966 putFile16BitBE(file, num_changed_custom_elements);
968 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
970 int element = EL_CUSTOM_START + i;
972 if (element_info[element].change.target_element != EL_EMPTY_SPACE)
974 if (check < num_changed_custom_elements)
976 putFile16BitBE(file, element);
977 putFile16BitBE(file, element_info[element].change.target_element);
984 if (check != num_changed_custom_elements) /* should not happen */
985 Error(ERR_WARN, "inconsistent number of custom target elements");
989 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
990 int num_changed_custom_elements)
992 int i, j, x, y, check = 0;
994 putFile16BitBE(file, num_changed_custom_elements);
996 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
998 int element = EL_CUSTOM_START + i;
1000 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1002 if (check < num_changed_custom_elements)
1004 putFile16BitBE(file, element);
1006 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1007 putFile8Bit(file, element_info[element].description[j]);
1009 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1011 /* some free bytes for future properties and padding */
1012 WriteUnusedBytesToFile(file, 7);
1014 putFile8Bit(file, element_info[element].use_gfx_element);
1015 putFile16BitBE(file, element_info[element].gfx_element);
1017 putFile8Bit(file, element_info[element].score);
1018 putFile8Bit(file, element_info[element].gem_count);
1020 putFile16BitBE(file, element_info[element].push_delay_fixed);
1021 putFile16BitBE(file, element_info[element].push_delay_random);
1022 putFile16BitBE(file, element_info[element].move_delay_fixed);
1023 putFile16BitBE(file, element_info[element].move_delay_random);
1025 putFile16BitBE(file, element_info[element].move_pattern);
1026 putFile8Bit(file, element_info[element].move_direction_initial);
1027 putFile8Bit(file, element_info[element].move_stepsize);
1031 putFile16BitBE(file, element_info[element].content[x][y]);
1033 putFile32BitBE(file, element_info[element].change.events);
1035 putFile16BitBE(file, element_info[element].change.target_element);
1037 putFile16BitBE(file, element_info[element].change.delay_fixed);
1038 putFile16BitBE(file, element_info[element].change.delay_random);
1039 putFile16BitBE(file, element_info[element].change.delay_frames);
1041 putFile16BitBE(file, element_info[element].change.trigger_element);
1043 putFile8Bit(file, element_info[element].change.explode);
1044 putFile8Bit(file, element_info[element].change.use_content);
1045 putFile8Bit(file, element_info[element].change.only_complete);
1046 putFile8Bit(file, element_info[element].change.use_random_change);
1048 putFile8Bit(file, element_info[element].change.random);
1049 putFile8Bit(file, element_info[element].change.power);
1053 putFile16BitBE(file, element_info[element].change.content[x][y]);
1055 putFile8Bit(file, element_info[element].slippery_type);
1057 /* some free bytes for future properties and padding */
1058 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1065 if (check != num_changed_custom_elements) /* should not happen */
1066 Error(ERR_WARN, "inconsistent number of custom element properties");
1069 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1071 int body_chunk_size;
1072 int num_changed_custom_elements = 0;
1073 int level_chunk_CUS3_size;
1077 if (!(file = fopen(filename, MODE_WRITE)))
1079 Error(ERR_WARN, "cannot save level file '%s'", filename);
1083 level->file_version = FILE_VERSION_ACTUAL;
1084 level->game_version = GAME_VERSION_ACTUAL;
1086 /* check level field for 16-bit elements */
1087 level->encoding_16bit_field = FALSE;
1088 for(y=0; y<level->fieldy; y++)
1089 for(x=0; x<level->fieldx; x++)
1090 if (level->field[x][y] > 255)
1091 level->encoding_16bit_field = TRUE;
1093 /* check yamyam content for 16-bit elements */
1094 level->encoding_16bit_yamyam = FALSE;
1095 for(i=0; i<level->num_yamyam_contents; i++)
1098 if (level->yamyam_content[i][x][y] > 255)
1099 level->encoding_16bit_yamyam = TRUE;
1101 /* check amoeba content for 16-bit elements */
1102 level->encoding_16bit_amoeba = FALSE;
1103 if (level->amoeba_content > 255)
1104 level->encoding_16bit_amoeba = TRUE;
1106 /* calculate size of "BODY" chunk */
1108 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1110 /* check for non-standard custom elements and calculate "CUS3" chunk size */
1111 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1112 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1113 num_changed_custom_elements++;
1114 level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1116 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1117 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1119 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1120 SaveLevel_VERS(file, level);
1122 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1123 SaveLevel_HEAD(file, level);
1125 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1126 SaveLevel_AUTH(file, level);
1128 putFileChunkBE(file, "BODY", body_chunk_size);
1129 SaveLevel_BODY(file, level);
1131 if (level->encoding_16bit_yamyam ||
1132 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1134 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1135 SaveLevel_CNT2(file, level, EL_YAMYAM);
1138 if (level->encoding_16bit_amoeba)
1140 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1141 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1144 if (num_changed_custom_elements > 0 && !level->use_custom_template)
1146 putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1147 SaveLevel_CUS3(file, level, num_changed_custom_elements);
1152 SetFilePermissions(filename, PERMS_PRIVATE);
1155 void SaveLevel(int level_nr)
1157 char *filename = getLevelFilename(level_nr);
1159 SaveLevelFromFilename(&level, filename);
1162 void SaveLevelTemplate()
1164 char *filename = getLevelFilename(-1);
1166 SaveLevelFromFilename(&level, filename);
1169 void DumpLevel(struct LevelInfo *level)
1171 printf_line("-", 79);
1172 printf("Level xxx (file version %08d, game version %08d)\n",
1173 level->file_version, level->game_version);
1174 printf_line("-", 79);
1176 printf("Level Author: '%s'\n", level->author);
1177 printf("Level Title: '%s'\n", level->name);
1179 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1181 printf("Level Time: %d seconds\n", level->time);
1182 printf("Gems needed: %d\n", level->gems_needed);
1184 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1185 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1186 printf("Time for Light: %d seconds\n", level->time_light);
1187 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1189 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1191 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1192 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1193 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1195 printf_line("-", 79);
1199 /* ========================================================================= */
1200 /* tape file functions */
1201 /* ========================================================================= */
1203 static void setTapeInfoToDefaults()
1207 /* always start with reliable default values (empty tape) */
1210 /* default values (also for pre-1.2 tapes) with only the first player */
1211 tape.player_participates[0] = TRUE;
1212 for(i=1; i<MAX_PLAYERS; i++)
1213 tape.player_participates[i] = FALSE;
1215 /* at least one (default: the first) player participates in every tape */
1216 tape.num_participating_players = 1;
1218 tape.level_nr = level_nr;
1220 tape.changed = FALSE;
1222 tape.recording = FALSE;
1223 tape.playing = FALSE;
1224 tape.pausing = FALSE;
1227 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1229 tape->file_version = getFileVersion(file);
1230 tape->game_version = getFileVersion(file);
1235 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1239 tape->random_seed = getFile32BitBE(file);
1240 tape->date = getFile32BitBE(file);
1241 tape->length = getFile32BitBE(file);
1243 /* read header fields that are new since version 1.2 */
1244 if (tape->file_version >= FILE_VERSION_1_2)
1246 byte store_participating_players = getFile8Bit(file);
1249 /* since version 1.2, tapes store which players participate in the tape */
1250 tape->num_participating_players = 0;
1251 for(i=0; i<MAX_PLAYERS; i++)
1253 tape->player_participates[i] = FALSE;
1255 if (store_participating_players & (1 << i))
1257 tape->player_participates[i] = TRUE;
1258 tape->num_participating_players++;
1262 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1264 engine_version = getFileVersion(file);
1265 if (engine_version > 0)
1266 tape->engine_version = engine_version;
1272 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1274 int level_identifier_size;
1277 level_identifier_size = getFile16BitBE(file);
1279 tape->level_identifier =
1280 checked_realloc(tape->level_identifier, level_identifier_size);
1282 for(i=0; i < level_identifier_size; i++)
1283 tape->level_identifier[i] = getFile8Bit(file);
1285 tape->level_nr = getFile16BitBE(file);
1287 chunk_size = 2 + level_identifier_size + 2;
1292 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1295 int chunk_size_expected =
1296 (tape->num_participating_players + 1) * tape->length;
1298 if (chunk_size_expected != chunk_size)
1300 ReadUnusedBytesFromFile(file, chunk_size);
1301 return chunk_size_expected;
1304 for(i=0; i<tape->length; i++)
1306 if (i >= MAX_TAPELEN)
1309 for(j=0; j<MAX_PLAYERS; j++)
1311 tape->pos[i].action[j] = MV_NO_MOVING;
1313 if (tape->player_participates[j])
1314 tape->pos[i].action[j] = getFile8Bit(file);
1317 tape->pos[i].delay = getFile8Bit(file);
1319 if (tape->file_version == FILE_VERSION_1_0)
1321 /* eliminate possible diagonal moves in old tapes */
1322 /* this is only for backward compatibility */
1324 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1325 byte action = tape->pos[i].action[0];
1326 int k, num_moves = 0;
1330 if (action & joy_dir[k])
1332 tape->pos[i + num_moves].action[0] = joy_dir[k];
1334 tape->pos[i + num_moves].delay = 0;
1343 tape->length += num_moves;
1346 else if (tape->file_version < FILE_VERSION_2_0)
1348 /* convert pre-2.0 tapes to new tape format */
1350 if (tape->pos[i].delay > 1)
1353 tape->pos[i + 1] = tape->pos[i];
1354 tape->pos[i + 1].delay = 1;
1357 for(j=0; j<MAX_PLAYERS; j++)
1358 tape->pos[i].action[j] = MV_NO_MOVING;
1359 tape->pos[i].delay--;
1370 if (i != tape->length)
1371 chunk_size = (tape->num_participating_players + 1) * i;
1376 void LoadTapeFromFilename(char *filename)
1378 char cookie[MAX_LINE_LEN];
1379 char chunk_name[CHUNK_ID_LEN + 1];
1383 /* always start with reliable default values */
1384 setTapeInfoToDefaults();
1386 if (!(file = fopen(filename, MODE_READ)))
1389 getFileChunkBE(file, chunk_name, NULL);
1390 if (strcmp(chunk_name, "RND1") == 0)
1392 getFile32BitBE(file); /* not used */
1394 getFileChunkBE(file, chunk_name, NULL);
1395 if (strcmp(chunk_name, "TAPE") != 0)
1397 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1402 else /* check for pre-2.0 file format with cookie string */
1404 strcpy(cookie, chunk_name);
1405 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1406 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1407 cookie[strlen(cookie) - 1] = '\0';
1409 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1411 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1416 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1418 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1423 /* pre-2.0 tape files have no game version, so use file version here */
1424 tape.game_version = tape.file_version;
1427 if (tape.file_version < FILE_VERSION_1_2)
1429 /* tape files from versions before 1.2.0 without chunk structure */
1430 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1431 LoadTape_BODY(file, 2 * tape.length, &tape);
1439 int (*loader)(FILE *, int, struct TapeInfo *);
1443 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1444 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1445 { "INFO", -1, LoadTape_INFO },
1446 { "BODY", -1, LoadTape_BODY },
1450 while (getFileChunkBE(file, chunk_name, &chunk_size))
1454 while (chunk_info[i].name != NULL &&
1455 strcmp(chunk_name, chunk_info[i].name) != 0)
1458 if (chunk_info[i].name == NULL)
1460 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1461 chunk_name, filename);
1462 ReadUnusedBytesFromFile(file, chunk_size);
1464 else if (chunk_info[i].size != -1 &&
1465 chunk_info[i].size != chunk_size)
1467 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1468 chunk_size, chunk_name, filename);
1469 ReadUnusedBytesFromFile(file, chunk_size);
1473 /* call function to load this tape chunk */
1474 int chunk_size_expected =
1475 (chunk_info[i].loader)(file, chunk_size, &tape);
1477 /* the size of some chunks cannot be checked before reading other
1478 chunks first (like "HEAD" and "BODY") that contain some header
1479 information, so check them here */
1480 if (chunk_size_expected != chunk_size)
1482 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1483 chunk_size, chunk_name, filename);
1491 tape.length_seconds = GetTapeLength();
1494 printf("tape version: %d\n", tape.game_version);
1498 void LoadTape(int level_nr)
1500 char *filename = getTapeFilename(level_nr);
1502 LoadTapeFromFilename(filename);
1505 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1507 putFileVersion(file, tape->file_version);
1508 putFileVersion(file, tape->game_version);
1511 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1514 byte store_participating_players = 0;
1516 /* set bits for participating players for compact storage */
1517 for(i=0; i<MAX_PLAYERS; i++)
1518 if (tape->player_participates[i])
1519 store_participating_players |= (1 << i);
1521 putFile32BitBE(file, tape->random_seed);
1522 putFile32BitBE(file, tape->date);
1523 putFile32BitBE(file, tape->length);
1525 putFile8Bit(file, store_participating_players);
1527 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1528 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1530 putFileVersion(file, tape->engine_version);
1533 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1535 int level_identifier_size = strlen(tape->level_identifier) + 1;
1538 putFile16BitBE(file, level_identifier_size);
1540 for(i=0; i < level_identifier_size; i++)
1541 putFile8Bit(file, tape->level_identifier[i]);
1543 putFile16BitBE(file, tape->level_nr);
1546 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1550 for(i=0; i<tape->length; i++)
1552 for(j=0; j<MAX_PLAYERS; j++)
1553 if (tape->player_participates[j])
1554 putFile8Bit(file, tape->pos[i].action[j]);
1556 putFile8Bit(file, tape->pos[i].delay);
1560 void SaveTape(int level_nr)
1562 char *filename = getTapeFilename(level_nr);
1564 boolean new_tape = TRUE;
1565 int num_participating_players = 0;
1566 int info_chunk_size;
1567 int body_chunk_size;
1570 InitTapeDirectory(leveldir_current->filename);
1572 /* if a tape still exists, ask to overwrite it */
1573 if (access(filename, F_OK) == 0)
1576 if (!Request("Replace old tape ?", REQ_ASK))
1580 if (!(file = fopen(filename, MODE_WRITE)))
1582 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1586 tape.file_version = FILE_VERSION_ACTUAL;
1587 tape.game_version = GAME_VERSION_ACTUAL;
1589 /* count number of participating players */
1590 for(i=0; i<MAX_PLAYERS; i++)
1591 if (tape.player_participates[i])
1592 num_participating_players++;
1594 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1595 body_chunk_size = (num_participating_players + 1) * tape.length;
1597 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1598 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1600 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1601 SaveTape_VERS(file, &tape);
1603 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1604 SaveTape_HEAD(file, &tape);
1606 putFileChunkBE(file, "INFO", info_chunk_size);
1607 SaveTape_INFO(file, &tape);
1609 putFileChunkBE(file, "BODY", body_chunk_size);
1610 SaveTape_BODY(file, &tape);
1614 SetFilePermissions(filename, PERMS_PRIVATE);
1616 tape.changed = FALSE;
1619 Request("tape saved !", REQ_CONFIRM);
1622 void DumpTape(struct TapeInfo *tape)
1626 if (TAPE_IS_EMPTY(*tape))
1628 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1632 printf_line("-", 79);
1633 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1634 tape->level_nr, tape->file_version, tape->game_version);
1635 printf("Level series identifier: '%s'\n", tape->level_identifier);
1636 printf_line("-", 79);
1638 for(i=0; i<tape->length; i++)
1640 if (i >= MAX_TAPELEN)
1643 printf("%03d: ", i);
1645 for(j=0; j<MAX_PLAYERS; j++)
1647 if (tape->player_participates[j])
1649 int action = tape->pos[i].action[j];
1651 printf("%d:%02x ", j, action);
1652 printf("[%c%c%c%c|%c%c] - ",
1653 (action & JOY_LEFT ? '<' : ' '),
1654 (action & JOY_RIGHT ? '>' : ' '),
1655 (action & JOY_UP ? '^' : ' '),
1656 (action & JOY_DOWN ? 'v' : ' '),
1657 (action & JOY_BUTTON_1 ? '1' : ' '),
1658 (action & JOY_BUTTON_2 ? '2' : ' '));
1662 printf("(%03d)\n", tape->pos[i].delay);
1665 printf_line("-", 79);
1669 /* ========================================================================= */
1670 /* score file functions */
1671 /* ========================================================================= */
1673 void LoadScore(int level_nr)
1676 char *filename = getScoreFilename(level_nr);
1677 char cookie[MAX_LINE_LEN];
1678 char line[MAX_LINE_LEN];
1682 /* always start with reliable default values */
1683 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1685 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1686 highscore[i].Score = 0;
1689 if (!(file = fopen(filename, MODE_READ)))
1692 /* check file identifier */
1693 fgets(cookie, MAX_LINE_LEN, file);
1694 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1695 cookie[strlen(cookie) - 1] = '\0';
1697 if (!checkCookieString(cookie, SCORE_COOKIE))
1699 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1704 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1706 fscanf(file, "%d", &highscore[i].Score);
1707 fgets(line, MAX_LINE_LEN, file);
1709 if (line[strlen(line) - 1] == '\n')
1710 line[strlen(line) - 1] = '\0';
1712 for (line_ptr = line; *line_ptr; line_ptr++)
1714 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1716 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1717 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1726 void SaveScore(int level_nr)
1729 char *filename = getScoreFilename(level_nr);
1732 InitScoreDirectory(leveldir_current->filename);
1734 if (!(file = fopen(filename, MODE_WRITE)))
1736 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1740 fprintf(file, "%s\n\n", SCORE_COOKIE);
1742 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1743 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1747 SetFilePermissions(filename, PERMS_PUBLIC);
1751 /* ========================================================================= */
1752 /* setup file functions */
1753 /* ========================================================================= */
1755 #define TOKEN_STR_PLAYER_PREFIX "player_"
1758 #define SETUP_TOKEN_PLAYER_NAME 0
1759 #define SETUP_TOKEN_SOUND 1
1760 #define SETUP_TOKEN_SOUND_LOOPS 2
1761 #define SETUP_TOKEN_SOUND_MUSIC 3
1762 #define SETUP_TOKEN_SOUND_SIMPLE 4
1763 #define SETUP_TOKEN_TOONS 5
1764 #define SETUP_TOKEN_SCROLL_DELAY 6
1765 #define SETUP_TOKEN_SOFT_SCROLLING 7
1766 #define SETUP_TOKEN_FADING 8
1767 #define SETUP_TOKEN_AUTORECORD 9
1768 #define SETUP_TOKEN_QUICK_DOORS 10
1769 #define SETUP_TOKEN_TEAM_MODE 11
1770 #define SETUP_TOKEN_HANDICAP 12
1771 #define SETUP_TOKEN_TIME_LIMIT 13
1772 #define SETUP_TOKEN_FULLSCREEN 14
1773 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1774 #define SETUP_TOKEN_GRAPHICS_SET 16
1775 #define SETUP_TOKEN_SOUNDS_SET 17
1776 #define SETUP_TOKEN_MUSIC_SET 18
1777 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1778 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1779 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1781 #define NUM_GLOBAL_SETUP_TOKENS 22
1784 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1785 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1786 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1787 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1788 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1789 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1790 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1791 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1792 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1794 #define NUM_EDITOR_SETUP_TOKENS 9
1796 /* shortcut setup */
1797 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1798 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1799 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1801 #define NUM_SHORTCUT_SETUP_TOKENS 3
1804 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1805 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1806 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1807 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1808 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1809 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1810 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1811 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1812 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1813 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1814 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1815 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1816 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1817 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1818 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1819 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1821 #define NUM_PLAYER_SETUP_TOKENS 16
1824 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1825 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1827 #define NUM_SYSTEM_SETUP_TOKENS 2
1830 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1832 #define NUM_OPTIONS_SETUP_TOKENS 1
1835 static struct SetupInfo si;
1836 static struct SetupEditorInfo sei;
1837 static struct SetupShortcutInfo ssi;
1838 static struct SetupInputInfo sii;
1839 static struct SetupSystemInfo syi;
1840 static struct OptionInfo soi;
1842 static struct TokenInfo global_setup_tokens[] =
1844 { TYPE_STRING, &si.player_name, "player_name" },
1845 { TYPE_SWITCH, &si.sound, "sound" },
1846 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1847 { TYPE_SWITCH, &si.sound_music, "background_music" },
1848 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1849 { TYPE_SWITCH, &si.toons, "toons" },
1850 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1851 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1852 { TYPE_SWITCH, &si.fading, "screen_fading" },
1853 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1854 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1855 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1856 { TYPE_SWITCH, &si.handicap, "handicap" },
1857 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1858 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1859 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1860 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1861 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1862 { TYPE_STRING, &si.music_set, "music_set" },
1863 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1864 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1865 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1868 static struct TokenInfo editor_setup_tokens[] =
1870 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1871 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1872 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1873 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1874 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1875 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1876 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1877 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1878 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1881 static struct TokenInfo shortcut_setup_tokens[] =
1883 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1884 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1885 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1888 static struct TokenInfo player_setup_tokens[] =
1890 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1891 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1892 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1893 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1894 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1895 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1896 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1897 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1898 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1899 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1900 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1901 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1902 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1903 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1904 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1905 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1908 static struct TokenInfo system_setup_tokens[] =
1910 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1911 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1914 static struct TokenInfo options_setup_tokens[] =
1916 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1919 static char *get_corrected_login_name(char *login_name)
1921 /* needed because player name must be a fixed length string */
1922 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1924 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1925 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1927 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1928 if (strchr(login_name_new, ' '))
1929 *strchr(login_name_new, ' ') = '\0';
1931 return login_name_new;
1934 static void setSetupInfoToDefaults(struct SetupInfo *si)
1938 si->player_name = get_corrected_login_name(getLoginName());
1941 si->sound_loops = TRUE;
1942 si->sound_music = TRUE;
1943 si->sound_simple = TRUE;
1945 si->double_buffering = TRUE;
1946 si->direct_draw = !si->double_buffering;
1947 si->scroll_delay = TRUE;
1948 si->soft_scrolling = TRUE;
1950 si->autorecord = TRUE;
1951 si->quick_doors = FALSE;
1952 si->team_mode = FALSE;
1953 si->handicap = TRUE;
1954 si->time_limit = TRUE;
1955 si->fullscreen = FALSE;
1956 si->ask_on_escape = TRUE;
1958 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1959 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1960 si->music_set = getStringCopy(MUSIC_SUBDIR);
1961 si->override_level_graphics = FALSE;
1962 si->override_level_sounds = FALSE;
1963 si->override_level_music = FALSE;
1965 si->editor.el_boulderdash = TRUE;
1966 si->editor.el_emerald_mine = TRUE;
1967 si->editor.el_more = TRUE;
1968 si->editor.el_sokoban = TRUE;
1969 si->editor.el_supaplex = TRUE;
1970 si->editor.el_diamond_caves = TRUE;
1971 si->editor.el_dx_boulderdash = TRUE;
1972 si->editor.el_chars = TRUE;
1973 si->editor.el_custom = TRUE;
1975 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1976 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1977 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1979 for (i=0; i<MAX_PLAYERS; i++)
1981 si->input[i].use_joystick = FALSE;
1982 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1983 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1984 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1985 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1986 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1987 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1988 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1989 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1990 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1991 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1992 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1993 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1994 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1995 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1996 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1999 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2000 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2002 si->options.verbose = FALSE;
2005 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2009 if (!setup_file_hash)
2014 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2015 setSetupInfo(global_setup_tokens, i,
2016 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2021 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2022 setSetupInfo(editor_setup_tokens, i,
2023 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2026 /* shortcut setup */
2027 ssi = setup.shortcut;
2028 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2029 setSetupInfo(shortcut_setup_tokens, i,
2030 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2031 setup.shortcut = ssi;
2034 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2038 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2040 sii = setup.input[pnr];
2041 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2043 char full_token[100];
2045 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2046 setSetupInfo(player_setup_tokens, i,
2047 getHashEntry(setup_file_hash, full_token));
2049 setup.input[pnr] = sii;
2054 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2055 setSetupInfo(system_setup_tokens, i,
2056 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2060 soi = setup.options;
2061 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2062 setSetupInfo(options_setup_tokens, i,
2063 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2064 setup.options = soi;
2069 char *filename = getSetupFilename();
2070 SetupFileHash *setup_file_hash = NULL;
2072 /* always start with reliable default values */
2073 setSetupInfoToDefaults(&setup);
2075 setup_file_hash = loadSetupFileHash(filename);
2077 if (setup_file_hash)
2079 char *player_name_new;
2081 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2082 decodeSetupFileHash(setup_file_hash);
2084 setup.direct_draw = !setup.double_buffering;
2086 freeSetupFileHash(setup_file_hash);
2088 /* needed to work around problems with fixed length strings */
2089 player_name_new = get_corrected_login_name(setup.player_name);
2090 free(setup.player_name);
2091 setup.player_name = player_name_new;
2094 Error(ERR_WARN, "using default setup values");
2099 char *filename = getSetupFilename();
2103 InitUserDataDirectory();
2105 if (!(file = fopen(filename, MODE_WRITE)))
2107 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2111 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2112 getCookie("SETUP")));
2113 fprintf(file, "\n");
2117 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2119 /* just to make things nicer :) */
2120 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2121 i == SETUP_TOKEN_GRAPHICS_SET)
2122 fprintf(file, "\n");
2124 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2129 fprintf(file, "\n");
2130 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2131 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2133 /* shortcut setup */
2134 ssi = setup.shortcut;
2135 fprintf(file, "\n");
2136 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2137 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2140 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2144 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2145 fprintf(file, "\n");
2147 sii = setup.input[pnr];
2148 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2149 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2154 fprintf(file, "\n");
2155 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2156 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2159 soi = setup.options;
2160 fprintf(file, "\n");
2161 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2162 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2166 SetFilePermissions(filename, PERMS_PRIVATE);
2169 void LoadCustomElementDescriptions()
2171 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2172 SetupFileHash *setup_file_hash;
2175 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2177 if (element_info[i].custom_description != NULL)
2179 free(element_info[i].custom_description);
2180 element_info[i].custom_description = NULL;
2184 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2187 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2189 char *token = getStringCat2(element_info[i].token_name, ".name");
2190 char *value = getHashEntry(setup_file_hash, token);
2193 element_info[i].custom_description = getStringCopy(value);
2198 freeSetupFileHash(setup_file_hash);
2201 void LoadSpecialMenuDesignSettings()
2203 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2204 SetupFileHash *setup_file_hash;
2207 /* always start with reliable default values from default config */
2208 for (i=0; image_config_vars[i].token != NULL; i++)
2209 for (j=0; image_config[j].token != NULL; j++)
2210 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2211 *image_config_vars[i].value =
2212 get_integer_from_string(image_config[j].value);
2214 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2217 /* special case: initialize with default values that may be overwritten */
2218 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2220 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2221 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2222 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2224 if (value_x != NULL)
2225 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2226 if (value_y != NULL)
2227 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2228 if (list_size != NULL)
2229 menu.list_size[i] = get_integer_from_string(list_size);
2232 /* read (and overwrite with) values that may be specified in config file */
2233 for (i=0; image_config_vars[i].token != NULL; i++)
2235 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2238 *image_config_vars[i].value = get_integer_from_string(value);
2241 freeSetupFileHash(setup_file_hash);