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 SkipBytesInFile(FILE *file, unsigned long bytes)
703 static void LoadLevel_HEAD(struct LevelInfo *level, FILE *file)
707 lev_fieldx = level->fieldx = fgetc(file);
708 lev_fieldy = level->fieldy = fgetc(file);
710 level->time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
711 level->gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
713 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
714 level->name[i] = fgetc(file);
715 level->name[MAX_LEVEL_NAME_LEN] = 0;
717 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
718 level->score[i] = fgetc(file);
720 level->num_yam_contents = STD_ELEMENT_CONTENTS;
721 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
727 if (i < STD_ELEMENT_CONTENTS)
728 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
730 level->yam_content[i][x][y] = EL_LEERRAUM;
735 level->amoeba_speed = fgetc(file);
736 level->time_magic_wall = fgetc(file);
737 level->time_wheel = fgetc(file);
738 level->amoeba_content = checkLevelElement(fgetc(file));
739 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
740 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
742 level->encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
744 SkipBytesInFile(file, LEVEL_HEADER_UNUSED); /* skip unused header bytes */
747 static void LoadLevel_AUTH(struct LevelInfo *level, FILE *file)
751 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
752 level->author[i] = fgetc(file);
753 level->author[MAX_LEVEL_NAME_LEN] = 0;
756 static void LoadLevel_CONT(struct LevelInfo *level, FILE *file)
761 level->num_yam_contents = fgetc(file);
765 if (level->num_yam_contents < 1 ||
766 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
769 printf("WARNING: num_yam_contents == %d (corrected)\n",
770 level->num_yam_contents);
772 level->num_yam_contents = STD_ELEMENT_CONTENTS;
775 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
778 level->yam_content[i][x][y] =
779 checkLevelElement(level->encoding_16bit ?
780 getFile16BitInteger(file,
781 BYTE_ORDER_BIG_ENDIAN) :
785 static void LoadLevel_BODY(struct LevelInfo *level, FILE *file)
789 for(y=0; y<level->fieldy; y++)
790 for(x=0; x<level->fieldx; x++)
791 Feld[x][y] = Ur[x][y] =
792 checkLevelElement(level->encoding_16bit ?
793 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
797 void LoadLevel(int level_nr)
799 char *filename = getLevelFilename(level_nr);
800 char cookie[MAX_LINE_LEN];
801 char chunk[CHUNK_ID_LEN + 1];
805 /* always start with reliable default values */
806 setLevelInfoToDefaults();
808 if (!(file = fopen(filename, MODE_READ)))
810 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
814 /* check file identifier */
815 fgets(cookie, MAX_LINE_LEN, file);
816 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
817 cookie[strlen(cookie) - 1] = '\0';
819 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
821 Error(ERR_WARN, "unknown format of level file '%s'", filename);
826 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
828 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
833 if (level.file_version < FILE_VERSION_1_2)
835 /* level files from versions before 1.2.0 without chunk structure */
836 LoadLevel_HEAD(&level, file);
837 LoadLevel_BODY(&level, file);
844 void (*chunk_loader)(struct LevelInfo *, FILE *);
849 { "HEAD", LoadLevel_HEAD, LEVEL_HEADER_SIZE },
850 { "AUTH", LoadLevel_AUTH, MAX_LEVEL_AUTHOR_LEN },
851 { "CONT", LoadLevel_CONT, 4 + MAX_ELEMENT_CONTENTS * 3 * 3 },
852 { "BODY", LoadLevel_BODY, 0 }, /* depends on contents of "HEAD" */
856 while (getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN))
860 while (chunk_info[i].chunk_name != NULL &&
861 strcmp(chunk, chunk_info[i].chunk_name) != 0)
864 if (chunk_info[i].chunk_name == NULL)
866 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
868 SkipBytesInFile(file, chunk_length);
870 else if (chunk_length != chunk_info[i].chunk_length)
872 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
873 chunk_length, chunk, filename);
874 SkipBytesInFile(file, chunk_length);
878 /* call function to load this level chunk */
879 (chunk_info[i].chunk_loader)(&level, file);
881 if (strcmp(chunk, "HEAD") == 0)
883 /* Note: "chunk_length" for CONT and BODY is wrong when elements are
884 stored with 16-bit encoding (and should be twice as big then). */
886 chunk_info[3].chunk_length = level.fieldx * level.fieldy;
892 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
893 if (strcmp(chunk, "HEAD") || chunk_length != LEVEL_HEADER_SIZE)
895 Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
900 LoadLevel_HEAD(&level, file);
902 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
904 /* look for optional author chunk */
905 if (strcmp(chunk, "AUTH") == 0 && chunk_length == MAX_LEVEL_AUTHOR_LEN)
907 LoadLevel_AUTH(&level, file);
909 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
912 /* look for optional content chunk */
913 if (strcmp(chunk, "CONT") == 0 &&
914 chunk_length == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
916 LoadLevel_CONT(&level, file);
918 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
921 /* next check body chunk identifier and chunk length */
922 if (strcmp(chunk, "BODY") != 0 || chunk_length != lev_fieldx * lev_fieldy)
924 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
929 LoadLevel_BODY(&level, file);
935 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
936 IS_LEVELCLASS_USER(leveldir_current))
938 /* for user contributed and private levels, use the version of
939 the game engine the levels were created for */
940 level.game_version = level.file_version;
942 /* player was faster than monsters in pre-1.0 levels */
943 if (level.file_version == FILE_VERSION_1_0)
945 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
946 Error(ERR_WARN, "using high speed movement for player");
947 level.double_speed = TRUE;
952 /* always use the latest version of the game engine for all but
953 user contributed and private levels */
954 level.game_version = GAME_VERSION_ACTUAL;
957 /* determine border element for this level */
961 void SaveLevel(int level_nr)
964 char *filename = getLevelFilename(level_nr);
966 boolean encoding_16bit_amoeba = FALSE;
967 boolean encoding_16bit_yamyam = FALSE;
969 boolean encoding_16bit = FALSE; /* default: only 8-bit elements */
970 char *oldest_possible_cookie;
973 if (!(file = fopen(filename, MODE_WRITE)))
975 Error(ERR_WARN, "cannot save level file '%s'", filename);
979 /* check yam content for 16-bit elements */
980 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
983 if (level.yam_content[i][x][y] > 255)
984 encoding_16bit = TRUE;
986 /* check level field for 16-bit elements */
987 for(y=0; y<lev_fieldy; y++)
988 for(x=0; x<lev_fieldx; x++)
990 encoding_16bit = TRUE;
992 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
994 fputs(oldest_possible_cookie, file); /* file identifier */
997 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
999 fputc(level.fieldx, file);
1000 fputc(level.fieldy, file);
1002 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
1003 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
1005 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1006 fputc(level.name[i], file);
1007 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1008 fputc(level.score[i], file);
1009 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1012 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
1013 fputc(level.amoeba_speed, file);
1014 fputc(level.time_magic_wall, file);
1015 fputc(level.time_wheel, file);
1016 fputc(level.amoeba_content, file);
1017 fputc((level.double_speed ? 1 : 0), file);
1018 fputc((level.gravity ? 1 : 0), file);
1020 fputc((encoding_16bit ? 1 : 0), file);
1022 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1025 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1027 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1028 fputc(level.author[i], file);
1030 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
1031 BYTE_ORDER_BIG_ENDIAN);
1033 fputc(EL_MAMPFER, file);
1034 fputc(level.num_yam_contents, file);
1038 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1042 putFile16BitInteger(file, level.yam_content[i][x][y],
1043 BYTE_ORDER_BIG_ENDIAN);
1045 fputc(level.yam_content[i][x][y], file);
1047 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
1049 for(y=0; y<lev_fieldy; y++)
1050 for(x=0; x<lev_fieldx; x++)
1052 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1054 fputc(Ur[x][y], file);
1058 chmod(filename, LEVEL_PERMS);
1061 void LoadTape(int level_nr)
1064 char *filename = getTapeFilename(level_nr);
1065 char cookie[MAX_LINE_LEN];
1066 char chunk[CHUNK_ID_LEN + 1];
1068 int num_participating_players;
1069 int file_version = FILE_VERSION_ACTUAL; /* last version of tape files */
1072 /* always start with reliable default values (empty tape) */
1073 tape.file_version = FILE_VERSION_ACTUAL;
1074 tape.game_version = GAME_VERSION_ACTUAL;
1077 /* default values (also for pre-1.2 tapes) with only the first player */
1078 tape.player_participates[0] = TRUE;
1079 for(i=1; i<MAX_PLAYERS; i++)
1080 tape.player_participates[i] = FALSE;
1082 /* at least one (default: the first) player participates in every tape */
1083 num_participating_players = 1;
1085 if (!(file = fopen(filename, MODE_READ)))
1088 /* check file identifier */
1089 fgets(cookie, MAX_LINE_LEN, file);
1090 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1091 cookie[strlen(cookie) - 1] = '\0';
1094 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
1095 file_version = FILE_VERSION_1_0;
1096 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
1098 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
1103 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1105 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1110 file_version = getFileVersionFromCookieString(cookie);
1113 tape.file_version = file_version;
1114 tape.game_version = file_version;
1116 /* read chunk "HEAD" */
1117 if (file_version >= FILE_VERSION_1_2)
1119 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
1120 if (strcmp(chunk, "HEAD") || chunk_length != TAPE_HEADER_SIZE)
1122 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
1128 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1129 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1130 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1132 /* read header fields that are new since version 1.2 */
1133 if (file_version >= FILE_VERSION_1_2)
1135 byte store_participating_players = fgetc(file);
1137 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
1140 /* since version 1.2, tapes store which players participate in the tape */
1141 num_participating_players = 0;
1142 for(i=0; i<MAX_PLAYERS; i++)
1144 tape.player_participates[i] = FALSE;
1146 if (store_participating_players & (1 << i))
1148 tape.player_participates[i] = TRUE;
1149 num_participating_players++;
1154 tape.level_nr = level_nr;
1156 tape.changed = FALSE;
1158 tape.recording = FALSE;
1159 tape.playing = FALSE;
1160 tape.pausing = FALSE;
1162 /* read chunk "BODY" */
1163 if (file_version >= FILE_VERSION_1_2)
1165 getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
1166 if (strcmp(chunk, "BODY") ||
1167 chunk_length != (num_participating_players + 1) * tape.length)
1169 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
1175 for(i=0; i<tape.length; i++)
1177 if (i >= MAX_TAPELEN)
1180 for(j=0; j<MAX_PLAYERS; j++)
1182 tape.pos[i].action[j] = MV_NO_MOVING;
1184 if (tape.player_participates[j])
1185 tape.pos[i].action[j] = fgetc(file);
1188 tape.pos[i].delay = fgetc(file);
1190 if (file_version == FILE_VERSION_1_0)
1192 /* eliminate possible diagonal moves in old tapes */
1193 /* this is only for backward compatibility */
1195 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1196 byte action = tape.pos[i].action[0];
1197 int k, num_moves = 0;
1201 if (action & joy_dir[k])
1203 tape.pos[i + num_moves].action[0] = joy_dir[k];
1205 tape.pos[i + num_moves].delay = 0;
1214 tape.length += num_moves;
1224 if (i != tape.length)
1225 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
1227 tape.length_seconds = GetTapeLength();
1230 void SaveTape(int level_nr)
1233 char *filename = getTapeFilename(level_nr);
1235 boolean new_tape = TRUE;
1236 byte store_participating_players;
1237 int num_participating_players;
1239 InitTapeDirectory(leveldir_current->filename);
1241 /* if a tape still exists, ask to overwrite it */
1242 if (access(filename, F_OK) == 0)
1245 if (!Request("Replace old tape ?", REQ_ASK))
1249 /* count number of players and set corresponding bits for compact storage */
1250 store_participating_players = 0;
1251 num_participating_players = 0;
1252 for(i=0; i<MAX_PLAYERS; i++)
1254 if (tape.player_participates[i])
1256 num_participating_players++;
1257 store_participating_players |= (1 << i);
1261 if (!(file = fopen(filename, MODE_WRITE)))
1263 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1267 fputs(TAPE_COOKIE, file); /* file identifier */
1270 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1272 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
1273 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
1274 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
1276 fputc(store_participating_players, file);
1278 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1281 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
1282 BYTE_ORDER_BIG_ENDIAN);
1284 for(i=0; i<tape.length; i++)
1288 for(j=0; j<MAX_PLAYERS; j++)
1289 if (tape.player_participates[j])
1290 fputc(tape.pos[i].action[j], file);
1292 fputc(tape.pos[i].delay, file);
1297 chmod(filename, TAPE_PERMS);
1299 tape.changed = FALSE;
1302 Request("tape saved !", REQ_CONFIRM);
1305 void LoadScore(int level_nr)
1308 char *filename = getScoreFilename(level_nr);
1309 char cookie[MAX_LINE_LEN];
1310 char line[MAX_LINE_LEN];
1314 /* always start with reliable default values */
1315 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1317 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1318 highscore[i].Score = 0;
1321 if (!(file = fopen(filename, MODE_READ)))
1324 /* check file identifier */
1325 fgets(cookie, MAX_LINE_LEN, file);
1326 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1327 cookie[strlen(cookie) - 1] = '\0';
1330 if (strcmp(cookie, SCORE_COOKIE) != 0)
1332 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
1337 if (!checkCookieString(cookie, SCORE_COOKIE)) /* unknown file format */
1339 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1345 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1347 fscanf(file, "%d", &highscore[i].Score);
1348 fgets(line, MAX_LINE_LEN, file);
1350 if (line[strlen(line) - 1] == '\n')
1351 line[strlen(line) - 1] = '\0';
1353 for (line_ptr = line; *line_ptr; line_ptr++)
1355 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1357 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1358 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1367 void SaveScore(int level_nr)
1370 char *filename = getScoreFilename(level_nr);
1373 InitScoreDirectory(leveldir_current->filename);
1375 if (!(file = fopen(filename, MODE_WRITE)))
1377 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1381 fprintf(file, "%s\n\n", SCORE_COOKIE);
1383 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1384 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1388 chmod(filename, SCORE_PERMS);
1391 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
1392 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1393 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1394 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1395 #define TOKEN_STR_PLAYER_PREFIX "player_"
1397 #define TOKEN_VALUE_POSITION 30
1400 #define SETUP_TOKEN_PLAYER_NAME 0
1401 #define SETUP_TOKEN_SOUND 1
1402 #define SETUP_TOKEN_SOUND_LOOPS 2
1403 #define SETUP_TOKEN_SOUND_MUSIC 3
1404 #define SETUP_TOKEN_SOUND_SIMPLE 4
1407 #define SETUP_TOKEN_TOONS 5
1408 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
1411 #define SETUP_TOKEN_SCROLL_DELAY 5
1412 #define SETUP_TOKEN_SOFT_SCROLLING 6
1413 #define SETUP_TOKEN_FADING 7
1414 #define SETUP_TOKEN_AUTORECORD 8
1415 #define SETUP_TOKEN_QUICK_DOORS 9
1416 #define SETUP_TOKEN_TEAM_MODE 10
1417 #define SETUP_TOKEN_HANDICAP 11
1418 #define SETUP_TOKEN_TIME_LIMIT 12
1419 #define SETUP_TOKEN_FULLSCREEN 13
1422 #define SETUP_TOKEN_USE_JOYSTICK 14
1423 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
1424 #define SETUP_TOKEN_JOY_XLEFT 16
1425 #define SETUP_TOKEN_JOY_XMIDDLE 17
1426 #define SETUP_TOKEN_JOY_XRIGHT 18
1427 #define SETUP_TOKEN_JOY_YUPPER 19
1428 #define SETUP_TOKEN_JOY_YMIDDLE 20
1429 #define SETUP_TOKEN_JOY_YLOWER 21
1430 #define SETUP_TOKEN_JOY_SNAP 22
1431 #define SETUP_TOKEN_JOY_BOMB 23
1432 #define SETUP_TOKEN_KEY_LEFT 24
1433 #define SETUP_TOKEN_KEY_RIGHT 25
1434 #define SETUP_TOKEN_KEY_UP 26
1435 #define SETUP_TOKEN_KEY_DOWN 27
1436 #define SETUP_TOKEN_KEY_SNAP 28
1437 #define SETUP_TOKEN_KEY_BOMB 29
1439 /* level directory info */
1440 #define LEVELINFO_TOKEN_NAME 30
1441 #define LEVELINFO_TOKEN_NAME_SHORT 31
1442 #define LEVELINFO_TOKEN_NAME_SORTING 32
1443 #define LEVELINFO_TOKEN_AUTHOR 33
1444 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
1445 #define LEVELINFO_TOKEN_LEVELS 35
1446 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
1447 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
1448 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
1449 #define LEVELINFO_TOKEN_READONLY 39
1451 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1452 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
1454 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1455 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1457 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1458 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1460 #define TYPE_BOOLEAN 1
1461 #define TYPE_SWITCH 2
1463 #define TYPE_INTEGER 4
1464 #define TYPE_STRING 5
1466 static struct SetupInfo si;
1467 static struct SetupInputInfo sii;
1468 static struct LevelDirInfo ldi;
1477 { TYPE_STRING, &si.player_name, "player_name" },
1478 { TYPE_SWITCH, &si.sound, "sound" },
1479 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1480 { TYPE_SWITCH, &si.sound_music, "background_music" },
1481 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1484 { TYPE_SWITCH, &si.toons, "toons" },
1485 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
1488 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1489 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1490 { TYPE_SWITCH, &si.fading, "screen_fading" },
1491 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1492 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1493 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1494 { TYPE_SWITCH, &si.handicap, "handicap" },
1495 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1496 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1499 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1500 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1501 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1502 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1503 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1504 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1505 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1506 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1507 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1508 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1509 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1510 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1511 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1512 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1513 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1514 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
1516 /* level directory info */
1517 { TYPE_STRING, &ldi.name, "name" },
1518 { TYPE_STRING, &ldi.name_short, "name_short" },
1519 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1520 { TYPE_STRING, &ldi.author, "author" },
1521 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1522 { TYPE_INTEGER, &ldi.levels, "levels" },
1523 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1524 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1525 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1526 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1529 static char *string_tolower(char *s)
1531 static char s_lower[100];
1534 if (strlen(s) >= 100)
1539 for (i=0; i<strlen(s_lower); i++)
1540 s_lower[i] = tolower(s_lower[i]);
1545 static int get_string_integer_value(char *s)
1547 static char *number_text[][3] =
1549 { "0", "zero", "null", },
1550 { "1", "one", "first" },
1551 { "2", "two", "second" },
1552 { "3", "three", "third" },
1553 { "4", "four", "fourth" },
1554 { "5", "five", "fifth" },
1555 { "6", "six", "sixth" },
1556 { "7", "seven", "seventh" },
1557 { "8", "eight", "eighth" },
1558 { "9", "nine", "ninth" },
1559 { "10", "ten", "tenth" },
1560 { "11", "eleven", "eleventh" },
1561 { "12", "twelve", "twelfth" },
1566 for (i=0; i<13; i++)
1568 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1574 static boolean get_string_boolean_value(char *s)
1576 if (strcmp(string_tolower(s), "true") == 0 ||
1577 strcmp(string_tolower(s), "yes") == 0 ||
1578 strcmp(string_tolower(s), "on") == 0 ||
1579 get_string_integer_value(s) == 1)
1585 static char *getFormattedSetupEntry(char *token, char *value)
1588 static char entry[MAX_LINE_LEN];
1590 sprintf(entry, "%s:", token);
1591 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1595 strcat(entry, value);
1600 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1602 if (!setup_file_list)
1605 if (setup_file_list->token)
1606 free(setup_file_list->token);
1607 if (setup_file_list->value)
1608 free(setup_file_list->value);
1609 if (setup_file_list->next)
1610 freeSetupFileList(setup_file_list->next);
1611 free(setup_file_list);
1614 static struct SetupFileList *newSetupFileList(char *token, char *value)
1616 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1618 new->token = checked_malloc(strlen(token) + 1);
1619 strcpy(new->token, token);
1621 new->value = checked_malloc(strlen(value) + 1);
1622 strcpy(new->value, value);
1629 static char *getTokenValue(struct SetupFileList *setup_file_list,
1632 if (!setup_file_list)
1635 if (strcmp(setup_file_list->token, token) == 0)
1636 return setup_file_list->value;
1638 return getTokenValue(setup_file_list->next, token);
1641 static void setTokenValue(struct SetupFileList *setup_file_list,
1642 char *token, char *value)
1644 if (!setup_file_list)
1647 if (strcmp(setup_file_list->token, token) == 0)
1649 free(setup_file_list->value);
1650 setup_file_list->value = checked_malloc(strlen(value) + 1);
1651 strcpy(setup_file_list->value, value);
1653 else if (setup_file_list->next == NULL)
1654 setup_file_list->next = newSetupFileList(token, value);
1656 setTokenValue(setup_file_list->next, token, value);
1660 static void printSetupFileList(struct SetupFileList *setup_file_list)
1662 if (!setup_file_list)
1665 printf("token: '%s'\n", setup_file_list->token);
1666 printf("value: '%s'\n", setup_file_list->value);
1668 printSetupFileList(setup_file_list->next);
1672 static struct SetupFileList *loadSetupFileList(char *filename)
1675 char line[MAX_LINE_LEN];
1676 char *token, *value, *line_ptr;
1677 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1678 struct SetupFileList *first_valid_list_entry;
1682 if (!(file = fopen(filename, MODE_READ)))
1684 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1690 /* read next line of input file */
1691 if (!fgets(line, MAX_LINE_LEN, file))
1694 /* cut trailing comment or whitespace from input line */
1695 for (line_ptr = line; *line_ptr; line_ptr++)
1697 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1704 /* cut trailing whitespaces from input line */
1705 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1706 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1709 /* ignore empty lines */
1713 line_len = strlen(line);
1715 /* cut leading whitespaces from token */
1716 for (token = line; *token; token++)
1717 if (*token != ' ' && *token != '\t')
1720 /* find end of token */
1721 for (line_ptr = token; *line_ptr; line_ptr++)
1723 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1730 if (line_ptr < line + line_len)
1731 value = line_ptr + 1;
1735 /* cut leading whitespaces from value */
1736 for (; *value; value++)
1737 if (*value != ' ' && *value != '\t')
1740 if (*token && *value)
1741 setTokenValue(setup_file_list, token, value);
1746 first_valid_list_entry = setup_file_list->next;
1748 /* free empty list header */
1749 setup_file_list->next = NULL;
1750 freeSetupFileList(setup_file_list);
1752 if (first_valid_list_entry == NULL)
1753 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1755 return first_valid_list_entry;
1758 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1761 if (!setup_file_list)
1764 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1766 if (strcmp(setup_file_list->value, identifier) != 0)
1768 Error(ERR_WARN, "configuration file has wrong version");
1775 if (setup_file_list->next)
1776 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1779 Error(ERR_WARN, "configuration file has no version information");
1784 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1786 ldi->filename = NULL;
1787 ldi->fullpath = NULL;
1788 ldi->basepath = NULL;
1789 ldi->name = getStringCopy(ANONYMOUS_NAME);
1790 ldi->name_short = NULL;
1791 ldi->name_sorting = NULL;
1792 ldi->author = getStringCopy(ANONYMOUS_NAME);
1793 ldi->imported_from = NULL;
1795 ldi->first_level = 0;
1796 ldi->last_level = 0;
1797 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1798 ldi->level_group = FALSE;
1799 ldi->parent_link = FALSE;
1800 ldi->user_defined = FALSE;
1801 ldi->readonly = TRUE;
1803 ldi->class_desc = NULL;
1804 ldi->handicap_level = 0;
1806 ldi->cl_cursor = -1;
1808 ldi->node_parent = NULL;
1809 ldi->node_group = NULL;
1813 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1814 struct LevelDirInfo *parent)
1818 setLevelDirInfoToDefaults(ldi);
1822 /* first copy all values from the parent structure ... */
1825 /* ... then set all fields to default that cannot be inherited from parent.
1826 This is especially important for all those fields that can be set from
1827 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1828 calls 'free()' for all already set token values which requires that no
1829 other structure's pointer may point to them!
1832 ldi->filename = NULL;
1833 ldi->fullpath = NULL;
1834 ldi->basepath = NULL;
1835 ldi->name = getStringCopy(ANONYMOUS_NAME);
1836 ldi->name_short = NULL;
1837 ldi->name_sorting = NULL;
1838 ldi->author = getStringCopy(parent->author);
1839 ldi->imported_from = getStringCopy(parent->imported_from);
1841 ldi->level_group = FALSE;
1842 ldi->parent_link = FALSE;
1844 ldi->node_parent = parent;
1845 ldi->node_group = NULL;
1849 static void setSetupInfoToDefaults(struct SetupInfo *si)
1853 si->player_name = getStringCopy(getLoginName());
1856 si->sound_loops = TRUE;
1857 si->sound_music = TRUE;
1858 si->sound_simple = TRUE;
1860 si->double_buffering = TRUE;
1861 si->direct_draw = !si->double_buffering;
1862 si->scroll_delay = TRUE;
1863 si->soft_scrolling = TRUE;
1865 si->autorecord = TRUE;
1866 si->quick_doors = FALSE;
1867 si->team_mode = FALSE;
1868 si->handicap = TRUE;
1869 si->time_limit = TRUE;
1870 si->fullscreen = FALSE;
1872 for (i=0; i<MAX_PLAYERS; i++)
1874 si->input[i].use_joystick = FALSE;
1875 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1876 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1877 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1878 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1879 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1880 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1881 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1882 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1883 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1884 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1885 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1886 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1887 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1888 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1889 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1893 static void setSetupInfo(int token_nr, char *token_value)
1895 int token_type = token_info[token_nr].type;
1896 void *setup_value = token_info[token_nr].value;
1898 if (token_value == NULL)
1901 /* set setup field to corresponding token value */
1906 *(boolean *)setup_value = get_string_boolean_value(token_value);
1910 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1914 *(int *)setup_value = get_string_integer_value(token_value);
1918 if (*(char **)setup_value != NULL)
1919 free(*(char **)setup_value);
1920 *(char **)setup_value = getStringCopy(token_value);
1928 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1932 if (!setup_file_list)
1935 /* handle global setup values */
1937 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1938 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1941 /* handle player specific setup values */
1942 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1946 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1948 sii = setup.input[pnr];
1949 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1951 char full_token[100];
1953 sprintf(full_token, "%s%s", prefix, token_info[i].text);
1954 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1956 setup.input[pnr] = sii;
1960 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1962 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
1963 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
1966 if (entry1->parent_link || entry2->parent_link)
1967 compare_result = (entry1->parent_link ? -1 : +1);
1968 else if (entry1->sort_priority == entry2->sort_priority)
1970 char *name1 = getStringToLower(entry1->name_sorting);
1971 char *name2 = getStringToLower(entry2->name_sorting);
1973 compare_result = strcmp(name1, name2);
1978 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1979 compare_result = entry1->sort_priority - entry2->sort_priority;
1981 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1983 return compare_result;
1986 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
1988 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1990 setLevelDirInfoToDefaults(leveldir_new);
1992 leveldir_new->node_parent = node_parent;
1993 leveldir_new->parent_link = TRUE;
1995 leveldir_new->name = ".. (parent directory)";
1996 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1997 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1999 leveldir_new->filename = "..";
2000 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
2002 leveldir_new->sort_priority = node_parent->sort_priority;
2003 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2005 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
2008 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
2009 struct LevelDirInfo *node_parent,
2010 char *level_directory)
2013 struct dirent *dir_entry;
2014 boolean valid_entry_found = FALSE;
2016 if ((dir = opendir(level_directory)) == NULL)
2018 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2022 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2024 struct SetupFileList *setup_file_list = NULL;
2025 struct stat file_status;
2026 char *directory_name = dir_entry->d_name;
2027 char *directory_path = getPath2(level_directory, directory_name);
2028 char *filename = NULL;
2030 /* skip entries for current and parent directory */
2031 if (strcmp(directory_name, ".") == 0 ||
2032 strcmp(directory_name, "..") == 0)
2034 free(directory_path);
2038 /* find out if directory entry is itself a directory */
2039 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2040 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2042 free(directory_path);
2046 filename = getPath2(directory_path, LEVELINFO_FILENAME);
2047 setup_file_list = loadSetupFileList(filename);
2049 if (setup_file_list)
2051 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2054 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
2055 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
2057 /* set all structure fields according to the token/value pairs */
2058 ldi = *leveldir_new;
2059 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2060 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2061 *leveldir_new = ldi;
2063 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2065 if (leveldir_new->name_short == NULL)
2066 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2068 if (leveldir_new->name_sorting == NULL)
2069 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2071 leveldir_new->filename = getStringCopy(directory_name);
2073 if (node_parent == NULL) /* top level group */
2075 leveldir_new->basepath = level_directory;
2076 leveldir_new->fullpath = leveldir_new->filename;
2078 else /* sub level group */
2080 leveldir_new->basepath = node_parent->basepath;
2081 leveldir_new->fullpath = getPath2(node_parent->fullpath,
2085 if (leveldir_new->levels < 1)
2086 leveldir_new->levels = 1;
2088 leveldir_new->last_level =
2089 leveldir_new->first_level + leveldir_new->levels - 1;
2091 leveldir_new->user_defined =
2092 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2094 leveldir_new->color = LEVELCOLOR(leveldir_new);
2095 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2097 leveldir_new->handicap_level = /* set handicap to default value */
2098 (leveldir_new->user_defined ?
2099 leveldir_new->last_level :
2100 leveldir_new->first_level);
2102 pushLevelDirInfo(node_first, leveldir_new);
2104 freeSetupFileList(setup_file_list);
2105 valid_entry_found = TRUE;
2107 if (leveldir_new->level_group)
2109 /* create node to link back to current level directory */
2110 createParentLevelDirNode(leveldir_new);
2112 /* step into sub-directory and look for more level series */
2113 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2114 leveldir_new, directory_path);
2118 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2120 free(directory_path);
2126 if (!valid_entry_found)
2127 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2131 void LoadLevelInfo()
2133 InitUserLevelDirectory(getLoginName());
2135 DrawInitText("Loading level series:", 120, FC_GREEN);
2137 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2138 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
2140 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2142 if (leveldir_first == NULL)
2143 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2145 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
2148 dumpLevelDirInfo(leveldir_first, 0);
2152 static void SaveUserLevelInfo()
2158 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2160 if (!(file = fopen(filename, MODE_WRITE)))
2162 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2167 /* always start with reliable default values */
2168 setLevelDirInfoToDefaults(&ldi);
2170 ldi.name = getLoginName();
2171 ldi.author = getRealName();
2173 ldi.first_level = 1;
2174 ldi.sort_priority = LEVELCLASS_USER_START;
2175 ldi.readonly = FALSE;
2177 fprintf(file, "%s\n\n",
2178 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
2180 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2181 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
2182 i != LEVELINFO_TOKEN_NAME_SORTING &&
2183 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2184 fprintf(file, "%s\n", getSetupLine("", i));
2189 chmod(filename, SETUP_PERMS);
2195 struct SetupFileList *setup_file_list = NULL;
2197 /* always start with reliable default values */
2198 setSetupInfoToDefaults(&setup);
2200 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2202 setup_file_list = loadSetupFileList(filename);
2204 if (setup_file_list)
2206 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
2207 decodeSetupFileList(setup_file_list);
2209 setup.direct_draw = !setup.double_buffering;
2211 freeSetupFileList(setup_file_list);
2213 /* needed to work around problems with fixed length strings */
2214 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
2215 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
2216 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
2218 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2220 strcpy(new_name, setup.player_name);
2221 free(setup.player_name);
2222 setup.player_name = new_name;
2226 Error(ERR_WARN, "using default setup values");
2231 static char *getSetupLine(char *prefix, int token_nr)
2234 static char entry[MAX_LINE_LEN];
2235 int token_type = token_info[token_nr].type;
2236 void *setup_value = token_info[token_nr].value;
2237 char *token_text = token_info[token_nr].text;
2239 /* start with the prefix, token and some spaces to format output line */
2240 sprintf(entry, "%s%s:", prefix, token_text);
2241 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2244 /* continue with the token's value (which can have different types) */
2248 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2252 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2257 Key key = *(Key *)setup_value;
2258 char *keyname = getKeyNameFromKey(key);
2260 strcat(entry, getX11KeyNameFromKey(key));
2261 for (i=strlen(entry); i<50; i++)
2264 /* add comment, if useful */
2265 if (strcmp(keyname, "(undefined)") != 0 &&
2266 strcmp(keyname, "(unknown)") != 0)
2268 strcat(entry, "# ");
2269 strcat(entry, keyname);
2276 char buffer[MAX_LINE_LEN];
2278 sprintf(buffer, "%d", *(int *)setup_value);
2279 strcat(entry, buffer);
2284 strcat(entry, *(char **)setup_value);
2300 InitUserDataDirectory();
2302 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2304 if (!(file = fopen(filename, MODE_WRITE)))
2306 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2311 fprintf(file, "%s\n",
2312 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2313 fprintf(file, "\n");
2315 /* handle global setup values */
2317 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2319 fprintf(file, "%s\n", getSetupLine("", i));
2321 /* just to make things nicer :) */
2322 if (i == SETUP_TOKEN_PLAYER_NAME)
2323 fprintf(file, "\n");
2326 /* handle player specific setup values */
2327 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2331 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2332 fprintf(file, "\n");
2334 sii = setup.input[pnr];
2335 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2336 fprintf(file, "%s\n", getSetupLine(prefix, i));
2342 chmod(filename, SETUP_PERMS);
2345 void LoadLevelSetup_LastSeries()
2348 struct SetupFileList *level_setup_list = NULL;
2350 /* always start with reliable default values */
2351 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2353 /* ----------------------------------------------------------------------- */
2354 /* ~/.rocksndiamonds/levelsetup.conf */
2355 /* ----------------------------------------------------------------------- */
2357 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2359 if ((level_setup_list = loadSetupFileList(filename)))
2361 char *last_level_series =
2362 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2364 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2365 if (leveldir_current == NULL)
2366 leveldir_current = leveldir_first;
2368 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2370 freeSetupFileList(level_setup_list);
2373 Error(ERR_WARN, "using default setup values");
2378 void SaveLevelSetup_LastSeries()
2381 char *level_subdir = leveldir_current->filename;
2384 /* ----------------------------------------------------------------------- */
2385 /* ~/.rocksndiamonds/levelsetup.conf */
2386 /* ----------------------------------------------------------------------- */
2388 InitUserDataDirectory();
2390 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2392 if (!(file = fopen(filename, MODE_WRITE)))
2394 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2399 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2400 LEVELSETUP_COOKIE));
2401 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2407 chmod(filename, SETUP_PERMS);
2410 static void checkSeriesInfo()
2412 static char *level_directory = NULL;
2414 struct dirent *dir_entry;
2416 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2418 level_directory = getPath2((leveldir_current->user_defined ?
2419 getUserLevelDir("") :
2420 options.level_directory),
2421 leveldir_current->fullpath);
2423 if ((dir = opendir(level_directory)) == NULL)
2425 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2429 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2431 if (strlen(dir_entry->d_name) > 4 &&
2432 dir_entry->d_name[3] == '.' &&
2433 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2435 char levelnum_str[4];
2438 strncpy(levelnum_str, dir_entry->d_name, 3);
2439 levelnum_str[3] = '\0';
2441 levelnum_value = atoi(levelnum_str);
2443 if (levelnum_value < leveldir_current->first_level)
2445 Error(ERR_WARN, "additional level %d found", levelnum_value);
2446 leveldir_current->first_level = levelnum_value;
2448 else if (levelnum_value > leveldir_current->last_level)
2450 Error(ERR_WARN, "additional level %d found", levelnum_value);
2451 leveldir_current->last_level = levelnum_value;
2459 void LoadLevelSetup_SeriesInfo()
2462 struct SetupFileList *level_setup_list = NULL;
2463 char *level_subdir = leveldir_current->filename;
2465 /* always start with reliable default values */
2466 level_nr = leveldir_current->first_level;
2468 checkSeriesInfo(leveldir_current);
2470 /* ----------------------------------------------------------------------- */
2471 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2472 /* ----------------------------------------------------------------------- */
2474 level_subdir = leveldir_current->filename;
2476 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2478 if ((level_setup_list = loadSetupFileList(filename)))
2482 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2486 level_nr = atoi(token_value);
2488 if (level_nr < leveldir_current->first_level)
2489 level_nr = leveldir_current->first_level;
2490 if (level_nr > leveldir_current->last_level)
2491 level_nr = leveldir_current->last_level;
2494 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2498 int level_nr = atoi(token_value);
2500 if (level_nr < leveldir_current->first_level)
2501 level_nr = leveldir_current->first_level;
2502 if (level_nr > leveldir_current->last_level + 1)
2503 level_nr = leveldir_current->last_level;
2505 if (leveldir_current->user_defined)
2506 level_nr = leveldir_current->last_level;
2508 leveldir_current->handicap_level = level_nr;
2511 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2513 freeSetupFileList(level_setup_list);
2516 Error(ERR_WARN, "using default setup values");
2521 void SaveLevelSetup_SeriesInfo()
2524 char *level_subdir = leveldir_current->filename;
2525 char *level_nr_str = int2str(level_nr, 0);
2526 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2529 /* ----------------------------------------------------------------------- */
2530 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2531 /* ----------------------------------------------------------------------- */
2533 InitLevelSetupDirectory(level_subdir);
2535 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2537 if (!(file = fopen(filename, MODE_WRITE)))
2539 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2544 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2545 LEVELSETUP_COOKIE));
2546 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2548 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2549 handicap_level_str));
2554 chmod(filename, SETUP_PERMS);
2556 /* LocalWords: Rocks'n