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 TAPE_HEADER_SIZE 20 /* size of tape file header */
31 #define TAPE_HEADER_UNUSED 7 /* unused tape header bytes */
34 #define FILE_VERSION_1_0 10 /* 1.0 file version (old) */
35 #define FILE_VERSION_1_2 12 /* 1.2 file version (still in use) */
36 #define FILE_VERSION_1_4 14 /* 1.4 file version (new) */
39 /* file identifier strings */
40 #define LEVEL_COOKIE "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_2.0"
41 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
42 #define TAPE_COOKIE "ROCKSNDIAMONDS_TAPE_FILE_VERSION_1.2"
43 #define SETUP_COOKIE "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
44 #define LEVELSETUP_COOKIE "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
45 #define LEVELINFO_COOKIE "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
46 /* old file identifiers for backward compatibility */
47 #define LEVEL_COOKIE_10 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0"
48 #define LEVEL_COOKIE_12 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.2"
49 #define LEVEL_COOKIE_14 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.4"
50 #define TAPE_COOKIE_10 "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
52 /* file names and filename extensions */
53 #if !defined(PLATFORM_MSDOS)
54 #define LEVELSETUP_DIRECTORY "levelsetup"
55 #define SETUP_FILENAME "setup.conf"
56 #define LEVELSETUP_FILENAME "levelsetup.conf"
57 #define LEVELINFO_FILENAME "levelinfo.conf"
58 #define LEVELFILE_EXTENSION "level"
59 #define TAPEFILE_EXTENSION "tape"
60 #define SCOREFILE_EXTENSION "score"
62 #define LEVELSETUP_DIRECTORY "lvlsetup"
63 #define SETUP_FILENAME "setup.cnf"
64 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
65 #define LEVELINFO_FILENAME "lvlinfo.cnf"
66 #define LEVELFILE_EXTENSION "lvl"
67 #define TAPEFILE_EXTENSION "tap"
68 #define SCOREFILE_EXTENSION "sco"
71 #if defined(PLATFORM_WIN32)
73 #define S_IRGRP S_IRUSR
76 #define S_IROTH S_IRUSR
79 #define S_IWGRP S_IWUSR
82 #define S_IWOTH S_IWUSR
85 #define S_IXGRP S_IXUSR
88 #define S_IXOTH S_IXUSR
90 #endif /* PLATFORM_WIN32 */
92 /* file permissions for newly written files */
93 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
94 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
95 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
96 #define LEVEL_PERMS (MODE_R_ALL | MODE_W_ALL)
97 #define SCORE_PERMS LEVEL_PERMS
98 #define TAPE_PERMS LEVEL_PERMS
99 #define SETUP_PERMS LEVEL_PERMS
101 /* sort priorities of level series (also used as level series classes) */
102 #define LEVELCLASS_TUTORIAL_START 10
103 #define LEVELCLASS_TUTORIAL_END 99
104 #define LEVELCLASS_CLASSICS_START 100
105 #define LEVELCLASS_CLASSICS_END 199
106 #define LEVELCLASS_CONTRIBUTION_START 200
107 #define LEVELCLASS_CONTRIBUTION_END 299
108 #define LEVELCLASS_USER_START 300
109 #define LEVELCLASS_USER_END 399
110 #define LEVELCLASS_BD_START 400
111 #define LEVELCLASS_BD_END 499
112 #define LEVELCLASS_EM_START 500
113 #define LEVELCLASS_EM_END 599
114 #define LEVELCLASS_SP_START 600
115 #define LEVELCLASS_SP_END 699
116 #define LEVELCLASS_DX_START 700
117 #define LEVELCLASS_DX_END 799
119 #define LEVELCLASS_TUTORIAL LEVELCLASS_TUTORIAL_START
120 #define LEVELCLASS_CLASSICS LEVELCLASS_CLASSICS_START
121 #define LEVELCLASS_CONTRIBUTION LEVELCLASS_CONTRIBUTION_START
122 #define LEVELCLASS_USER LEVELCLASS_USER_START
123 #define LEVELCLASS_BD LEVELCLASS_BD_START
124 #define LEVELCLASS_EM LEVELCLASS_EM_START
125 #define LEVELCLASS_SP LEVELCLASS_SP_START
126 #define LEVELCLASS_DX LEVELCLASS_DX_START
128 #define LEVELCLASS_UNDEFINED 999
130 #define NUM_LEVELCLASS_DESC 8
131 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
143 #define IS_LEVELCLASS_TUTORIAL(p) \
144 ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
145 (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
146 #define IS_LEVELCLASS_CLASSICS(p) \
147 ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
148 (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
149 #define IS_LEVELCLASS_CONTRIBUTION(p) \
150 ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
151 (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
152 #define IS_LEVELCLASS_USER(p) \
153 ((p)->sort_priority >= LEVELCLASS_USER_START && \
154 (p)->sort_priority <= LEVELCLASS_USER_END)
155 #define IS_LEVELCLASS_BD(p) \
156 ((p)->sort_priority >= LEVELCLASS_BD_START && \
157 (p)->sort_priority <= LEVELCLASS_BD_END)
158 #define IS_LEVELCLASS_EM(p) \
159 ((p)->sort_priority >= LEVELCLASS_EM_START && \
160 (p)->sort_priority <= LEVELCLASS_EM_END)
161 #define IS_LEVELCLASS_SP(p) \
162 ((p)->sort_priority >= LEVELCLASS_SP_START && \
163 (p)->sort_priority <= LEVELCLASS_SP_END)
164 #define IS_LEVELCLASS_DX(p) \
165 ((p)->sort_priority >= LEVELCLASS_DX_START && \
166 (p)->sort_priority <= LEVELCLASS_DX_END)
168 #define LEVELCLASS(n) (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
169 IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
170 IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
171 IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
172 IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
173 IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
174 IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
175 IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
176 LEVELCLASS_UNDEFINED)
178 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
179 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
180 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
181 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
182 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
183 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
184 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
185 IS_LEVELCLASS_USER(n) ? FC_RED : \
188 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
189 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
190 IS_LEVELCLASS_BD(n) ? 2 : \
191 IS_LEVELCLASS_EM(n) ? 3 : \
192 IS_LEVELCLASS_SP(n) ? 4 : \
193 IS_LEVELCLASS_DX(n) ? 5 : \
194 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
195 IS_LEVELCLASS_USER(n) ? 7 : \
198 static int getFileVersionFromCookieString(const char *cookie)
200 const char *ptr_cookie1, *ptr_cookie2;
201 const char *pattern1 = "_FILE_VERSION_";
202 const char *pattern2 = "?.?";
203 const int len_cookie = strlen(cookie);
204 const int len_pattern1 = strlen(pattern1);
205 const int len_pattern2 = strlen(pattern2);
206 const int len_pattern = len_pattern1 + len_pattern2;
207 int version_major, version_minor;
209 if (len_cookie <= len_pattern)
212 ptr_cookie1 = &cookie[len_cookie - len_pattern];
213 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
215 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
218 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
219 ptr_cookie2[1] != '.' ||
220 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
223 version_major = ptr_cookie2[0] - '0';
224 version_minor = ptr_cookie2[2] - '0';
226 return (version_major * 10 + version_minor);
229 boolean checkCookieString(const char *cookie, const char *template)
231 const char *pattern = "_FILE_VERSION_?.?";
232 const int len_cookie = strlen(cookie);
233 const int len_template = strlen(template);
234 const int len_pattern = strlen(pattern);
236 if (len_cookie != len_template)
239 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
245 char *getLevelClassDescription(struct LevelDirInfo *ldi)
247 int position = ldi->sort_priority / 100;
249 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
250 return levelclass_desc[position];
252 return "Unknown Level Class";
255 static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */
256 static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */
258 static char *getSetupDir()
260 return getUserDataDir();
263 static char *getUserLevelDir(char *level_subdir)
265 static char *userlevel_dir = NULL;
266 char *data_dir = getUserDataDir();
267 char *userlevel_subdir = LEVELS_DIRECTORY;
272 if (strlen(level_subdir) > 0)
273 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
275 userlevel_dir = getPath2(data_dir, userlevel_subdir);
277 return userlevel_dir;
280 static char *getTapeDir(char *level_subdir)
282 static char *tape_dir = NULL;
283 char *data_dir = getUserDataDir();
284 char *tape_subdir = TAPES_DIRECTORY;
289 if (strlen(level_subdir) > 0)
290 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
292 tape_dir = getPath2(data_dir, tape_subdir);
297 static char *getScoreDir(char *level_subdir)
299 static char *score_dir = NULL;
300 char *data_dir = options.rw_base_directory;
301 char *score_subdir = SCORES_DIRECTORY;
306 if (strlen(level_subdir) > 0)
307 score_dir = getPath3(data_dir, score_subdir, level_subdir);
309 score_dir = getPath2(data_dir, score_subdir);
314 static char *getLevelSetupDir(char *level_subdir)
316 static char *levelsetup_dir = NULL;
317 char *data_dir = getUserDataDir();
318 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
321 free(levelsetup_dir);
323 if (strlen(level_subdir) > 0)
324 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
326 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
328 return levelsetup_dir;
331 static char *getLevelFilename(int nr)
333 static char *filename = NULL;
334 char basename[MAX_FILENAME_LEN];
336 if (filename != NULL)
339 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
340 filename = getPath3((leveldir_current->user_defined ?
341 getUserLevelDir("") :
342 options.level_directory),
343 leveldir_current->fullpath,
349 static char *getTapeFilename(int nr)
351 static char *filename = NULL;
352 char basename[MAX_FILENAME_LEN];
354 if (filename != NULL)
357 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
358 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
363 static char *getScoreFilename(int nr)
365 static char *filename = NULL;
366 char basename[MAX_FILENAME_LEN];
368 if (filename != NULL)
371 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
372 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
377 static void InitTapeDirectory(char *level_subdir)
379 createDirectory(getUserDataDir(), "user data");
380 createDirectory(getTapeDir(""), "main tape");
381 createDirectory(getTapeDir(level_subdir), "level tape");
384 static void InitScoreDirectory(char *level_subdir)
386 createDirectory(getScoreDir(""), "main score");
387 createDirectory(getScoreDir(level_subdir), "level score");
390 static void InitUserLevelDirectory(char *level_subdir)
392 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
394 createDirectory(getUserDataDir(), "user data");
395 createDirectory(getUserLevelDir(""), "main user level");
396 createDirectory(getUserLevelDir(level_subdir), "user level");
402 static void InitLevelSetupDirectory(char *level_subdir)
404 createDirectory(getUserDataDir(), "user data");
405 createDirectory(getLevelSetupDir(""), "main level setup");
406 createDirectory(getLevelSetupDir(level_subdir), "level setup");
409 static void setLevelInfoToDefaults()
413 level.file_version = FILE_VERSION_ACTUAL;
414 level.game_version = GAME_VERSION_ACTUAL;
416 level.encoding_16bit = FALSE; /* default: only 8-bit elements */
418 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
419 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
421 for(x=0; x<MAX_LEV_FIELDX; x++)
422 for(y=0; y<MAX_LEV_FIELDY; y++)
423 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
426 level.gems_needed = 0;
427 level.amoeba_speed = 10;
428 level.time_magic_wall = 10;
429 level.time_wheel = 10;
430 level.time_light = 10;
431 level.time_timegate = 10;
432 level.amoeba_content = EL_DIAMANT;
433 level.double_speed = FALSE;
434 level.gravity = FALSE;
436 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
437 level.name[i] = '\0';
438 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
439 level.author[i] = '\0';
441 strcpy(level.name, NAMELESS_LEVEL_NAME);
442 strcpy(level.author, ANONYMOUS_NAME);
444 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
447 level.num_yam_contents = STD_ELEMENT_CONTENTS;
448 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
451 level.yam_content[i][x][y] = EL_FELSBROCKEN;
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[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, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
554 if (strcmp(chunk, "HEAD") || chunk_length != 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, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
606 /* look for optional author chunk */
607 if (strcmp(chunk, "AUTH") == 0 && chunk_length == 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, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
616 /* look for optional content chunk */
617 if (strcmp(chunk, "CONT") == 0 &&
618 chunk_length == 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, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
647 /* next check body chunk identifier and chunk length */
648 if (strcmp(chunk, "BODY") != 0 || chunk_length != lev_fieldx * lev_fieldy)
650 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
656 /* clear all other level fields (needed if resized in level editor later) */
657 for(x=0; x<MAX_LEV_FIELDX; x++)
658 for(y=0; y<MAX_LEV_FIELDY; y++)
659 Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
661 /* now read in the valid level fields from level file */
662 for(y=0; y<lev_fieldy; y++)
663 for(x=0; x<lev_fieldx; x++)
664 Feld[x][y] = Ur[x][y] =
665 checkLevelElement(encoding_16bit ?
666 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
671 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
672 IS_LEVELCLASS_USER(leveldir_current))
674 /* for user contributed and private levels, use the version of
675 the game engine the levels were created for */
676 level.game_version = file_version;
678 /* player was faster than monsters in pre-1.0 levels */
679 if (file_version == FILE_VERSION_1_0)
681 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
682 Error(ERR_WARN, "using high speed movement for player");
683 level.double_speed = TRUE;
688 /* always use the latest version of the game engine for all but
689 user contributed and private levels */
690 level.game_version = GAME_VERSION_ACTUAL;
693 /* determine border element for this level */
697 static void LoadLevel_HEAD(struct LevelInfo *level, FILE *file)
701 lev_fieldx = level->fieldx = fgetc(file);
702 lev_fieldy = level->fieldy = fgetc(file);
704 level->time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
705 level->gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
707 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
708 level->name[i] = fgetc(file);
709 level->name[MAX_LEVEL_NAME_LEN] = 0;
711 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
712 level->score[i] = fgetc(file);
714 level->num_yam_contents = STD_ELEMENT_CONTENTS;
715 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
721 if (i < STD_ELEMENT_CONTENTS)
722 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
724 level->yam_content[i][x][y] = EL_LEERRAUM;
729 level->amoeba_speed = fgetc(file);
730 level->time_magic_wall = fgetc(file);
731 level->time_wheel = fgetc(file);
732 level->amoeba_content = checkLevelElement(fgetc(file));
733 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
734 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
736 level->encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
738 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
742 static void LoadLevel_AUTH(struct LevelInfo *level, FILE *file)
746 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
747 level->author[i] = fgetc(file);
748 level->author[MAX_LEVEL_NAME_LEN] = 0;
751 static void LoadLevel_CONT(struct LevelInfo *level, FILE *file)
756 level->num_yam_contents = fgetc(file);
760 if (level->num_yam_contents < 1 ||
761 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
764 printf("WARNING: num_yam_contents == %d (corrected)\n",
765 level->num_yam_contents);
767 level->num_yam_contents = STD_ELEMENT_CONTENTS;
770 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
773 level->yam_content[i][x][y] =
774 checkLevelElement(level->encoding_16bit ?
775 getFile16BitInteger(file,
776 BYTE_ORDER_BIG_ENDIAN) :
780 static void LoadLevel_BODY(struct LevelInfo *level, FILE *file)
784 /* now read in the valid level fields from level file */
785 for(y=0; y<lev_fieldy; y++)
786 for(x=0; x<lev_fieldx; x++)
787 Feld[x][y] = Ur[x][y] =
788 checkLevelElement(level->encoding_16bit ?
789 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
793 void LoadLevel(int level_nr)
795 char *filename = getLevelFilename(level_nr);
796 char cookie[MAX_LINE_LEN];
797 char chunk[CHUNK_ID_LEN + 1];
801 /* always start with reliable default values */
802 setLevelInfoToDefaults();
804 if (!(file = fopen(filename, MODE_READ)))
806 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
810 /* check file identifier */
811 fgets(cookie, MAX_LINE_LEN, file);
812 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
813 cookie[strlen(cookie) - 1] = '\0';
815 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
817 Error(ERR_WARN, "unknown format of level file '%s'", filename);
822 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
824 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
829 if (level.file_version < FILE_VERSION_1_2)
831 /* level files from versions before 1.2.0 without chunk structure */
832 LoadLevel_HEAD(&level, file);
833 LoadLevel_BODY(&level, file);
837 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
838 if (strcmp(chunk, "HEAD") || chunk_length != LEVEL_HEADER_SIZE)
840 Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
845 LoadLevel_HEAD(&level, file);
847 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
849 /* look for optional author chunk */
850 if (strcmp(chunk, "AUTH") == 0 && chunk_length == MAX_LEVEL_AUTHOR_LEN)
852 LoadLevel_AUTH(&level, file);
854 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
857 /* look for optional content chunk */
858 if (strcmp(chunk, "CONT") == 0 &&
859 chunk_length == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
861 LoadLevel_CONT(&level, file);
863 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
866 /* next check body chunk identifier and chunk length */
867 if (strcmp(chunk, "BODY") != 0 || chunk_length != lev_fieldx * lev_fieldy)
869 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
874 LoadLevel_BODY(&level, file);
879 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
880 IS_LEVELCLASS_USER(leveldir_current))
882 /* for user contributed and private levels, use the version of
883 the game engine the levels were created for */
884 level.game_version = level.file_version;
886 /* player was faster than monsters in pre-1.0 levels */
887 if (level.file_version == FILE_VERSION_1_0)
889 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
890 Error(ERR_WARN, "using high speed movement for player");
891 level.double_speed = TRUE;
896 /* always use the latest version of the game engine for all but
897 user contributed and private levels */
898 level.game_version = GAME_VERSION_ACTUAL;
901 /* determine border element for this level */
905 void SaveLevel(int level_nr)
908 char *filename = getLevelFilename(level_nr);
910 boolean encoding_16bit_amoeba = FALSE;
911 boolean encoding_16bit_yamyam = FALSE;
913 boolean encoding_16bit = FALSE; /* default: only 8-bit elements */
914 char *oldest_possible_cookie;
917 if (!(file = fopen(filename, MODE_WRITE)))
919 Error(ERR_WARN, "cannot save level file '%s'", filename);
923 /* check yam content for 16-bit elements */
924 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
927 if (level.yam_content[i][x][y] > 255)
928 encoding_16bit = TRUE;
930 /* check level field for 16-bit elements */
931 for(y=0; y<lev_fieldy; y++)
932 for(x=0; x<lev_fieldx; x++)
934 encoding_16bit = TRUE;
936 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
938 fputs(oldest_possible_cookie, file); /* file identifier */
941 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
943 fputc(level.fieldx, file);
944 fputc(level.fieldy, file);
946 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
947 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
949 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
950 fputc(level.name[i], file);
951 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
952 fputc(level.score[i], file);
953 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
956 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
957 fputc(level.amoeba_speed, file);
958 fputc(level.time_magic_wall, file);
959 fputc(level.time_wheel, file);
960 fputc(level.amoeba_content, file);
961 fputc((level.double_speed ? 1 : 0), file);
962 fputc((level.gravity ? 1 : 0), file);
964 fputc((encoding_16bit ? 1 : 0), file);
966 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
969 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
971 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
972 fputc(level.author[i], file);
974 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
975 BYTE_ORDER_BIG_ENDIAN);
977 fputc(EL_MAMPFER, file);
978 fputc(level.num_yam_contents, file);
982 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
986 putFile16BitInteger(file, level.yam_content[i][x][y],
987 BYTE_ORDER_BIG_ENDIAN);
989 fputc(level.yam_content[i][x][y], file);
991 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
993 for(y=0; y<lev_fieldy; y++)
994 for(x=0; x<lev_fieldx; x++)
996 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
998 fputc(Ur[x][y], file);
1002 chmod(filename, LEVEL_PERMS);
1005 void LoadTape(int level_nr)
1008 char *filename = getTapeFilename(level_nr);
1009 char cookie[MAX_LINE_LEN];
1010 char chunk[CHUNK_ID_LEN + 1];
1012 int num_participating_players;
1013 int file_version = FILE_VERSION_ACTUAL; /* last version of tape files */
1016 /* always start with reliable default values (empty tape) */
1017 tape.file_version = FILE_VERSION_ACTUAL;
1018 tape.game_version = GAME_VERSION_ACTUAL;
1021 /* default values (also for pre-1.2 tapes) with only the first player */
1022 tape.player_participates[0] = TRUE;
1023 for(i=1; i<MAX_PLAYERS; i++)
1024 tape.player_participates[i] = FALSE;
1026 /* at least one (default: the first) player participates in every tape */
1027 num_participating_players = 1;
1029 if (!(file = fopen(filename, MODE_READ)))
1032 /* check file identifier */
1033 fgets(cookie, MAX_LINE_LEN, file);
1034 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1035 cookie[strlen(cookie) - 1] = '\0';
1038 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
1039 file_version = FILE_VERSION_1_0;
1040 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
1042 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
1047 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1049 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1054 file_version = getFileVersionFromCookieString(cookie);
1057 tape.file_version = file_version;
1058 tape.game_version = file_version;
1060 /* read chunk "HEAD" */
1061 if (file_version >= FILE_VERSION_1_2)
1063 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
1064 if (strcmp(chunk, "HEAD") || chunk_length != TAPE_HEADER_SIZE)
1066 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
1072 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1073 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1074 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1076 /* read header fields that are new since version 1.2 */
1077 if (file_version >= FILE_VERSION_1_2)
1079 byte store_participating_players = fgetc(file);
1081 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
1084 /* since version 1.2, tapes store which players participate in the tape */
1085 num_participating_players = 0;
1086 for(i=0; i<MAX_PLAYERS; i++)
1088 tape.player_participates[i] = FALSE;
1090 if (store_participating_players & (1 << i))
1092 tape.player_participates[i] = TRUE;
1093 num_participating_players++;
1098 tape.level_nr = level_nr;
1100 tape.changed = FALSE;
1102 tape.recording = FALSE;
1103 tape.playing = FALSE;
1104 tape.pausing = FALSE;
1106 /* read chunk "BODY" */
1107 if (file_version >= FILE_VERSION_1_2)
1109 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
1110 if (strcmp(chunk, "BODY") ||
1111 chunk_length != (num_participating_players + 1) * tape.length)
1113 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
1119 for(i=0; i<tape.length; i++)
1121 if (i >= MAX_TAPELEN)
1124 for(j=0; j<MAX_PLAYERS; j++)
1126 tape.pos[i].action[j] = MV_NO_MOVING;
1128 if (tape.player_participates[j])
1129 tape.pos[i].action[j] = fgetc(file);
1132 tape.pos[i].delay = fgetc(file);
1134 if (file_version == FILE_VERSION_1_0)
1136 /* eliminate possible diagonal moves in old tapes */
1137 /* this is only for backward compatibility */
1139 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1140 byte action = tape.pos[i].action[0];
1141 int k, num_moves = 0;
1145 if (action & joy_dir[k])
1147 tape.pos[i + num_moves].action[0] = joy_dir[k];
1149 tape.pos[i + num_moves].delay = 0;
1158 tape.length += num_moves;
1168 if (i != tape.length)
1169 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
1171 tape.length_seconds = GetTapeLength();
1174 void SaveTape(int level_nr)
1177 char *filename = getTapeFilename(level_nr);
1179 boolean new_tape = TRUE;
1180 byte store_participating_players;
1181 int num_participating_players;
1183 InitTapeDirectory(leveldir_current->filename);
1185 /* if a tape still exists, ask to overwrite it */
1186 if (access(filename, F_OK) == 0)
1189 if (!Request("Replace old tape ?", REQ_ASK))
1193 /* count number of players and set corresponding bits for compact storage */
1194 store_participating_players = 0;
1195 num_participating_players = 0;
1196 for(i=0; i<MAX_PLAYERS; i++)
1198 if (tape.player_participates[i])
1200 num_participating_players++;
1201 store_participating_players |= (1 << i);
1205 if (!(file = fopen(filename, MODE_WRITE)))
1207 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1211 fputs(TAPE_COOKIE, file); /* file identifier */
1214 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1216 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
1217 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
1218 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
1220 fputc(store_participating_players, file);
1222 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1225 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
1226 BYTE_ORDER_BIG_ENDIAN);
1228 for(i=0; i<tape.length; i++)
1232 for(j=0; j<MAX_PLAYERS; j++)
1233 if (tape.player_participates[j])
1234 fputc(tape.pos[i].action[j], file);
1236 fputc(tape.pos[i].delay, file);
1241 chmod(filename, TAPE_PERMS);
1243 tape.changed = FALSE;
1246 Request("tape saved !", REQ_CONFIRM);
1249 void LoadScore(int level_nr)
1252 char *filename = getScoreFilename(level_nr);
1253 char cookie[MAX_LINE_LEN];
1254 char line[MAX_LINE_LEN];
1258 /* always start with reliable default values */
1259 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1261 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1262 highscore[i].Score = 0;
1265 if (!(file = fopen(filename, MODE_READ)))
1268 /* check file identifier */
1269 fgets(cookie, MAX_LINE_LEN, file);
1270 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1271 cookie[strlen(cookie) - 1] = '\0';
1274 if (strcmp(cookie, SCORE_COOKIE) != 0)
1276 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
1281 if (!checkCookieString(cookie, SCORE_COOKIE)) /* unknown file format */
1283 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1289 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1291 fscanf(file, "%d", &highscore[i].Score);
1292 fgets(line, MAX_LINE_LEN, file);
1294 if (line[strlen(line) - 1] == '\n')
1295 line[strlen(line) - 1] = '\0';
1297 for (line_ptr = line; *line_ptr; line_ptr++)
1299 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1301 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1302 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1311 void SaveScore(int level_nr)
1314 char *filename = getScoreFilename(level_nr);
1317 InitScoreDirectory(leveldir_current->filename);
1319 if (!(file = fopen(filename, MODE_WRITE)))
1321 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1325 fprintf(file, "%s\n\n", SCORE_COOKIE);
1327 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1328 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1332 chmod(filename, SCORE_PERMS);
1335 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
1336 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1337 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1338 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1339 #define TOKEN_STR_PLAYER_PREFIX "player_"
1341 #define TOKEN_VALUE_POSITION 30
1344 #define SETUP_TOKEN_PLAYER_NAME 0
1345 #define SETUP_TOKEN_SOUND 1
1346 #define SETUP_TOKEN_SOUND_LOOPS 2
1347 #define SETUP_TOKEN_SOUND_MUSIC 3
1348 #define SETUP_TOKEN_SOUND_SIMPLE 4
1351 #define SETUP_TOKEN_TOONS 5
1352 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
1355 #define SETUP_TOKEN_SCROLL_DELAY 5
1356 #define SETUP_TOKEN_SOFT_SCROLLING 6
1357 #define SETUP_TOKEN_FADING 7
1358 #define SETUP_TOKEN_AUTORECORD 8
1359 #define SETUP_TOKEN_QUICK_DOORS 9
1360 #define SETUP_TOKEN_TEAM_MODE 10
1361 #define SETUP_TOKEN_HANDICAP 11
1362 #define SETUP_TOKEN_TIME_LIMIT 12
1363 #define SETUP_TOKEN_FULLSCREEN 13
1366 #define SETUP_TOKEN_USE_JOYSTICK 14
1367 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
1368 #define SETUP_TOKEN_JOY_XLEFT 16
1369 #define SETUP_TOKEN_JOY_XMIDDLE 17
1370 #define SETUP_TOKEN_JOY_XRIGHT 18
1371 #define SETUP_TOKEN_JOY_YUPPER 19
1372 #define SETUP_TOKEN_JOY_YMIDDLE 20
1373 #define SETUP_TOKEN_JOY_YLOWER 21
1374 #define SETUP_TOKEN_JOY_SNAP 22
1375 #define SETUP_TOKEN_JOY_BOMB 23
1376 #define SETUP_TOKEN_KEY_LEFT 24
1377 #define SETUP_TOKEN_KEY_RIGHT 25
1378 #define SETUP_TOKEN_KEY_UP 26
1379 #define SETUP_TOKEN_KEY_DOWN 27
1380 #define SETUP_TOKEN_KEY_SNAP 28
1381 #define SETUP_TOKEN_KEY_BOMB 29
1383 /* level directory info */
1384 #define LEVELINFO_TOKEN_NAME 30
1385 #define LEVELINFO_TOKEN_NAME_SHORT 31
1386 #define LEVELINFO_TOKEN_NAME_SORTING 32
1387 #define LEVELINFO_TOKEN_AUTHOR 33
1388 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
1389 #define LEVELINFO_TOKEN_LEVELS 35
1390 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
1391 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
1392 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
1393 #define LEVELINFO_TOKEN_READONLY 39
1395 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1396 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
1398 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1399 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1401 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1402 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1404 #define TYPE_BOOLEAN 1
1405 #define TYPE_SWITCH 2
1407 #define TYPE_INTEGER 4
1408 #define TYPE_STRING 5
1410 static struct SetupInfo si;
1411 static struct SetupInputInfo sii;
1412 static struct LevelDirInfo ldi;
1421 { TYPE_STRING, &si.player_name, "player_name" },
1422 { TYPE_SWITCH, &si.sound, "sound" },
1423 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1424 { TYPE_SWITCH, &si.sound_music, "background_music" },
1425 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1428 { TYPE_SWITCH, &si.toons, "toons" },
1429 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
1432 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1433 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1434 { TYPE_SWITCH, &si.fading, "screen_fading" },
1435 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1436 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1437 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1438 { TYPE_SWITCH, &si.handicap, "handicap" },
1439 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1440 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1443 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1444 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1445 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1446 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1447 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1448 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1449 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1450 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1451 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1452 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1453 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1454 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1455 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1456 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1457 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1458 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
1460 /* level directory info */
1461 { TYPE_STRING, &ldi.name, "name" },
1462 { TYPE_STRING, &ldi.name_short, "name_short" },
1463 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1464 { TYPE_STRING, &ldi.author, "author" },
1465 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1466 { TYPE_INTEGER, &ldi.levels, "levels" },
1467 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1468 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1469 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1470 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1473 static char *string_tolower(char *s)
1475 static char s_lower[100];
1478 if (strlen(s) >= 100)
1483 for (i=0; i<strlen(s_lower); i++)
1484 s_lower[i] = tolower(s_lower[i]);
1489 static int get_string_integer_value(char *s)
1491 static char *number_text[][3] =
1493 { "0", "zero", "null", },
1494 { "1", "one", "first" },
1495 { "2", "two", "second" },
1496 { "3", "three", "third" },
1497 { "4", "four", "fourth" },
1498 { "5", "five", "fifth" },
1499 { "6", "six", "sixth" },
1500 { "7", "seven", "seventh" },
1501 { "8", "eight", "eighth" },
1502 { "9", "nine", "ninth" },
1503 { "10", "ten", "tenth" },
1504 { "11", "eleven", "eleventh" },
1505 { "12", "twelve", "twelfth" },
1510 for (i=0; i<13; i++)
1512 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1518 static boolean get_string_boolean_value(char *s)
1520 if (strcmp(string_tolower(s), "true") == 0 ||
1521 strcmp(string_tolower(s), "yes") == 0 ||
1522 strcmp(string_tolower(s), "on") == 0 ||
1523 get_string_integer_value(s) == 1)
1529 static char *getFormattedSetupEntry(char *token, char *value)
1532 static char entry[MAX_LINE_LEN];
1534 sprintf(entry, "%s:", token);
1535 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1539 strcat(entry, value);
1544 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1546 if (!setup_file_list)
1549 if (setup_file_list->token)
1550 free(setup_file_list->token);
1551 if (setup_file_list->value)
1552 free(setup_file_list->value);
1553 if (setup_file_list->next)
1554 freeSetupFileList(setup_file_list->next);
1555 free(setup_file_list);
1558 static struct SetupFileList *newSetupFileList(char *token, char *value)
1560 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1562 new->token = checked_malloc(strlen(token) + 1);
1563 strcpy(new->token, token);
1565 new->value = checked_malloc(strlen(value) + 1);
1566 strcpy(new->value, value);
1573 static char *getTokenValue(struct SetupFileList *setup_file_list,
1576 if (!setup_file_list)
1579 if (strcmp(setup_file_list->token, token) == 0)
1580 return setup_file_list->value;
1582 return getTokenValue(setup_file_list->next, token);
1585 static void setTokenValue(struct SetupFileList *setup_file_list,
1586 char *token, char *value)
1588 if (!setup_file_list)
1591 if (strcmp(setup_file_list->token, token) == 0)
1593 free(setup_file_list->value);
1594 setup_file_list->value = checked_malloc(strlen(value) + 1);
1595 strcpy(setup_file_list->value, value);
1597 else if (setup_file_list->next == NULL)
1598 setup_file_list->next = newSetupFileList(token, value);
1600 setTokenValue(setup_file_list->next, token, value);
1604 static void printSetupFileList(struct SetupFileList *setup_file_list)
1606 if (!setup_file_list)
1609 printf("token: '%s'\n", setup_file_list->token);
1610 printf("value: '%s'\n", setup_file_list->value);
1612 printSetupFileList(setup_file_list->next);
1616 static struct SetupFileList *loadSetupFileList(char *filename)
1619 char line[MAX_LINE_LEN];
1620 char *token, *value, *line_ptr;
1621 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1622 struct SetupFileList *first_valid_list_entry;
1626 if (!(file = fopen(filename, MODE_READ)))
1628 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1634 /* read next line of input file */
1635 if (!fgets(line, MAX_LINE_LEN, file))
1638 /* cut trailing comment or whitespace from input line */
1639 for (line_ptr = line; *line_ptr; line_ptr++)
1641 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1648 /* cut trailing whitespaces from input line */
1649 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1650 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1653 /* ignore empty lines */
1657 line_len = strlen(line);
1659 /* cut leading whitespaces from token */
1660 for (token = line; *token; token++)
1661 if (*token != ' ' && *token != '\t')
1664 /* find end of token */
1665 for (line_ptr = token; *line_ptr; line_ptr++)
1667 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1674 if (line_ptr < line + line_len)
1675 value = line_ptr + 1;
1679 /* cut leading whitespaces from value */
1680 for (; *value; value++)
1681 if (*value != ' ' && *value != '\t')
1684 if (*token && *value)
1685 setTokenValue(setup_file_list, token, value);
1690 first_valid_list_entry = setup_file_list->next;
1692 /* free empty list header */
1693 setup_file_list->next = NULL;
1694 freeSetupFileList(setup_file_list);
1696 if (first_valid_list_entry == NULL)
1697 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1699 return first_valid_list_entry;
1702 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1705 if (!setup_file_list)
1708 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1710 if (strcmp(setup_file_list->value, identifier) != 0)
1712 Error(ERR_WARN, "configuration file has wrong version");
1719 if (setup_file_list->next)
1720 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1723 Error(ERR_WARN, "configuration file has no version information");
1728 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1730 ldi->filename = NULL;
1731 ldi->fullpath = NULL;
1732 ldi->basepath = NULL;
1733 ldi->name = getStringCopy(ANONYMOUS_NAME);
1734 ldi->name_short = NULL;
1735 ldi->name_sorting = NULL;
1736 ldi->author = getStringCopy(ANONYMOUS_NAME);
1737 ldi->imported_from = NULL;
1739 ldi->first_level = 0;
1740 ldi->last_level = 0;
1741 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1742 ldi->level_group = FALSE;
1743 ldi->parent_link = FALSE;
1744 ldi->user_defined = FALSE;
1745 ldi->readonly = TRUE;
1747 ldi->class_desc = NULL;
1748 ldi->handicap_level = 0;
1750 ldi->cl_cursor = -1;
1752 ldi->node_parent = NULL;
1753 ldi->node_group = NULL;
1757 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1758 struct LevelDirInfo *parent)
1762 setLevelDirInfoToDefaults(ldi);
1766 /* first copy all values from the parent structure ... */
1769 /* ... then set all fields to default that cannot be inherited from parent.
1770 This is especially important for all those fields that can be set from
1771 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1772 calls 'free()' for all already set token values which requires that no
1773 other structure's pointer may point to them!
1776 ldi->filename = NULL;
1777 ldi->fullpath = NULL;
1778 ldi->basepath = NULL;
1779 ldi->name = getStringCopy(ANONYMOUS_NAME);
1780 ldi->name_short = NULL;
1781 ldi->name_sorting = NULL;
1782 ldi->author = getStringCopy(parent->author);
1783 ldi->imported_from = getStringCopy(parent->imported_from);
1785 ldi->level_group = FALSE;
1786 ldi->parent_link = FALSE;
1788 ldi->node_parent = parent;
1789 ldi->node_group = NULL;
1793 static void setSetupInfoToDefaults(struct SetupInfo *si)
1797 si->player_name = getStringCopy(getLoginName());
1800 si->sound_loops = TRUE;
1801 si->sound_music = TRUE;
1802 si->sound_simple = TRUE;
1804 si->double_buffering = TRUE;
1805 si->direct_draw = !si->double_buffering;
1806 si->scroll_delay = TRUE;
1807 si->soft_scrolling = TRUE;
1809 si->autorecord = TRUE;
1810 si->quick_doors = FALSE;
1811 si->team_mode = FALSE;
1812 si->handicap = TRUE;
1813 si->time_limit = TRUE;
1814 si->fullscreen = FALSE;
1816 for (i=0; i<MAX_PLAYERS; i++)
1818 si->input[i].use_joystick = FALSE;
1819 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1820 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1821 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1822 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1823 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1824 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1825 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1826 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1827 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1828 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1829 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1830 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1831 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1832 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1833 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1837 static void setSetupInfo(int token_nr, char *token_value)
1839 int token_type = token_info[token_nr].type;
1840 void *setup_value = token_info[token_nr].value;
1842 if (token_value == NULL)
1845 /* set setup field to corresponding token value */
1850 *(boolean *)setup_value = get_string_boolean_value(token_value);
1854 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1858 *(int *)setup_value = get_string_integer_value(token_value);
1862 if (*(char **)setup_value != NULL)
1863 free(*(char **)setup_value);
1864 *(char **)setup_value = getStringCopy(token_value);
1872 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1876 if (!setup_file_list)
1879 /* handle global setup values */
1881 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1882 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1885 /* handle player specific setup values */
1886 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1890 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1892 sii = setup.input[pnr];
1893 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1895 char full_token[100];
1897 sprintf(full_token, "%s%s", prefix, token_info[i].text);
1898 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1900 setup.input[pnr] = sii;
1904 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1906 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
1907 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
1910 if (entry1->parent_link || entry2->parent_link)
1911 compare_result = (entry1->parent_link ? -1 : +1);
1912 else if (entry1->sort_priority == entry2->sort_priority)
1914 char *name1 = getStringToLower(entry1->name_sorting);
1915 char *name2 = getStringToLower(entry2->name_sorting);
1917 compare_result = strcmp(name1, name2);
1922 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1923 compare_result = entry1->sort_priority - entry2->sort_priority;
1925 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1927 return compare_result;
1930 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
1932 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1934 setLevelDirInfoToDefaults(leveldir_new);
1936 leveldir_new->node_parent = node_parent;
1937 leveldir_new->parent_link = TRUE;
1939 leveldir_new->name = ".. (parent directory)";
1940 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1941 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1943 leveldir_new->filename = "..";
1944 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
1946 leveldir_new->sort_priority = node_parent->sort_priority;
1947 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1949 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
1952 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
1953 struct LevelDirInfo *node_parent,
1954 char *level_directory)
1957 struct dirent *dir_entry;
1958 boolean valid_entry_found = FALSE;
1960 if ((dir = opendir(level_directory)) == NULL)
1962 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1966 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1968 struct SetupFileList *setup_file_list = NULL;
1969 struct stat file_status;
1970 char *directory_name = dir_entry->d_name;
1971 char *directory_path = getPath2(level_directory, directory_name);
1972 char *filename = NULL;
1974 /* skip entries for current and parent directory */
1975 if (strcmp(directory_name, ".") == 0 ||
1976 strcmp(directory_name, "..") == 0)
1978 free(directory_path);
1982 /* find out if directory entry is itself a directory */
1983 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1984 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1986 free(directory_path);
1990 filename = getPath2(directory_path, LEVELINFO_FILENAME);
1991 setup_file_list = loadSetupFileList(filename);
1993 if (setup_file_list)
1995 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1998 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1999 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
2001 /* set all structure fields according to the token/value pairs */
2002 ldi = *leveldir_new;
2003 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2004 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2005 *leveldir_new = ldi;
2007 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2009 if (leveldir_new->name_short == NULL)
2010 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2012 if (leveldir_new->name_sorting == NULL)
2013 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2015 leveldir_new->filename = getStringCopy(directory_name);
2017 if (node_parent == NULL) /* top level group */
2019 leveldir_new->basepath = level_directory;
2020 leveldir_new->fullpath = leveldir_new->filename;
2022 else /* sub level group */
2024 leveldir_new->basepath = node_parent->basepath;
2025 leveldir_new->fullpath = getPath2(node_parent->fullpath,
2029 if (leveldir_new->levels < 1)
2030 leveldir_new->levels = 1;
2032 leveldir_new->last_level =
2033 leveldir_new->first_level + leveldir_new->levels - 1;
2035 leveldir_new->user_defined =
2036 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2038 leveldir_new->color = LEVELCOLOR(leveldir_new);
2039 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2041 leveldir_new->handicap_level = /* set handicap to default value */
2042 (leveldir_new->user_defined ?
2043 leveldir_new->last_level :
2044 leveldir_new->first_level);
2046 pushLevelDirInfo(node_first, leveldir_new);
2048 freeSetupFileList(setup_file_list);
2049 valid_entry_found = TRUE;
2051 if (leveldir_new->level_group)
2053 /* create node to link back to current level directory */
2054 createParentLevelDirNode(leveldir_new);
2056 /* step into sub-directory and look for more level series */
2057 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2058 leveldir_new, directory_path);
2062 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2064 free(directory_path);
2070 if (!valid_entry_found)
2071 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2075 void LoadLevelInfo()
2077 InitUserLevelDirectory(getLoginName());
2079 DrawInitText("Loading level series:", 120, FC_GREEN);
2081 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2082 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
2084 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2086 if (leveldir_first == NULL)
2087 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2089 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
2092 dumpLevelDirInfo(leveldir_first, 0);
2096 static void SaveUserLevelInfo()
2102 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2104 if (!(file = fopen(filename, MODE_WRITE)))
2106 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2111 /* always start with reliable default values */
2112 setLevelDirInfoToDefaults(&ldi);
2114 ldi.name = getLoginName();
2115 ldi.author = getRealName();
2117 ldi.first_level = 1;
2118 ldi.sort_priority = LEVELCLASS_USER_START;
2119 ldi.readonly = FALSE;
2121 fprintf(file, "%s\n\n",
2122 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
2124 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2125 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
2126 i != LEVELINFO_TOKEN_NAME_SORTING &&
2127 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2128 fprintf(file, "%s\n", getSetupLine("", i));
2133 chmod(filename, SETUP_PERMS);
2139 struct SetupFileList *setup_file_list = NULL;
2141 /* always start with reliable default values */
2142 setSetupInfoToDefaults(&setup);
2144 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2146 setup_file_list = loadSetupFileList(filename);
2148 if (setup_file_list)
2150 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
2151 decodeSetupFileList(setup_file_list);
2153 setup.direct_draw = !setup.double_buffering;
2155 freeSetupFileList(setup_file_list);
2157 /* needed to work around problems with fixed length strings */
2158 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
2159 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
2160 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
2162 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2164 strcpy(new_name, setup.player_name);
2165 free(setup.player_name);
2166 setup.player_name = new_name;
2170 Error(ERR_WARN, "using default setup values");
2175 static char *getSetupLine(char *prefix, int token_nr)
2178 static char entry[MAX_LINE_LEN];
2179 int token_type = token_info[token_nr].type;
2180 void *setup_value = token_info[token_nr].value;
2181 char *token_text = token_info[token_nr].text;
2183 /* start with the prefix, token and some spaces to format output line */
2184 sprintf(entry, "%s%s:", prefix, token_text);
2185 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2188 /* continue with the token's value (which can have different types) */
2192 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2196 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2201 Key key = *(Key *)setup_value;
2202 char *keyname = getKeyNameFromKey(key);
2204 strcat(entry, getX11KeyNameFromKey(key));
2205 for (i=strlen(entry); i<50; i++)
2208 /* add comment, if useful */
2209 if (strcmp(keyname, "(undefined)") != 0 &&
2210 strcmp(keyname, "(unknown)") != 0)
2212 strcat(entry, "# ");
2213 strcat(entry, keyname);
2220 char buffer[MAX_LINE_LEN];
2222 sprintf(buffer, "%d", *(int *)setup_value);
2223 strcat(entry, buffer);
2228 strcat(entry, *(char **)setup_value);
2244 InitUserDataDirectory();
2246 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2248 if (!(file = fopen(filename, MODE_WRITE)))
2250 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2255 fprintf(file, "%s\n",
2256 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2257 fprintf(file, "\n");
2259 /* handle global setup values */
2261 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2263 fprintf(file, "%s\n", getSetupLine("", i));
2265 /* just to make things nicer :) */
2266 if (i == SETUP_TOKEN_PLAYER_NAME)
2267 fprintf(file, "\n");
2270 /* handle player specific setup values */
2271 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2275 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2276 fprintf(file, "\n");
2278 sii = setup.input[pnr];
2279 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2280 fprintf(file, "%s\n", getSetupLine(prefix, i));
2286 chmod(filename, SETUP_PERMS);
2289 void LoadLevelSetup_LastSeries()
2292 struct SetupFileList *level_setup_list = NULL;
2294 /* always start with reliable default values */
2295 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2297 /* ----------------------------------------------------------------------- */
2298 /* ~/.rocksndiamonds/levelsetup.conf */
2299 /* ----------------------------------------------------------------------- */
2301 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2303 if ((level_setup_list = loadSetupFileList(filename)))
2305 char *last_level_series =
2306 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2308 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2309 if (leveldir_current == NULL)
2310 leveldir_current = leveldir_first;
2312 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2314 freeSetupFileList(level_setup_list);
2317 Error(ERR_WARN, "using default setup values");
2322 void SaveLevelSetup_LastSeries()
2325 char *level_subdir = leveldir_current->filename;
2328 /* ----------------------------------------------------------------------- */
2329 /* ~/.rocksndiamonds/levelsetup.conf */
2330 /* ----------------------------------------------------------------------- */
2332 InitUserDataDirectory();
2334 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2336 if (!(file = fopen(filename, MODE_WRITE)))
2338 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2343 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2344 LEVELSETUP_COOKIE));
2345 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2351 chmod(filename, SETUP_PERMS);
2354 static void checkSeriesInfo()
2356 static char *level_directory = NULL;
2358 struct dirent *dir_entry;
2360 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2362 level_directory = getPath2((leveldir_current->user_defined ?
2363 getUserLevelDir("") :
2364 options.level_directory),
2365 leveldir_current->fullpath);
2367 if ((dir = opendir(level_directory)) == NULL)
2369 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2373 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2375 if (strlen(dir_entry->d_name) > 4 &&
2376 dir_entry->d_name[3] == '.' &&
2377 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2379 char levelnum_str[4];
2382 strncpy(levelnum_str, dir_entry->d_name, 3);
2383 levelnum_str[3] = '\0';
2385 levelnum_value = atoi(levelnum_str);
2387 if (levelnum_value < leveldir_current->first_level)
2389 Error(ERR_WARN, "additional level %d found", levelnum_value);
2390 leveldir_current->first_level = levelnum_value;
2392 else if (levelnum_value > leveldir_current->last_level)
2394 Error(ERR_WARN, "additional level %d found", levelnum_value);
2395 leveldir_current->last_level = levelnum_value;
2403 void LoadLevelSetup_SeriesInfo()
2406 struct SetupFileList *level_setup_list = NULL;
2407 char *level_subdir = leveldir_current->filename;
2409 /* always start with reliable default values */
2410 level_nr = leveldir_current->first_level;
2412 checkSeriesInfo(leveldir_current);
2414 /* ----------------------------------------------------------------------- */
2415 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2416 /* ----------------------------------------------------------------------- */
2418 level_subdir = leveldir_current->filename;
2420 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2422 if ((level_setup_list = loadSetupFileList(filename)))
2426 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2430 level_nr = atoi(token_value);
2432 if (level_nr < leveldir_current->first_level)
2433 level_nr = leveldir_current->first_level;
2434 if (level_nr > leveldir_current->last_level)
2435 level_nr = leveldir_current->last_level;
2438 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2442 int level_nr = atoi(token_value);
2444 if (level_nr < leveldir_current->first_level)
2445 level_nr = leveldir_current->first_level;
2446 if (level_nr > leveldir_current->last_level + 1)
2447 level_nr = leveldir_current->last_level;
2449 if (leveldir_current->user_defined)
2450 level_nr = leveldir_current->last_level;
2452 leveldir_current->handicap_level = level_nr;
2455 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2457 freeSetupFileList(level_setup_list);
2460 Error(ERR_WARN, "using default setup values");
2465 void SaveLevelSetup_SeriesInfo()
2468 char *level_subdir = leveldir_current->filename;
2469 char *level_nr_str = int2str(level_nr, 0);
2470 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2473 /* ----------------------------------------------------------------------- */
2474 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2475 /* ----------------------------------------------------------------------- */
2477 InitLevelSetupDirectory(level_subdir);
2479 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2481 if (!(file = fopen(filename, MODE_WRITE)))
2483 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2488 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2489 LEVELSETUP_COOKIE));
2490 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2492 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2493 handicap_level_str));
2498 chmod(filename, SETUP_PERMS);
2500 /* LocalWords: Rocks'n