1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2001 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
18 #include "libgame/libgame.h"
25 #define MAX_FILENAME_LEN 256 /* maximal filename length */
26 #define MAX_LINE_LEN 1000 /* maximal input line length */
27 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
28 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
29 #define LEVEL_HEADER_UNUSED 15 /* unused level header bytes */
30 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
31 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
32 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
33 #define TAPE_HEADER_UNUSED 7 /* unused tape header bytes */
35 /* file identifier strings */
36 #define LEVEL_COOKIE "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_2.0"
37 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
38 #define TAPE_COOKIE "ROCKSNDIAMONDS_TAPE_FILE_VERSION_2.0"
39 #define SETUP_COOKIE "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
40 #define LEVELSETUP_COOKIE "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
41 #define LEVELINFO_COOKIE "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
42 /* old file identifiers for backward compatibility */
43 #define LEVEL_COOKIE_10 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0"
44 #define LEVEL_COOKIE_12 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.2"
45 #define LEVEL_COOKIE_14 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.4"
46 #define TAPE_COOKIE_10 "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
47 #define TAPE_COOKIE_12 "ROCKSNDIAMONDS_TAPE_FILE_VERSION_1.2"
49 /* file names and filename extensions */
50 #if !defined(PLATFORM_MSDOS)
51 #define LEVELSETUP_DIRECTORY "levelsetup"
52 #define SETUP_FILENAME "setup.conf"
53 #define LEVELSETUP_FILENAME "levelsetup.conf"
54 #define LEVELINFO_FILENAME "levelinfo.conf"
55 #define LEVELFILE_EXTENSION "level"
56 #define TAPEFILE_EXTENSION "tape"
57 #define SCOREFILE_EXTENSION "score"
59 #define LEVELSETUP_DIRECTORY "lvlsetup"
60 #define SETUP_FILENAME "setup.cnf"
61 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
62 #define LEVELINFO_FILENAME "lvlinfo.cnf"
63 #define LEVELFILE_EXTENSION "lvl"
64 #define TAPEFILE_EXTENSION "tap"
65 #define SCOREFILE_EXTENSION "sco"
68 #if defined(PLATFORM_WIN32)
70 #define S_IRGRP S_IRUSR
73 #define S_IROTH S_IRUSR
76 #define S_IWGRP S_IWUSR
79 #define S_IWOTH S_IWUSR
82 #define S_IXGRP S_IXUSR
85 #define S_IXOTH S_IXUSR
87 #endif /* PLATFORM_WIN32 */
89 /* file permissions for newly written files */
90 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
91 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
92 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
93 #define LEVEL_PERMS (MODE_R_ALL | MODE_W_ALL)
94 #define SCORE_PERMS LEVEL_PERMS
95 #define TAPE_PERMS LEVEL_PERMS
96 #define SETUP_PERMS LEVEL_PERMS
98 /* sort priorities of level series (also used as level series classes) */
99 #define LEVELCLASS_TUTORIAL_START 10
100 #define LEVELCLASS_TUTORIAL_END 99
101 #define LEVELCLASS_CLASSICS_START 100
102 #define LEVELCLASS_CLASSICS_END 199
103 #define LEVELCLASS_CONTRIBUTION_START 200
104 #define LEVELCLASS_CONTRIBUTION_END 299
105 #define LEVELCLASS_USER_START 300
106 #define LEVELCLASS_USER_END 399
107 #define LEVELCLASS_BD_START 400
108 #define LEVELCLASS_BD_END 499
109 #define LEVELCLASS_EM_START 500
110 #define LEVELCLASS_EM_END 599
111 #define LEVELCLASS_SP_START 600
112 #define LEVELCLASS_SP_END 699
113 #define LEVELCLASS_DX_START 700
114 #define LEVELCLASS_DX_END 799
116 #define LEVELCLASS_TUTORIAL LEVELCLASS_TUTORIAL_START
117 #define LEVELCLASS_CLASSICS LEVELCLASS_CLASSICS_START
118 #define LEVELCLASS_CONTRIBUTION LEVELCLASS_CONTRIBUTION_START
119 #define LEVELCLASS_USER LEVELCLASS_USER_START
120 #define LEVELCLASS_BD LEVELCLASS_BD_START
121 #define LEVELCLASS_EM LEVELCLASS_EM_START
122 #define LEVELCLASS_SP LEVELCLASS_SP_START
123 #define LEVELCLASS_DX LEVELCLASS_DX_START
125 #define LEVELCLASS_UNDEFINED 999
127 #define NUM_LEVELCLASS_DESC 8
128 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
140 #define IS_LEVELCLASS_TUTORIAL(p) \
141 ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
142 (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
143 #define IS_LEVELCLASS_CLASSICS(p) \
144 ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
145 (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
146 #define IS_LEVELCLASS_CONTRIBUTION(p) \
147 ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
148 (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
149 #define IS_LEVELCLASS_USER(p) \
150 ((p)->sort_priority >= LEVELCLASS_USER_START && \
151 (p)->sort_priority <= LEVELCLASS_USER_END)
152 #define IS_LEVELCLASS_BD(p) \
153 ((p)->sort_priority >= LEVELCLASS_BD_START && \
154 (p)->sort_priority <= LEVELCLASS_BD_END)
155 #define IS_LEVELCLASS_EM(p) \
156 ((p)->sort_priority >= LEVELCLASS_EM_START && \
157 (p)->sort_priority <= LEVELCLASS_EM_END)
158 #define IS_LEVELCLASS_SP(p) \
159 ((p)->sort_priority >= LEVELCLASS_SP_START && \
160 (p)->sort_priority <= LEVELCLASS_SP_END)
161 #define IS_LEVELCLASS_DX(p) \
162 ((p)->sort_priority >= LEVELCLASS_DX_START && \
163 (p)->sort_priority <= LEVELCLASS_DX_END)
165 #define LEVELCLASS(n) (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
166 IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
167 IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
168 IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
169 IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
170 IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
171 IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
172 IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
173 LEVELCLASS_UNDEFINED)
175 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
176 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
177 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
178 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
179 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
180 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
181 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
182 IS_LEVELCLASS_USER(n) ? FC_RED : \
185 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
186 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
187 IS_LEVELCLASS_BD(n) ? 2 : \
188 IS_LEVELCLASS_EM(n) ? 3 : \
189 IS_LEVELCLASS_SP(n) ? 4 : \
190 IS_LEVELCLASS_DX(n) ? 5 : \
191 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
192 IS_LEVELCLASS_USER(n) ? 7 : \
195 static int getFileVersionFromCookieString(const char *cookie)
197 const char *ptr_cookie1, *ptr_cookie2;
198 const char *pattern1 = "_FILE_VERSION_";
199 const char *pattern2 = "?.?";
200 const int len_cookie = strlen(cookie);
201 const int len_pattern1 = strlen(pattern1);
202 const int len_pattern2 = strlen(pattern2);
203 const int len_pattern = len_pattern1 + len_pattern2;
204 int version_major, version_minor;
206 if (len_cookie <= len_pattern)
209 ptr_cookie1 = &cookie[len_cookie - len_pattern];
210 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
212 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
215 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
216 ptr_cookie2[1] != '.' ||
217 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
220 version_major = ptr_cookie2[0] - '0';
221 version_minor = ptr_cookie2[2] - '0';
223 return (version_major * 10 + version_minor);
226 boolean checkCookieString(const char *cookie, const char *template)
228 const char *pattern = "_FILE_VERSION_?.?";
229 const int len_cookie = strlen(cookie);
230 const int len_template = strlen(template);
231 const int len_pattern = strlen(pattern);
233 if (len_cookie != len_template)
236 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
242 char *getLevelClassDescription(struct LevelDirInfo *ldi)
244 int position = ldi->sort_priority / 100;
246 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
247 return levelclass_desc[position];
249 return "Unknown Level Class";
252 static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */
253 static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */
255 static char *getSetupDir()
257 return getUserDataDir();
260 static char *getUserLevelDir(char *level_subdir)
262 static char *userlevel_dir = NULL;
263 char *data_dir = getUserDataDir();
264 char *userlevel_subdir = LEVELS_DIRECTORY;
269 if (strlen(level_subdir) > 0)
270 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
272 userlevel_dir = getPath2(data_dir, userlevel_subdir);
274 return userlevel_dir;
277 static char *getTapeDir(char *level_subdir)
279 static char *tape_dir = NULL;
280 char *data_dir = getUserDataDir();
281 char *tape_subdir = TAPES_DIRECTORY;
286 if (strlen(level_subdir) > 0)
287 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
289 tape_dir = getPath2(data_dir, tape_subdir);
294 static char *getScoreDir(char *level_subdir)
296 static char *score_dir = NULL;
297 char *data_dir = options.rw_base_directory;
298 char *score_subdir = SCORES_DIRECTORY;
303 if (strlen(level_subdir) > 0)
304 score_dir = getPath3(data_dir, score_subdir, level_subdir);
306 score_dir = getPath2(data_dir, score_subdir);
311 static char *getLevelSetupDir(char *level_subdir)
313 static char *levelsetup_dir = NULL;
314 char *data_dir = getUserDataDir();
315 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
318 free(levelsetup_dir);
320 if (strlen(level_subdir) > 0)
321 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
323 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
325 return levelsetup_dir;
328 static char *getLevelFilename(int nr)
330 static char *filename = NULL;
331 char basename[MAX_FILENAME_LEN];
333 if (filename != NULL)
336 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
337 filename = getPath3((leveldir_current->user_defined ?
338 getUserLevelDir("") :
339 options.level_directory),
340 leveldir_current->fullpath,
346 static char *getTapeFilename(int nr)
348 static char *filename = NULL;
349 char basename[MAX_FILENAME_LEN];
351 if (filename != NULL)
354 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
355 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
360 static char *getScoreFilename(int nr)
362 static char *filename = NULL;
363 char basename[MAX_FILENAME_LEN];
365 if (filename != NULL)
368 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
369 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
374 static void InitTapeDirectory(char *level_subdir)
376 createDirectory(getUserDataDir(), "user data");
377 createDirectory(getTapeDir(""), "main tape");
378 createDirectory(getTapeDir(level_subdir), "level tape");
381 static void InitScoreDirectory(char *level_subdir)
383 createDirectory(getScoreDir(""), "main score");
384 createDirectory(getScoreDir(level_subdir), "level score");
387 static void InitUserLevelDirectory(char *level_subdir)
389 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
391 createDirectory(getUserDataDir(), "user data");
392 createDirectory(getUserLevelDir(""), "main user level");
393 createDirectory(getUserLevelDir(level_subdir), "user level");
399 static void InitLevelSetupDirectory(char *level_subdir)
401 createDirectory(getUserDataDir(), "user data");
402 createDirectory(getLevelSetupDir(""), "main level setup");
403 createDirectory(getLevelSetupDir(level_subdir), "level setup");
406 static void setLevelInfoToDefaults()
410 level.file_version = FILE_VERSION_ACTUAL;
411 level.game_version = GAME_VERSION_ACTUAL;
413 level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
414 level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
415 level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
417 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
418 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
420 for(x=0; x<MAX_LEV_FIELDX; x++)
421 for(y=0; y<MAX_LEV_FIELDY; y++)
422 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
425 level.gems_needed = 0;
426 level.amoeba_speed = 10;
427 level.time_magic_wall = 10;
428 level.time_wheel = 10;
429 level.time_light = 10;
430 level.time_timegate = 10;
431 level.amoeba_content = EL_DIAMANT;
432 level.double_speed = FALSE;
433 level.gravity = FALSE;
435 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
436 level.name[i] = '\0';
437 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
438 level.author[i] = '\0';
440 strcpy(level.name, NAMELESS_LEVEL_NAME);
441 strcpy(level.author, ANONYMOUS_NAME);
443 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
446 level.num_yam_contents = STD_ELEMENT_CONTENTS;
447 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
450 level.yam_content[i][x][y] =
451 (i < STD_ELEMENT_CONTENTS ? EL_FELSBROCKEN : EL_LEERRAUM);
453 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
454 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
455 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
457 BorderElement = EL_BETON;
459 /* try to determine better author name than 'anonymous' */
460 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
462 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
463 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
467 switch (LEVELCLASS(leveldir_current))
469 case LEVELCLASS_TUTORIAL:
470 strcpy(level.author, PROGRAM_AUTHOR_STRING);
473 case LEVELCLASS_CONTRIBUTION:
474 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
475 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
478 case LEVELCLASS_USER:
479 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
480 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
484 /* keep default value */
490 static int checkLevelElement(int element)
492 if (element >= EL_FIRST_RUNTIME_EL)
494 Error(ERR_WARN, "invalid level element %d", element);
495 element = EL_CHAR_FRAGE;
501 void OLD_LoadLevel(int level_nr)
504 char *filename = getLevelFilename(level_nr);
505 char cookie[MAX_LINE_LEN];
506 char chunk_name[CHUNK_ID_LEN + 1];
507 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
508 int file_version = FILE_VERSION_ACTUAL;
512 /* always start with reliable default values */
513 setLevelInfoToDefaults();
515 if (!(file = fopen(filename, MODE_READ)))
517 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
521 /* check file identifier */
522 fgets(cookie, MAX_LINE_LEN, file);
523 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
524 cookie[strlen(cookie) - 1] = '\0';
527 if (strcmp(cookie, LEVEL_COOKIE_10) == 0) /* old 1.0 level format */
528 file_version = FILE_VERSION_1_0;
529 else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
530 file_version = FILE_VERSION_1_2;
531 else if (strcmp(cookie, LEVEL_COOKIE) != 0) /* unknown level format */
533 Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
538 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
540 Error(ERR_WARN, "unknown format of level file '%s'", filename);
545 file_version = getFileVersionFromCookieString(cookie);
548 level.file_version = file_version;
550 /* read chunk "HEAD" */
551 if (file_version >= FILE_VERSION_1_2)
553 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
554 if (strcmp(chunk_name, "HEAD") || chunk_size != LEVEL_HEADER_SIZE)
556 Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
562 lev_fieldx = level.fieldx = fgetc(file);
563 lev_fieldy = level.fieldy = fgetc(file);
565 level.time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
566 level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
568 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
569 level.name[i] = fgetc(file);
570 level.name[MAX_LEVEL_NAME_LEN] = 0;
572 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
573 level.score[i] = fgetc(file);
575 level.num_yam_contents = STD_ELEMENT_CONTENTS;
576 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
582 if (i < STD_ELEMENT_CONTENTS)
583 level.yam_content[i][x][y] = checkLevelElement(fgetc(file));
585 level.yam_content[i][x][y] = EL_LEERRAUM;
590 level.amoeba_speed = fgetc(file);
591 level.time_magic_wall = fgetc(file);
592 level.time_wheel = fgetc(file);
593 level.amoeba_content = checkLevelElement(fgetc(file));
594 level.double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
595 level.gravity = (fgetc(file) == 1 ? TRUE : FALSE);
597 encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
599 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
602 if (file_version >= FILE_VERSION_1_2)
604 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
606 /* look for optional author chunk */
607 if (strcmp(chunk_name, "AUTH") == 0 && chunk_size == MAX_LEVEL_AUTHOR_LEN)
609 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
610 level.author[i] = fgetc(file);
611 level.author[MAX_LEVEL_NAME_LEN] = 0;
613 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
616 /* look for optional content chunk */
617 if (strcmp(chunk_name, "CONT") == 0 &&
618 chunk_size == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
621 level.num_yam_contents = fgetc(file);
625 if (level.num_yam_contents < 1 ||
626 level.num_yam_contents > MAX_ELEMENT_CONTENTS)
629 printf("WARNING: num_yam_contents == %d (corrected)\n",
630 level.num_yam_contents);
632 level.num_yam_contents = STD_ELEMENT_CONTENTS;
635 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
638 level.yam_content[i][x][y] =
639 checkLevelElement(encoding_16bit ?
640 getFile16BitInteger(file,
641 BYTE_ORDER_BIG_ENDIAN) :
644 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
647 /* next check body chunk identifier and chunk size */
648 if (strcmp(chunk_name, "BODY") != 0 ||
649 chunk_size != lev_fieldx * lev_fieldy)
651 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
657 /* clear all other level fields (needed if resized in level editor later) */
658 for(x=0; x<MAX_LEV_FIELDX; x++)
659 for(y=0; y<MAX_LEV_FIELDY; y++)
660 Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
662 /* now read in the valid level fields from level file */
663 for(y=0; y<lev_fieldy; y++)
664 for(x=0; x<lev_fieldx; x++)
665 Feld[x][y] = Ur[x][y] =
666 checkLevelElement(encoding_16bit ?
667 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
672 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
673 IS_LEVELCLASS_USER(leveldir_current))
675 /* for user contributed and private levels, use the version of
676 the game engine the levels were created for */
677 level.game_version = file_version;
679 /* player was faster than monsters in pre-1.0 levels */
680 if (file_version == FILE_VERSION_1_0)
682 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
683 Error(ERR_WARN, "using high speed movement for player");
684 level.double_speed = TRUE;
689 /* always use the latest version of the game engine for all but
690 user contributed and private levels */
691 level.game_version = GAME_VERSION_ACTUAL;
694 /* determine border element for this level */
698 static void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
704 static void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
710 static int LoadLevel_HEAD(struct LevelInfo *level, FILE *file, int chunk_size)
714 lev_fieldx = level->fieldx = fgetc(file);
715 lev_fieldy = level->fieldy = fgetc(file);
717 level->time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
718 level->gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
720 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
721 level->name[i] = fgetc(file);
722 level->name[MAX_LEVEL_NAME_LEN] = 0;
724 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
725 level->score[i] = fgetc(file);
727 level->num_yam_contents = STD_ELEMENT_CONTENTS;
728 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
731 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
733 level->amoeba_speed = fgetc(file);
734 level->time_magic_wall = fgetc(file);
735 level->time_wheel = fgetc(file);
736 level->amoeba_content = checkLevelElement(fgetc(file));
737 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
738 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
740 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
742 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
747 static int LoadLevel_AUTH(struct LevelInfo *level, FILE *file, int chunk_size)
751 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
752 level->author[i] = fgetc(file);
753 level->author[MAX_LEVEL_NAME_LEN] = 0;
758 static int LoadLevel_CONT(struct LevelInfo *level, FILE *file, int chunk_size)
762 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
763 int chunk_size_expected = header_size + content_size;
765 /* Note: "chunk_size" was wrong before version 2.0 when elements are
766 stored with 16-bit encoding (and should be twice as big then).
767 Even worse, playfield data was stored 16-bit when only yamyam content
768 contained 16-bit elements and vice versa. */
770 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
771 chunk_size_expected += content_size;
773 if (chunk_size_expected != chunk_size)
775 ReadUnusedBytesFromFile(file, chunk_size);
776 return chunk_size_expected;
780 level->num_yam_contents = fgetc(file);
784 /* correct invalid number of content fields -- should never happen */
785 if (level->num_yam_contents < 1 ||
786 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
787 level->num_yam_contents = STD_ELEMENT_CONTENTS;
789 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
792 level->yam_content[i][x][y] =
793 checkLevelElement(level->encoding_16bit_field ?
794 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
799 static int LoadLevel_BODY(struct LevelInfo *level, FILE *file, int chunk_size)
802 int chunk_size_expected = level->fieldx * level->fieldy;
804 /* Note: "chunk_size" was wrong before version 2.0 when elements are
805 stored with 16-bit encoding (and should be twice as big then).
806 Even worse, playfield data was stored 16-bit when only yamyam content
807 contained 16-bit elements and vice versa. */
809 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
810 chunk_size_expected *= 2;
812 if (chunk_size_expected != chunk_size)
814 ReadUnusedBytesFromFile(file, chunk_size);
815 return chunk_size_expected;
818 for(y=0; y<level->fieldy; y++)
819 for(x=0; x<level->fieldx; x++)
820 Feld[x][y] = Ur[x][y] =
821 checkLevelElement(level->encoding_16bit_field ?
822 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
827 static int LoadLevel_CNT2(struct LevelInfo *level, FILE *file, int chunk_size)
831 int num_contents, content_xsize, content_ysize;
832 int content_array[MAX_ELEMENT_CONTENTS][3][3];
834 element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
835 num_contents = fgetc(file);
836 content_xsize = fgetc(file);
837 content_ysize = fgetc(file);
838 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
840 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
843 content_array[i][x][y] =
844 checkLevelElement(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
846 /* correct invalid number of content fields -- should never happen */
847 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
848 num_contents = STD_ELEMENT_CONTENTS;
850 if (element == EL_MAMPFER)
852 level->num_yam_contents = num_contents;
854 for(i=0; i<num_contents; i++)
857 level->yam_content[i][x][y] = content_array[i][x][y];
859 else if (element == EL_AMOEBE_BD)
861 level->amoeba_content = content_array[0][0][0];
865 Error(ERR_WARN, "cannot load content for element '%d'", element);
871 void LoadLevel(int level_nr)
873 char *filename = getLevelFilename(level_nr);
874 char cookie[MAX_LINE_LEN];
875 char chunk_name[CHUNK_ID_LEN + 1];
879 /* always start with reliable default values */
880 setLevelInfoToDefaults();
882 if (!(file = fopen(filename, MODE_READ)))
884 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
888 /* check file identifier */
889 fgets(cookie, MAX_LINE_LEN, file);
890 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
891 cookie[strlen(cookie) - 1] = '\0';
893 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
895 Error(ERR_WARN, "unknown format of level file '%s'", filename);
900 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
902 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
907 if (level.file_version < FILE_VERSION_1_2)
909 /* level files from versions before 1.2.0 without chunk structure */
910 LoadLevel_HEAD(&level, file, LEVEL_HEADER_SIZE);
911 LoadLevel_BODY(&level, file, level.fieldx * level.fieldy);
919 int (*loader)(struct LevelInfo *, FILE *, int);
923 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
924 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
925 { "CONT", -1, LoadLevel_CONT },
926 { "BODY", -1, LoadLevel_BODY },
927 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
931 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
935 while (chunk_info[i].name != NULL &&
936 strcmp(chunk_name, chunk_info[i].name) != 0)
939 if (chunk_info[i].name == NULL)
941 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
942 chunk_name, filename);
943 ReadUnusedBytesFromFile(file, chunk_size);
945 else if (chunk_info[i].size != -1 &&
946 chunk_info[i].size != chunk_size)
948 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
949 chunk_size, chunk_name, filename);
950 ReadUnusedBytesFromFile(file, chunk_size);
954 /* call function to load this level chunk */
955 int chunk_size_expected =
956 (chunk_info[i].loader)(&level, file, chunk_size);
958 /* the size of some chunks cannot be checked before reading other
959 chunks first (like "HEAD" and "BODY") that contain some header
960 information, so check them here */
961 if (chunk_size_expected != chunk_size)
963 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
964 chunk_size, chunk_name, filename);
972 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
973 IS_LEVELCLASS_USER(leveldir_current))
975 /* for user contributed and private levels, use the version of
976 the game engine the levels were created for */
977 level.game_version = level.file_version;
979 /* player was faster than monsters in pre-1.0 levels */
980 if (level.file_version == FILE_VERSION_1_0)
982 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
983 Error(ERR_WARN, "using high speed movement for player");
984 level.double_speed = TRUE;
989 /* always use the latest version of the game engine for all but
990 user contributed and private levels */
991 level.game_version = GAME_VERSION_ACTUAL;
994 /* determine border element for this level */
998 void OLD_SaveLevel(int level_nr)
1001 char *filename = getLevelFilename(level_nr);
1003 boolean encoding_16bit_amoeba = FALSE;
1004 boolean encoding_16bit_yamyam = FALSE;
1006 boolean encoding_16bit = FALSE; /* default: only 8-bit elements */
1007 char *oldest_possible_cookie;
1010 if (!(file = fopen(filename, MODE_WRITE)))
1012 Error(ERR_WARN, "cannot save level file '%s'", filename);
1016 /* check yam content for 16-bit elements */
1017 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1020 if (level.yam_content[i][x][y] > 255)
1021 encoding_16bit = TRUE;
1023 /* check level field for 16-bit elements */
1024 for(y=0; y<lev_fieldy; y++)
1025 for(x=0; x<lev_fieldx; x++)
1027 encoding_16bit = TRUE;
1029 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
1031 fputs(oldest_possible_cookie, file); /* file identifier */
1034 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1036 fputc(level.fieldx, file);
1037 fputc(level.fieldy, file);
1039 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
1040 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
1042 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1043 fputc(level.name[i], file);
1044 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1045 fputc(level.score[i], file);
1046 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1049 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
1050 fputc(level.amoeba_speed, file);
1051 fputc(level.time_magic_wall, file);
1052 fputc(level.time_wheel, file);
1053 fputc(level.amoeba_content, file);
1054 fputc((level.double_speed ? 1 : 0), file);
1055 fputc((level.gravity ? 1 : 0), file);
1057 fputc((encoding_16bit ? 1 : 0), file);
1059 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1062 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1064 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1065 fputc(level.author[i], file);
1067 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
1068 BYTE_ORDER_BIG_ENDIAN);
1070 fputc(EL_MAMPFER, file);
1071 fputc(level.num_yam_contents, file);
1075 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1079 putFile16BitInteger(file, level.yam_content[i][x][y],
1080 BYTE_ORDER_BIG_ENDIAN);
1082 fputc(level.yam_content[i][x][y], file);
1084 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
1086 for(y=0; y<lev_fieldy; y++)
1087 for(x=0; x<lev_fieldx; x++)
1089 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1091 fputc(Ur[x][y], file);
1095 chmod(filename, LEVEL_PERMS);
1098 static void SaveLevel_HEAD(struct LevelInfo *level, FILE *file)
1102 fputc(level->fieldx, file);
1103 fputc(level->fieldy, file);
1105 putFile16BitInteger(file, level->time, BYTE_ORDER_BIG_ENDIAN);
1106 putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
1108 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1109 fputc(level->name[i], file);
1111 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1112 fputc(level->score[i], file);
1114 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1117 fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
1118 level->yam_content[i][x][y]),
1120 fputc(level->amoeba_speed, file);
1121 fputc(level->time_magic_wall, file);
1122 fputc(level->time_wheel, file);
1123 fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
1125 fputc((level->double_speed ? 1 : 0), file);
1126 fputc((level->gravity ? 1 : 0), file);
1128 fputc((level->encoding_16bit_field ? 1 : 0), file);
1130 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1133 static void SaveLevel_AUTH(struct LevelInfo *level, FILE *file)
1137 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1138 fputc(level->author[i], file);
1142 static void SaveLevel_CONT(struct LevelInfo *level, FILE *file)
1146 fputc(EL_MAMPFER, file);
1147 fputc(level->num_yam_contents, file);
1151 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1154 if (level->encoding_16bit_field)
1155 putFile16BitInteger(file, level->yam_content[i][x][y],
1156 BYTE_ORDER_BIG_ENDIAN);
1158 fputc(level->yam_content[i][x][y], file);
1162 static void SaveLevel_BODY(struct LevelInfo *level, FILE *file)
1166 for(y=0; y<lev_fieldy; y++)
1167 for(x=0; x<lev_fieldx; x++)
1168 if (level->encoding_16bit_field)
1169 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1171 fputc(Ur[x][y], file);
1174 static void SaveLevel_CNT2(struct LevelInfo *level, FILE *file, int element)
1177 int num_contents, content_xsize, content_ysize;
1178 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1180 if (element == EL_MAMPFER)
1182 num_contents = level->num_yam_contents;
1186 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1189 content_array[i][x][y] = level->yam_content[i][x][y];
1191 else if (element == EL_AMOEBE_BD)
1197 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1200 content_array[i][x][y] = EL_LEERRAUM;
1201 content_array[0][0][0] = level->amoeba_content;
1205 /* chunk header already written -- write empty chunk data */
1206 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1208 Error(ERR_WARN, "cannot save content for element '%d'", element);
1212 putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
1213 fputc(num_contents, file);
1214 fputc(content_xsize, file);
1215 fputc(content_ysize, file);
1217 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1219 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1222 putFile16BitInteger(file, content_array[i][x][y],
1223 BYTE_ORDER_BIG_ENDIAN);
1226 void SaveLevel(int level_nr)
1229 char *filename = getLevelFilename(level_nr);
1230 int body_chunk_size;
1233 if (!(file = fopen(filename, MODE_WRITE)))
1235 Error(ERR_WARN, "cannot save level file '%s'", filename);
1239 /* check level field for 16-bit elements */
1240 for(y=0; y<level.fieldy; y++)
1241 for(x=0; x<level.fieldx; x++)
1243 level.encoding_16bit_field = TRUE;
1245 /* check yamyam content for 16-bit elements */
1246 for(i=0; i<level.num_yam_contents; i++)
1249 if (level.yam_content[i][x][y] > 255)
1250 level.encoding_16bit_yamyam = TRUE;
1252 /* check amoeba content for 16-bit elements */
1253 if (level.amoeba_content > 255)
1254 level.encoding_16bit_amoeba = TRUE;
1257 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
1259 fputs(LEVEL_COOKIE, file); /* file identifier */
1262 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1263 SaveLevel_HEAD(&level, file);
1265 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1266 SaveLevel_AUTH(&level, file);
1269 if (level.encoding_16bit_field) /* obsolete since new "CNT2" chunk */
1271 chunk_size = 4 + 2 * (MAX_ELEMENT_CONTENTS * 3 * 3);
1273 putFileChunk(file, "CONT", chunk_size, BYTE_ORDER_BIG_ENDIAN);
1274 SaveLevel_CONT(&level, file);
1278 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1279 SaveLevel_BODY(&level, file);
1281 if (level.encoding_16bit_yamyam ||
1282 level.num_yam_contents != STD_ELEMENT_CONTENTS)
1284 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1285 SaveLevel_CNT2(&level, file, EL_MAMPFER);
1288 if (level.encoding_16bit_amoeba)
1290 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1291 SaveLevel_CNT2(&level, file, EL_AMOEBE_BD);
1296 chmod(filename, LEVEL_PERMS);
1299 static void setTapeInfoToDefaults()
1303 /* always start with reliable default values (empty tape) */
1304 tape.file_version = FILE_VERSION_ACTUAL;
1305 tape.game_version = GAME_VERSION_ACTUAL;
1308 /* default values (also for pre-1.2 tapes) with only the first player */
1309 tape.player_participates[0] = TRUE;
1310 for(i=1; i<MAX_PLAYERS; i++)
1311 tape.player_participates[i] = FALSE;
1313 /* at least one (default: the first) player participates in every tape */
1314 tape.num_participating_players = 1;
1316 tape.level_nr = level_nr;
1318 tape.changed = FALSE;
1320 tape.recording = FALSE;
1321 tape.playing = FALSE;
1322 tape.pausing = FALSE;
1325 void OLD_LoadTape(int level_nr)
1328 char *filename = getTapeFilename(level_nr);
1329 char cookie[MAX_LINE_LEN];
1330 char chunk_name[CHUNK_ID_LEN + 1];
1332 int num_participating_players;
1333 int file_version = FILE_VERSION_ACTUAL; /* last version of tape files */
1336 /* always start with reliable default values (empty tape) */
1337 tape.file_version = FILE_VERSION_ACTUAL;
1338 tape.game_version = GAME_VERSION_ACTUAL;
1341 /* default values (also for pre-1.2 tapes) with only the first player */
1342 tape.player_participates[0] = TRUE;
1343 for(i=1; i<MAX_PLAYERS; i++)
1344 tape.player_participates[i] = FALSE;
1346 /* at least one (default: the first) player participates in every tape */
1347 num_participating_players = 1;
1349 if (!(file = fopen(filename, MODE_READ)))
1352 /* check file identifier */
1353 fgets(cookie, MAX_LINE_LEN, file);
1354 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1355 cookie[strlen(cookie) - 1] = '\0';
1358 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
1359 file_version = FILE_VERSION_1_0;
1360 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
1362 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
1367 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1369 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1374 file_version = getFileVersionFromCookieString(cookie);
1377 tape.file_version = file_version;
1378 tape.game_version = file_version;
1380 /* read chunk "HEAD" */
1381 if (file_version >= FILE_VERSION_1_2)
1383 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1384 if (strcmp(chunk_name, "HEAD") || chunk_size != TAPE_HEADER_SIZE)
1386 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
1392 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1393 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1394 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1396 /* read header fields that are new since version 1.2 */
1397 if (file_version >= FILE_VERSION_1_2)
1399 byte store_participating_players = fgetc(file);
1401 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
1404 /* since version 1.2, tapes store which players participate in the tape */
1405 num_participating_players = 0;
1406 for(i=0; i<MAX_PLAYERS; i++)
1408 tape.player_participates[i] = FALSE;
1410 if (store_participating_players & (1 << i))
1412 tape.player_participates[i] = TRUE;
1413 num_participating_players++;
1418 tape.level_nr = level_nr;
1420 tape.changed = FALSE;
1422 tape.recording = FALSE;
1423 tape.playing = FALSE;
1424 tape.pausing = FALSE;
1426 /* read chunk "BODY" */
1427 if (file_version >= FILE_VERSION_1_2)
1429 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1430 if (strcmp(chunk_name, "BODY") ||
1431 chunk_size != (num_participating_players + 1) * tape.length)
1433 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
1440 printf("\nTAPE OF LEVEL %d\n", level_nr);
1443 for(i=0; i<tape.length; i++)
1445 if (i >= MAX_TAPELEN)
1448 for(j=0; j<MAX_PLAYERS; j++)
1450 tape.pos[i].action[j] = MV_NO_MOVING;
1452 if (tape.player_participates[j])
1453 tape.pos[i].action[j] = fgetc(file);
1457 int x = tape.pos[i].action[j];
1459 printf("%d:%02x ", j, x);
1460 printf("[%c%c%c%c|%c%c] - ",
1461 (x & JOY_LEFT ? '<' : ' '),
1462 (x & JOY_RIGHT ? '>' : ' '),
1463 (x & JOY_UP ? '^' : ' '),
1464 (x & JOY_DOWN ? 'v' : ' '),
1465 (x & JOY_BUTTON_1 ? '1' : ' '),
1466 (x & JOY_BUTTON_2 ? '2' : ' '));
1472 tape.pos[i].delay = fgetc(file);
1475 printf("[%03d]\n", tape.pos[i].delay);
1478 if (file_version == FILE_VERSION_1_0)
1480 /* eliminate possible diagonal moves in old tapes */
1481 /* this is only for backward compatibility */
1483 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1484 byte action = tape.pos[i].action[0];
1485 int k, num_moves = 0;
1489 if (action & joy_dir[k])
1491 tape.pos[i + num_moves].action[0] = joy_dir[k];
1493 tape.pos[i + num_moves].delay = 0;
1502 tape.length += num_moves;
1505 else if (file_version < FILE_VERSION_2_0)
1507 if (tape.pos[i].delay > 1)
1510 tape.pos[i + 1] = tape.pos[i];
1511 tape.pos[i + 1].delay = 1;
1514 for(j=0; j<MAX_PLAYERS; j++)
1515 tape.pos[i].action[j] = MV_NO_MOVING;
1516 tape.pos[i].delay--;
1529 if (i != tape.length)
1530 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
1532 tape.length_seconds = GetTapeLength();
1535 static int LoadTape_HEAD(struct TapeInfo *tape, FILE *file, int chunk_size)
1539 tape->random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1540 tape->date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1541 tape->length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1543 /* read header fields that are new since version 1.2 */
1544 if (tape->file_version >= FILE_VERSION_1_2)
1546 byte store_participating_players = fgetc(file);
1548 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1550 /* since version 1.2, tapes store which players participate in the tape */
1551 tape->num_participating_players = 0;
1552 for(i=0; i<MAX_PLAYERS; i++)
1554 tape->player_participates[i] = FALSE;
1556 if (store_participating_players & (1 << i))
1558 tape->player_participates[i] = TRUE;
1559 tape->num_participating_players++;
1567 static int LoadTape_BODY(struct TapeInfo *tape, FILE *file, int chunk_size)
1570 int chunk_size_expected =
1571 (tape->num_participating_players + 1) * tape->length;
1573 if (chunk_size_expected != chunk_size)
1575 ReadUnusedBytesFromFile(file, chunk_size);
1576 return chunk_size_expected;
1579 for(i=0; i<tape->length; i++)
1581 if (i >= MAX_TAPELEN)
1584 for(j=0; j<MAX_PLAYERS; j++)
1586 tape->pos[i].action[j] = MV_NO_MOVING;
1588 if (tape->player_participates[j])
1589 tape->pos[i].action[j] = fgetc(file);
1592 tape->pos[i].delay = fgetc(file);
1594 if (tape->file_version == FILE_VERSION_1_0)
1596 /* eliminate possible diagonal moves in old tapes */
1597 /* this is only for backward compatibility */
1599 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1600 byte action = tape->pos[i].action[0];
1601 int k, num_moves = 0;
1605 if (action & joy_dir[k])
1607 tape->pos[i + num_moves].action[0] = joy_dir[k];
1609 tape->pos[i + num_moves].delay = 0;
1618 tape->length += num_moves;
1621 else if (tape->file_version < FILE_VERSION_2_0)
1623 if (tape->pos[i].delay > 1)
1626 tape->pos[i + 1] = tape->pos[i];
1627 tape->pos[i + 1].delay = 1;
1630 for(j=0; j<MAX_PLAYERS; j++)
1631 tape->pos[i].action[j] = MV_NO_MOVING;
1632 tape->pos[i].delay--;
1643 if (i != tape->length)
1644 chunk_size = (tape->num_participating_players + 1) * i;
1649 void LoadTape(int level_nr)
1651 char *filename = getTapeFilename(level_nr);
1652 char cookie[MAX_LINE_LEN];
1653 char chunk_name[CHUNK_ID_LEN + 1];
1657 /* always start with reliable default values */
1658 setTapeInfoToDefaults();
1660 if (!(file = fopen(filename, MODE_READ)))
1663 /* check file identifier */
1664 fgets(cookie, MAX_LINE_LEN, file);
1665 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1666 cookie[strlen(cookie) - 1] = '\0';
1668 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1670 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1675 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1677 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1682 tape.game_version = tape.file_version;
1684 if (tape.file_version < FILE_VERSION_1_2)
1686 /* tape files from versions before 1.2.0 without chunk structure */
1687 LoadTape_HEAD(&tape, file, TAPE_HEADER_SIZE);
1688 LoadTape_BODY(&tape, file, 2 * tape.length);
1696 int (*loader)(struct TapeInfo *, FILE *, int);
1700 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1701 { "BODY", -1, LoadTape_BODY },
1705 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
1709 while (chunk_info[i].name != NULL &&
1710 strcmp(chunk_name, chunk_info[i].name) != 0)
1713 if (chunk_info[i].name == NULL)
1715 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1716 chunk_name, filename);
1717 ReadUnusedBytesFromFile(file, chunk_size);
1719 else if (chunk_info[i].size != -1 &&
1720 chunk_info[i].size != chunk_size)
1722 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1723 chunk_size, chunk_name, filename);
1724 ReadUnusedBytesFromFile(file, chunk_size);
1728 /* call function to load this tape chunk */
1729 int chunk_size_expected =
1730 (chunk_info[i].loader)(&tape, file, chunk_size);
1732 /* the size of some chunks cannot be checked before reading other
1733 chunks first (like "HEAD" and "BODY") that contain some header
1734 information, so check them here */
1735 if (chunk_size_expected != chunk_size)
1737 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1738 chunk_size, chunk_name, filename);
1746 tape.length_seconds = GetTapeLength();
1749 void OLD_SaveTape(int level_nr)
1752 char *filename = getTapeFilename(level_nr);
1754 boolean new_tape = TRUE;
1755 byte store_participating_players;
1756 int num_participating_players;
1758 InitTapeDirectory(leveldir_current->filename);
1760 /* if a tape still exists, ask to overwrite it */
1761 if (access(filename, F_OK) == 0)
1764 if (!Request("Replace old tape ?", REQ_ASK))
1768 /* count number of players and set corresponding bits for compact storage */
1769 store_participating_players = 0;
1770 num_participating_players = 0;
1771 for(i=0; i<MAX_PLAYERS; i++)
1773 if (tape.player_participates[i])
1775 num_participating_players++;
1776 store_participating_players |= (1 << i);
1780 if (!(file = fopen(filename, MODE_WRITE)))
1782 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1786 fputs(TAPE_COOKIE, file); /* file identifier */
1789 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1791 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
1792 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
1793 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
1795 fputc(store_participating_players, file);
1797 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1800 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
1801 BYTE_ORDER_BIG_ENDIAN);
1803 for(i=0; i<tape.length; i++)
1807 for(j=0; j<MAX_PLAYERS; j++)
1808 if (tape.player_participates[j])
1809 fputc(tape.pos[i].action[j], file);
1811 fputc(tape.pos[i].delay, file);
1816 chmod(filename, TAPE_PERMS);
1818 tape.changed = FALSE;
1821 Request("tape saved !", REQ_CONFIRM);
1824 static void SaveTape_HEAD(struct TapeInfo *tape, FILE *file)
1827 byte store_participating_players = 0;
1829 /* set bits for participating players for compact storage */
1830 for(i=0; i<MAX_PLAYERS; i++)
1831 if (tape->player_participates[i])
1832 store_participating_players |= (1 << i);
1834 putFile32BitInteger(file, tape->random_seed, BYTE_ORDER_BIG_ENDIAN);
1835 putFile32BitInteger(file, tape->date, BYTE_ORDER_BIG_ENDIAN);
1836 putFile32BitInteger(file, tape->length, BYTE_ORDER_BIG_ENDIAN);
1838 fputc(store_participating_players, file);
1840 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1843 static void SaveTape_BODY(struct TapeInfo *tape, FILE *file)
1847 for(i=0; i<tape->length; i++)
1849 for(j=0; j<MAX_PLAYERS; j++)
1850 if (tape->player_participates[j])
1851 fputc(tape->pos[i].action[j], file);
1853 fputc(tape->pos[i].delay, file);
1857 void SaveTape(int level_nr)
1860 char *filename = getTapeFilename(level_nr);
1862 boolean new_tape = TRUE;
1863 int num_participating_players = 0;
1864 int body_chunk_size;
1866 InitTapeDirectory(leveldir_current->filename);
1868 /* if a tape still exists, ask to overwrite it */
1869 if (access(filename, F_OK) == 0)
1872 if (!Request("Replace old tape ?", REQ_ASK))
1876 if (!(file = fopen(filename, MODE_WRITE)))
1878 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1882 /* count number of participating players */
1883 for(i=0; i<MAX_PLAYERS; i++)
1884 if (tape.player_participates[i])
1885 num_participating_players++;
1887 body_chunk_size = (num_participating_players + 1) * tape.length;
1889 fputs(TAPE_COOKIE, file); /* file identifier */
1892 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1893 SaveTape_HEAD(&tape, file);
1895 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1896 SaveTape_BODY(&tape, file);
1900 chmod(filename, TAPE_PERMS);
1902 tape.changed = FALSE;
1905 Request("tape saved !", REQ_CONFIRM);
1908 void DumpTape(struct TapeInfo *tape)
1912 if (TAPE_IS_EMPTY(*tape))
1914 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1919 printf("-------------------------------------------------------------------------------\n");
1920 printf("TAPE OF LEVEL %d\n", tape->level_nr);
1921 printf("-------------------------------------------------------------------------------\n");
1923 for(i=0; i<tape->length; i++)
1925 if (i >= MAX_TAPELEN)
1928 for(j=0; j<MAX_PLAYERS; j++)
1930 if (tape->player_participates[j])
1932 int action = tape->pos[i].action[j];
1934 printf("%d:%02x ", j, action);
1935 printf("[%c%c%c%c|%c%c] - ",
1936 (action & JOY_LEFT ? '<' : ' '),
1937 (action & JOY_RIGHT ? '>' : ' '),
1938 (action & JOY_UP ? '^' : ' '),
1939 (action & JOY_DOWN ? 'v' : ' '),
1940 (action & JOY_BUTTON_1 ? '1' : ' '),
1941 (action & JOY_BUTTON_2 ? '2' : ' '));
1945 printf("(%03d)\n", tape->pos[i].delay);
1948 printf("-------------------------------------------------------------------------------\n");
1951 void LoadScore(int level_nr)
1954 char *filename = getScoreFilename(level_nr);
1955 char cookie[MAX_LINE_LEN];
1956 char line[MAX_LINE_LEN];
1960 /* always start with reliable default values */
1961 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1963 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1964 highscore[i].Score = 0;
1967 if (!(file = fopen(filename, MODE_READ)))
1970 /* check file identifier */
1971 fgets(cookie, MAX_LINE_LEN, file);
1972 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1973 cookie[strlen(cookie) - 1] = '\0';
1976 if (strcmp(cookie, SCORE_COOKIE) != 0)
1978 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
1983 if (!checkCookieString(cookie, SCORE_COOKIE)) /* unknown file format */
1985 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1991 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1993 fscanf(file, "%d", &highscore[i].Score);
1994 fgets(line, MAX_LINE_LEN, file);
1996 if (line[strlen(line) - 1] == '\n')
1997 line[strlen(line) - 1] = '\0';
1999 for (line_ptr = line; *line_ptr; line_ptr++)
2001 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2003 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2004 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2013 void SaveScore(int level_nr)
2016 char *filename = getScoreFilename(level_nr);
2019 InitScoreDirectory(leveldir_current->filename);
2021 if (!(file = fopen(filename, MODE_WRITE)))
2023 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2027 fprintf(file, "%s\n\n", SCORE_COOKIE);
2029 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2030 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2034 chmod(filename, SCORE_PERMS);
2037 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
2038 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
2039 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
2040 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
2041 #define TOKEN_STR_PLAYER_PREFIX "player_"
2043 #define TOKEN_VALUE_POSITION 30
2046 #define SETUP_TOKEN_PLAYER_NAME 0
2047 #define SETUP_TOKEN_SOUND 1
2048 #define SETUP_TOKEN_SOUND_LOOPS 2
2049 #define SETUP_TOKEN_SOUND_MUSIC 3
2050 #define SETUP_TOKEN_SOUND_SIMPLE 4
2053 #define SETUP_TOKEN_TOONS 5
2054 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
2057 #define SETUP_TOKEN_SCROLL_DELAY 5
2058 #define SETUP_TOKEN_SOFT_SCROLLING 6
2059 #define SETUP_TOKEN_FADING 7
2060 #define SETUP_TOKEN_AUTORECORD 8
2061 #define SETUP_TOKEN_QUICK_DOORS 9
2062 #define SETUP_TOKEN_TEAM_MODE 10
2063 #define SETUP_TOKEN_HANDICAP 11
2064 #define SETUP_TOKEN_TIME_LIMIT 12
2065 #define SETUP_TOKEN_FULLSCREEN 13
2068 #define SETUP_TOKEN_USE_JOYSTICK 14
2069 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
2070 #define SETUP_TOKEN_JOY_XLEFT 16
2071 #define SETUP_TOKEN_JOY_XMIDDLE 17
2072 #define SETUP_TOKEN_JOY_XRIGHT 18
2073 #define SETUP_TOKEN_JOY_YUPPER 19
2074 #define SETUP_TOKEN_JOY_YMIDDLE 20
2075 #define SETUP_TOKEN_JOY_YLOWER 21
2076 #define SETUP_TOKEN_JOY_SNAP 22
2077 #define SETUP_TOKEN_JOY_BOMB 23
2078 #define SETUP_TOKEN_KEY_LEFT 24
2079 #define SETUP_TOKEN_KEY_RIGHT 25
2080 #define SETUP_TOKEN_KEY_UP 26
2081 #define SETUP_TOKEN_KEY_DOWN 27
2082 #define SETUP_TOKEN_KEY_SNAP 28
2083 #define SETUP_TOKEN_KEY_BOMB 29
2085 /* level directory info */
2086 #define LEVELINFO_TOKEN_NAME 30
2087 #define LEVELINFO_TOKEN_NAME_SHORT 31
2088 #define LEVELINFO_TOKEN_NAME_SORTING 32
2089 #define LEVELINFO_TOKEN_AUTHOR 33
2090 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
2091 #define LEVELINFO_TOKEN_LEVELS 35
2092 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
2093 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
2094 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
2095 #define LEVELINFO_TOKEN_READONLY 39
2097 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
2098 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
2100 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
2101 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
2103 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
2104 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
2106 #define TYPE_BOOLEAN 1
2107 #define TYPE_SWITCH 2
2109 #define TYPE_INTEGER 4
2110 #define TYPE_STRING 5
2112 static struct SetupInfo si;
2113 static struct SetupInputInfo sii;
2114 static struct LevelDirInfo ldi;
2123 { TYPE_STRING, &si.player_name, "player_name" },
2124 { TYPE_SWITCH, &si.sound, "sound" },
2125 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2126 { TYPE_SWITCH, &si.sound_music, "background_music" },
2127 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2130 { TYPE_SWITCH, &si.toons, "toons" },
2131 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
2134 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2135 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2136 { TYPE_SWITCH, &si.fading, "screen_fading" },
2137 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2138 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2139 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2140 { TYPE_SWITCH, &si.handicap, "handicap" },
2141 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2142 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2145 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2146 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2147 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2148 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2149 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2150 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2151 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2152 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2153 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2154 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2155 { TYPE_KEY, &sii.key.left, ".key.move_left" },
2156 { TYPE_KEY, &sii.key.right, ".key.move_right" },
2157 { TYPE_KEY, &sii.key.up, ".key.move_up" },
2158 { TYPE_KEY, &sii.key.down, ".key.move_down" },
2159 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
2160 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
2162 /* level directory info */
2163 { TYPE_STRING, &ldi.name, "name" },
2164 { TYPE_STRING, &ldi.name_short, "name_short" },
2165 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
2166 { TYPE_STRING, &ldi.author, "author" },
2167 { TYPE_STRING, &ldi.imported_from, "imported_from" },
2168 { TYPE_INTEGER, &ldi.levels, "levels" },
2169 { TYPE_INTEGER, &ldi.first_level, "first_level" },
2170 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
2171 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
2172 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
2175 static char *string_tolower(char *s)
2177 static char s_lower[100];
2180 if (strlen(s) >= 100)
2185 for (i=0; i<strlen(s_lower); i++)
2186 s_lower[i] = tolower(s_lower[i]);
2191 static int get_string_integer_value(char *s)
2193 static char *number_text[][3] =
2195 { "0", "zero", "null", },
2196 { "1", "one", "first" },
2197 { "2", "two", "second" },
2198 { "3", "three", "third" },
2199 { "4", "four", "fourth" },
2200 { "5", "five", "fifth" },
2201 { "6", "six", "sixth" },
2202 { "7", "seven", "seventh" },
2203 { "8", "eight", "eighth" },
2204 { "9", "nine", "ninth" },
2205 { "10", "ten", "tenth" },
2206 { "11", "eleven", "eleventh" },
2207 { "12", "twelve", "twelfth" },
2212 for (i=0; i<13; i++)
2214 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
2220 static boolean get_string_boolean_value(char *s)
2222 if (strcmp(string_tolower(s), "true") == 0 ||
2223 strcmp(string_tolower(s), "yes") == 0 ||
2224 strcmp(string_tolower(s), "on") == 0 ||
2225 get_string_integer_value(s) == 1)
2231 static char *getFormattedSetupEntry(char *token, char *value)
2234 static char entry[MAX_LINE_LEN];
2236 sprintf(entry, "%s:", token);
2237 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2241 strcat(entry, value);
2246 static void freeSetupFileList(struct SetupFileList *setup_file_list)
2248 if (!setup_file_list)
2251 if (setup_file_list->token)
2252 free(setup_file_list->token);
2253 if (setup_file_list->value)
2254 free(setup_file_list->value);
2255 if (setup_file_list->next)
2256 freeSetupFileList(setup_file_list->next);
2257 free(setup_file_list);
2260 static struct SetupFileList *newSetupFileList(char *token, char *value)
2262 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
2264 new->token = checked_malloc(strlen(token) + 1);
2265 strcpy(new->token, token);
2267 new->value = checked_malloc(strlen(value) + 1);
2268 strcpy(new->value, value);
2275 static char *getTokenValue(struct SetupFileList *setup_file_list,
2278 if (!setup_file_list)
2281 if (strcmp(setup_file_list->token, token) == 0)
2282 return setup_file_list->value;
2284 return getTokenValue(setup_file_list->next, token);
2287 static void setTokenValue(struct SetupFileList *setup_file_list,
2288 char *token, char *value)
2290 if (!setup_file_list)
2293 if (strcmp(setup_file_list->token, token) == 0)
2295 free(setup_file_list->value);
2296 setup_file_list->value = checked_malloc(strlen(value) + 1);
2297 strcpy(setup_file_list->value, value);
2299 else if (setup_file_list->next == NULL)
2300 setup_file_list->next = newSetupFileList(token, value);
2302 setTokenValue(setup_file_list->next, token, value);
2306 static void printSetupFileList(struct SetupFileList *setup_file_list)
2308 if (!setup_file_list)
2311 printf("token: '%s'\n", setup_file_list->token);
2312 printf("value: '%s'\n", setup_file_list->value);
2314 printSetupFileList(setup_file_list->next);
2318 static struct SetupFileList *loadSetupFileList(char *filename)
2321 char line[MAX_LINE_LEN];
2322 char *token, *value, *line_ptr;
2323 struct SetupFileList *setup_file_list = newSetupFileList("", "");
2324 struct SetupFileList *first_valid_list_entry;
2328 if (!(file = fopen(filename, MODE_READ)))
2330 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
2336 /* read next line of input file */
2337 if (!fgets(line, MAX_LINE_LEN, file))
2340 /* cut trailing comment or whitespace from input line */
2341 for (line_ptr = line; *line_ptr; line_ptr++)
2343 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
2350 /* cut trailing whitespaces from input line */
2351 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
2352 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
2355 /* ignore empty lines */
2359 line_len = strlen(line);
2361 /* cut leading whitespaces from token */
2362 for (token = line; *token; token++)
2363 if (*token != ' ' && *token != '\t')
2366 /* find end of token */
2367 for (line_ptr = token; *line_ptr; line_ptr++)
2369 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
2376 if (line_ptr < line + line_len)
2377 value = line_ptr + 1;
2381 /* cut leading whitespaces from value */
2382 for (; *value; value++)
2383 if (*value != ' ' && *value != '\t')
2386 if (*token && *value)
2387 setTokenValue(setup_file_list, token, value);
2392 first_valid_list_entry = setup_file_list->next;
2394 /* free empty list header */
2395 setup_file_list->next = NULL;
2396 freeSetupFileList(setup_file_list);
2398 if (first_valid_list_entry == NULL)
2399 Error(ERR_WARN, "configuration file '%s' is empty", filename);
2401 return first_valid_list_entry;
2404 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
2407 if (!setup_file_list)
2410 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
2412 if (strcmp(setup_file_list->value, identifier) != 0)
2414 Error(ERR_WARN, "configuration file has wrong version");
2421 if (setup_file_list->next)
2422 checkSetupFileListIdentifier(setup_file_list->next, identifier);
2425 Error(ERR_WARN, "configuration file has no version information");
2430 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
2432 ldi->filename = NULL;
2433 ldi->fullpath = NULL;
2434 ldi->basepath = NULL;
2435 ldi->name = getStringCopy(ANONYMOUS_NAME);
2436 ldi->name_short = NULL;
2437 ldi->name_sorting = NULL;
2438 ldi->author = getStringCopy(ANONYMOUS_NAME);
2439 ldi->imported_from = NULL;
2441 ldi->first_level = 0;
2442 ldi->last_level = 0;
2443 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
2444 ldi->level_group = FALSE;
2445 ldi->parent_link = FALSE;
2446 ldi->user_defined = FALSE;
2447 ldi->readonly = TRUE;
2449 ldi->class_desc = NULL;
2450 ldi->handicap_level = 0;
2452 ldi->cl_cursor = -1;
2454 ldi->node_parent = NULL;
2455 ldi->node_group = NULL;
2459 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
2460 struct LevelDirInfo *parent)
2464 setLevelDirInfoToDefaults(ldi);
2468 /* first copy all values from the parent structure ... */
2471 /* ... then set all fields to default that cannot be inherited from parent.
2472 This is especially important for all those fields that can be set from
2473 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
2474 calls 'free()' for all already set token values which requires that no
2475 other structure's pointer may point to them!
2478 ldi->filename = NULL;
2479 ldi->fullpath = NULL;
2480 ldi->basepath = NULL;
2481 ldi->name = getStringCopy(ANONYMOUS_NAME);
2482 ldi->name_short = NULL;
2483 ldi->name_sorting = NULL;
2484 ldi->author = getStringCopy(parent->author);
2485 ldi->imported_from = getStringCopy(parent->imported_from);
2487 ldi->level_group = FALSE;
2488 ldi->parent_link = FALSE;
2490 ldi->node_parent = parent;
2491 ldi->node_group = NULL;
2495 static void setSetupInfoToDefaults(struct SetupInfo *si)
2499 si->player_name = getStringCopy(getLoginName());
2502 si->sound_loops = TRUE;
2503 si->sound_music = TRUE;
2504 si->sound_simple = TRUE;
2506 si->double_buffering = TRUE;
2507 si->direct_draw = !si->double_buffering;
2508 si->scroll_delay = TRUE;
2509 si->soft_scrolling = TRUE;
2511 si->autorecord = TRUE;
2512 si->quick_doors = FALSE;
2513 si->team_mode = FALSE;
2514 si->handicap = TRUE;
2515 si->time_limit = TRUE;
2516 si->fullscreen = FALSE;
2518 for (i=0; i<MAX_PLAYERS; i++)
2520 si->input[i].use_joystick = FALSE;
2521 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
2522 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2523 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2524 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2525 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2526 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2527 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2528 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2529 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2530 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2531 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2532 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2533 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2534 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2535 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2539 static void setSetupInfo(int token_nr, char *token_value)
2541 int token_type = token_info[token_nr].type;
2542 void *setup_value = token_info[token_nr].value;
2544 if (token_value == NULL)
2547 /* set setup field to corresponding token value */
2552 *(boolean *)setup_value = get_string_boolean_value(token_value);
2556 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
2560 *(int *)setup_value = get_string_integer_value(token_value);
2564 if (*(char **)setup_value != NULL)
2565 free(*(char **)setup_value);
2566 *(char **)setup_value = getStringCopy(token_value);
2574 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
2578 if (!setup_file_list)
2581 /* handle global setup values */
2583 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2584 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2587 /* handle player specific setup values */
2588 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2592 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2594 sii = setup.input[pnr];
2595 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2597 char full_token[100];
2599 sprintf(full_token, "%s%s", prefix, token_info[i].text);
2600 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
2602 setup.input[pnr] = sii;
2606 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
2608 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
2609 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
2612 if (entry1->parent_link || entry2->parent_link)
2613 compare_result = (entry1->parent_link ? -1 : +1);
2614 else if (entry1->sort_priority == entry2->sort_priority)
2616 char *name1 = getStringToLower(entry1->name_sorting);
2617 char *name2 = getStringToLower(entry2->name_sorting);
2619 compare_result = strcmp(name1, name2);
2624 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
2625 compare_result = entry1->sort_priority - entry2->sort_priority;
2627 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
2629 return compare_result;
2632 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
2634 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2636 setLevelDirInfoToDefaults(leveldir_new);
2638 leveldir_new->node_parent = node_parent;
2639 leveldir_new->parent_link = TRUE;
2641 leveldir_new->name = ".. (parent directory)";
2642 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2643 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2645 leveldir_new->filename = "..";
2646 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
2648 leveldir_new->sort_priority = node_parent->sort_priority;
2649 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2651 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
2654 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
2655 struct LevelDirInfo *node_parent,
2656 char *level_directory)
2659 struct dirent *dir_entry;
2660 boolean valid_entry_found = FALSE;
2662 if ((dir = opendir(level_directory)) == NULL)
2664 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2668 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2670 struct SetupFileList *setup_file_list = NULL;
2671 struct stat file_status;
2672 char *directory_name = dir_entry->d_name;
2673 char *directory_path = getPath2(level_directory, directory_name);
2674 char *filename = NULL;
2676 /* skip entries for current and parent directory */
2677 if (strcmp(directory_name, ".") == 0 ||
2678 strcmp(directory_name, "..") == 0)
2680 free(directory_path);
2684 /* find out if directory entry is itself a directory */
2685 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2686 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2688 free(directory_path);
2692 filename = getPath2(directory_path, LEVELINFO_FILENAME);
2693 setup_file_list = loadSetupFileList(filename);
2695 if (setup_file_list)
2697 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2700 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
2701 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
2703 /* set all structure fields according to the token/value pairs */
2704 ldi = *leveldir_new;
2705 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2706 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2707 *leveldir_new = ldi;
2709 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2711 if (leveldir_new->name_short == NULL)
2712 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2714 if (leveldir_new->name_sorting == NULL)
2715 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2717 leveldir_new->filename = getStringCopy(directory_name);
2719 if (node_parent == NULL) /* top level group */
2721 leveldir_new->basepath = level_directory;
2722 leveldir_new->fullpath = leveldir_new->filename;
2724 else /* sub level group */
2726 leveldir_new->basepath = node_parent->basepath;
2727 leveldir_new->fullpath = getPath2(node_parent->fullpath,
2731 if (leveldir_new->levels < 1)
2732 leveldir_new->levels = 1;
2734 leveldir_new->last_level =
2735 leveldir_new->first_level + leveldir_new->levels - 1;
2737 leveldir_new->user_defined =
2738 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2740 leveldir_new->color = LEVELCOLOR(leveldir_new);
2741 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2743 leveldir_new->handicap_level = /* set handicap to default value */
2744 (leveldir_new->user_defined ?
2745 leveldir_new->last_level :
2746 leveldir_new->first_level);
2748 pushLevelDirInfo(node_first, leveldir_new);
2750 freeSetupFileList(setup_file_list);
2751 valid_entry_found = TRUE;
2753 if (leveldir_new->level_group)
2755 /* create node to link back to current level directory */
2756 createParentLevelDirNode(leveldir_new);
2758 /* step into sub-directory and look for more level series */
2759 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2760 leveldir_new, directory_path);
2764 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2766 free(directory_path);
2772 if (!valid_entry_found)
2773 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2777 void LoadLevelInfo()
2779 InitUserLevelDirectory(getLoginName());
2781 DrawInitText("Loading level series:", 120, FC_GREEN);
2783 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2784 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
2786 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2788 if (leveldir_first == NULL)
2789 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2791 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
2794 dumpLevelDirInfo(leveldir_first, 0);
2798 static void SaveUserLevelInfo()
2804 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2806 if (!(file = fopen(filename, MODE_WRITE)))
2808 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2813 /* always start with reliable default values */
2814 setLevelDirInfoToDefaults(&ldi);
2816 ldi.name = getLoginName();
2817 ldi.author = getRealName();
2819 ldi.first_level = 1;
2820 ldi.sort_priority = LEVELCLASS_USER_START;
2821 ldi.readonly = FALSE;
2823 fprintf(file, "%s\n\n",
2824 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
2826 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2827 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
2828 i != LEVELINFO_TOKEN_NAME_SORTING &&
2829 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2830 fprintf(file, "%s\n", getSetupLine("", i));
2835 chmod(filename, SETUP_PERMS);
2841 struct SetupFileList *setup_file_list = NULL;
2843 /* always start with reliable default values */
2844 setSetupInfoToDefaults(&setup);
2846 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2848 setup_file_list = loadSetupFileList(filename);
2850 if (setup_file_list)
2852 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
2853 decodeSetupFileList(setup_file_list);
2855 setup.direct_draw = !setup.double_buffering;
2857 freeSetupFileList(setup_file_list);
2859 /* needed to work around problems with fixed length strings */
2860 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
2861 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
2862 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
2864 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2866 strcpy(new_name, setup.player_name);
2867 free(setup.player_name);
2868 setup.player_name = new_name;
2872 Error(ERR_WARN, "using default setup values");
2877 static char *getSetupLine(char *prefix, int token_nr)
2880 static char entry[MAX_LINE_LEN];
2881 int token_type = token_info[token_nr].type;
2882 void *setup_value = token_info[token_nr].value;
2883 char *token_text = token_info[token_nr].text;
2885 /* start with the prefix, token and some spaces to format output line */
2886 sprintf(entry, "%s%s:", prefix, token_text);
2887 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2890 /* continue with the token's value (which can have different types) */
2894 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2898 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2903 Key key = *(Key *)setup_value;
2904 char *keyname = getKeyNameFromKey(key);
2906 strcat(entry, getX11KeyNameFromKey(key));
2907 for (i=strlen(entry); i<50; i++)
2910 /* add comment, if useful */
2911 if (strcmp(keyname, "(undefined)") != 0 &&
2912 strcmp(keyname, "(unknown)") != 0)
2914 strcat(entry, "# ");
2915 strcat(entry, keyname);
2922 char buffer[MAX_LINE_LEN];
2924 sprintf(buffer, "%d", *(int *)setup_value);
2925 strcat(entry, buffer);
2930 strcat(entry, *(char **)setup_value);
2946 InitUserDataDirectory();
2948 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2950 if (!(file = fopen(filename, MODE_WRITE)))
2952 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2957 fprintf(file, "%s\n",
2958 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2959 fprintf(file, "\n");
2961 /* handle global setup values */
2963 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2965 fprintf(file, "%s\n", getSetupLine("", i));
2967 /* just to make things nicer :) */
2968 if (i == SETUP_TOKEN_PLAYER_NAME)
2969 fprintf(file, "\n");
2972 /* handle player specific setup values */
2973 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2977 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2978 fprintf(file, "\n");
2980 sii = setup.input[pnr];
2981 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2982 fprintf(file, "%s\n", getSetupLine(prefix, i));
2988 chmod(filename, SETUP_PERMS);
2991 void LoadLevelSetup_LastSeries()
2994 struct SetupFileList *level_setup_list = NULL;
2996 /* always start with reliable default values */
2997 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2999 /* ----------------------------------------------------------------------- */
3000 /* ~/.rocksndiamonds/levelsetup.conf */
3001 /* ----------------------------------------------------------------------- */
3003 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
3005 if ((level_setup_list = loadSetupFileList(filename)))
3007 char *last_level_series =
3008 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
3010 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
3011 if (leveldir_current == NULL)
3012 leveldir_current = leveldir_first;
3014 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
3016 freeSetupFileList(level_setup_list);
3019 Error(ERR_WARN, "using default setup values");
3024 void SaveLevelSetup_LastSeries()
3027 char *level_subdir = leveldir_current->filename;
3030 /* ----------------------------------------------------------------------- */
3031 /* ~/.rocksndiamonds/levelsetup.conf */
3032 /* ----------------------------------------------------------------------- */
3034 InitUserDataDirectory();
3036 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
3038 if (!(file = fopen(filename, MODE_WRITE)))
3040 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3045 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3046 LEVELSETUP_COOKIE));
3047 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
3053 chmod(filename, SETUP_PERMS);
3056 static void checkSeriesInfo()
3058 static char *level_directory = NULL;
3060 struct dirent *dir_entry;
3062 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
3064 level_directory = getPath2((leveldir_current->user_defined ?
3065 getUserLevelDir("") :
3066 options.level_directory),
3067 leveldir_current->fullpath);
3069 if ((dir = opendir(level_directory)) == NULL)
3071 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
3075 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
3077 if (strlen(dir_entry->d_name) > 4 &&
3078 dir_entry->d_name[3] == '.' &&
3079 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
3081 char levelnum_str[4];
3084 strncpy(levelnum_str, dir_entry->d_name, 3);
3085 levelnum_str[3] = '\0';
3087 levelnum_value = atoi(levelnum_str);
3089 if (levelnum_value < leveldir_current->first_level)
3091 Error(ERR_WARN, "additional level %d found", levelnum_value);
3092 leveldir_current->first_level = levelnum_value;
3094 else if (levelnum_value > leveldir_current->last_level)
3096 Error(ERR_WARN, "additional level %d found", levelnum_value);
3097 leveldir_current->last_level = levelnum_value;
3105 void LoadLevelSetup_SeriesInfo()
3108 struct SetupFileList *level_setup_list = NULL;
3109 char *level_subdir = leveldir_current->filename;
3111 /* always start with reliable default values */
3112 level_nr = leveldir_current->first_level;
3114 checkSeriesInfo(leveldir_current);
3116 /* ----------------------------------------------------------------------- */
3117 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
3118 /* ----------------------------------------------------------------------- */
3120 level_subdir = leveldir_current->filename;
3122 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3124 if ((level_setup_list = loadSetupFileList(filename)))
3128 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
3132 level_nr = atoi(token_value);
3134 if (level_nr < leveldir_current->first_level)
3135 level_nr = leveldir_current->first_level;
3136 if (level_nr > leveldir_current->last_level)
3137 level_nr = leveldir_current->last_level;
3140 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
3144 int level_nr = atoi(token_value);
3146 if (level_nr < leveldir_current->first_level)
3147 level_nr = leveldir_current->first_level;
3148 if (level_nr > leveldir_current->last_level + 1)
3149 level_nr = leveldir_current->last_level;
3151 if (leveldir_current->user_defined)
3152 level_nr = leveldir_current->last_level;
3154 leveldir_current->handicap_level = level_nr;
3157 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
3159 freeSetupFileList(level_setup_list);
3162 Error(ERR_WARN, "using default setup values");
3167 void SaveLevelSetup_SeriesInfo()
3170 char *level_subdir = leveldir_current->filename;
3171 char *level_nr_str = int2str(level_nr, 0);
3172 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
3175 /* ----------------------------------------------------------------------- */
3176 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
3177 /* ----------------------------------------------------------------------- */
3179 InitLevelSetupDirectory(level_subdir);
3181 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3183 if (!(file = fopen(filename, MODE_WRITE)))
3185 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3190 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3191 LEVELSETUP_COOKIE));
3192 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
3194 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
3195 handicap_level_str));
3200 chmod(filename, SETUP_PERMS);
3202 /* LocalWords: Rocks'n