1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
17 #include "libgame/libgame.h"
25 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
26 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
27 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
28 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
29 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
30 #define LEVEL_HEADER_UNUSED 13 /* unused level header bytes */
31 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
32 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
33 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
34 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
35 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
36 #define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
38 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + x * LEVEL_CPART_CUS3_SIZE)
40 /* file identifier strings */
41 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
42 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
43 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
46 /* ========================================================================= */
47 /* level file functions */
48 /* ========================================================================= */
50 static void setLevelInfoToDefaults(struct LevelInfo *level)
54 level->file_version = FILE_VERSION_ACTUAL;
55 level->game_version = GAME_VERSION_ACTUAL;
57 level->encoding_16bit_field = FALSE; /* default: only 8-bit elements */
58 level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
59 level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
61 level->fieldx = STD_LEV_FIELDX;
62 level->fieldy = STD_LEV_FIELDY;
64 for(x=0; x<MAX_LEV_FIELDX; x++)
65 for(y=0; y<MAX_LEV_FIELDY; y++)
66 level->field[x][y] = EL_SAND;
69 level->gems_needed = 0;
70 level->amoeba_speed = 10;
71 level->time_magic_wall = 10;
72 level->time_wheel = 10;
73 level->time_light = 10;
74 level->time_timegate = 10;
75 level->amoeba_content = EL_DIAMOND;
76 level->double_speed = FALSE;
77 level->gravity = FALSE;
78 level->em_slippery_gems = FALSE;
80 level->use_custom_template = FALSE;
82 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
83 level->name[i] = '\0';
84 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
85 level->author[i] = '\0';
87 strcpy(level->name, NAMELESS_LEVEL_NAME);
88 strcpy(level->author, ANONYMOUS_NAME);
90 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
93 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
94 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
97 level->yamyam_content[i][x][y] =
98 (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
100 level->field[0][0] = EL_PLAYER_1;
101 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
103 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
105 int element = EL_CUSTOM_START + i;
107 for(j=0; j<MAX_ELEMENT_NAME_LEN + 1; j++)
108 element_info[element].description[j] = '\0';
109 if (element_info[element].custom_description != NULL)
110 strncpy(element_info[element].description,
111 element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
113 strcpy(element_info[element].description,
114 element_info[element].editor_description);
116 element_info[element].use_gfx_element = FALSE;
117 element_info[element].gfx_element = EL_EMPTY_SPACE;
119 element_info[element].collect_score = 10; /* special default */
120 element_info[element].collect_count = 1; /* special default */
122 element_info[element].push_delay_fixed = 2; /* special default */
123 element_info[element].push_delay_random = 8; /* special default */
124 element_info[element].move_delay_fixed = 0;
125 element_info[element].move_delay_random = 0;
127 element_info[element].move_pattern = MV_ALL_DIRECTIONS;
128 element_info[element].move_direction_initial = MV_NO_MOVING;
129 element_info[element].move_stepsize = TILEX / 8;
131 element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
135 element_info[element].content[x][y] = EL_EMPTY_SPACE;
137 element_info[element].change.events = CE_BITMASK_DEFAULT;
138 element_info[element].change.target_element = EL_EMPTY_SPACE;
140 element_info[element].change.delay_fixed = 0;
141 element_info[element].change.delay_random = 0;
142 element_info[element].change.delay_frames = -1; /* use default */
144 element_info[element].change.trigger_element = EL_EMPTY_SPACE;
146 element_info[element].change.explode = FALSE;
147 element_info[element].change.use_content = FALSE;
148 element_info[element].change.only_complete = FALSE;
149 element_info[element].change.use_random_change = FALSE;
150 element_info[element].change.random = 0;
151 element_info[element].change.power = CP_NON_DESTRUCTIVE;
155 element_info[element].change.content[x][y] = EL_EMPTY_SPACE;
157 element_info[element].access_type = 0;
158 element_info[element].access_layer = 0;
159 element_info[element].walk_to_action = 0;
160 element_info[element].smash_targets = 0;
161 element_info[element].deadliness = 0;
162 element_info[element].consistency = 0;
163 element_info[element].change_player_action = 0;
164 element_info[element].change_collide_action = 0;
165 element_info[element].change_other_action = 0;
167 element_info[element].can_explode_by_fire = FALSE;
168 element_info[element].can_explode_smashed = FALSE;
169 element_info[element].can_explode_impact = FALSE;
171 /* start with no properties at all */
172 for (j=0; j < NUM_EP_BITFIELDS; j++)
173 Properties[element][j] = EP_BITMASK_DEFAULT;
175 element_info[element].modified_settings = FALSE;
178 BorderElement = EL_STEELWALL;
180 level->no_level_file = FALSE;
182 if (leveldir_current == NULL) /* only when dumping level */
185 /* try to determine better author name than 'anonymous' */
186 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
188 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
189 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
193 switch (LEVELCLASS(leveldir_current))
195 case LEVELCLASS_TUTORIAL:
196 strcpy(level->author, PROGRAM_AUTHOR_STRING);
199 case LEVELCLASS_CONTRIBUTION:
200 strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
201 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
204 case LEVELCLASS_USER:
205 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
206 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
210 /* keep default value */
216 static void ActivateLevelTemplate()
218 /* Currently there is no special action needed to activate the template
219 data, because 'element_info' and 'Properties' overwrite the original
220 level data, while all other variables do not change. */
223 boolean LevelFileExists(int level_nr)
225 char *filename = getLevelFilename(level_nr);
227 return (access(filename, F_OK) == 0);
230 static int checkLevelElement(int element)
232 if (element >= NUM_FILE_ELEMENTS)
234 Error(ERR_WARN, "invalid level element %d", element);
235 element = EL_CHAR_QUESTION;
237 else if (element == EL_PLAYER_OBSOLETE)
238 element = EL_PLAYER_1;
239 else if (element == EL_KEY_OBSOLETE)
245 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
247 level->file_version = getFileVersion(file);
248 level->game_version = getFileVersion(file);
253 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
257 level->fieldx = getFile8Bit(file);
258 level->fieldy = getFile8Bit(file);
260 level->time = getFile16BitBE(file);
261 level->gems_needed = getFile16BitBE(file);
263 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
264 level->name[i] = getFile8Bit(file);
265 level->name[MAX_LEVEL_NAME_LEN] = 0;
267 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
268 level->score[i] = getFile8Bit(file);
270 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
271 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
274 level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
276 level->amoeba_speed = getFile8Bit(file);
277 level->time_magic_wall = getFile8Bit(file);
278 level->time_wheel = getFile8Bit(file);
279 level->amoeba_content = checkLevelElement(getFile8Bit(file));
280 level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
281 level->gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
282 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
283 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
285 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
287 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
292 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
296 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
297 level->author[i] = getFile8Bit(file);
298 level->author[MAX_LEVEL_NAME_LEN] = 0;
303 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
306 int chunk_size_expected = level->fieldx * level->fieldy;
308 /* Note: "chunk_size" was wrong before version 2.0 when elements are
309 stored with 16-bit encoding (and should be twice as big then).
310 Even worse, playfield data was stored 16-bit when only yamyam content
311 contained 16-bit elements and vice versa. */
313 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
314 chunk_size_expected *= 2;
316 if (chunk_size_expected != chunk_size)
318 ReadUnusedBytesFromFile(file, chunk_size);
319 return chunk_size_expected;
322 for(y=0; y<level->fieldy; y++)
323 for(x=0; x<level->fieldx; x++)
325 checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
330 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
334 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
335 int chunk_size_expected = header_size + content_size;
337 /* Note: "chunk_size" was wrong before version 2.0 when elements are
338 stored with 16-bit encoding (and should be twice as big then).
339 Even worse, playfield data was stored 16-bit when only yamyam content
340 contained 16-bit elements and vice versa. */
342 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
343 chunk_size_expected += content_size;
345 if (chunk_size_expected != chunk_size)
347 ReadUnusedBytesFromFile(file, chunk_size);
348 return chunk_size_expected;
352 level->num_yamyam_contents = getFile8Bit(file);
356 /* correct invalid number of content fields -- should never happen */
357 if (level->num_yamyam_contents < 1 ||
358 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
359 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
361 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
364 level->yamyam_content[i][x][y] =
365 checkLevelElement(level->encoding_16bit_field ?
366 getFile16BitBE(file) : getFile8Bit(file));
370 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
374 int num_contents, content_xsize, content_ysize;
375 int content_array[MAX_ELEMENT_CONTENTS][3][3];
377 element = checkLevelElement(getFile16BitBE(file));
378 num_contents = getFile8Bit(file);
379 content_xsize = getFile8Bit(file);
380 content_ysize = getFile8Bit(file);
381 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
383 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
386 content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
388 /* correct invalid number of content fields -- should never happen */
389 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
390 num_contents = STD_ELEMENT_CONTENTS;
392 if (element == EL_YAMYAM)
394 level->num_yamyam_contents = num_contents;
396 for(i=0; i<num_contents; i++)
399 level->yamyam_content[i][x][y] = content_array[i][x][y];
401 else if (element == EL_BD_AMOEBA)
403 level->amoeba_content = content_array[0][0][0];
407 Error(ERR_WARN, "cannot load content for element '%d'", element);
413 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
415 int num_changed_custom_elements = getFile16BitBE(file);
416 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
419 if (chunk_size_expected != chunk_size)
421 ReadUnusedBytesFromFile(file, chunk_size - 2);
422 return chunk_size_expected;
425 for (i=0; i < num_changed_custom_elements; i++)
427 int element = getFile16BitBE(file);
428 int properties = getFile32BitBE(file);
430 if (IS_CUSTOM_ELEMENT(element))
431 Properties[element][EP_BITFIELD_BASE] = properties;
433 Error(ERR_WARN, "invalid custom element number %d", element);
439 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
441 int num_changed_custom_elements = getFile16BitBE(file);
442 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
445 if (chunk_size_expected != chunk_size)
447 ReadUnusedBytesFromFile(file, chunk_size - 2);
448 return chunk_size_expected;
451 for (i=0; i < num_changed_custom_elements; i++)
453 int element = getFile16BitBE(file);
454 int custom_target_element = getFile16BitBE(file);
456 if (IS_CUSTOM_ELEMENT(element))
457 element_info[element].change.target_element = custom_target_element;
459 Error(ERR_WARN, "invalid custom element number %d", element);
465 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
467 int num_changed_custom_elements = getFile16BitBE(file);
468 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
471 if (chunk_size_expected != chunk_size)
473 ReadUnusedBytesFromFile(file, chunk_size - 2);
474 return chunk_size_expected;
477 for (i=0; i < num_changed_custom_elements; i++)
479 int element = getFile16BitBE(file);
481 if (!IS_CUSTOM_ELEMENT(element))
483 Error(ERR_WARN, "invalid custom element number %d", element);
485 element = EL_DEFAULT; /* dummy element used for artwork config */
488 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
489 element_info[element].description[j] = getFile8Bit(file);
490 element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
492 Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
494 /* some free bytes for future properties and padding */
495 ReadUnusedBytesFromFile(file, 7);
497 element_info[element].use_gfx_element = getFile8Bit(file);
498 element_info[element].gfx_element =
499 checkLevelElement(getFile16BitBE(file));
501 element_info[element].collect_score = getFile8Bit(file);
502 element_info[element].collect_count = getFile8Bit(file);
504 element_info[element].push_delay_fixed = getFile16BitBE(file);
505 element_info[element].push_delay_random = getFile16BitBE(file);
506 element_info[element].move_delay_fixed = getFile16BitBE(file);
507 element_info[element].move_delay_random = getFile16BitBE(file);
509 element_info[element].move_pattern = getFile16BitBE(file);
510 element_info[element].move_direction_initial = getFile8Bit(file);
511 element_info[element].move_stepsize = getFile8Bit(file);
515 element_info[element].content[x][y] =
516 checkLevelElement(getFile16BitBE(file));
518 element_info[element].change.events = getFile32BitBE(file);
520 element_info[element].change.target_element =
521 checkLevelElement(getFile16BitBE(file));
523 element_info[element].change.delay_fixed = getFile16BitBE(file);
524 element_info[element].change.delay_random = getFile16BitBE(file);
525 element_info[element].change.delay_frames = getFile16BitBE(file);
527 element_info[element].change.trigger_element =
528 checkLevelElement(getFile16BitBE(file));
530 element_info[element].change.explode = getFile8Bit(file);
531 element_info[element].change.use_content = getFile8Bit(file);
532 element_info[element].change.only_complete = getFile8Bit(file);
533 element_info[element].change.use_random_change = getFile8Bit(file);
535 element_info[element].change.random = getFile8Bit(file);
536 element_info[element].change.power = getFile8Bit(file);
540 element_info[element].change.content[x][y] =
541 checkLevelElement(getFile16BitBE(file));
543 element_info[element].slippery_type = getFile8Bit(file);
545 /* some free bytes for future properties and padding */
546 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
548 /* mark that this custom element has been modified */
549 element_info[element].modified_settings = TRUE;
555 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
557 char cookie[MAX_LINE_LEN];
558 char chunk_name[CHUNK_ID_LEN + 1];
562 /* always start with reliable default values */
563 setLevelInfoToDefaults(level);
565 if (!(file = fopen(filename, MODE_READ)))
567 level->no_level_file = TRUE;
569 if (level != &level_template)
570 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
575 getFileChunkBE(file, chunk_name, NULL);
576 if (strcmp(chunk_name, "RND1") == 0)
578 getFile32BitBE(file); /* not used */
580 getFileChunkBE(file, chunk_name, NULL);
581 if (strcmp(chunk_name, "CAVE") != 0)
583 Error(ERR_WARN, "unknown format of level file '%s'", filename);
588 else /* check for pre-2.0 file format with cookie string */
590 strcpy(cookie, chunk_name);
591 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
592 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
593 cookie[strlen(cookie) - 1] = '\0';
595 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
597 Error(ERR_WARN, "unknown format of level file '%s'", filename);
602 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
604 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
609 /* pre-2.0 level files have no game version, so use file version here */
610 level->game_version = level->file_version;
613 if (level->file_version < FILE_VERSION_1_2)
615 /* level files from versions before 1.2.0 without chunk structure */
616 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, level);
617 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
625 int (*loader)(FILE *, int, struct LevelInfo *);
629 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
630 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
631 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
632 { "BODY", -1, LoadLevel_BODY },
633 { "CONT", -1, LoadLevel_CONT },
634 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
635 { "CUS1", -1, LoadLevel_CUS1 },
636 { "CUS2", -1, LoadLevel_CUS2 },
637 { "CUS3", -1, LoadLevel_CUS3 },
641 while (getFileChunkBE(file, chunk_name, &chunk_size))
645 while (chunk_info[i].name != NULL &&
646 strcmp(chunk_name, chunk_info[i].name) != 0)
649 if (chunk_info[i].name == NULL)
651 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
652 chunk_name, filename);
653 ReadUnusedBytesFromFile(file, chunk_size);
655 else if (chunk_info[i].size != -1 &&
656 chunk_info[i].size != chunk_size)
658 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
659 chunk_size, chunk_name, filename);
660 ReadUnusedBytesFromFile(file, chunk_size);
664 /* call function to load this level chunk */
665 int chunk_size_expected =
666 (chunk_info[i].loader)(file, chunk_size, level);
668 /* the size of some chunks cannot be checked before reading other
669 chunks first (like "HEAD" and "BODY") that contain some header
670 information, so check them here */
671 if (chunk_size_expected != chunk_size)
673 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
674 chunk_size, chunk_name, filename);
683 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
687 if (leveldir_current == NULL) /* only when dumping level */
690 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
691 IS_LEVELCLASS_USER(leveldir_current))
694 printf("::: This level is private or contributed: '%s'\n", filename);
697 /* For user contributed and private levels, use the version of
698 the game engine the levels were created for.
699 Since 2.0.1, the game engine version is now directly stored
700 in the level file (chunk "VERS"), so there is no need anymore
701 to set the game version from the file version (except for old,
702 pre-2.0 levels, where the game version is still taken from the
703 file format version used to store the level -- see above). */
705 /* do some special adjustments to support older level versions */
706 if (level->file_version == FILE_VERSION_1_0)
708 Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
709 Error(ERR_WARN, "using high speed movement for player");
711 /* player was faster than monsters in (pre-)1.0 levels */
712 level->double_speed = TRUE;
715 /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
716 if (level->game_version == VERSION_IDENT(2,0,1))
717 level->em_slippery_gems = TRUE;
722 printf("::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
723 leveldir_current->sort_priority, filename);
726 /* Always use the latest version of the game engine for all but
727 user contributed and private levels; this allows for actual
728 corrections in the game engine to take effect for existing,
729 converted levels (from "classic" or other existing games) to
730 make the game emulation more accurate, while (hopefully) not
731 breaking existing levels created from other players. */
733 level->game_version = GAME_VERSION_ACTUAL;
735 /* Set special EM style gems behaviour: EM style gems slip down from
736 normal, steel and growing wall. As this is a more fundamental change,
737 it seems better to set the default behaviour to "off" (as it is more
738 natural) and make it configurable in the level editor (as a property
739 of gem style elements). Already existing converted levels (neither
740 private nor contributed levels) are changed to the new behaviour. */
742 if (level->file_version < FILE_VERSION_2_0)
743 level->em_slippery_gems = TRUE;
746 /* map elements which have changed in newer versions */
747 for(y=0; y<level->fieldy; y++)
749 for(x=0; x<level->fieldx; x++)
751 int element = level->field[x][y];
753 if (level->game_version <= VERSION_IDENT(2,2,0))
755 /* map game font elements */
756 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
757 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
758 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
759 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
762 if (level->game_version < VERSION_IDENT(3,0,0))
764 /* map Supaplex gravity tube elements */
765 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
766 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
767 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
768 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
772 level->field[x][y] = element;
776 /* copy elements to runtime playfield array */
777 for(x=0; x<MAX_LEV_FIELDX; x++)
778 for(y=0; y<MAX_LEV_FIELDY; y++)
779 Feld[x][y] = level->field[x][y];
781 /* initialize level size variables for faster access */
782 lev_fieldx = level->fieldx;
783 lev_fieldy = level->fieldy;
785 /* determine border element for this level */
788 /* initialize element properties for level editor etc. */
789 InitElementPropertiesEngine(level->game_version);
792 void LoadLevelTemplate(int level_nr)
794 char *filename = getLevelFilename(level_nr);
796 LoadLevelFromFilename(&level_template, filename);
798 ActivateLevelTemplate();
801 void LoadLevel(int level_nr)
803 char *filename = getLevelFilename(level_nr);
805 LoadLevelFromFilename(&level, filename);
807 if (level.use_custom_template)
808 LoadLevelTemplate(-1);
810 LoadLevel_InitLevel(&level, filename);
813 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
815 putFileVersion(file, level->file_version);
816 putFileVersion(file, level->game_version);
819 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
823 putFile8Bit(file, level->fieldx);
824 putFile8Bit(file, level->fieldy);
826 putFile16BitBE(file, level->time);
827 putFile16BitBE(file, level->gems_needed);
829 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
830 putFile8Bit(file, level->name[i]);
832 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
833 putFile8Bit(file, level->score[i]);
835 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
838 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
839 level->yamyam_content[i][x][y]));
840 putFile8Bit(file, level->amoeba_speed);
841 putFile8Bit(file, level->time_magic_wall);
842 putFile8Bit(file, level->time_wheel);
843 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
844 level->amoeba_content));
845 putFile8Bit(file, (level->double_speed ? 1 : 0));
846 putFile8Bit(file, (level->gravity ? 1 : 0));
847 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
848 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
850 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
852 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
855 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
859 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
860 putFile8Bit(file, level->author[i]);
863 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
867 for(y=0; y<level->fieldy; y++)
868 for(x=0; x<level->fieldx; x++)
869 if (level->encoding_16bit_field)
870 putFile16BitBE(file, level->field[x][y]);
872 putFile8Bit(file, level->field[x][y]);
876 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
880 putFile8Bit(file, EL_YAMYAM);
881 putFile8Bit(file, level->num_yamyam_contents);
882 putFile8Bit(file, 0);
883 putFile8Bit(file, 0);
885 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
888 if (level->encoding_16bit_field)
889 putFile16BitBE(file, level->yamyam_content[i][x][y]);
891 putFile8Bit(file, level->yamyam_content[i][x][y]);
895 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
898 int num_contents, content_xsize, content_ysize;
899 int content_array[MAX_ELEMENT_CONTENTS][3][3];
901 if (element == EL_YAMYAM)
903 num_contents = level->num_yamyam_contents;
907 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
910 content_array[i][x][y] = level->yamyam_content[i][x][y];
912 else if (element == EL_BD_AMOEBA)
918 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
921 content_array[i][x][y] = EL_EMPTY;
922 content_array[0][0][0] = level->amoeba_content;
926 /* chunk header already written -- write empty chunk data */
927 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
929 Error(ERR_WARN, "cannot save content for element '%d'", element);
933 putFile16BitBE(file, element);
934 putFile8Bit(file, num_contents);
935 putFile8Bit(file, content_xsize);
936 putFile8Bit(file, content_ysize);
938 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
940 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
943 putFile16BitBE(file, content_array[i][x][y]);
947 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
948 int num_changed_custom_elements)
952 putFile16BitBE(file, num_changed_custom_elements);
954 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
956 int element = EL_CUSTOM_START + i;
958 if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
960 if (check < num_changed_custom_elements)
962 putFile16BitBE(file, element);
963 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
970 if (check != num_changed_custom_elements) /* should not happen */
971 Error(ERR_WARN, "inconsistent number of custom element properties");
976 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
977 int num_changed_custom_elements)
981 putFile16BitBE(file, num_changed_custom_elements);
983 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
985 int element = EL_CUSTOM_START + i;
987 if (element_info[element].change.target_element != EL_EMPTY_SPACE)
989 if (check < num_changed_custom_elements)
991 putFile16BitBE(file, element);
992 putFile16BitBE(file, element_info[element].change.target_element);
999 if (check != num_changed_custom_elements) /* should not happen */
1000 Error(ERR_WARN, "inconsistent number of custom target elements");
1004 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1005 int num_changed_custom_elements)
1007 int i, j, x, y, check = 0;
1009 putFile16BitBE(file, num_changed_custom_elements);
1011 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1013 int element = EL_CUSTOM_START + i;
1015 if (element_info[element].modified_settings)
1017 if (check < num_changed_custom_elements)
1019 putFile16BitBE(file, element);
1021 for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1022 putFile8Bit(file, element_info[element].description[j]);
1024 putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1026 /* some free bytes for future properties and padding */
1027 WriteUnusedBytesToFile(file, 7);
1029 putFile8Bit(file, element_info[element].use_gfx_element);
1030 putFile16BitBE(file, element_info[element].gfx_element);
1032 putFile8Bit(file, element_info[element].collect_score);
1033 putFile8Bit(file, element_info[element].collect_count);
1035 putFile16BitBE(file, element_info[element].push_delay_fixed);
1036 putFile16BitBE(file, element_info[element].push_delay_random);
1037 putFile16BitBE(file, element_info[element].move_delay_fixed);
1038 putFile16BitBE(file, element_info[element].move_delay_random);
1040 putFile16BitBE(file, element_info[element].move_pattern);
1041 putFile8Bit(file, element_info[element].move_direction_initial);
1042 putFile8Bit(file, element_info[element].move_stepsize);
1046 putFile16BitBE(file, element_info[element].content[x][y]);
1048 putFile32BitBE(file, element_info[element].change.events);
1050 putFile16BitBE(file, element_info[element].change.target_element);
1052 putFile16BitBE(file, element_info[element].change.delay_fixed);
1053 putFile16BitBE(file, element_info[element].change.delay_random);
1054 putFile16BitBE(file, element_info[element].change.delay_frames);
1056 putFile16BitBE(file, element_info[element].change.trigger_element);
1058 putFile8Bit(file, element_info[element].change.explode);
1059 putFile8Bit(file, element_info[element].change.use_content);
1060 putFile8Bit(file, element_info[element].change.only_complete);
1061 putFile8Bit(file, element_info[element].change.use_random_change);
1063 putFile8Bit(file, element_info[element].change.random);
1064 putFile8Bit(file, element_info[element].change.power);
1068 putFile16BitBE(file, element_info[element].change.content[x][y]);
1070 putFile8Bit(file, element_info[element].slippery_type);
1072 /* some free bytes for future properties and padding */
1073 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1080 if (check != num_changed_custom_elements) /* should not happen */
1081 Error(ERR_WARN, "inconsistent number of custom element properties");
1084 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1086 int body_chunk_size;
1087 int num_changed_custom_elements = 0;
1088 int level_chunk_CUS3_size;
1092 if (!(file = fopen(filename, MODE_WRITE)))
1094 Error(ERR_WARN, "cannot save level file '%s'", filename);
1098 level->file_version = FILE_VERSION_ACTUAL;
1099 level->game_version = GAME_VERSION_ACTUAL;
1101 /* check level field for 16-bit elements */
1102 level->encoding_16bit_field = FALSE;
1103 for(y=0; y<level->fieldy; y++)
1104 for(x=0; x<level->fieldx; x++)
1105 if (level->field[x][y] > 255)
1106 level->encoding_16bit_field = TRUE;
1108 /* check yamyam content for 16-bit elements */
1109 level->encoding_16bit_yamyam = FALSE;
1110 for(i=0; i<level->num_yamyam_contents; i++)
1113 if (level->yamyam_content[i][x][y] > 255)
1114 level->encoding_16bit_yamyam = TRUE;
1116 /* check amoeba content for 16-bit elements */
1117 level->encoding_16bit_amoeba = FALSE;
1118 if (level->amoeba_content > 255)
1119 level->encoding_16bit_amoeba = TRUE;
1121 /* calculate size of "BODY" chunk */
1123 level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1125 /* check for non-standard custom elements and calculate "CUS3" chunk size */
1126 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1127 if (element_info[EL_CUSTOM_START + i].modified_settings)
1128 num_changed_custom_elements++;
1129 level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1131 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1132 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1134 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1135 SaveLevel_VERS(file, level);
1137 putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1138 SaveLevel_HEAD(file, level);
1140 putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1141 SaveLevel_AUTH(file, level);
1143 putFileChunkBE(file, "BODY", body_chunk_size);
1144 SaveLevel_BODY(file, level);
1146 if (level->encoding_16bit_yamyam ||
1147 level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1149 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1150 SaveLevel_CNT2(file, level, EL_YAMYAM);
1153 if (level->encoding_16bit_amoeba)
1155 putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1156 SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1159 if (num_changed_custom_elements > 0 && !level->use_custom_template)
1161 putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1162 SaveLevel_CUS3(file, level, num_changed_custom_elements);
1167 SetFilePermissions(filename, PERMS_PRIVATE);
1170 void SaveLevel(int level_nr)
1172 char *filename = getLevelFilename(level_nr);
1174 SaveLevelFromFilename(&level, filename);
1177 void SaveLevelTemplate()
1179 char *filename = getLevelFilename(-1);
1181 SaveLevelFromFilename(&level, filename);
1184 void DumpLevel(struct LevelInfo *level)
1186 printf_line("-", 79);
1187 printf("Level xxx (file version %08d, game version %08d)\n",
1188 level->file_version, level->game_version);
1189 printf_line("-", 79);
1191 printf("Level Author: '%s'\n", level->author);
1192 printf("Level Title: '%s'\n", level->name);
1194 printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1196 printf("Level Time: %d seconds\n", level->time);
1197 printf("Gems needed: %d\n", level->gems_needed);
1199 printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1200 printf("Time for Wheel: %d seconds\n", level->time_wheel);
1201 printf("Time for Light: %d seconds\n", level->time_light);
1202 printf("Time for Timegate: %d seconds\n", level->time_timegate);
1204 printf("Amoeba Speed: %d\n", level->amoeba_speed);
1206 printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
1207 printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
1208 printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1210 printf_line("-", 79);
1214 /* ========================================================================= */
1215 /* tape file functions */
1216 /* ========================================================================= */
1218 static void setTapeInfoToDefaults()
1222 /* always start with reliable default values (empty tape) */
1225 /* default values (also for pre-1.2 tapes) with only the first player */
1226 tape.player_participates[0] = TRUE;
1227 for(i=1; i<MAX_PLAYERS; i++)
1228 tape.player_participates[i] = FALSE;
1230 /* at least one (default: the first) player participates in every tape */
1231 tape.num_participating_players = 1;
1233 tape.level_nr = level_nr;
1235 tape.changed = FALSE;
1237 tape.recording = FALSE;
1238 tape.playing = FALSE;
1239 tape.pausing = FALSE;
1242 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1244 tape->file_version = getFileVersion(file);
1245 tape->game_version = getFileVersion(file);
1250 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1254 tape->random_seed = getFile32BitBE(file);
1255 tape->date = getFile32BitBE(file);
1256 tape->length = getFile32BitBE(file);
1258 /* read header fields that are new since version 1.2 */
1259 if (tape->file_version >= FILE_VERSION_1_2)
1261 byte store_participating_players = getFile8Bit(file);
1264 /* since version 1.2, tapes store which players participate in the tape */
1265 tape->num_participating_players = 0;
1266 for(i=0; i<MAX_PLAYERS; i++)
1268 tape->player_participates[i] = FALSE;
1270 if (store_participating_players & (1 << i))
1272 tape->player_participates[i] = TRUE;
1273 tape->num_participating_players++;
1277 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1279 engine_version = getFileVersion(file);
1280 if (engine_version > 0)
1281 tape->engine_version = engine_version;
1287 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1289 int level_identifier_size;
1292 level_identifier_size = getFile16BitBE(file);
1294 tape->level_identifier =
1295 checked_realloc(tape->level_identifier, level_identifier_size);
1297 for(i=0; i < level_identifier_size; i++)
1298 tape->level_identifier[i] = getFile8Bit(file);
1300 tape->level_nr = getFile16BitBE(file);
1302 chunk_size = 2 + level_identifier_size + 2;
1307 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1310 int chunk_size_expected =
1311 (tape->num_participating_players + 1) * tape->length;
1313 if (chunk_size_expected != chunk_size)
1315 ReadUnusedBytesFromFile(file, chunk_size);
1316 return chunk_size_expected;
1319 for(i=0; i<tape->length; i++)
1321 if (i >= MAX_TAPELEN)
1324 for(j=0; j<MAX_PLAYERS; j++)
1326 tape->pos[i].action[j] = MV_NO_MOVING;
1328 if (tape->player_participates[j])
1329 tape->pos[i].action[j] = getFile8Bit(file);
1332 tape->pos[i].delay = getFile8Bit(file);
1334 if (tape->file_version == FILE_VERSION_1_0)
1336 /* eliminate possible diagonal moves in old tapes */
1337 /* this is only for backward compatibility */
1339 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1340 byte action = tape->pos[i].action[0];
1341 int k, num_moves = 0;
1345 if (action & joy_dir[k])
1347 tape->pos[i + num_moves].action[0] = joy_dir[k];
1349 tape->pos[i + num_moves].delay = 0;
1358 tape->length += num_moves;
1361 else if (tape->file_version < FILE_VERSION_2_0)
1363 /* convert pre-2.0 tapes to new tape format */
1365 if (tape->pos[i].delay > 1)
1368 tape->pos[i + 1] = tape->pos[i];
1369 tape->pos[i + 1].delay = 1;
1372 for(j=0; j<MAX_PLAYERS; j++)
1373 tape->pos[i].action[j] = MV_NO_MOVING;
1374 tape->pos[i].delay--;
1385 if (i != tape->length)
1386 chunk_size = (tape->num_participating_players + 1) * i;
1391 void LoadTapeFromFilename(char *filename)
1393 char cookie[MAX_LINE_LEN];
1394 char chunk_name[CHUNK_ID_LEN + 1];
1398 /* always start with reliable default values */
1399 setTapeInfoToDefaults();
1401 if (!(file = fopen(filename, MODE_READ)))
1404 getFileChunkBE(file, chunk_name, NULL);
1405 if (strcmp(chunk_name, "RND1") == 0)
1407 getFile32BitBE(file); /* not used */
1409 getFileChunkBE(file, chunk_name, NULL);
1410 if (strcmp(chunk_name, "TAPE") != 0)
1412 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1417 else /* check for pre-2.0 file format with cookie string */
1419 strcpy(cookie, chunk_name);
1420 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1421 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1422 cookie[strlen(cookie) - 1] = '\0';
1424 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1426 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1431 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1433 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1438 /* pre-2.0 tape files have no game version, so use file version here */
1439 tape.game_version = tape.file_version;
1442 if (tape.file_version < FILE_VERSION_1_2)
1444 /* tape files from versions before 1.2.0 without chunk structure */
1445 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1446 LoadTape_BODY(file, 2 * tape.length, &tape);
1454 int (*loader)(FILE *, int, struct TapeInfo *);
1458 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1459 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1460 { "INFO", -1, LoadTape_INFO },
1461 { "BODY", -1, LoadTape_BODY },
1465 while (getFileChunkBE(file, chunk_name, &chunk_size))
1469 while (chunk_info[i].name != NULL &&
1470 strcmp(chunk_name, chunk_info[i].name) != 0)
1473 if (chunk_info[i].name == NULL)
1475 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1476 chunk_name, filename);
1477 ReadUnusedBytesFromFile(file, chunk_size);
1479 else if (chunk_info[i].size != -1 &&
1480 chunk_info[i].size != chunk_size)
1482 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1483 chunk_size, chunk_name, filename);
1484 ReadUnusedBytesFromFile(file, chunk_size);
1488 /* call function to load this tape chunk */
1489 int chunk_size_expected =
1490 (chunk_info[i].loader)(file, chunk_size, &tape);
1492 /* the size of some chunks cannot be checked before reading other
1493 chunks first (like "HEAD" and "BODY") that contain some header
1494 information, so check them here */
1495 if (chunk_size_expected != chunk_size)
1497 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1498 chunk_size, chunk_name, filename);
1506 tape.length_seconds = GetTapeLength();
1509 printf("tape version: %d\n", tape.game_version);
1513 void LoadTape(int level_nr)
1515 char *filename = getTapeFilename(level_nr);
1517 LoadTapeFromFilename(filename);
1520 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1522 putFileVersion(file, tape->file_version);
1523 putFileVersion(file, tape->game_version);
1526 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1529 byte store_participating_players = 0;
1531 /* set bits for participating players for compact storage */
1532 for(i=0; i<MAX_PLAYERS; i++)
1533 if (tape->player_participates[i])
1534 store_participating_players |= (1 << i);
1536 putFile32BitBE(file, tape->random_seed);
1537 putFile32BitBE(file, tape->date);
1538 putFile32BitBE(file, tape->length);
1540 putFile8Bit(file, store_participating_players);
1542 /* unused bytes not at the end here for 4-byte alignment of engine_version */
1543 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1545 putFileVersion(file, tape->engine_version);
1548 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1550 int level_identifier_size = strlen(tape->level_identifier) + 1;
1553 putFile16BitBE(file, level_identifier_size);
1555 for(i=0; i < level_identifier_size; i++)
1556 putFile8Bit(file, tape->level_identifier[i]);
1558 putFile16BitBE(file, tape->level_nr);
1561 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1565 for(i=0; i<tape->length; i++)
1567 for(j=0; j<MAX_PLAYERS; j++)
1568 if (tape->player_participates[j])
1569 putFile8Bit(file, tape->pos[i].action[j]);
1571 putFile8Bit(file, tape->pos[i].delay);
1575 void SaveTape(int level_nr)
1577 char *filename = getTapeFilename(level_nr);
1579 boolean new_tape = TRUE;
1580 int num_participating_players = 0;
1581 int info_chunk_size;
1582 int body_chunk_size;
1585 InitTapeDirectory(leveldir_current->filename);
1587 /* if a tape still exists, ask to overwrite it */
1588 if (access(filename, F_OK) == 0)
1591 if (!Request("Replace old tape ?", REQ_ASK))
1595 if (!(file = fopen(filename, MODE_WRITE)))
1597 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1601 tape.file_version = FILE_VERSION_ACTUAL;
1602 tape.game_version = GAME_VERSION_ACTUAL;
1604 /* count number of participating players */
1605 for(i=0; i<MAX_PLAYERS; i++)
1606 if (tape.player_participates[i])
1607 num_participating_players++;
1609 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1610 body_chunk_size = (num_participating_players + 1) * tape.length;
1612 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1613 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1615 putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1616 SaveTape_VERS(file, &tape);
1618 putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1619 SaveTape_HEAD(file, &tape);
1621 putFileChunkBE(file, "INFO", info_chunk_size);
1622 SaveTape_INFO(file, &tape);
1624 putFileChunkBE(file, "BODY", body_chunk_size);
1625 SaveTape_BODY(file, &tape);
1629 SetFilePermissions(filename, PERMS_PRIVATE);
1631 tape.changed = FALSE;
1634 Request("tape saved !", REQ_CONFIRM);
1637 void DumpTape(struct TapeInfo *tape)
1641 if (TAPE_IS_EMPTY(*tape))
1643 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1647 printf_line("-", 79);
1648 printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1649 tape->level_nr, tape->file_version, tape->game_version);
1650 printf("Level series identifier: '%s'\n", tape->level_identifier);
1651 printf_line("-", 79);
1653 for(i=0; i<tape->length; i++)
1655 if (i >= MAX_TAPELEN)
1658 printf("%03d: ", i);
1660 for(j=0; j<MAX_PLAYERS; j++)
1662 if (tape->player_participates[j])
1664 int action = tape->pos[i].action[j];
1666 printf("%d:%02x ", j, action);
1667 printf("[%c%c%c%c|%c%c] - ",
1668 (action & JOY_LEFT ? '<' : ' '),
1669 (action & JOY_RIGHT ? '>' : ' '),
1670 (action & JOY_UP ? '^' : ' '),
1671 (action & JOY_DOWN ? 'v' : ' '),
1672 (action & JOY_BUTTON_1 ? '1' : ' '),
1673 (action & JOY_BUTTON_2 ? '2' : ' '));
1677 printf("(%03d)\n", tape->pos[i].delay);
1680 printf_line("-", 79);
1684 /* ========================================================================= */
1685 /* score file functions */
1686 /* ========================================================================= */
1688 void LoadScore(int level_nr)
1691 char *filename = getScoreFilename(level_nr);
1692 char cookie[MAX_LINE_LEN];
1693 char line[MAX_LINE_LEN];
1697 /* always start with reliable default values */
1698 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1700 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1701 highscore[i].Score = 0;
1704 if (!(file = fopen(filename, MODE_READ)))
1707 /* check file identifier */
1708 fgets(cookie, MAX_LINE_LEN, file);
1709 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1710 cookie[strlen(cookie) - 1] = '\0';
1712 if (!checkCookieString(cookie, SCORE_COOKIE))
1714 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1719 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1721 fscanf(file, "%d", &highscore[i].Score);
1722 fgets(line, MAX_LINE_LEN, file);
1724 if (line[strlen(line) - 1] == '\n')
1725 line[strlen(line) - 1] = '\0';
1727 for (line_ptr = line; *line_ptr; line_ptr++)
1729 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1731 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1732 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1741 void SaveScore(int level_nr)
1744 char *filename = getScoreFilename(level_nr);
1747 InitScoreDirectory(leveldir_current->filename);
1749 if (!(file = fopen(filename, MODE_WRITE)))
1751 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1755 fprintf(file, "%s\n\n", SCORE_COOKIE);
1757 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1758 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1762 SetFilePermissions(filename, PERMS_PUBLIC);
1766 /* ========================================================================= */
1767 /* setup file functions */
1768 /* ========================================================================= */
1770 #define TOKEN_STR_PLAYER_PREFIX "player_"
1773 #define SETUP_TOKEN_PLAYER_NAME 0
1774 #define SETUP_TOKEN_SOUND 1
1775 #define SETUP_TOKEN_SOUND_LOOPS 2
1776 #define SETUP_TOKEN_SOUND_MUSIC 3
1777 #define SETUP_TOKEN_SOUND_SIMPLE 4
1778 #define SETUP_TOKEN_TOONS 5
1779 #define SETUP_TOKEN_SCROLL_DELAY 6
1780 #define SETUP_TOKEN_SOFT_SCROLLING 7
1781 #define SETUP_TOKEN_FADING 8
1782 #define SETUP_TOKEN_AUTORECORD 9
1783 #define SETUP_TOKEN_QUICK_DOORS 10
1784 #define SETUP_TOKEN_TEAM_MODE 11
1785 #define SETUP_TOKEN_HANDICAP 12
1786 #define SETUP_TOKEN_TIME_LIMIT 13
1787 #define SETUP_TOKEN_FULLSCREEN 14
1788 #define SETUP_TOKEN_ASK_ON_ESCAPE 15
1789 #define SETUP_TOKEN_GRAPHICS_SET 16
1790 #define SETUP_TOKEN_SOUNDS_SET 17
1791 #define SETUP_TOKEN_MUSIC_SET 18
1792 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 19
1793 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 20
1794 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 21
1796 #define NUM_GLOBAL_SETUP_TOKENS 22
1799 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH 0
1800 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE 1
1801 #define SETUP_TOKEN_EDITOR_EL_MORE 2
1802 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN 3
1803 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX 4
1804 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES 5
1805 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH 6
1806 #define SETUP_TOKEN_EDITOR_EL_CHARS 7
1807 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
1809 #define NUM_EDITOR_SETUP_TOKENS 9
1811 /* shortcut setup */
1812 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
1813 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
1814 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
1816 #define NUM_SHORTCUT_SETUP_TOKENS 3
1819 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
1820 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
1821 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
1822 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
1823 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
1824 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
1825 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
1826 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
1827 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
1828 #define SETUP_TOKEN_PLAYER_JOY_BOMB 9
1829 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
1830 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
1831 #define SETUP_TOKEN_PLAYER_KEY_UP 12
1832 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
1833 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
1834 #define SETUP_TOKEN_PLAYER_KEY_BOMB 15
1836 #define NUM_PLAYER_SETUP_TOKENS 16
1839 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 0
1840 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 1
1842 #define NUM_SYSTEM_SETUP_TOKENS 2
1845 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
1847 #define NUM_OPTIONS_SETUP_TOKENS 1
1850 static struct SetupInfo si;
1851 static struct SetupEditorInfo sei;
1852 static struct SetupShortcutInfo ssi;
1853 static struct SetupInputInfo sii;
1854 static struct SetupSystemInfo syi;
1855 static struct OptionInfo soi;
1857 static struct TokenInfo global_setup_tokens[] =
1859 { TYPE_STRING, &si.player_name, "player_name" },
1860 { TYPE_SWITCH, &si.sound, "sound" },
1861 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1862 { TYPE_SWITCH, &si.sound_music, "background_music" },
1863 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1864 { TYPE_SWITCH, &si.toons, "toons" },
1865 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1866 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1867 { TYPE_SWITCH, &si.fading, "screen_fading" },
1868 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1869 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1870 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1871 { TYPE_SWITCH, &si.handicap, "handicap" },
1872 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1873 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1874 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
1875 { TYPE_STRING, &si.graphics_set, "graphics_set" },
1876 { TYPE_STRING, &si.sounds_set, "sounds_set" },
1877 { TYPE_STRING, &si.music_set, "music_set" },
1878 { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1879 { TYPE_SWITCH, &si.override_level_sounds, "override_level_sounds" },
1880 { TYPE_SWITCH, &si.override_level_music, "override_level_music" },
1883 static struct TokenInfo editor_setup_tokens[] =
1885 { TYPE_SWITCH, &sei.el_boulderdash, "editor.el_boulderdash" },
1886 { TYPE_SWITCH, &sei.el_emerald_mine, "editor.el_emerald_mine" },
1887 { TYPE_SWITCH, &sei.el_more, "editor.el_more" },
1888 { TYPE_SWITCH, &sei.el_sokoban, "editor.el_sokoban" },
1889 { TYPE_SWITCH, &sei.el_supaplex, "editor.el_supaplex" },
1890 { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves" },
1891 { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash" },
1892 { TYPE_SWITCH, &sei.el_chars, "editor.el_chars" },
1893 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
1894 { TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
1897 static struct TokenInfo shortcut_setup_tokens[] =
1899 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
1900 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
1901 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" }
1904 static struct TokenInfo player_setup_tokens[] =
1906 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1907 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1908 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1909 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1910 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1911 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1912 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1913 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1914 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1915 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1916 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
1917 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
1918 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
1919 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
1920 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
1921 { TYPE_KEY_X11, &sii.key.bomb, ".key.place_bomb" }
1924 static struct TokenInfo system_setup_tokens[] =
1926 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
1927 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1930 static struct TokenInfo options_setup_tokens[] =
1932 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" }
1935 static char *get_corrected_login_name(char *login_name)
1937 /* needed because player name must be a fixed length string */
1938 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1940 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1941 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1943 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
1944 if (strchr(login_name_new, ' '))
1945 *strchr(login_name_new, ' ') = '\0';
1947 return login_name_new;
1950 static void setSetupInfoToDefaults(struct SetupInfo *si)
1954 si->player_name = get_corrected_login_name(getLoginName());
1957 si->sound_loops = TRUE;
1958 si->sound_music = TRUE;
1959 si->sound_simple = TRUE;
1961 si->double_buffering = TRUE;
1962 si->direct_draw = !si->double_buffering;
1963 si->scroll_delay = TRUE;
1964 si->soft_scrolling = TRUE;
1966 si->autorecord = TRUE;
1967 si->quick_doors = FALSE;
1968 si->team_mode = FALSE;
1969 si->handicap = TRUE;
1970 si->time_limit = TRUE;
1971 si->fullscreen = FALSE;
1972 si->ask_on_escape = TRUE;
1974 si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1975 si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1976 si->music_set = getStringCopy(MUSIC_SUBDIR);
1977 si->override_level_graphics = FALSE;
1978 si->override_level_sounds = FALSE;
1979 si->override_level_music = FALSE;
1981 si->editor.el_boulderdash = TRUE;
1982 si->editor.el_emerald_mine = TRUE;
1983 si->editor.el_more = TRUE;
1984 si->editor.el_sokoban = TRUE;
1985 si->editor.el_supaplex = TRUE;
1986 si->editor.el_diamond_caves = TRUE;
1987 si->editor.el_dx_boulderdash = TRUE;
1988 si->editor.el_chars = TRUE;
1989 si->editor.el_custom = TRUE;
1990 si->editor.el_custom_more = FALSE;
1992 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1993 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1994 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1996 for (i=0; i<MAX_PLAYERS; i++)
1998 si->input[i].use_joystick = FALSE;
1999 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2000 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2001 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2002 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2003 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2004 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2005 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2006 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2007 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2008 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2009 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2010 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2011 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2012 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2013 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2016 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2017 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2019 si->options.verbose = FALSE;
2022 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2026 if (!setup_file_hash)
2031 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2032 setSetupInfo(global_setup_tokens, i,
2033 getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2038 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2039 setSetupInfo(editor_setup_tokens, i,
2040 getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2043 /* shortcut setup */
2044 ssi = setup.shortcut;
2045 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2046 setSetupInfo(shortcut_setup_tokens, i,
2047 getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2048 setup.shortcut = ssi;
2051 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2055 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2057 sii = setup.input[pnr];
2058 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2060 char full_token[100];
2062 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2063 setSetupInfo(player_setup_tokens, i,
2064 getHashEntry(setup_file_hash, full_token));
2066 setup.input[pnr] = sii;
2071 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2072 setSetupInfo(system_setup_tokens, i,
2073 getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2077 soi = setup.options;
2078 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2079 setSetupInfo(options_setup_tokens, i,
2080 getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2081 setup.options = soi;
2086 char *filename = getSetupFilename();
2087 SetupFileHash *setup_file_hash = NULL;
2089 /* always start with reliable default values */
2090 setSetupInfoToDefaults(&setup);
2092 setup_file_hash = loadSetupFileHash(filename);
2094 if (setup_file_hash)
2096 char *player_name_new;
2098 checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2099 decodeSetupFileHash(setup_file_hash);
2101 setup.direct_draw = !setup.double_buffering;
2103 freeSetupFileHash(setup_file_hash);
2105 /* needed to work around problems with fixed length strings */
2106 player_name_new = get_corrected_login_name(setup.player_name);
2107 free(setup.player_name);
2108 setup.player_name = player_name_new;
2111 Error(ERR_WARN, "using default setup values");
2116 char *filename = getSetupFilename();
2120 InitUserDataDirectory();
2122 if (!(file = fopen(filename, MODE_WRITE)))
2124 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2128 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2129 getCookie("SETUP")));
2130 fprintf(file, "\n");
2134 for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2136 /* just to make things nicer :) */
2137 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2138 i == SETUP_TOKEN_GRAPHICS_SET)
2139 fprintf(file, "\n");
2141 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2146 fprintf(file, "\n");
2147 for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2148 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2150 /* shortcut setup */
2151 ssi = setup.shortcut;
2152 fprintf(file, "\n");
2153 for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2154 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2157 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2161 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2162 fprintf(file, "\n");
2164 sii = setup.input[pnr];
2165 for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2166 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2171 fprintf(file, "\n");
2172 for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2173 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2176 soi = setup.options;
2177 fprintf(file, "\n");
2178 for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2179 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2183 SetFilePermissions(filename, PERMS_PRIVATE);
2186 void LoadCustomElementDescriptions()
2188 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2189 SetupFileHash *setup_file_hash;
2192 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2194 if (element_info[i].custom_description != NULL)
2196 free(element_info[i].custom_description);
2197 element_info[i].custom_description = NULL;
2201 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2204 for (i=0; i<NUM_FILE_ELEMENTS; i++)
2206 char *token = getStringCat2(element_info[i].token_name, ".name");
2207 char *value = getHashEntry(setup_file_hash, token);
2210 element_info[i].custom_description = getStringCopy(value);
2215 freeSetupFileHash(setup_file_hash);
2218 void LoadSpecialMenuDesignSettings()
2220 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2221 SetupFileHash *setup_file_hash;
2224 /* always start with reliable default values from default config */
2225 for (i=0; image_config_vars[i].token != NULL; i++)
2226 for (j=0; image_config[j].token != NULL; j++)
2227 if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2228 *image_config_vars[i].value =
2229 get_integer_from_string(image_config[j].value);
2231 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2234 /* special case: initialize with default values that may be overwritten */
2235 for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2237 char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2238 char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2239 char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2241 if (value_x != NULL)
2242 menu.draw_xoffset[i] = get_integer_from_string(value_x);
2243 if (value_y != NULL)
2244 menu.draw_yoffset[i] = get_integer_from_string(value_y);
2245 if (list_size != NULL)
2246 menu.list_size[i] = get_integer_from_string(list_size);
2249 /* read (and overwrite with) values that may be specified in config file */
2250 for (i=0; image_config_vars[i].token != NULL; i++)
2252 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2255 *image_config_vars[i].value = get_integer_from_string(value);
2258 freeSetupFileHash(setup_file_hash);