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 CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
29 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
30 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
31 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
32 #define LEVEL_HEADER_UNUSED 15 /* unused level header bytes */
33 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
34 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
35 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
36 #define TAPE_HEADER_UNUSED 7 /* unused tape header bytes */
38 /* file identifier strings */
39 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
40 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
41 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
42 #define SETUP_COOKIE "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
43 #define LEVELSETUP_COOKIE "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
44 #define LEVELINFO_COOKIE "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
46 /* file names and filename extensions */
47 #if !defined(PLATFORM_MSDOS)
48 #define LEVELSETUP_DIRECTORY "levelsetup"
49 #define SETUP_FILENAME "setup.conf"
50 #define LEVELSETUP_FILENAME "levelsetup.conf"
51 #define LEVELINFO_FILENAME "levelinfo.conf"
52 #define LEVELFILE_EXTENSION "level"
53 #define TAPEFILE_EXTENSION "tape"
54 #define SCOREFILE_EXTENSION "score"
56 #define LEVELSETUP_DIRECTORY "lvlsetup"
57 #define SETUP_FILENAME "setup.cnf"
58 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
59 #define LEVELINFO_FILENAME "lvlinfo.cnf"
60 #define LEVELFILE_EXTENSION "lvl"
61 #define TAPEFILE_EXTENSION "tap"
62 #define SCOREFILE_EXTENSION "sco"
65 /* sort priorities of level series (also used as level series classes) */
66 #define LEVELCLASS_TUTORIAL_START 10
67 #define LEVELCLASS_TUTORIAL_END 99
68 #define LEVELCLASS_CLASSICS_START 100
69 #define LEVELCLASS_CLASSICS_END 199
70 #define LEVELCLASS_CONTRIBUTION_START 200
71 #define LEVELCLASS_CONTRIBUTION_END 299
72 #define LEVELCLASS_USER_START 300
73 #define LEVELCLASS_USER_END 399
74 #define LEVELCLASS_BD_START 400
75 #define LEVELCLASS_BD_END 499
76 #define LEVELCLASS_EM_START 500
77 #define LEVELCLASS_EM_END 599
78 #define LEVELCLASS_SP_START 600
79 #define LEVELCLASS_SP_END 699
80 #define LEVELCLASS_DX_START 700
81 #define LEVELCLASS_DX_END 799
83 #define LEVELCLASS_TUTORIAL LEVELCLASS_TUTORIAL_START
84 #define LEVELCLASS_CLASSICS LEVELCLASS_CLASSICS_START
85 #define LEVELCLASS_CONTRIBUTION LEVELCLASS_CONTRIBUTION_START
86 #define LEVELCLASS_USER LEVELCLASS_USER_START
87 #define LEVELCLASS_BD LEVELCLASS_BD_START
88 #define LEVELCLASS_EM LEVELCLASS_EM_START
89 #define LEVELCLASS_SP LEVELCLASS_SP_START
90 #define LEVELCLASS_DX LEVELCLASS_DX_START
92 #define LEVELCLASS_UNDEFINED 999
94 #define NUM_LEVELCLASS_DESC 8
95 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
107 #define IS_LEVELCLASS_TUTORIAL(p) \
108 ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
109 (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
110 #define IS_LEVELCLASS_CLASSICS(p) \
111 ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
112 (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
113 #define IS_LEVELCLASS_CONTRIBUTION(p) \
114 ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
115 (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
116 #define IS_LEVELCLASS_USER(p) \
117 ((p)->sort_priority >= LEVELCLASS_USER_START && \
118 (p)->sort_priority <= LEVELCLASS_USER_END)
119 #define IS_LEVELCLASS_BD(p) \
120 ((p)->sort_priority >= LEVELCLASS_BD_START && \
121 (p)->sort_priority <= LEVELCLASS_BD_END)
122 #define IS_LEVELCLASS_EM(p) \
123 ((p)->sort_priority >= LEVELCLASS_EM_START && \
124 (p)->sort_priority <= LEVELCLASS_EM_END)
125 #define IS_LEVELCLASS_SP(p) \
126 ((p)->sort_priority >= LEVELCLASS_SP_START && \
127 (p)->sort_priority <= LEVELCLASS_SP_END)
128 #define IS_LEVELCLASS_DX(p) \
129 ((p)->sort_priority >= LEVELCLASS_DX_START && \
130 (p)->sort_priority <= LEVELCLASS_DX_END)
132 #define LEVELCLASS(n) (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
133 IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
134 IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
135 IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
136 IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
137 IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
138 IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
139 IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
140 LEVELCLASS_UNDEFINED)
142 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
143 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
144 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
145 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
146 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
147 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
148 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
149 IS_LEVELCLASS_USER(n) ? FC_RED : \
152 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
153 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
154 IS_LEVELCLASS_BD(n) ? 2 : \
155 IS_LEVELCLASS_EM(n) ? 3 : \
156 IS_LEVELCLASS_SP(n) ? 4 : \
157 IS_LEVELCLASS_DX(n) ? 5 : \
158 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
159 IS_LEVELCLASS_USER(n) ? 7 : \
162 char *getLevelClassDescription(struct LevelDirInfo *ldi)
164 int position = ldi->sort_priority / 100;
166 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
167 return levelclass_desc[position];
169 return "Unknown Level Class";
172 static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */
173 static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */
175 static char *getSetupDir()
177 return getUserDataDir();
180 static char *getUserLevelDir(char *level_subdir)
182 static char *userlevel_dir = NULL;
183 char *data_dir = getUserDataDir();
184 char *userlevel_subdir = LEVELS_DIRECTORY;
189 if (strlen(level_subdir) > 0)
190 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
192 userlevel_dir = getPath2(data_dir, userlevel_subdir);
194 return userlevel_dir;
197 static char *getTapeDir(char *level_subdir)
199 static char *tape_dir = NULL;
200 char *data_dir = getUserDataDir();
201 char *tape_subdir = TAPES_DIRECTORY;
206 if (strlen(level_subdir) > 0)
207 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
209 tape_dir = getPath2(data_dir, tape_subdir);
214 static char *getScoreDir(char *level_subdir)
216 static char *score_dir = NULL;
217 char *data_dir = options.rw_base_directory;
218 char *score_subdir = SCORES_DIRECTORY;
223 if (strlen(level_subdir) > 0)
224 score_dir = getPath3(data_dir, score_subdir, level_subdir);
226 score_dir = getPath2(data_dir, score_subdir);
231 static char *getLevelSetupDir(char *level_subdir)
233 static char *levelsetup_dir = NULL;
234 char *data_dir = getUserDataDir();
235 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
238 free(levelsetup_dir);
240 if (strlen(level_subdir) > 0)
241 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
243 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
245 return levelsetup_dir;
248 static char *getLevelFilename(int nr)
250 static char *filename = NULL;
251 char basename[MAX_FILENAME_LEN];
253 if (filename != NULL)
256 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
257 filename = getPath3((leveldir_current->user_defined ?
258 getUserLevelDir("") :
259 options.level_directory),
260 leveldir_current->fullpath,
266 static char *getTapeFilename(int nr)
268 static char *filename = NULL;
269 char basename[MAX_FILENAME_LEN];
271 if (filename != NULL)
274 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
275 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
280 static char *getScoreFilename(int nr)
282 static char *filename = NULL;
283 char basename[MAX_FILENAME_LEN];
285 if (filename != NULL)
288 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
289 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
294 static void InitTapeDirectory(char *level_subdir)
296 createDirectory(getUserDataDir(), "user data");
297 createDirectory(getTapeDir(""), "main tape");
298 createDirectory(getTapeDir(level_subdir), "level tape");
301 static void InitScoreDirectory(char *level_subdir)
303 createDirectory(getScoreDir(""), "main score");
304 createDirectory(getScoreDir(level_subdir), "level score");
307 static void InitUserLevelDirectory(char *level_subdir)
309 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
311 createDirectory(getUserDataDir(), "user data");
312 createDirectory(getUserLevelDir(""), "main user level");
313 createDirectory(getUserLevelDir(level_subdir), "user level");
319 static void InitLevelSetupDirectory(char *level_subdir)
321 createDirectory(getUserDataDir(), "user data");
322 createDirectory(getLevelSetupDir(""), "main level setup");
323 createDirectory(getLevelSetupDir(level_subdir), "level setup");
326 static void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
332 static void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
338 static void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
340 int file_version_major, file_version_minor, file_version_patch;
341 int game_version_major, game_version_minor, game_version_patch;
343 file_version_major = fgetc(file);
344 file_version_minor = fgetc(file);
345 file_version_patch = fgetc(file);
346 fgetc(file); /* not used */
348 game_version_major = fgetc(file);
349 game_version_minor = fgetc(file);
350 game_version_patch = fgetc(file);
351 fgetc(file); /* not used */
353 *file_version = VERSION_IDENT(file_version_major,
357 *game_version = VERSION_IDENT(game_version_major,
362 static void WriteChunk_VERS(FILE *file, int file_version, int game_version)
364 int file_version_major = VERSION_MAJOR(file_version);
365 int file_version_minor = VERSION_MINOR(file_version);
366 int file_version_patch = VERSION_PATCH(file_version);
367 int game_version_major = VERSION_MAJOR(game_version);
368 int game_version_minor = VERSION_MINOR(game_version);
369 int game_version_patch = VERSION_PATCH(game_version);
371 fputc(file_version_major, file);
372 fputc(file_version_minor, file);
373 fputc(file_version_patch, file);
374 fputc(0, file); /* not used */
376 fputc(game_version_major, file);
377 fputc(game_version_minor, file);
378 fputc(game_version_patch, file);
379 fputc(0, file); /* not used */
382 static int getFileVersionFromCookieString(const char *cookie)
384 const char *ptr_cookie1, *ptr_cookie2;
385 const char *pattern1 = "_FILE_VERSION_";
386 const char *pattern2 = "?.?";
387 const int len_cookie = strlen(cookie);
388 const int len_pattern1 = strlen(pattern1);
389 const int len_pattern2 = strlen(pattern2);
390 const int len_pattern = len_pattern1 + len_pattern2;
391 int version_major, version_minor;
393 if (len_cookie <= len_pattern)
396 ptr_cookie1 = &cookie[len_cookie - len_pattern];
397 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
399 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
402 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
403 ptr_cookie2[1] != '.' ||
404 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
407 version_major = ptr_cookie2[0] - '0';
408 version_minor = ptr_cookie2[2] - '0';
410 return VERSION_IDENT(version_major, version_minor, 0);
413 boolean checkCookieString(const char *cookie, const char *template)
415 const char *pattern = "_FILE_VERSION_?.?";
416 const int len_cookie = strlen(cookie);
417 const int len_template = strlen(template);
418 const int len_pattern = strlen(pattern);
420 if (len_cookie != len_template)
423 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
429 static void setLevelInfoToDefaults()
433 level.file_version = FILE_VERSION_ACTUAL;
434 level.game_version = GAME_VERSION_ACTUAL;
436 level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
437 level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
438 level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
440 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
441 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
443 for(x=0; x<MAX_LEV_FIELDX; x++)
444 for(y=0; y<MAX_LEV_FIELDY; y++)
445 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
448 level.gems_needed = 0;
449 level.amoeba_speed = 10;
450 level.time_magic_wall = 10;
451 level.time_wheel = 10;
452 level.time_light = 10;
453 level.time_timegate = 10;
454 level.amoeba_content = EL_DIAMANT;
455 level.double_speed = FALSE;
456 level.gravity = FALSE;
458 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
459 level.name[i] = '\0';
460 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
461 level.author[i] = '\0';
463 strcpy(level.name, NAMELESS_LEVEL_NAME);
464 strcpy(level.author, ANONYMOUS_NAME);
466 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
469 level.num_yam_contents = STD_ELEMENT_CONTENTS;
470 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
473 level.yam_content[i][x][y] =
474 (i < STD_ELEMENT_CONTENTS ? EL_FELSBROCKEN : EL_LEERRAUM);
476 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
477 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
478 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
480 BorderElement = EL_BETON;
482 /* try to determine better author name than 'anonymous' */
483 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
485 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
486 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
490 switch (LEVELCLASS(leveldir_current))
492 case LEVELCLASS_TUTORIAL:
493 strcpy(level.author, PROGRAM_AUTHOR_STRING);
496 case LEVELCLASS_CONTRIBUTION:
497 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
498 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
501 case LEVELCLASS_USER:
502 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
503 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
507 /* keep default value */
513 static int checkLevelElement(int element)
515 if (element >= EL_FIRST_RUNTIME_EL)
517 Error(ERR_WARN, "invalid level element %d", element);
518 element = EL_CHAR_FRAGE;
524 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
526 ReadChunk_VERS(file, &(level->file_version), &(level->game_version));
531 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
535 lev_fieldx = level->fieldx = fgetc(file);
536 lev_fieldy = level->fieldy = fgetc(file);
538 level->time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
539 level->gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
541 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
542 level->name[i] = fgetc(file);
543 level->name[MAX_LEVEL_NAME_LEN] = 0;
545 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
546 level->score[i] = fgetc(file);
548 level->num_yam_contents = STD_ELEMENT_CONTENTS;
549 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
552 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
554 level->amoeba_speed = fgetc(file);
555 level->time_magic_wall = fgetc(file);
556 level->time_wheel = fgetc(file);
557 level->amoeba_content = checkLevelElement(fgetc(file));
558 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
559 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
561 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
563 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
568 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
572 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
573 level->author[i] = fgetc(file);
574 level->author[MAX_LEVEL_NAME_LEN] = 0;
579 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
583 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
584 int chunk_size_expected = header_size + content_size;
586 /* Note: "chunk_size" was wrong before version 2.0 when elements are
587 stored with 16-bit encoding (and should be twice as big then).
588 Even worse, playfield data was stored 16-bit when only yamyam content
589 contained 16-bit elements and vice versa. */
591 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
592 chunk_size_expected += content_size;
594 if (chunk_size_expected != chunk_size)
596 ReadUnusedBytesFromFile(file, chunk_size);
597 return chunk_size_expected;
601 level->num_yam_contents = fgetc(file);
605 /* correct invalid number of content fields -- should never happen */
606 if (level->num_yam_contents < 1 ||
607 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
608 level->num_yam_contents = STD_ELEMENT_CONTENTS;
610 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
613 level->yam_content[i][x][y] =
614 checkLevelElement(level->encoding_16bit_field ?
615 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
620 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
623 int chunk_size_expected = level->fieldx * level->fieldy;
625 /* Note: "chunk_size" was wrong before version 2.0 when elements are
626 stored with 16-bit encoding (and should be twice as big then).
627 Even worse, playfield data was stored 16-bit when only yamyam content
628 contained 16-bit elements and vice versa. */
630 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
631 chunk_size_expected *= 2;
633 if (chunk_size_expected != chunk_size)
635 ReadUnusedBytesFromFile(file, chunk_size);
636 return chunk_size_expected;
639 for(y=0; y<level->fieldy; y++)
640 for(x=0; x<level->fieldx; x++)
641 Feld[x][y] = Ur[x][y] =
642 checkLevelElement(level->encoding_16bit_field ?
643 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
648 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
652 int num_contents, content_xsize, content_ysize;
653 int content_array[MAX_ELEMENT_CONTENTS][3][3];
655 element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
656 num_contents = fgetc(file);
657 content_xsize = fgetc(file);
658 content_ysize = fgetc(file);
659 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
661 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
664 content_array[i][x][y] =
665 checkLevelElement(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
667 /* correct invalid number of content fields -- should never happen */
668 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
669 num_contents = STD_ELEMENT_CONTENTS;
671 if (element == EL_MAMPFER)
673 level->num_yam_contents = num_contents;
675 for(i=0; i<num_contents; i++)
678 level->yam_content[i][x][y] = content_array[i][x][y];
680 else if (element == EL_AMOEBE_BD)
682 level->amoeba_content = content_array[0][0][0];
686 Error(ERR_WARN, "cannot load content for element '%d'", element);
692 void LoadLevel(int level_nr)
694 char *filename = getLevelFilename(level_nr);
695 char cookie[MAX_LINE_LEN];
696 char chunk_name[CHUNK_ID_LEN + 1];
700 /* always start with reliable default values */
701 setLevelInfoToDefaults();
703 if (!(file = fopen(filename, MODE_READ)))
705 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
709 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
710 if (strcmp(chunk_name, "RND1") == 0)
712 getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN); /* not used */
714 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
715 if (strcmp(chunk_name, "CAVE") != 0)
717 Error(ERR_WARN, "unknown format of level file '%s'", filename);
722 else /* check for pre-2.0 file format with cookie string */
724 strcpy(cookie, chunk_name);
725 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
726 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
727 cookie[strlen(cookie) - 1] = '\0';
729 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
731 Error(ERR_WARN, "unknown format of level file '%s'", filename);
736 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
738 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
744 if (level.file_version < FILE_VERSION_1_2)
746 /* level files from versions before 1.2.0 without chunk structure */
747 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
748 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
756 int (*loader)(FILE *, int, struct LevelInfo *);
760 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
761 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
762 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
763 { "CONT", -1, LoadLevel_CONT },
764 { "BODY", -1, LoadLevel_BODY },
765 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
769 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
773 while (chunk_info[i].name != NULL &&
774 strcmp(chunk_name, chunk_info[i].name) != 0)
777 if (chunk_info[i].name == NULL)
779 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
780 chunk_name, filename);
781 ReadUnusedBytesFromFile(file, chunk_size);
783 else if (chunk_info[i].size != -1 &&
784 chunk_info[i].size != chunk_size)
786 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
787 chunk_size, chunk_name, filename);
788 ReadUnusedBytesFromFile(file, chunk_size);
792 /* call function to load this level chunk */
793 int chunk_size_expected =
794 (chunk_info[i].loader)(file, chunk_size, &level);
796 /* the size of some chunks cannot be checked before reading other
797 chunks first (like "HEAD" and "BODY") that contain some header
798 information, so check them here */
799 if (chunk_size_expected != chunk_size)
801 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
802 chunk_size, chunk_name, filename);
810 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
811 IS_LEVELCLASS_USER(leveldir_current))
813 /* for user contributed and private levels, use the version of
814 the game engine the levels were created for */
815 level.game_version = level.file_version;
817 /* player was faster than monsters in pre-1.0 levels */
818 if (level.file_version == FILE_VERSION_1_0)
820 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
821 Error(ERR_WARN, "using high speed movement for player");
822 level.double_speed = TRUE;
827 /* always use the latest version of the game engine for all but
828 user contributed and private levels */
829 level.game_version = GAME_VERSION_ACTUAL;
832 /* determine border element for this level */
836 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
840 fputc(level->fieldx, file);
841 fputc(level->fieldy, file);
843 putFile16BitInteger(file, level->time, BYTE_ORDER_BIG_ENDIAN);
844 putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
846 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
847 fputc(level->name[i], file);
849 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
850 fputc(level->score[i], file);
852 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
855 fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
856 level->yam_content[i][x][y]),
858 fputc(level->amoeba_speed, file);
859 fputc(level->time_magic_wall, file);
860 fputc(level->time_wheel, file);
861 fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
863 fputc((level->double_speed ? 1 : 0), file);
864 fputc((level->gravity ? 1 : 0), file);
866 fputc((level->encoding_16bit_field ? 1 : 0), file);
868 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
871 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
875 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
876 fputc(level->author[i], file);
880 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
884 fputc(EL_MAMPFER, file);
885 fputc(level->num_yam_contents, file);
889 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
892 if (level->encoding_16bit_field)
893 putFile16BitInteger(file, level->yam_content[i][x][y],
894 BYTE_ORDER_BIG_ENDIAN);
896 fputc(level->yam_content[i][x][y], file);
900 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
904 for(y=0; y<lev_fieldy; y++)
905 for(x=0; x<lev_fieldx; x++)
906 if (level->encoding_16bit_field)
907 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
909 fputc(Ur[x][y], file);
912 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
915 int num_contents, content_xsize, content_ysize;
916 int content_array[MAX_ELEMENT_CONTENTS][3][3];
918 if (element == EL_MAMPFER)
920 num_contents = level->num_yam_contents;
924 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
927 content_array[i][x][y] = level->yam_content[i][x][y];
929 else if (element == EL_AMOEBE_BD)
935 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
938 content_array[i][x][y] = EL_LEERRAUM;
939 content_array[0][0][0] = level->amoeba_content;
943 /* chunk header already written -- write empty chunk data */
944 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
946 Error(ERR_WARN, "cannot save content for element '%d'", element);
950 putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
951 fputc(num_contents, file);
952 fputc(content_xsize, file);
953 fputc(content_ysize, file);
955 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
957 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
960 putFile16BitInteger(file, content_array[i][x][y],
961 BYTE_ORDER_BIG_ENDIAN);
964 void SaveLevel(int level_nr)
967 char *filename = getLevelFilename(level_nr);
971 if (!(file = fopen(filename, MODE_WRITE)))
973 Error(ERR_WARN, "cannot save level file '%s'", filename);
977 /* check level field for 16-bit elements */
978 for(y=0; y<level.fieldy; y++)
979 for(x=0; x<level.fieldx; x++)
981 level.encoding_16bit_field = TRUE;
983 /* check yamyam content for 16-bit elements */
984 for(i=0; i<level.num_yam_contents; i++)
987 if (level.yam_content[i][x][y] > 255)
988 level.encoding_16bit_yamyam = TRUE;
990 /* check amoeba content for 16-bit elements */
991 if (level.amoeba_content > 255)
992 level.encoding_16bit_amoeba = TRUE;
995 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
997 putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
998 putFileChunk(file, "CAVE", CHUNK_SIZE_NONE, BYTE_ORDER_BIG_ENDIAN);
1000 putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
1001 WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
1003 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1004 SaveLevel_HEAD(file, &level);
1006 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1007 SaveLevel_AUTH(file, &level);
1009 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1010 SaveLevel_BODY(file, &level);
1012 if (level.encoding_16bit_yamyam ||
1013 level.num_yam_contents != STD_ELEMENT_CONTENTS)
1015 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1016 SaveLevel_CNT2(file, &level, EL_MAMPFER);
1019 if (level.encoding_16bit_amoeba)
1021 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1022 SaveLevel_CNT2(file, &level, EL_AMOEBE_BD);
1027 SetFilePermissions_Level(filename);
1030 static void setTapeInfoToDefaults()
1034 /* always start with reliable default values (empty tape) */
1035 tape.file_version = FILE_VERSION_ACTUAL;
1036 tape.game_version = GAME_VERSION_ACTUAL;
1039 /* default values (also for pre-1.2 tapes) with only the first player */
1040 tape.player_participates[0] = TRUE;
1041 for(i=1; i<MAX_PLAYERS; i++)
1042 tape.player_participates[i] = FALSE;
1044 /* at least one (default: the first) player participates in every tape */
1045 tape.num_participating_players = 1;
1047 tape.level_nr = level_nr;
1049 tape.changed = FALSE;
1051 tape.recording = FALSE;
1052 tape.playing = FALSE;
1053 tape.pausing = FALSE;
1056 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1058 ReadChunk_VERS(file, &(tape->file_version), &(tape->game_version));
1063 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1067 tape->random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1068 tape->date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1069 tape->length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1071 /* read header fields that are new since version 1.2 */
1072 if (tape->file_version >= FILE_VERSION_1_2)
1074 byte store_participating_players = fgetc(file);
1076 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1078 /* since version 1.2, tapes store which players participate in the tape */
1079 tape->num_participating_players = 0;
1080 for(i=0; i<MAX_PLAYERS; i++)
1082 tape->player_participates[i] = FALSE;
1084 if (store_participating_players & (1 << i))
1086 tape->player_participates[i] = TRUE;
1087 tape->num_participating_players++;
1095 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1098 int chunk_size_expected =
1099 (tape->num_participating_players + 1) * tape->length;
1101 if (chunk_size_expected != chunk_size)
1103 ReadUnusedBytesFromFile(file, chunk_size);
1104 return chunk_size_expected;
1107 for(i=0; i<tape->length; i++)
1109 if (i >= MAX_TAPELEN)
1112 for(j=0; j<MAX_PLAYERS; j++)
1114 tape->pos[i].action[j] = MV_NO_MOVING;
1116 if (tape->player_participates[j])
1117 tape->pos[i].action[j] = fgetc(file);
1120 tape->pos[i].delay = fgetc(file);
1122 if (tape->file_version == FILE_VERSION_1_0)
1124 /* eliminate possible diagonal moves in old tapes */
1125 /* this is only for backward compatibility */
1127 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1128 byte action = tape->pos[i].action[0];
1129 int k, num_moves = 0;
1133 if (action & joy_dir[k])
1135 tape->pos[i + num_moves].action[0] = joy_dir[k];
1137 tape->pos[i + num_moves].delay = 0;
1146 tape->length += num_moves;
1149 else if (tape->file_version < FILE_VERSION_2_0)
1151 if (tape->pos[i].delay > 1)
1154 tape->pos[i + 1] = tape->pos[i];
1155 tape->pos[i + 1].delay = 1;
1158 for(j=0; j<MAX_PLAYERS; j++)
1159 tape->pos[i].action[j] = MV_NO_MOVING;
1160 tape->pos[i].delay--;
1171 if (i != tape->length)
1172 chunk_size = (tape->num_participating_players + 1) * i;
1177 void LoadTape(int level_nr)
1179 char *filename = getTapeFilename(level_nr);
1180 char cookie[MAX_LINE_LEN];
1181 char chunk_name[CHUNK_ID_LEN + 1];
1185 /* always start with reliable default values */
1186 setTapeInfoToDefaults();
1188 if (!(file = fopen(filename, MODE_READ)))
1191 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
1192 if (strcmp(chunk_name, "RND1") == 0)
1194 getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN); /* not used */
1196 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
1197 if (strcmp(chunk_name, "TAPE") != 0)
1199 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1204 else /* check for pre-2.0 file format with cookie string */
1206 strcpy(cookie, chunk_name);
1207 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1208 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1209 cookie[strlen(cookie) - 1] = '\0';
1211 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1213 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1218 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1220 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1226 tape.game_version = tape.file_version;
1228 if (tape.file_version < FILE_VERSION_1_2)
1230 /* tape files from versions before 1.2.0 without chunk structure */
1231 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1232 LoadTape_BODY(file, 2 * tape.length, &tape);
1240 int (*loader)(FILE *, int, struct TapeInfo *);
1244 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1245 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1246 { "BODY", -1, LoadTape_BODY },
1250 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
1254 while (chunk_info[i].name != NULL &&
1255 strcmp(chunk_name, chunk_info[i].name) != 0)
1258 if (chunk_info[i].name == NULL)
1260 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1261 chunk_name, filename);
1262 ReadUnusedBytesFromFile(file, chunk_size);
1264 else if (chunk_info[i].size != -1 &&
1265 chunk_info[i].size != chunk_size)
1267 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1268 chunk_size, chunk_name, filename);
1269 ReadUnusedBytesFromFile(file, chunk_size);
1273 /* call function to load this tape chunk */
1274 int chunk_size_expected =
1275 (chunk_info[i].loader)(file, chunk_size, &tape);
1277 /* the size of some chunks cannot be checked before reading other
1278 chunks first (like "HEAD" and "BODY") that contain some header
1279 information, so check them here */
1280 if (chunk_size_expected != chunk_size)
1282 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1283 chunk_size, chunk_name, filename);
1291 tape.length_seconds = GetTapeLength();
1294 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1297 byte store_participating_players = 0;
1299 /* set bits for participating players for compact storage */
1300 for(i=0; i<MAX_PLAYERS; i++)
1301 if (tape->player_participates[i])
1302 store_participating_players |= (1 << i);
1304 putFile32BitInteger(file, tape->random_seed, BYTE_ORDER_BIG_ENDIAN);
1305 putFile32BitInteger(file, tape->date, BYTE_ORDER_BIG_ENDIAN);
1306 putFile32BitInteger(file, tape->length, BYTE_ORDER_BIG_ENDIAN);
1308 fputc(store_participating_players, file);
1310 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1313 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1317 for(i=0; i<tape->length; i++)
1319 for(j=0; j<MAX_PLAYERS; j++)
1320 if (tape->player_participates[j])
1321 fputc(tape->pos[i].action[j], file);
1323 fputc(tape->pos[i].delay, file);
1327 void SaveTape(int level_nr)
1330 char *filename = getTapeFilename(level_nr);
1332 boolean new_tape = TRUE;
1333 int num_participating_players = 0;
1334 int body_chunk_size;
1336 InitTapeDirectory(leveldir_current->filename);
1338 /* if a tape still exists, ask to overwrite it */
1339 if (access(filename, F_OK) == 0)
1342 if (!Request("Replace old tape ?", REQ_ASK))
1346 if (!(file = fopen(filename, MODE_WRITE)))
1348 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1352 /* count number of participating players */
1353 for(i=0; i<MAX_PLAYERS; i++)
1354 if (tape.player_participates[i])
1355 num_participating_players++;
1357 body_chunk_size = (num_participating_players + 1) * tape.length;
1359 putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
1360 putFileChunk(file, "TAPE", CHUNK_SIZE_NONE, BYTE_ORDER_BIG_ENDIAN);
1362 putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
1363 WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
1365 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1366 SaveTape_HEAD(file, &tape);
1368 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1369 SaveTape_BODY(file, &tape);
1373 SetFilePermissions_Tape(filename);
1375 tape.changed = FALSE;
1378 Request("tape saved !", REQ_CONFIRM);
1381 void DumpTape(struct TapeInfo *tape)
1385 if (TAPE_IS_EMPTY(*tape))
1387 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1392 printf("-------------------------------------------------------------------------------\n");
1393 printf("Tape of Level %d (file version %06d, game version %06d\n",
1394 tape->level_nr, tape->file_version, tape->game_version);
1395 printf("-------------------------------------------------------------------------------\n");
1397 for(i=0; i<tape->length; i++)
1399 if (i >= MAX_TAPELEN)
1402 for(j=0; j<MAX_PLAYERS; j++)
1404 if (tape->player_participates[j])
1406 int action = tape->pos[i].action[j];
1408 printf("%d:%02x ", j, action);
1409 printf("[%c%c%c%c|%c%c] - ",
1410 (action & JOY_LEFT ? '<' : ' '),
1411 (action & JOY_RIGHT ? '>' : ' '),
1412 (action & JOY_UP ? '^' : ' '),
1413 (action & JOY_DOWN ? 'v' : ' '),
1414 (action & JOY_BUTTON_1 ? '1' : ' '),
1415 (action & JOY_BUTTON_2 ? '2' : ' '));
1419 printf("(%03d)\n", tape->pos[i].delay);
1422 printf("-------------------------------------------------------------------------------\n");
1425 void LoadScore(int level_nr)
1428 char *filename = getScoreFilename(level_nr);
1429 char cookie[MAX_LINE_LEN];
1430 char line[MAX_LINE_LEN];
1434 /* always start with reliable default values */
1435 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1437 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1438 highscore[i].Score = 0;
1441 if (!(file = fopen(filename, MODE_READ)))
1444 /* check file identifier */
1445 fgets(cookie, MAX_LINE_LEN, file);
1446 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1447 cookie[strlen(cookie) - 1] = '\0';
1449 if (!checkCookieString(cookie, SCORE_COOKIE))
1451 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1456 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1458 fscanf(file, "%d", &highscore[i].Score);
1459 fgets(line, MAX_LINE_LEN, file);
1461 if (line[strlen(line) - 1] == '\n')
1462 line[strlen(line) - 1] = '\0';
1464 for (line_ptr = line; *line_ptr; line_ptr++)
1466 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1468 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1469 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1478 void SaveScore(int level_nr)
1481 char *filename = getScoreFilename(level_nr);
1484 InitScoreDirectory(leveldir_current->filename);
1486 if (!(file = fopen(filename, MODE_WRITE)))
1488 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1492 fprintf(file, "%s\n\n", SCORE_COOKIE);
1494 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1495 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1499 SetFilePermissions_Score(filename);
1502 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
1503 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1504 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1505 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1506 #define TOKEN_STR_PLAYER_PREFIX "player_"
1508 #define TOKEN_VALUE_POSITION 30
1511 #define SETUP_TOKEN_PLAYER_NAME 0
1512 #define SETUP_TOKEN_SOUND 1
1513 #define SETUP_TOKEN_SOUND_LOOPS 2
1514 #define SETUP_TOKEN_SOUND_MUSIC 3
1515 #define SETUP_TOKEN_SOUND_SIMPLE 4
1516 #define SETUP_TOKEN_SCROLL_DELAY 5
1517 #define SETUP_TOKEN_SOFT_SCROLLING 6
1518 #define SETUP_TOKEN_FADING 7
1519 #define SETUP_TOKEN_AUTORECORD 8
1520 #define SETUP_TOKEN_QUICK_DOORS 9
1521 #define SETUP_TOKEN_TEAM_MODE 10
1522 #define SETUP_TOKEN_HANDICAP 11
1523 #define SETUP_TOKEN_TIME_LIMIT 12
1524 #define SETUP_TOKEN_FULLSCREEN 13
1527 #define SETUP_TOKEN_USE_JOYSTICK 14
1528 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
1529 #define SETUP_TOKEN_JOY_XLEFT 16
1530 #define SETUP_TOKEN_JOY_XMIDDLE 17
1531 #define SETUP_TOKEN_JOY_XRIGHT 18
1532 #define SETUP_TOKEN_JOY_YUPPER 19
1533 #define SETUP_TOKEN_JOY_YMIDDLE 20
1534 #define SETUP_TOKEN_JOY_YLOWER 21
1535 #define SETUP_TOKEN_JOY_SNAP 22
1536 #define SETUP_TOKEN_JOY_BOMB 23
1537 #define SETUP_TOKEN_KEY_LEFT 24
1538 #define SETUP_TOKEN_KEY_RIGHT 25
1539 #define SETUP_TOKEN_KEY_UP 26
1540 #define SETUP_TOKEN_KEY_DOWN 27
1541 #define SETUP_TOKEN_KEY_SNAP 28
1542 #define SETUP_TOKEN_KEY_BOMB 29
1544 /* level directory info */
1545 #define LEVELINFO_TOKEN_NAME 30
1546 #define LEVELINFO_TOKEN_NAME_SHORT 31
1547 #define LEVELINFO_TOKEN_NAME_SORTING 32
1548 #define LEVELINFO_TOKEN_AUTHOR 33
1549 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
1550 #define LEVELINFO_TOKEN_LEVELS 35
1551 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
1552 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
1553 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
1554 #define LEVELINFO_TOKEN_READONLY 39
1556 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1557 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
1559 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1560 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1562 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1563 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1565 #define TYPE_BOOLEAN 1
1566 #define TYPE_SWITCH 2
1568 #define TYPE_INTEGER 4
1569 #define TYPE_STRING 5
1571 static struct SetupInfo si;
1572 static struct SetupInputInfo sii;
1573 static struct LevelDirInfo ldi;
1582 { TYPE_STRING, &si.player_name, "player_name" },
1583 { TYPE_SWITCH, &si.sound, "sound" },
1584 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1585 { TYPE_SWITCH, &si.sound_music, "background_music" },
1586 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1587 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1588 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1589 { TYPE_SWITCH, &si.fading, "screen_fading" },
1590 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1591 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1592 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1593 { TYPE_SWITCH, &si.handicap, "handicap" },
1594 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1595 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1598 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1599 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1600 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1601 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1602 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1603 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1604 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1605 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1606 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1607 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1608 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1609 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1610 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1611 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1612 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1613 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
1615 /* level directory info */
1616 { TYPE_STRING, &ldi.name, "name" },
1617 { TYPE_STRING, &ldi.name_short, "name_short" },
1618 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1619 { TYPE_STRING, &ldi.author, "author" },
1620 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1621 { TYPE_INTEGER, &ldi.levels, "levels" },
1622 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1623 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1624 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1625 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1628 static char *string_tolower(char *s)
1630 static char s_lower[100];
1633 if (strlen(s) >= 100)
1638 for (i=0; i<strlen(s_lower); i++)
1639 s_lower[i] = tolower(s_lower[i]);
1644 static int get_string_integer_value(char *s)
1646 static char *number_text[][3] =
1648 { "0", "zero", "null", },
1649 { "1", "one", "first" },
1650 { "2", "two", "second" },
1651 { "3", "three", "third" },
1652 { "4", "four", "fourth" },
1653 { "5", "five", "fifth" },
1654 { "6", "six", "sixth" },
1655 { "7", "seven", "seventh" },
1656 { "8", "eight", "eighth" },
1657 { "9", "nine", "ninth" },
1658 { "10", "ten", "tenth" },
1659 { "11", "eleven", "eleventh" },
1660 { "12", "twelve", "twelfth" },
1665 for (i=0; i<13; i++)
1667 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1673 static boolean get_string_boolean_value(char *s)
1675 if (strcmp(string_tolower(s), "true") == 0 ||
1676 strcmp(string_tolower(s), "yes") == 0 ||
1677 strcmp(string_tolower(s), "on") == 0 ||
1678 get_string_integer_value(s) == 1)
1684 static char *getFormattedSetupEntry(char *token, char *value)
1687 static char entry[MAX_LINE_LEN];
1689 sprintf(entry, "%s:", token);
1690 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1694 strcat(entry, value);
1699 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1701 if (!setup_file_list)
1704 if (setup_file_list->token)
1705 free(setup_file_list->token);
1706 if (setup_file_list->value)
1707 free(setup_file_list->value);
1708 if (setup_file_list->next)
1709 freeSetupFileList(setup_file_list->next);
1710 free(setup_file_list);
1713 static struct SetupFileList *newSetupFileList(char *token, char *value)
1715 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1717 new->token = checked_malloc(strlen(token) + 1);
1718 strcpy(new->token, token);
1720 new->value = checked_malloc(strlen(value) + 1);
1721 strcpy(new->value, value);
1728 static char *getTokenValue(struct SetupFileList *setup_file_list,
1731 if (!setup_file_list)
1734 if (strcmp(setup_file_list->token, token) == 0)
1735 return setup_file_list->value;
1737 return getTokenValue(setup_file_list->next, token);
1740 static void setTokenValue(struct SetupFileList *setup_file_list,
1741 char *token, char *value)
1743 if (!setup_file_list)
1746 if (strcmp(setup_file_list->token, token) == 0)
1748 free(setup_file_list->value);
1749 setup_file_list->value = checked_malloc(strlen(value) + 1);
1750 strcpy(setup_file_list->value, value);
1752 else if (setup_file_list->next == NULL)
1753 setup_file_list->next = newSetupFileList(token, value);
1755 setTokenValue(setup_file_list->next, token, value);
1759 static void printSetupFileList(struct SetupFileList *setup_file_list)
1761 if (!setup_file_list)
1764 printf("token: '%s'\n", setup_file_list->token);
1765 printf("value: '%s'\n", setup_file_list->value);
1767 printSetupFileList(setup_file_list->next);
1771 static struct SetupFileList *loadSetupFileList(char *filename)
1774 char line[MAX_LINE_LEN];
1775 char *token, *value, *line_ptr;
1776 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1777 struct SetupFileList *first_valid_list_entry;
1781 if (!(file = fopen(filename, MODE_READ)))
1783 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1789 /* read next line of input file */
1790 if (!fgets(line, MAX_LINE_LEN, file))
1793 /* cut trailing comment or whitespace from input line */
1794 for (line_ptr = line; *line_ptr; line_ptr++)
1796 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1803 /* cut trailing whitespaces from input line */
1804 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1805 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1808 /* ignore empty lines */
1812 line_len = strlen(line);
1814 /* cut leading whitespaces from token */
1815 for (token = line; *token; token++)
1816 if (*token != ' ' && *token != '\t')
1819 /* find end of token */
1820 for (line_ptr = token; *line_ptr; line_ptr++)
1822 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1829 if (line_ptr < line + line_len)
1830 value = line_ptr + 1;
1834 /* cut leading whitespaces from value */
1835 for (; *value; value++)
1836 if (*value != ' ' && *value != '\t')
1839 if (*token && *value)
1840 setTokenValue(setup_file_list, token, value);
1845 first_valid_list_entry = setup_file_list->next;
1847 /* free empty list header */
1848 setup_file_list->next = NULL;
1849 freeSetupFileList(setup_file_list);
1851 if (first_valid_list_entry == NULL)
1852 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1854 return first_valid_list_entry;
1857 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1860 if (!setup_file_list)
1863 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1865 if (strcmp(setup_file_list->value, identifier) != 0)
1867 Error(ERR_WARN, "configuration file has wrong version");
1874 if (setup_file_list->next)
1875 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1878 Error(ERR_WARN, "configuration file has no version information");
1883 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1885 ldi->filename = NULL;
1886 ldi->fullpath = NULL;
1887 ldi->basepath = NULL;
1888 ldi->name = getStringCopy(ANONYMOUS_NAME);
1889 ldi->name_short = NULL;
1890 ldi->name_sorting = NULL;
1891 ldi->author = getStringCopy(ANONYMOUS_NAME);
1892 ldi->imported_from = NULL;
1894 ldi->first_level = 0;
1895 ldi->last_level = 0;
1896 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1897 ldi->level_group = FALSE;
1898 ldi->parent_link = FALSE;
1899 ldi->user_defined = FALSE;
1900 ldi->readonly = TRUE;
1902 ldi->class_desc = NULL;
1903 ldi->handicap_level = 0;
1905 ldi->cl_cursor = -1;
1907 ldi->node_parent = NULL;
1908 ldi->node_group = NULL;
1912 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1913 struct LevelDirInfo *parent)
1917 setLevelDirInfoToDefaults(ldi);
1921 /* first copy all values from the parent structure ... */
1924 /* ... then set all fields to default that cannot be inherited from parent.
1925 This is especially important for all those fields that can be set from
1926 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1927 calls 'free()' for all already set token values which requires that no
1928 other structure's pointer may point to them!
1931 ldi->filename = NULL;
1932 ldi->fullpath = NULL;
1933 ldi->basepath = NULL;
1934 ldi->name = getStringCopy(ANONYMOUS_NAME);
1935 ldi->name_short = NULL;
1936 ldi->name_sorting = NULL;
1937 ldi->author = getStringCopy(parent->author);
1938 ldi->imported_from = getStringCopy(parent->imported_from);
1940 ldi->level_group = FALSE;
1941 ldi->parent_link = FALSE;
1943 ldi->node_parent = parent;
1944 ldi->node_group = NULL;
1948 static void setSetupInfoToDefaults(struct SetupInfo *si)
1952 si->player_name = getStringCopy(getLoginName());
1955 si->sound_loops = TRUE;
1956 si->sound_music = TRUE;
1957 si->sound_simple = TRUE;
1959 si->double_buffering = TRUE;
1960 si->direct_draw = !si->double_buffering;
1961 si->scroll_delay = TRUE;
1962 si->soft_scrolling = TRUE;
1964 si->autorecord = TRUE;
1965 si->quick_doors = FALSE;
1966 si->team_mode = FALSE;
1967 si->handicap = TRUE;
1968 si->time_limit = TRUE;
1969 si->fullscreen = FALSE;
1971 for (i=0; i<MAX_PLAYERS; i++)
1973 si->input[i].use_joystick = FALSE;
1974 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1975 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1976 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1977 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1978 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1979 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1980 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1981 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1982 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1983 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1984 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1985 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1986 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1987 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1988 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1992 static void setSetupInfo(int token_nr, char *token_value)
1994 int token_type = token_info[token_nr].type;
1995 void *setup_value = token_info[token_nr].value;
1997 if (token_value == NULL)
2000 /* set setup field to corresponding token value */
2005 *(boolean *)setup_value = get_string_boolean_value(token_value);
2009 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
2013 *(int *)setup_value = get_string_integer_value(token_value);
2017 if (*(char **)setup_value != NULL)
2018 free(*(char **)setup_value);
2019 *(char **)setup_value = getStringCopy(token_value);
2027 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
2031 if (!setup_file_list)
2034 /* handle global setup values */
2036 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2037 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2040 /* handle player specific setup values */
2041 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2045 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2047 sii = setup.input[pnr];
2048 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2050 char full_token[100];
2052 sprintf(full_token, "%s%s", prefix, token_info[i].text);
2053 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
2055 setup.input[pnr] = sii;
2059 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
2061 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
2062 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
2065 if (entry1->parent_link || entry2->parent_link)
2066 compare_result = (entry1->parent_link ? -1 : +1);
2067 else if (entry1->sort_priority == entry2->sort_priority)
2069 char *name1 = getStringToLower(entry1->name_sorting);
2070 char *name2 = getStringToLower(entry2->name_sorting);
2072 compare_result = strcmp(name1, name2);
2077 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
2078 compare_result = entry1->sort_priority - entry2->sort_priority;
2080 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
2082 return compare_result;
2085 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
2087 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2089 setLevelDirInfoToDefaults(leveldir_new);
2091 leveldir_new->node_parent = node_parent;
2092 leveldir_new->parent_link = TRUE;
2094 leveldir_new->name = ".. (parent directory)";
2095 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2096 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2098 leveldir_new->filename = "..";
2099 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
2101 leveldir_new->sort_priority = node_parent->sort_priority;
2102 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2104 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
2107 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
2108 struct LevelDirInfo *node_parent,
2109 char *level_directory)
2112 struct dirent *dir_entry;
2113 boolean valid_entry_found = FALSE;
2115 if ((dir = opendir(level_directory)) == NULL)
2117 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2121 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2123 struct SetupFileList *setup_file_list = NULL;
2124 struct stat file_status;
2125 char *directory_name = dir_entry->d_name;
2126 char *directory_path = getPath2(level_directory, directory_name);
2127 char *filename = NULL;
2129 /* skip entries for current and parent directory */
2130 if (strcmp(directory_name, ".") == 0 ||
2131 strcmp(directory_name, "..") == 0)
2133 free(directory_path);
2137 /* find out if directory entry is itself a directory */
2138 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2139 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2141 free(directory_path);
2145 filename = getPath2(directory_path, LEVELINFO_FILENAME);
2146 setup_file_list = loadSetupFileList(filename);
2148 if (setup_file_list)
2150 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2153 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
2154 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
2156 /* set all structure fields according to the token/value pairs */
2157 ldi = *leveldir_new;
2158 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2159 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2160 *leveldir_new = ldi;
2162 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2164 if (leveldir_new->name_short == NULL)
2165 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2167 if (leveldir_new->name_sorting == NULL)
2168 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2170 leveldir_new->filename = getStringCopy(directory_name);
2172 if (node_parent == NULL) /* top level group */
2174 leveldir_new->basepath = level_directory;
2175 leveldir_new->fullpath = leveldir_new->filename;
2177 else /* sub level group */
2179 leveldir_new->basepath = node_parent->basepath;
2180 leveldir_new->fullpath = getPath2(node_parent->fullpath,
2184 if (leveldir_new->levels < 1)
2185 leveldir_new->levels = 1;
2187 leveldir_new->last_level =
2188 leveldir_new->first_level + leveldir_new->levels - 1;
2190 leveldir_new->user_defined =
2191 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2193 leveldir_new->color = LEVELCOLOR(leveldir_new);
2194 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2196 leveldir_new->handicap_level = /* set handicap to default value */
2197 (leveldir_new->user_defined ?
2198 leveldir_new->last_level :
2199 leveldir_new->first_level);
2201 pushLevelDirInfo(node_first, leveldir_new);
2203 freeSetupFileList(setup_file_list);
2204 valid_entry_found = TRUE;
2206 if (leveldir_new->level_group)
2208 /* create node to link back to current level directory */
2209 createParentLevelDirNode(leveldir_new);
2211 /* step into sub-directory and look for more level series */
2212 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2213 leveldir_new, directory_path);
2217 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2219 free(directory_path);
2225 if (!valid_entry_found)
2226 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2230 void LoadLevelInfo()
2232 InitUserLevelDirectory(getLoginName());
2234 DrawInitText("Loading level series:", 120, FC_GREEN);
2236 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2237 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
2239 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2241 if (leveldir_first == NULL)
2242 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2244 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
2247 dumpLevelDirInfo(leveldir_first, 0);
2251 static void SaveUserLevelInfo()
2257 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2259 if (!(file = fopen(filename, MODE_WRITE)))
2261 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2266 /* always start with reliable default values */
2267 setLevelDirInfoToDefaults(&ldi);
2269 ldi.name = getLoginName();
2270 ldi.author = getRealName();
2272 ldi.first_level = 1;
2273 ldi.sort_priority = LEVELCLASS_USER_START;
2274 ldi.readonly = FALSE;
2276 fprintf(file, "%s\n\n",
2277 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
2279 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2280 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
2281 i != LEVELINFO_TOKEN_NAME_SORTING &&
2282 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2283 fprintf(file, "%s\n", getSetupLine("", i));
2288 SetFilePermissions_Setup(filename);
2294 struct SetupFileList *setup_file_list = NULL;
2296 /* always start with reliable default values */
2297 setSetupInfoToDefaults(&setup);
2299 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2301 setup_file_list = loadSetupFileList(filename);
2303 if (setup_file_list)
2305 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
2306 decodeSetupFileList(setup_file_list);
2308 setup.direct_draw = !setup.double_buffering;
2310 freeSetupFileList(setup_file_list);
2312 /* needed to work around problems with fixed length strings */
2313 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
2314 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
2315 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
2317 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2319 strcpy(new_name, setup.player_name);
2320 free(setup.player_name);
2321 setup.player_name = new_name;
2325 Error(ERR_WARN, "using default setup values");
2330 static char *getSetupLine(char *prefix, int token_nr)
2333 static char entry[MAX_LINE_LEN];
2334 int token_type = token_info[token_nr].type;
2335 void *setup_value = token_info[token_nr].value;
2336 char *token_text = token_info[token_nr].text;
2338 /* start with the prefix, token and some spaces to format output line */
2339 sprintf(entry, "%s%s:", prefix, token_text);
2340 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2343 /* continue with the token's value (which can have different types) */
2347 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2351 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2356 Key key = *(Key *)setup_value;
2357 char *keyname = getKeyNameFromKey(key);
2359 strcat(entry, getX11KeyNameFromKey(key));
2360 for (i=strlen(entry); i<50; i++)
2363 /* add comment, if useful */
2364 if (strcmp(keyname, "(undefined)") != 0 &&
2365 strcmp(keyname, "(unknown)") != 0)
2367 strcat(entry, "# ");
2368 strcat(entry, keyname);
2375 char buffer[MAX_LINE_LEN];
2377 sprintf(buffer, "%d", *(int *)setup_value);
2378 strcat(entry, buffer);
2383 strcat(entry, *(char **)setup_value);
2399 InitUserDataDirectory();
2401 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2403 if (!(file = fopen(filename, MODE_WRITE)))
2405 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2410 fprintf(file, "%s\n",
2411 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2412 fprintf(file, "\n");
2414 /* handle global setup values */
2416 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2418 fprintf(file, "%s\n", getSetupLine("", i));
2420 /* just to make things nicer :) */
2421 if (i == SETUP_TOKEN_PLAYER_NAME)
2422 fprintf(file, "\n");
2425 /* handle player specific setup values */
2426 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2430 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2431 fprintf(file, "\n");
2433 sii = setup.input[pnr];
2434 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2435 fprintf(file, "%s\n", getSetupLine(prefix, i));
2441 SetFilePermissions_Setup(filename);
2444 void LoadLevelSetup_LastSeries()
2447 struct SetupFileList *level_setup_list = NULL;
2449 /* always start with reliable default values */
2450 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2452 /* ----------------------------------------------------------------------- */
2453 /* ~/.rocksndiamonds/levelsetup.conf */
2454 /* ----------------------------------------------------------------------- */
2456 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2458 if ((level_setup_list = loadSetupFileList(filename)))
2460 char *last_level_series =
2461 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2463 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2464 if (leveldir_current == NULL)
2465 leveldir_current = leveldir_first;
2467 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2469 freeSetupFileList(level_setup_list);
2472 Error(ERR_WARN, "using default setup values");
2477 void SaveLevelSetup_LastSeries()
2480 char *level_subdir = leveldir_current->filename;
2483 /* ----------------------------------------------------------------------- */
2484 /* ~/.rocksndiamonds/levelsetup.conf */
2485 /* ----------------------------------------------------------------------- */
2487 InitUserDataDirectory();
2489 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2491 if (!(file = fopen(filename, MODE_WRITE)))
2493 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2498 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2499 LEVELSETUP_COOKIE));
2500 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2506 SetFilePermissions_Setup(filename);
2509 static void checkSeriesInfo()
2511 static char *level_directory = NULL;
2513 struct dirent *dir_entry;
2515 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2517 level_directory = getPath2((leveldir_current->user_defined ?
2518 getUserLevelDir("") :
2519 options.level_directory),
2520 leveldir_current->fullpath);
2522 if ((dir = opendir(level_directory)) == NULL)
2524 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2528 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2530 if (strlen(dir_entry->d_name) > 4 &&
2531 dir_entry->d_name[3] == '.' &&
2532 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2534 char levelnum_str[4];
2537 strncpy(levelnum_str, dir_entry->d_name, 3);
2538 levelnum_str[3] = '\0';
2540 levelnum_value = atoi(levelnum_str);
2542 if (levelnum_value < leveldir_current->first_level)
2544 Error(ERR_WARN, "additional level %d found", levelnum_value);
2545 leveldir_current->first_level = levelnum_value;
2547 else if (levelnum_value > leveldir_current->last_level)
2549 Error(ERR_WARN, "additional level %d found", levelnum_value);
2550 leveldir_current->last_level = levelnum_value;
2558 void LoadLevelSetup_SeriesInfo()
2561 struct SetupFileList *level_setup_list = NULL;
2562 char *level_subdir = leveldir_current->filename;
2564 /* always start with reliable default values */
2565 level_nr = leveldir_current->first_level;
2567 checkSeriesInfo(leveldir_current);
2569 /* ----------------------------------------------------------------------- */
2570 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2571 /* ----------------------------------------------------------------------- */
2573 level_subdir = leveldir_current->filename;
2575 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2577 if ((level_setup_list = loadSetupFileList(filename)))
2581 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2585 level_nr = atoi(token_value);
2587 if (level_nr < leveldir_current->first_level)
2588 level_nr = leveldir_current->first_level;
2589 if (level_nr > leveldir_current->last_level)
2590 level_nr = leveldir_current->last_level;
2593 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2597 int level_nr = atoi(token_value);
2599 if (level_nr < leveldir_current->first_level)
2600 level_nr = leveldir_current->first_level;
2601 if (level_nr > leveldir_current->last_level + 1)
2602 level_nr = leveldir_current->last_level;
2604 if (leveldir_current->user_defined)
2605 level_nr = leveldir_current->last_level;
2607 leveldir_current->handicap_level = level_nr;
2610 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2612 freeSetupFileList(level_setup_list);
2615 Error(ERR_WARN, "using default setup values");
2620 void SaveLevelSetup_SeriesInfo()
2623 char *level_subdir = leveldir_current->filename;
2624 char *level_nr_str = int2str(level_nr, 0);
2625 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2628 /* ----------------------------------------------------------------------- */
2629 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2630 /* ----------------------------------------------------------------------- */
2632 InitLevelSetupDirectory(level_subdir);
2634 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2636 if (!(file = fopen(filename, MODE_WRITE)))
2638 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2643 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2644 LEVELSETUP_COOKIE));
2645 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2647 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2648 handicap_level_str));
2653 SetFilePermissions_Setup(filename);