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))
689 printf("::: This level is private or contributed: '%s'\n", filename);
692 /* For user contributed and private levels, use the version of
693 the game engine the levels were created for.
694 Since 2.0.1, the game engine version is now directly stored
695 in the level file (chunk "VERS"), so there is no need anymore
696 to set the game version from the file version (except for old,
697 pre-2.0 levels, where the game version is still taken from the
698 file format version used to store the level -- see above). */
700 /* do some special adjustments to support older level versions */
701 if (level->file_version == FILE_VERSION_1_0)
703 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
704 Error(ERR_WARN, "using high speed movement for player");
706 /* player was faster than monsters in (pre-)1.0 levels */
707 level->double_speed = TRUE;
710 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
711 if (level->game_version == VERSION_IDENT(2,0,1))
712 level->em_slippery_gems = TRUE;
717 printf("::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
718 leveldir_current->sort_priority, filename);
721 /* Always use the latest version of the game engine for all but
722 user contributed and private levels; this allows for actual
723 corrections in the game engine to take effect for existing,
724 converted levels (from "classic" or other existing games) to
725 make the game emulation more accurate, while (hopefully) not
726 breaking existing levels created from other players. */
728 level->game_version = GAME_VERSION_ACTUAL;
730 /* Set special EM style gems behaviour: EM style gems slip down from
731 normal, steel and growing wall. As this is a more fundamental change,
732 it seems better to set the default behaviour to "off" (as it is more
733 natural) and make it configurable in the level editor (as a property
734 of gem style elements). Already existing converted levels (neither
735 private nor contributed levels) are changed to the new behaviour. */
737 if (level->file_version < FILE_VERSION_2_0)
738 level->em_slippery_gems = TRUE;
741 /* map elements which have changed in newer versions */
742 for(y=0; y<level->fieldy; y++)
744 for(x=0; x<level->fieldx; x++)
746 int element = level->field[x][y];
748 if (level->game_version <= VERSION_IDENT(2,2,0))
750 /* map game font elements */
751 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
752 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
753 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
754 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
756 else if (level->game_version < VERSION_IDENT(3,0,0))
758 /* map Supaplex gravity tube elements */
759 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
760 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
761 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
762 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
766 level->field[x][y] = element;
770 /* copy elements to runtime playfield array */
771 for(x=0; x<MAX_LEV_FIELDX; x++)
772 for(y=0; y<MAX_LEV_FIELDY; y++)
773 Feld[x][y] = level->field[x][y];
775 /* initialize level size variables for faster access */
776 lev_fieldx = level->fieldx;
777 lev_fieldy = level->fieldy;
779 /* determine border element for this level */
782 /* initialize element properties for level editor etc. */
783 InitElementPropertiesEngine(level->game_version);
786 void LoadLevelTemplate(int level_nr)
788 char *filename = getLevelFilename(level_nr);
790 LoadLevelFromFilename(&level_template, filename);
792 ActivateLevelTemplate();
795 void LoadLevel(int level_nr)
797 char *filename = getLevelFilename(level_nr);
799 LoadLevelFromFilename(&level, filename);
801 if (level.use_custom_template)
802 LoadLevelTemplate(-1);
804 LoadLevel_InitLevel(&level, filename);
807 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
809 putFileVersion(file, level->file_version);
810 putFileVersion(file, level->game_version);
813 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
817 putFile8Bit(file, level->fieldx);
818 putFile8Bit(file, level->fieldy);
820 putFile16BitBE(file, level->time);
821 putFile16BitBE(file, level->gems_needed);
823 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
824 putFile8Bit(file, level->name[i]);
826 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
827 putFile8Bit(file, level->score[i]);
829 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
832 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
833 level->yamyam_content[i][x][y]));
834 putFile8Bit(file, level->amoeba_speed);
835 putFile8Bit(file, level->time_magic_wall);
836 putFile8Bit(file, level->time_wheel);
837 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
838 level->amoeba_content));
839 putFile8Bit(file, (level->double_speed ? 1 : 0));
840 putFile8Bit(file, (level->gravity ? 1 : 0));
841 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
842 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
844 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
846 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
849 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
853 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
854 putFile8Bit(file, level->author[i]);
857 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
861 for(y=0; y<level->fieldy; y++)
862 for(x=0; x<level->fieldx; x++)
863 if (level->encoding_16bit_field)
864 putFile16BitBE(file, level->field[x][y]);
866 putFile8Bit(file, level->field[x][y]);
870 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
874 putFile8Bit(file, EL_YAMYAM);
875 putFile8Bit(file, level->num_yamyam_contents);
876 putFile8Bit(file, 0);
877 putFile8Bit(file, 0);
879 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
882 if (level->encoding_16bit_field)
883 putFile16BitBE(file, level->yamyam_content[i][x][y]);
885 putFile8Bit(file, level->yamyam_content[i][x][y]);
889 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
892 int num_contents, content_xsize, content_ysize;
893 int content_array[MAX_ELEMENT_CONTENTS][3][3];
895 if (element == EL_YAMYAM)
897 num_contents = level->num_yamyam_contents;
901 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
904 content_array[i][x][y] = level->yamyam_content[i][x][y];
906 else if (element == EL_BD_AMOEBA)
912 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
915 content_array[i][x][y] = EL_EMPTY;
916 content_array[0][0][0] = level->amoeba_content;
920 /* chunk header already written -- write empty chunk data */
921 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
923 Error(ERR_WARN, "cannot save content for element '%d'", element);
927 putFile16BitBE(file, element);
928 putFile8Bit(file, num_contents);
929 putFile8Bit(file, content_xsize);
930 putFile8Bit(file, content_ysize);
932 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
934 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
937 putFile16BitBE(file, content_array[i][x][y]);
941 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
942 int num_changed_custom_elements)
946 putFile16BitBE(file, num_changed_custom_elements);
948 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
950 int element = EL_CUSTOM_START + i;
952 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
954 if (check < num_changed_custom_elements)
956 putFile16BitBE(file, element);
957 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
964 if (check != num_changed_custom_elements) /* should not happen */
965 Error(ERR_WARN, "inconsistent number of custom element properties");
970 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
971 int num_changed_custom_elements)
975 putFile16BitBE(file, num_changed_custom_elements);
977 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
979 int element = EL_CUSTOM_START + i;
981 if (element_info[element].change.target_element != EL_EMPTY_SPACE)
983 if (check < num_changed_custom_elements)
985 putFile16BitBE(file, element);
986 putFile16BitBE(file, element_info[element].change.target_element);
993 if (check != num_changed_custom_elements) /* should not happen */
994 Error(ERR_WARN, "inconsistent number of custom target elements");
998 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
999 int num_changed_custom_elements)
1001 int i, j, x, y, check = 0;
1003 putFile16BitBE(file, num_changed_custom_elements);
1005 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1007 int element = EL_CUSTOM_START + i;
1009 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1011 if (check < num_changed_custom_elements)
1013 putFile16BitBE(file, element);
1015 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1016 putFile8Bit(file, element_info[element].description[j]);
1018 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1020 /* some free bytes for future properties and padding */
1021 WriteUnusedBytesToFile(file, 7);
1023 putFile8Bit(file, element_info[element].use_gfx_element);
1024 putFile16BitBE(file, element_info[element].gfx_element);
1026 putFile8Bit(file, element_info[element].score);
1027 putFile8Bit(file, element_info[element].gem_count);
1029 putFile16BitBE(file, element_info[element].push_delay_fixed);
1030 putFile16BitBE(file, element_info[element].push_delay_random);
1031 putFile16BitBE(file, element_info[element].move_delay_fixed);
1032 putFile16BitBE(file, element_info[element].move_delay_random);
1034 putFile16BitBE(file, element_info[element].move_pattern);
1035 putFile8Bit(file, element_info[element].move_direction_initial);
1036 putFile8Bit(file, element_info[element].move_stepsize);
1040 putFile16BitBE(file, element_info[element].content[x][y]);
1042 putFile32BitBE(file, element_info[element].change.events);
1044 putFile16BitBE(file, element_info[element].change.target_element);
1046 putFile16BitBE(file, element_info[element].change.delay_fixed);
1047 putFile16BitBE(file, element_info[element].change.delay_random);
1048 putFile16BitBE(file, element_info[element].change.delay_frames);
1050 putFile16BitBE(file, element_info[element].change.trigger_element);
1052 putFile8Bit(file, element_info[element].change.explode);
1053 putFile8Bit(file, element_info[element].change.use_content);
1054 putFile8Bit(file, element_info[element].change.only_complete);
1055 putFile8Bit(file, element_info[element].change.use_random_change);
1057 putFile8Bit(file, element_info[element].change.random);
1058 putFile8Bit(file, element_info[element].change.power);
1062 putFile16BitBE(file, element_info[element].change.content[x][y]);
1064 putFile8Bit(file, element_info[element].slippery_type);
1066 /* some free bytes for future properties and padding */
1067 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1074 if (check != num_changed_custom_elements) /* should not happen */
1075 Error(ERR_WARN, "inconsistent number of custom element properties");
1078 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1080 int body_chunk_size;
1081 int num_changed_custom_elements = 0;
1082 int level_chunk_CUS3_size;
1086 if (!(file = fopen(filename, MODE_WRITE)))
1088 Error(ERR_WARN, "cannot save level file '%s'", filename);
1092 level->file_version = FILE_VERSION_ACTUAL;
1093 level->game_version = GAME_VERSION_ACTUAL;
1095 /* check level field for 16-bit elements */
1096 level->encoding_16bit_field = FALSE;
1097 for(y=0; y<level->fieldy; y++)
1098 for(x=0; x<level->fieldx; x++)
1099 if (level->field[x][y] > 255)
1100 level->encoding_16bit_field = TRUE;
1102 /* check yamyam content for 16-bit elements */
1103 level->encoding_16bit_yamyam = FALSE;
1104 for(i=0; i<level->num_yamyam_contents; i++)
1107 if (level->yamyam_content[i][x][y] > 255)
1108 level->encoding_16bit_yamyam = TRUE;
1110 /* check amoeba content for 16-bit elements */
1111 level->encoding_16bit_amoeba = FALSE;
1112 if (level->amoeba_content > 255)
1113 level->encoding_16bit_amoeba = TRUE;
1115 /* calculate size of "BODY" chunk */
1117 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1119 /* check for non-standard custom elements and calculate "CUS3" chunk size */
1120 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1121 if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1122 num_changed_custom_elements++;
1123 level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1125 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1126 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1128 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1129 SaveLevel_VERS(file, level);
1131 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1132 SaveLevel_HEAD(file, level);
1134 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1135 SaveLevel_AUTH(file, level);
1137 putFileChunkBE(file, "BODY", body_chunk_size);
1138 SaveLevel_BODY(file, level);
1140 if (level->encoding_16bit_yamyam ||
1141 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1143 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1144 SaveLevel_CNT2(file, level, EL_YAMYAM);
1147 if (level->encoding_16bit_amoeba)
1149 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1150 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1153 if (num_changed_custom_elements > 0 && !level->use_custom_template)
1155 putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1156 SaveLevel_CUS3(file, level, num_changed_custom_elements);
1161 SetFilePermissions(filename, PERMS_PRIVATE);
1164 void SaveLevel(int level_nr)
1166 char *filename = getLevelFilename(level_nr);
1168 SaveLevelFromFilename(&level, filename);
1171 void SaveLevelTemplate()
1173 char *filename = getLevelFilename(-1);
1175 SaveLevelFromFilename(&level, filename);
1178 void DumpLevel(struct LevelInfo *level)
1180 printf_line("-", 79);
1181 printf("Level xxx (file version %08d, game version %08d)\n",
1182 level->file_version, level->game_version);
1183 printf_line("-", 79);
1185 printf("Level Author: '%s'\n", level->author);
1186 printf("Level Title: '%s'\n", level->name);
1188 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1190 printf("Level Time: %d seconds\n", level->time);
1191 printf("Gems needed: %d\n", level->gems_needed);
1193 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1194 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1195 printf("Time for Light: %d seconds\n", level->time_light);
1196 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1198 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1200 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1201 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1202 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1204 printf_line("-", 79);
1208 /* ========================================================================= */
1209 /* tape file functions */
1210 /* ========================================================================= */
1212 static void setTapeInfoToDefaults()
1216 /* always start with reliable default values (empty tape) */
1219 /* default values (also for pre-1.2 tapes) with only the first player */
1220 tape.player_participates[0] = TRUE;
1221 for(i=1; i<MAX_PLAYERS; i++)
1222 tape.player_participates[i] = FALSE;
1224 /* at least one (default: the first) player participates in every tape */
1225 tape.num_participating_players = 1;
1227 tape.level_nr = level_nr;
1229 tape.changed = FALSE;
1231 tape.recording = FALSE;
1232 tape.playing = FALSE;
1233 tape.pausing = FALSE;
1236 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1238 tape->file_version = getFileVersion(file);
1239 tape->game_version = getFileVersion(file);
1244 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1248 tape->random_seed = getFile32BitBE(file);
1249 tape->date = getFile32BitBE(file);
1250 tape->length = getFile32BitBE(file);
1252 /* read header fields that are new since version 1.2 */
1253 if (tape->file_version >= FILE_VERSION_1_2)
1255 byte store_participating_players = getFile8Bit(file);
1258 /* since version 1.2, tapes store which players participate in the tape */
1259 tape->num_participating_players = 0;
1260 for(i=0; i<MAX_PLAYERS; i++)
1262 tape->player_participates[i] = FALSE;
1264 if (store_participating_players & (1 << i))
1266 tape->player_participates[i] = TRUE;
1267 tape->num_participating_players++;
1271 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1273 engine_version = getFileVersion(file);
1274 if (engine_version > 0)
1275 tape->engine_version = engine_version;
1281 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1283 int level_identifier_size;
1286 level_identifier_size = getFile16BitBE(file);
1288 tape->level_identifier =
1289 checked_realloc(tape->level_identifier, level_identifier_size);
1291 for(i=0; i < level_identifier_size; i++)
1292 tape->level_identifier[i] = getFile8Bit(file);
1294 tape->level_nr = getFile16BitBE(file);
1296 chunk_size = 2 + level_identifier_size + 2;
1301 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1304 int chunk_size_expected =
1305 (tape->num_participating_players + 1) * tape->length;
1307 if (chunk_size_expected != chunk_size)
1309 ReadUnusedBytesFromFile(file, chunk_size);
1310 return chunk_size_expected;
1313 for(i=0; i<tape->length; i++)
1315 if (i >= MAX_TAPELEN)
1318 for(j=0; j<MAX_PLAYERS; j++)
1320 tape->pos[i].action[j] = MV_NO_MOVING;
1322 if (tape->player_participates[j])
1323 tape->pos[i].action[j] = getFile8Bit(file);
1326 tape->pos[i].delay = getFile8Bit(file);
1328 if (tape->file_version == FILE_VERSION_1_0)
1330 /* eliminate possible diagonal moves in old tapes */
1331 /* this is only for backward compatibility */
1333 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1334 byte action = tape->pos[i].action[0];
1335 int k, num_moves = 0;
1339 if (action & joy_dir[k])
1341 tape->pos[i + num_moves].action[0] = joy_dir[k];
1343 tape->pos[i + num_moves].delay = 0;
1352 tape->length += num_moves;
1355 else if (tape->file_version < FILE_VERSION_2_0)
1357 /* convert pre-2.0 tapes to new tape format */
1359 if (tape->pos[i].delay > 1)
1362 tape->pos[i + 1] = tape->pos[i];
1363 tape->pos[i + 1].delay = 1;
1366 for(j=0; j<MAX_PLAYERS; j++)
1367 tape->pos[i].action[j] = MV_NO_MOVING;
1368 tape->pos[i].delay--;
1379 if (i != tape->length)
1380 chunk_size = (tape->num_participating_players + 1) * i;
1385 void LoadTapeFromFilename(char *filename)
1387 char cookie[MAX_LINE_LEN];
1388 char chunk_name[CHUNK_ID_LEN + 1];
1392 /* always start with reliable default values */
1393 setTapeInfoToDefaults();
1395 if (!(file = fopen(filename, MODE_READ)))
1398 getFileChunkBE(file, chunk_name, NULL);
1399 if (strcmp(chunk_name, "RND1") == 0)
1401 getFile32BitBE(file); /* not used */
1403 getFileChunkBE(file, chunk_name, NULL);
1404 if (strcmp(chunk_name, "TAPE") != 0)
1406 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1411 else /* check for pre-2.0 file format with cookie string */
1413 strcpy(cookie, chunk_name);
1414 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1415 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1416 cookie[strlen(cookie) - 1] = '\0';
1418 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1420 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1425 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1427 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1432 /* pre-2.0 tape files have no game version, so use file version here */
1433 tape.game_version = tape.file_version;
1436 if (tape.file_version < FILE_VERSION_1_2)
1438 /* tape files from versions before 1.2.0 without chunk structure */
1439 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1440 LoadTape_BODY(file, 2 * tape.length, &tape);
1448 int (*loader)(FILE *, int, struct TapeInfo *);
1452 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1453 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1454 { "INFO", -1, LoadTape_INFO },
1455 { "BODY", -1, LoadTape_BODY },
1459 while (getFileChunkBE(file, chunk_name, &chunk_size))
1463 while (chunk_info[i].name != NULL &&
1464 strcmp(chunk_name, chunk_info[i].name) != 0)
1467 if (chunk_info[i].name == NULL)
1469 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1470 chunk_name, filename);
1471 ReadUnusedBytesFromFile(file, chunk_size);
1473 else if (chunk_info[i].size != -1 &&
1474 chunk_info[i].size != chunk_size)
1476 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1477 chunk_size, chunk_name, filename);
1478 ReadUnusedBytesFromFile(file, chunk_size);
1482 /* call function to load this tape chunk */
1483 int chunk_size_expected =
1484 (chunk_info[i].loader)(file, chunk_size, &tape);
1486 /* the size of some chunks cannot be checked before reading other
1487 chunks first (like "HEAD" and "BODY") that contain some header
1488 information, so check them here */
1489 if (chunk_size_expected != chunk_size)
1491 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1492 chunk_size, chunk_name, filename);
1500 tape.length_seconds = GetTapeLength();
1503 printf("tape version: %d\n", tape.game_version);
1507 void LoadTape(int level_nr)
1509 char *filename = getTapeFilename(level_nr);
1511 LoadTapeFromFilename(filename);
1514 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1516 putFileVersion(file, tape->file_version);
1517 putFileVersion(file, tape->game_version);
1520 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1523 byte store_participating_players = 0;
1525 /* set bits for participating players for compact storage */
1526 for(i=0; i<MAX_PLAYERS; i++)
1527 if (tape->player_participates[i])
1528 store_participating_players |= (1 << i);
1530 putFile32BitBE(file, tape->random_seed);
1531 putFile32BitBE(file, tape->date);
1532 putFile32BitBE(file, tape->length);
1534 putFile8Bit(file, store_participating_players);
1536 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1537 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1539 putFileVersion(file, tape->engine_version);
1542 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1544 int level_identifier_size = strlen(tape->level_identifier) + 1;
1547 putFile16BitBE(file, level_identifier_size);
1549 for(i=0; i < level_identifier_size; i++)
1550 putFile8Bit(file, tape->level_identifier[i]);
1552 putFile16BitBE(file, tape->level_nr);
1555 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1559 for(i=0; i<tape->length; i++)
1561 for(j=0; j<MAX_PLAYERS; j++)
1562 if (tape->player_participates[j])
1563 putFile8Bit(file, tape->pos[i].action[j]);
1565 putFile8Bit(file, tape->pos[i].delay);
1569 void SaveTape(int level_nr)
1571 char *filename = getTapeFilename(level_nr);
1573 boolean new_tape = TRUE;
1574 int num_participating_players = 0;
1575 int info_chunk_size;
1576 int body_chunk_size;
1579 InitTapeDirectory(leveldir_current->filename);
1581 /* if a tape still exists, ask to overwrite it */
1582 if (access(filename, F_OK) == 0)
1585 if (!Request("Replace old tape ?", REQ_ASK))
1589 if (!(file = fopen(filename, MODE_WRITE)))
1591 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1595 tape.file_version = FILE_VERSION_ACTUAL;
1596 tape.game_version = GAME_VERSION_ACTUAL;
1598 /* count number of participating players */
1599 for(i=0; i<MAX_PLAYERS; i++)
1600 if (tape.player_participates[i])
1601 num_participating_players++;
1603 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1604 body_chunk_size = (num_participating_players + 1) * tape.length;
1606 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1607 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1609 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1610 SaveTape_VERS(file, &tape);
1612 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1613 SaveTape_HEAD(file, &tape);
1615 putFileChunkBE(file, "INFO", info_chunk_size);
1616 SaveTape_INFO(file, &tape);
1618 putFileChunkBE(file, "BODY", body_chunk_size);
1619 SaveTape_BODY(file, &tape);
1623 SetFilePermissions(filename, PERMS_PRIVATE);
1625 tape.changed = FALSE;
1628 Request("tape saved !", REQ_CONFIRM);
1631 void DumpTape(struct TapeInfo *tape)
1635 if (TAPE_IS_EMPTY(*tape))
1637 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1641 printf_line("-", 79);
1642 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1643 tape->level_nr, tape->file_version, tape->game_version);
1644 printf("Level series identifier: '%s'\n", tape->level_identifier);
1645 printf_line("-", 79);
1647 for(i=0; i<tape->length; i++)
1649 if (i >= MAX_TAPELEN)
1652 printf("%03d: ", i);
1654 for(j=0; j<MAX_PLAYERS; j++)
1656 if (tape->player_participates[j])
1658 int action = tape->pos[i].action[j];
1660 printf("%d:%02x ", j, action);
1661 printf("[%c%c%c%c|%c%c] - ",
1662 (action & JOY_LEFT ? '<' : ' '),
1663 (action & JOY_RIGHT ? '>' : ' '),
1664 (action & JOY_UP ? '^' : ' '),
1665 (action & JOY_DOWN ? 'v' : ' '),
1666 (action & JOY_BUTTON_1 ? '1' : ' '),
1667 (action & JOY_BUTTON_2 ? '2' : ' '));
1671 printf("(%03d)\n", tape->pos[i].delay);
1674 printf_line("-", 79);
1678 /* ========================================================================= */
1679 /* score file functions */
1680 /* ========================================================================= */
1682 void LoadScore(int level_nr)
1685 char *filename = getScoreFilename(level_nr);
1686 char cookie[MAX_LINE_LEN];
1687 char line[MAX_LINE_LEN];
1691 /* always start with reliable default values */
1692 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1694 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1695 highscore[i].Score = 0;
1698 if (!(file = fopen(filename, MODE_READ)))
1701 /* check file identifier */
1702 fgets(cookie, MAX_LINE_LEN, file);
1703 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1704 cookie[strlen(cookie) - 1] = '\0';
1706 if (!checkCookieString(cookie, SCORE_COOKIE))
1708 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1713 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1715 fscanf(file, "%d", &highscore[i].Score);
1716 fgets(line, MAX_LINE_LEN, file);
1718 if (line[strlen(line) - 1] == '\n')
1719 line[strlen(line) - 1] = '\0';
1721 for (line_ptr = line; *line_ptr; line_ptr++)
1723 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1725 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1726 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1735 void SaveScore(int level_nr)
1738 char *filename = getScoreFilename(level_nr);
1741 InitScoreDirectory(leveldir_current->filename);
1743 if (!(file = fopen(filename, MODE_WRITE)))
1745 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1749 fprintf(file, "%s\n\n", SCORE_COOKIE);
1751 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1752 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1756 SetFilePermissions(filename, PERMS_PUBLIC);
1760 /* ========================================================================= */
1761 /* setup file functions */
1762 /* ========================================================================= */
1764 #define TOKEN_STR_PLAYER_PREFIX "player_"
1767 #define SETUP_TOKEN_PLAYER_NAME 0
1768 #define SETUP_TOKEN_SOUND 1
1769 #define SETUP_TOKEN_SOUND_LOOPS 2
1770 #define SETUP_TOKEN_SOUND_MUSIC 3
1771 #define SETUP_TOKEN_SOUND_SIMPLE 4
1772 #define SETUP_TOKEN_TOONS 5
1773 #define SETUP_TOKEN_SCROLL_DELAY 6
1774 #define SETUP_TOKEN_SOFT_SCROLLING 7
1775 #define SETUP_TOKEN_FADING 8
1776 #define SETUP_TOKEN_AUTORECORD 9
1777 #define SETUP_TOKEN_QUICK_DOORS 10
1778 #define SETUP_TOKEN_TEAM_MODE 11
1779 #define SETUP_TOKEN_HANDICAP 12
1780 #define SETUP_TOKEN_TIME_LIMIT 13
1781 #define SETUP_TOKEN_FULLSCREEN 14
1782 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1783 #define SETUP_TOKEN_GRAPHICS_SET 16
1784 #define SETUP_TOKEN_SOUNDS_SET 17
1785 #define SETUP_TOKEN_MUSIC_SET 18
1786 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1787 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1788 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1790 #define NUM_GLOBAL_SETUP_TOKENS 22
1793 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1794 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1795 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1796 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1797 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1798 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1799 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1800 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1801 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1803 #define NUM_EDITOR_SETUP_TOKENS 9
1805 /* shortcut setup */
1806 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1807 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1808 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1810 #define NUM_SHORTCUT_SETUP_TOKENS 3
1813 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1814 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1815 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1816 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1817 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1818 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1819 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1820 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1821 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1822 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1823 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1824 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1825 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1826 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1827 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1828 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1830 #define NUM_PLAYER_SETUP_TOKENS 16
1833 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1834 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1836 #define NUM_SYSTEM_SETUP_TOKENS 2
1839 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1841 #define NUM_OPTIONS_SETUP_TOKENS 1
1844 static struct SetupInfo si;
1845 static struct SetupEditorInfo sei;
1846 static struct SetupShortcutInfo ssi;
1847 static struct SetupInputInfo sii;
1848 static struct SetupSystemInfo syi;
1849 static struct OptionInfo soi;
1851 static struct TokenInfo global_setup_tokens[] =
1853 { TYPE_STRING, &si.player_name, "player_name" },
1854 { TYPE_SWITCH, &si.sound, "sound" },
1855 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1856 { TYPE_SWITCH, &si.sound_music, "background_music" },
1857 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1858 { TYPE_SWITCH, &si.toons, "toons" },
1859 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1860 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1861 { TYPE_SWITCH, &si.fading, "screen_fading" },
1862 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1863 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1864 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1865 { TYPE_SWITCH, &si.handicap, "handicap" },
1866 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1867 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1868 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1869 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1870 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1871 { TYPE_STRING, &si.music_set, "music_set" },
1872 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1873 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1874 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1877 static struct TokenInfo editor_setup_tokens[] =
1879 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1880 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1881 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1882 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1883 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1884 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1885 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1886 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1887 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1890 static struct TokenInfo shortcut_setup_tokens[] =
1892 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1893 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1894 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1897 static struct TokenInfo player_setup_tokens[] =
1899 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1900 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1901 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1902 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1903 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1904 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1905 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1906 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1907 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1908 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1909 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1910 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1911 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1912 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1913 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1914 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1917 static struct TokenInfo system_setup_tokens[] =
1919 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1920 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1923 static struct TokenInfo options_setup_tokens[] =
1925 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1928 static char *get_corrected_login_name(char *login_name)
1930 /* needed because player name must be a fixed length string */
1931 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1933 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1934 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1936 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1937 if (strchr(login_name_new, ' '))
1938 *strchr(login_name_new, ' ') = '\0';
1940 return login_name_new;
1943 static void setSetupInfoToDefaults(struct SetupInfo *si)
1947 si->player_name = get_corrected_login_name(getLoginName());
1950 si->sound_loops = TRUE;
1951 si->sound_music = TRUE;
1952 si->sound_simple = TRUE;
1954 si->double_buffering = TRUE;
1955 si->direct_draw = !si->double_buffering;
1956 si->scroll_delay = TRUE;
1957 si->soft_scrolling = TRUE;
1959 si->autorecord = TRUE;
1960 si->quick_doors = FALSE;
1961 si->team_mode = FALSE;
1962 si->handicap = TRUE;
1963 si->time_limit = TRUE;
1964 si->fullscreen = FALSE;
1965 si->ask_on_escape = TRUE;
1967 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1968 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1969 si->music_set = getStringCopy(MUSIC_SUBDIR);
1970 si->override_level_graphics = FALSE;
1971 si->override_level_sounds = FALSE;
1972 si->override_level_music = FALSE;
1974 si->editor.el_boulderdash = TRUE;
1975 si->editor.el_emerald_mine = TRUE;
1976 si->editor.el_more = TRUE;
1977 si->editor.el_sokoban = TRUE;
1978 si->editor.el_supaplex = TRUE;
1979 si->editor.el_diamond_caves = TRUE;
1980 si->editor.el_dx_boulderdash = TRUE;
1981 si->editor.el_chars = TRUE;
1982 si->editor.el_custom = TRUE;
1984 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1985 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1986 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1988 for (i=0; i<MAX_PLAYERS; i++)
1990 si->input[i].use_joystick = FALSE;
1991 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1992 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1993 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1994 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1995 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1996 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1997 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1998 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1999 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2000 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2001 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2002 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2003 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2004 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2005 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2008 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2009 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2011 si->options.verbose = FALSE;
2014 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2018 if (!setup_file_hash)
2023 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2024 setSetupInfo(global_setup_tokens, i,
2025 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2030 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2031 setSetupInfo(editor_setup_tokens, i,
2032 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2035 /* shortcut setup */
2036 ssi = setup.shortcut;
2037 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2038 setSetupInfo(shortcut_setup_tokens, i,
2039 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2040 setup.shortcut = ssi;
2043 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2047 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2049 sii = setup.input[pnr];
2050 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2052 char full_token[100];
2054 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2055 setSetupInfo(player_setup_tokens, i,
2056 getHashEntry(setup_file_hash, full_token));
2058 setup.input[pnr] = sii;
2063 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2064 setSetupInfo(system_setup_tokens, i,
2065 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2069 soi = setup.options;
2070 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2071 setSetupInfo(options_setup_tokens, i,
2072 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2073 setup.options = soi;
2078 char *filename = getSetupFilename();
2079 SetupFileHash *setup_file_hash = NULL;
2081 /* always start with reliable default values */
2082 setSetupInfoToDefaults(&setup);
2084 setup_file_hash = loadSetupFileHash(filename);
2086 if (setup_file_hash)
2088 char *player_name_new;
2090 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2091 decodeSetupFileHash(setup_file_hash);
2093 setup.direct_draw = !setup.double_buffering;
2095 freeSetupFileHash(setup_file_hash);
2097 /* needed to work around problems with fixed length strings */
2098 player_name_new = get_corrected_login_name(setup.player_name);
2099 free(setup.player_name);
2100 setup.player_name = player_name_new;
2103 Error(ERR_WARN, "using default setup values");
2108 char *filename = getSetupFilename();
2112 InitUserDataDirectory();
2114 if (!(file = fopen(filename, MODE_WRITE)))
2116 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2120 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2121 getCookie("SETUP")));
2122 fprintf(file, "\n");
2126 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2128 /* just to make things nicer :) */
2129 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2130 i == SETUP_TOKEN_GRAPHICS_SET)
2131 fprintf(file, "\n");
2133 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2138 fprintf(file, "\n");
2139 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2140 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2142 /* shortcut setup */
2143 ssi = setup.shortcut;
2144 fprintf(file, "\n");
2145 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2146 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2149 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2153 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2154 fprintf(file, "\n");
2156 sii = setup.input[pnr];
2157 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2158 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2163 fprintf(file, "\n");
2164 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2165 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2168 soi = setup.options;
2169 fprintf(file, "\n");
2170 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2171 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2175 SetFilePermissions(filename, PERMS_PRIVATE);
2178 void LoadCustomElementDescriptions()
2180 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2181 SetupFileHash *setup_file_hash;
2184 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2186 if (element_info[i].custom_description != NULL)
2188 free(element_info[i].custom_description);
2189 element_info[i].custom_description = NULL;
2193 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2196 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2198 char *token = getStringCat2(element_info[i].token_name, ".name");
2199 char *value = getHashEntry(setup_file_hash, token);
2202 element_info[i].custom_description = getStringCopy(value);
2207 freeSetupFileHash(setup_file_hash);
2210 void LoadSpecialMenuDesignSettings()
2212 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2213 SetupFileHash *setup_file_hash;
2216 /* always start with reliable default values from default config */
2217 for (i=0; image_config_vars[i].token != NULL; i++)
2218 for (j=0; image_config[j].token != NULL; j++)
2219 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2220 *image_config_vars[i].value =
2221 get_integer_from_string(image_config[j].value);
2223 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2226 /* special case: initialize with default values that may be overwritten */
2227 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2229 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2230 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2231 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2233 if (value_x != NULL)
2234 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2235 if (value_y != NULL)
2236 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2237 if (list_size != NULL)
2238 menu.list_size[i] = get_integer_from_string(list_size);
2241 /* read (and overwrite with) values that may be specified in config file */
2242 for (i=0; image_config_vars[i].token != NULL; i++)
2244 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2247 *image_config_vars[i].value = get_integer_from_string(value);
2250 freeSetupFileHash(setup_file_hash);