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 CHUNK_ID_LEN 4 /* IFF style chunk id length */
26 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
27 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
28 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
29 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
30 #define LEVEL_HEADER_UNUSED 15 /* unused level header bytes */
31 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
32 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
33 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
34 #define TAPE_HEADER_UNUSED 7 /* unused tape header bytes */
36 /* file identifier strings */
37 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
38 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
39 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
40 #define SETUP_COOKIE "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
41 #define LEVELSETUP_COOKIE "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
42 #define LEVELINFO_COOKIE "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
44 /* file names and filename extensions */
45 #if !defined(PLATFORM_MSDOS)
46 #define LEVELSETUP_DIRECTORY "levelsetup"
47 #define SETUP_FILENAME "setup.conf"
48 #define LEVELSETUP_FILENAME "levelsetup.conf"
49 #define LEVELINFO_FILENAME "levelinfo.conf"
50 #define LEVELFILE_EXTENSION "level"
51 #define TAPEFILE_EXTENSION "tape"
52 #define SCOREFILE_EXTENSION "score"
54 #define LEVELSETUP_DIRECTORY "lvlsetup"
55 #define SETUP_FILENAME "setup.cnf"
56 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
57 #define LEVELINFO_FILENAME "lvlinfo.cnf"
58 #define LEVELFILE_EXTENSION "lvl"
59 #define TAPEFILE_EXTENSION "tap"
60 #define SCOREFILE_EXTENSION "sco"
63 /* sort priorities of level series (also used as level series classes) */
64 #define LEVELCLASS_TUTORIAL_START 10
65 #define LEVELCLASS_TUTORIAL_END 99
66 #define LEVELCLASS_CLASSICS_START 100
67 #define LEVELCLASS_CLASSICS_END 199
68 #define LEVELCLASS_CONTRIBUTION_START 200
69 #define LEVELCLASS_CONTRIBUTION_END 299
70 #define LEVELCLASS_USER_START 300
71 #define LEVELCLASS_USER_END 399
72 #define LEVELCLASS_BD_START 400
73 #define LEVELCLASS_BD_END 499
74 #define LEVELCLASS_EM_START 500
75 #define LEVELCLASS_EM_END 599
76 #define LEVELCLASS_SP_START 600
77 #define LEVELCLASS_SP_END 699
78 #define LEVELCLASS_DX_START 700
79 #define LEVELCLASS_DX_END 799
81 #define LEVELCLASS_TUTORIAL LEVELCLASS_TUTORIAL_START
82 #define LEVELCLASS_CLASSICS LEVELCLASS_CLASSICS_START
83 #define LEVELCLASS_CONTRIBUTION LEVELCLASS_CONTRIBUTION_START
84 #define LEVELCLASS_USER LEVELCLASS_USER_START
85 #define LEVELCLASS_BD LEVELCLASS_BD_START
86 #define LEVELCLASS_EM LEVELCLASS_EM_START
87 #define LEVELCLASS_SP LEVELCLASS_SP_START
88 #define LEVELCLASS_DX LEVELCLASS_DX_START
90 #define LEVELCLASS_UNDEFINED 999
92 #define NUM_LEVELCLASS_DESC 8
93 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
105 #define IS_LEVELCLASS_TUTORIAL(p) \
106 ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
107 (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
108 #define IS_LEVELCLASS_CLASSICS(p) \
109 ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
110 (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
111 #define IS_LEVELCLASS_CONTRIBUTION(p) \
112 ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
113 (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
114 #define IS_LEVELCLASS_USER(p) \
115 ((p)->sort_priority >= LEVELCLASS_USER_START && \
116 (p)->sort_priority <= LEVELCLASS_USER_END)
117 #define IS_LEVELCLASS_BD(p) \
118 ((p)->sort_priority >= LEVELCLASS_BD_START && \
119 (p)->sort_priority <= LEVELCLASS_BD_END)
120 #define IS_LEVELCLASS_EM(p) \
121 ((p)->sort_priority >= LEVELCLASS_EM_START && \
122 (p)->sort_priority <= LEVELCLASS_EM_END)
123 #define IS_LEVELCLASS_SP(p) \
124 ((p)->sort_priority >= LEVELCLASS_SP_START && \
125 (p)->sort_priority <= LEVELCLASS_SP_END)
126 #define IS_LEVELCLASS_DX(p) \
127 ((p)->sort_priority >= LEVELCLASS_DX_START && \
128 (p)->sort_priority <= LEVELCLASS_DX_END)
130 #define LEVELCLASS(n) (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
131 IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
132 IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
133 IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
134 IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
135 IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
136 IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
137 IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
138 LEVELCLASS_UNDEFINED)
140 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
141 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
142 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
143 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
144 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
145 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
146 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
147 IS_LEVELCLASS_USER(n) ? FC_RED : \
150 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
151 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
152 IS_LEVELCLASS_BD(n) ? 2 : \
153 IS_LEVELCLASS_EM(n) ? 3 : \
154 IS_LEVELCLASS_SP(n) ? 4 : \
155 IS_LEVELCLASS_DX(n) ? 5 : \
156 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
157 IS_LEVELCLASS_USER(n) ? 7 : \
160 char *getLevelClassDescription(struct LevelDirInfo *ldi)
162 int position = ldi->sort_priority / 100;
164 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
165 return levelclass_desc[position];
167 return "Unknown Level Class";
170 static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */
171 static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */
173 static char *getUserLevelDir(char *level_subdir)
175 static char *userlevel_dir = NULL;
176 char *data_dir = getUserDataDir();
177 char *userlevel_subdir = LEVELS_DIRECTORY;
182 if (strlen(level_subdir) > 0)
183 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
185 userlevel_dir = getPath2(data_dir, userlevel_subdir);
187 return userlevel_dir;
190 static char *getTapeDir(char *level_subdir)
192 static char *tape_dir = NULL;
193 char *data_dir = getUserDataDir();
194 char *tape_subdir = TAPES_DIRECTORY;
199 if (strlen(level_subdir) > 0)
200 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
202 tape_dir = getPath2(data_dir, tape_subdir);
207 static char *getScoreDir(char *level_subdir)
209 static char *score_dir = NULL;
210 char *data_dir = options.rw_base_directory;
211 char *score_subdir = SCORES_DIRECTORY;
216 if (strlen(level_subdir) > 0)
217 score_dir = getPath3(data_dir, score_subdir, level_subdir);
219 score_dir = getPath2(data_dir, score_subdir);
224 static char *getLevelSetupDir(char *level_subdir)
226 static char *levelsetup_dir = NULL;
227 char *data_dir = getUserDataDir();
228 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
231 free(levelsetup_dir);
233 if (strlen(level_subdir) > 0)
234 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
236 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
238 return levelsetup_dir;
241 static char *getLevelFilename(int nr)
243 static char *filename = NULL;
244 char basename[MAX_FILENAME_LEN];
246 if (filename != NULL)
249 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
250 filename = getPath3((leveldir_current->user_defined ?
251 getUserLevelDir("") :
252 options.level_directory),
253 leveldir_current->fullpath,
259 static char *getTapeFilename(int nr)
261 static char *filename = NULL;
262 char basename[MAX_FILENAME_LEN];
264 if (filename != NULL)
267 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
268 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
273 static char *getScoreFilename(int nr)
275 static char *filename = NULL;
276 char basename[MAX_FILENAME_LEN];
278 if (filename != NULL)
281 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
282 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
287 static void InitTapeDirectory(char *level_subdir)
289 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
290 createDirectory(getTapeDir(""), "main tape", PERMS_PRIVATE);
291 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
294 static void InitScoreDirectory(char *level_subdir)
296 createDirectory(getScoreDir(""), "main score", PERMS_PUBLIC);
297 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
300 static void InitUserLevelDirectory(char *level_subdir)
302 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
304 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
305 createDirectory(getUserLevelDir(""), "main user level", PERMS_PRIVATE);
306 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
312 static void InitLevelSetupDirectory(char *level_subdir)
314 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
315 createDirectory(getLevelSetupDir(""), "main level setup", PERMS_PRIVATE);
316 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
319 static void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
321 int file_version_major, file_version_minor, file_version_patch;
322 int game_version_major, game_version_minor, game_version_patch;
324 file_version_major = fgetc(file);
325 file_version_minor = fgetc(file);
326 file_version_patch = fgetc(file);
327 fgetc(file); /* not used */
329 game_version_major = fgetc(file);
330 game_version_minor = fgetc(file);
331 game_version_patch = fgetc(file);
332 fgetc(file); /* not used */
334 *file_version = VERSION_IDENT(file_version_major,
338 *game_version = VERSION_IDENT(game_version_major,
343 static void WriteChunk_VERS(FILE *file, int file_version, int game_version)
345 int file_version_major = VERSION_MAJOR(file_version);
346 int file_version_minor = VERSION_MINOR(file_version);
347 int file_version_patch = VERSION_PATCH(file_version);
348 int game_version_major = VERSION_MAJOR(game_version);
349 int game_version_minor = VERSION_MINOR(game_version);
350 int game_version_patch = VERSION_PATCH(game_version);
352 fputc(file_version_major, file);
353 fputc(file_version_minor, file);
354 fputc(file_version_patch, file);
355 fputc(0, file); /* not used */
357 fputc(game_version_major, file);
358 fputc(game_version_minor, file);
359 fputc(game_version_patch, file);
360 fputc(0, file); /* not used */
363 static void setLevelInfoToDefaults()
367 level.file_version = FILE_VERSION_ACTUAL;
368 level.game_version = GAME_VERSION_ACTUAL;
370 level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
371 level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
372 level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
374 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
375 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
377 for(x=0; x<MAX_LEV_FIELDX; x++)
378 for(y=0; y<MAX_LEV_FIELDY; y++)
379 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
382 level.gems_needed = 0;
383 level.amoeba_speed = 10;
384 level.time_magic_wall = 10;
385 level.time_wheel = 10;
386 level.time_light = 10;
387 level.time_timegate = 10;
388 level.amoeba_content = EL_DIAMANT;
389 level.double_speed = FALSE;
390 level.gravity = FALSE;
392 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
393 level.name[i] = '\0';
394 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
395 level.author[i] = '\0';
397 strcpy(level.name, NAMELESS_LEVEL_NAME);
398 strcpy(level.author, ANONYMOUS_NAME);
400 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
403 level.num_yam_contents = STD_ELEMENT_CONTENTS;
404 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
407 level.yam_content[i][x][y] =
408 (i < STD_ELEMENT_CONTENTS ? EL_FELSBROCKEN : EL_LEERRAUM);
410 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
411 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
412 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
414 BorderElement = EL_BETON;
416 /* try to determine better author name than 'anonymous' */
417 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
419 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
420 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
424 switch (LEVELCLASS(leveldir_current))
426 case LEVELCLASS_TUTORIAL:
427 strcpy(level.author, PROGRAM_AUTHOR_STRING);
430 case LEVELCLASS_CONTRIBUTION:
431 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
432 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
435 case LEVELCLASS_USER:
436 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
437 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
441 /* keep default value */
447 static int checkLevelElement(int element)
449 if (element >= EL_FIRST_RUNTIME_EL)
451 Error(ERR_WARN, "invalid level element %d", element);
452 element = EL_CHAR_FRAGE;
458 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
460 ReadChunk_VERS(file, &(level->file_version), &(level->game_version));
465 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
469 lev_fieldx = level->fieldx = fgetc(file);
470 lev_fieldy = level->fieldy = fgetc(file);
472 level->time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
473 level->gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
475 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
476 level->name[i] = fgetc(file);
477 level->name[MAX_LEVEL_NAME_LEN] = 0;
479 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
480 level->score[i] = fgetc(file);
482 level->num_yam_contents = STD_ELEMENT_CONTENTS;
483 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
486 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
488 level->amoeba_speed = fgetc(file);
489 level->time_magic_wall = fgetc(file);
490 level->time_wheel = fgetc(file);
491 level->amoeba_content = checkLevelElement(fgetc(file));
492 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
493 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
495 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
497 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
502 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
506 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
507 level->author[i] = fgetc(file);
508 level->author[MAX_LEVEL_NAME_LEN] = 0;
513 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
517 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
518 int chunk_size_expected = header_size + content_size;
520 /* Note: "chunk_size" was wrong before version 2.0 when elements are
521 stored with 16-bit encoding (and should be twice as big then).
522 Even worse, playfield data was stored 16-bit when only yamyam content
523 contained 16-bit elements and vice versa. */
525 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
526 chunk_size_expected += content_size;
528 if (chunk_size_expected != chunk_size)
530 ReadUnusedBytesFromFile(file, chunk_size);
531 return chunk_size_expected;
535 level->num_yam_contents = fgetc(file);
539 /* correct invalid number of content fields -- should never happen */
540 if (level->num_yam_contents < 1 ||
541 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
542 level->num_yam_contents = STD_ELEMENT_CONTENTS;
544 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
547 level->yam_content[i][x][y] =
548 checkLevelElement(level->encoding_16bit_field ?
549 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
554 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
557 int chunk_size_expected = level->fieldx * level->fieldy;
559 /* Note: "chunk_size" was wrong before version 2.0 when elements are
560 stored with 16-bit encoding (and should be twice as big then).
561 Even worse, playfield data was stored 16-bit when only yamyam content
562 contained 16-bit elements and vice versa. */
564 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
565 chunk_size_expected *= 2;
567 if (chunk_size_expected != chunk_size)
569 ReadUnusedBytesFromFile(file, chunk_size);
570 return chunk_size_expected;
573 for(y=0; y<level->fieldy; y++)
574 for(x=0; x<level->fieldx; x++)
575 Feld[x][y] = Ur[x][y] =
576 checkLevelElement(level->encoding_16bit_field ?
577 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
582 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
586 int num_contents, content_xsize, content_ysize;
587 int content_array[MAX_ELEMENT_CONTENTS][3][3];
589 element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
590 num_contents = fgetc(file);
591 content_xsize = fgetc(file);
592 content_ysize = fgetc(file);
593 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
595 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
598 content_array[i][x][y] =
599 checkLevelElement(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
601 /* correct invalid number of content fields -- should never happen */
602 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
603 num_contents = STD_ELEMENT_CONTENTS;
605 if (element == EL_MAMPFER)
607 level->num_yam_contents = num_contents;
609 for(i=0; i<num_contents; i++)
612 level->yam_content[i][x][y] = content_array[i][x][y];
614 else if (element == EL_AMOEBE_BD)
616 level->amoeba_content = content_array[0][0][0];
620 Error(ERR_WARN, "cannot load content for element '%d'", element);
626 void LoadLevel(int level_nr)
628 char *filename = getLevelFilename(level_nr);
629 char cookie[MAX_LINE_LEN];
630 char chunk_name[CHUNK_ID_LEN + 1];
634 /* always start with reliable default values */
635 setLevelInfoToDefaults();
637 if (!(file = fopen(filename, MODE_READ)))
639 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
643 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
644 if (strcmp(chunk_name, "RND1") == 0)
646 getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN); /* not used */
648 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
649 if (strcmp(chunk_name, "CAVE") != 0)
651 Error(ERR_WARN, "unknown format of level file '%s'", filename);
656 else /* check for pre-2.0 file format with cookie string */
658 strcpy(cookie, chunk_name);
659 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
660 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
661 cookie[strlen(cookie) - 1] = '\0';
663 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
665 Error(ERR_WARN, "unknown format of level file '%s'", filename);
670 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
672 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
678 if (level.file_version < FILE_VERSION_1_2)
680 /* level files from versions before 1.2.0 without chunk structure */
681 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
682 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
690 int (*loader)(FILE *, int, struct LevelInfo *);
694 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
695 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
696 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
697 { "CONT", -1, LoadLevel_CONT },
698 { "BODY", -1, LoadLevel_BODY },
699 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
703 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
707 while (chunk_info[i].name != NULL &&
708 strcmp(chunk_name, chunk_info[i].name) != 0)
711 if (chunk_info[i].name == NULL)
713 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
714 chunk_name, filename);
715 ReadUnusedBytesFromFile(file, chunk_size);
717 else if (chunk_info[i].size != -1 &&
718 chunk_info[i].size != chunk_size)
720 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
721 chunk_size, chunk_name, filename);
722 ReadUnusedBytesFromFile(file, chunk_size);
726 /* call function to load this level chunk */
727 int chunk_size_expected =
728 (chunk_info[i].loader)(file, chunk_size, &level);
730 /* the size of some chunks cannot be checked before reading other
731 chunks first (like "HEAD" and "BODY") that contain some header
732 information, so check them here */
733 if (chunk_size_expected != chunk_size)
735 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
736 chunk_size, chunk_name, filename);
744 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
745 IS_LEVELCLASS_USER(leveldir_current))
747 /* for user contributed and private levels, use the version of
748 the game engine the levels were created for */
749 level.game_version = level.file_version;
751 /* player was faster than monsters in pre-1.0 levels */
752 if (level.file_version == FILE_VERSION_1_0)
754 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
755 Error(ERR_WARN, "using high speed movement for player");
756 level.double_speed = TRUE;
761 /* always use the latest version of the game engine for all but
762 user contributed and private levels */
763 level.game_version = GAME_VERSION_ACTUAL;
766 /* determine border element for this level */
770 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
774 fputc(level->fieldx, file);
775 fputc(level->fieldy, file);
777 putFile16BitInteger(file, level->time, BYTE_ORDER_BIG_ENDIAN);
778 putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
780 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
781 fputc(level->name[i], file);
783 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
784 fputc(level->score[i], file);
786 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
789 fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
790 level->yam_content[i][x][y]),
792 fputc(level->amoeba_speed, file);
793 fputc(level->time_magic_wall, file);
794 fputc(level->time_wheel, file);
795 fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
797 fputc((level->double_speed ? 1 : 0), file);
798 fputc((level->gravity ? 1 : 0), file);
800 fputc((level->encoding_16bit_field ? 1 : 0), file);
802 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
805 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
809 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
810 fputc(level->author[i], file);
814 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
818 fputc(EL_MAMPFER, file);
819 fputc(level->num_yam_contents, file);
823 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
826 if (level->encoding_16bit_field)
827 putFile16BitInteger(file, level->yam_content[i][x][y],
828 BYTE_ORDER_BIG_ENDIAN);
830 fputc(level->yam_content[i][x][y], file);
834 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
838 for(y=0; y<level->fieldy; y++)
839 for(x=0; x<level->fieldx; x++)
840 if (level->encoding_16bit_field)
841 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
843 fputc(Ur[x][y], file);
846 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
849 int num_contents, content_xsize, content_ysize;
850 int content_array[MAX_ELEMENT_CONTENTS][3][3];
852 if (element == EL_MAMPFER)
854 num_contents = level->num_yam_contents;
858 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
861 content_array[i][x][y] = level->yam_content[i][x][y];
863 else if (element == EL_AMOEBE_BD)
869 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
872 content_array[i][x][y] = EL_LEERRAUM;
873 content_array[0][0][0] = level->amoeba_content;
877 /* chunk header already written -- write empty chunk data */
878 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
880 Error(ERR_WARN, "cannot save content for element '%d'", element);
884 putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
885 fputc(num_contents, file);
886 fputc(content_xsize, file);
887 fputc(content_ysize, file);
889 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
891 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
894 putFile16BitInteger(file, content_array[i][x][y],
895 BYTE_ORDER_BIG_ENDIAN);
898 void SaveLevel(int level_nr)
901 char *filename = getLevelFilename(level_nr);
905 if (!(file = fopen(filename, MODE_WRITE)))
907 Error(ERR_WARN, "cannot save level file '%s'", filename);
912 /* check level field for 16-bit elements */
913 level.encoding_16bit_field = FALSE;
914 for(y=0; y<level.fieldy; y++)
915 for(x=0; x<level.fieldx; x++)
917 level.encoding_16bit_field = TRUE;
919 /* check yamyam content for 16-bit elements */
920 level.encoding_16bit_yamyam = FALSE;
921 for(i=0; i<level.num_yam_contents; i++)
924 if (level.yam_content[i][x][y] > 255)
925 level.encoding_16bit_yamyam = TRUE;
927 /* check amoeba content for 16-bit elements */
928 level.encoding_16bit_amoeba = FALSE;
929 if (level.amoeba_content > 255)
930 level.encoding_16bit_amoeba = TRUE;
933 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
935 putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
936 putFileChunk(file, "CAVE", CHUNK_SIZE_NONE, BYTE_ORDER_BIG_ENDIAN);
938 putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
939 WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
941 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
942 SaveLevel_HEAD(file, &level);
944 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
945 SaveLevel_AUTH(file, &level);
947 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
948 SaveLevel_BODY(file, &level);
950 if (level.encoding_16bit_yamyam ||
951 level.num_yam_contents != STD_ELEMENT_CONTENTS)
953 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
954 SaveLevel_CNT2(file, &level, EL_MAMPFER);
957 if (level.encoding_16bit_amoeba)
959 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
960 SaveLevel_CNT2(file, &level, EL_AMOEBE_BD);
965 SetFilePermissions(filename, PERMS_PRIVATE);
968 static void setTapeInfoToDefaults()
972 /* always start with reliable default values (empty tape) */
973 tape.file_version = FILE_VERSION_ACTUAL;
974 tape.game_version = GAME_VERSION_ACTUAL;
977 /* default values (also for pre-1.2 tapes) with only the first player */
978 tape.player_participates[0] = TRUE;
979 for(i=1; i<MAX_PLAYERS; i++)
980 tape.player_participates[i] = FALSE;
982 /* at least one (default: the first) player participates in every tape */
983 tape.num_participating_players = 1;
985 tape.level_nr = level_nr;
987 tape.changed = FALSE;
989 tape.recording = FALSE;
990 tape.playing = FALSE;
991 tape.pausing = FALSE;
994 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
996 ReadChunk_VERS(file, &(tape->file_version), &(tape->game_version));
1001 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1005 tape->random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1006 tape->date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1007 tape->length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1009 /* read header fields that are new since version 1.2 */
1010 if (tape->file_version >= FILE_VERSION_1_2)
1012 byte store_participating_players = fgetc(file);
1014 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1016 /* since version 1.2, tapes store which players participate in the tape */
1017 tape->num_participating_players = 0;
1018 for(i=0; i<MAX_PLAYERS; i++)
1020 tape->player_participates[i] = FALSE;
1022 if (store_participating_players & (1 << i))
1024 tape->player_participates[i] = TRUE;
1025 tape->num_participating_players++;
1033 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1036 int chunk_size_expected =
1037 (tape->num_participating_players + 1) * tape->length;
1039 if (chunk_size_expected != chunk_size)
1041 ReadUnusedBytesFromFile(file, chunk_size);
1042 return chunk_size_expected;
1045 for(i=0; i<tape->length; i++)
1047 if (i >= MAX_TAPELEN)
1050 for(j=0; j<MAX_PLAYERS; j++)
1052 tape->pos[i].action[j] = MV_NO_MOVING;
1054 if (tape->player_participates[j])
1055 tape->pos[i].action[j] = fgetc(file);
1058 tape->pos[i].delay = fgetc(file);
1060 if (tape->file_version == FILE_VERSION_1_0)
1062 /* eliminate possible diagonal moves in old tapes */
1063 /* this is only for backward compatibility */
1065 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1066 byte action = tape->pos[i].action[0];
1067 int k, num_moves = 0;
1071 if (action & joy_dir[k])
1073 tape->pos[i + num_moves].action[0] = joy_dir[k];
1075 tape->pos[i + num_moves].delay = 0;
1084 tape->length += num_moves;
1087 else if (tape->file_version < FILE_VERSION_2_0)
1089 if (tape->pos[i].delay > 1)
1092 tape->pos[i + 1] = tape->pos[i];
1093 tape->pos[i + 1].delay = 1;
1096 for(j=0; j<MAX_PLAYERS; j++)
1097 tape->pos[i].action[j] = MV_NO_MOVING;
1098 tape->pos[i].delay--;
1109 if (i != tape->length)
1110 chunk_size = (tape->num_participating_players + 1) * i;
1115 void LoadTape(int level_nr)
1117 char *filename = getTapeFilename(level_nr);
1118 char cookie[MAX_LINE_LEN];
1119 char chunk_name[CHUNK_ID_LEN + 1];
1123 /* always start with reliable default values */
1124 setTapeInfoToDefaults();
1126 if (!(file = fopen(filename, MODE_READ)))
1129 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
1130 if (strcmp(chunk_name, "RND1") == 0)
1132 getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN); /* not used */
1134 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
1135 if (strcmp(chunk_name, "TAPE") != 0)
1137 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1142 else /* check for pre-2.0 file format with cookie string */
1144 strcpy(cookie, chunk_name);
1145 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1146 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1147 cookie[strlen(cookie) - 1] = '\0';
1149 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1151 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1156 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1158 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1164 tape.game_version = tape.file_version;
1166 if (tape.file_version < FILE_VERSION_1_2)
1168 /* tape files from versions before 1.2.0 without chunk structure */
1169 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1170 LoadTape_BODY(file, 2 * tape.length, &tape);
1178 int (*loader)(FILE *, int, struct TapeInfo *);
1182 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1183 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1184 { "BODY", -1, LoadTape_BODY },
1188 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
1192 while (chunk_info[i].name != NULL &&
1193 strcmp(chunk_name, chunk_info[i].name) != 0)
1196 if (chunk_info[i].name == NULL)
1198 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1199 chunk_name, filename);
1200 ReadUnusedBytesFromFile(file, chunk_size);
1202 else if (chunk_info[i].size != -1 &&
1203 chunk_info[i].size != chunk_size)
1205 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1206 chunk_size, chunk_name, filename);
1207 ReadUnusedBytesFromFile(file, chunk_size);
1211 /* call function to load this tape chunk */
1212 int chunk_size_expected =
1213 (chunk_info[i].loader)(file, chunk_size, &tape);
1215 /* the size of some chunks cannot be checked before reading other
1216 chunks first (like "HEAD" and "BODY") that contain some header
1217 information, so check them here */
1218 if (chunk_size_expected != chunk_size)
1220 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1221 chunk_size, chunk_name, filename);
1229 tape.length_seconds = GetTapeLength();
1232 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1235 byte store_participating_players = 0;
1237 /* set bits for participating players for compact storage */
1238 for(i=0; i<MAX_PLAYERS; i++)
1239 if (tape->player_participates[i])
1240 store_participating_players |= (1 << i);
1242 putFile32BitInteger(file, tape->random_seed, BYTE_ORDER_BIG_ENDIAN);
1243 putFile32BitInteger(file, tape->date, BYTE_ORDER_BIG_ENDIAN);
1244 putFile32BitInteger(file, tape->length, BYTE_ORDER_BIG_ENDIAN);
1246 fputc(store_participating_players, file);
1248 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1251 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1255 for(i=0; i<tape->length; i++)
1257 for(j=0; j<MAX_PLAYERS; j++)
1258 if (tape->player_participates[j])
1259 fputc(tape->pos[i].action[j], file);
1261 fputc(tape->pos[i].delay, file);
1265 void SaveTape(int level_nr)
1268 char *filename = getTapeFilename(level_nr);
1270 boolean new_tape = TRUE;
1271 int num_participating_players = 0;
1272 int body_chunk_size;
1274 InitTapeDirectory(leveldir_current->filename);
1276 /* if a tape still exists, ask to overwrite it */
1277 if (access(filename, F_OK) == 0)
1280 if (!Request("Replace old tape ?", REQ_ASK))
1284 if (!(file = fopen(filename, MODE_WRITE)))
1286 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1290 /* count number of participating players */
1291 for(i=0; i<MAX_PLAYERS; i++)
1292 if (tape.player_participates[i])
1293 num_participating_players++;
1295 body_chunk_size = (num_participating_players + 1) * tape.length;
1297 putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
1298 putFileChunk(file, "TAPE", CHUNK_SIZE_NONE, BYTE_ORDER_BIG_ENDIAN);
1300 putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
1301 WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
1303 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1304 SaveTape_HEAD(file, &tape);
1306 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1307 SaveTape_BODY(file, &tape);
1311 SetFilePermissions(filename, PERMS_PRIVATE);
1313 tape.changed = FALSE;
1316 Request("tape saved !", REQ_CONFIRM);
1319 void DumpTape(struct TapeInfo *tape)
1323 if (TAPE_IS_EMPTY(*tape))
1325 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1330 printf("-------------------------------------------------------------------------------\n");
1331 printf("Tape of Level %d (file version %06d, game version %06d\n",
1332 tape->level_nr, tape->file_version, tape->game_version);
1333 printf("-------------------------------------------------------------------------------\n");
1335 for(i=0; i<tape->length; i++)
1337 if (i >= MAX_TAPELEN)
1340 for(j=0; j<MAX_PLAYERS; j++)
1342 if (tape->player_participates[j])
1344 int action = tape->pos[i].action[j];
1346 printf("%d:%02x ", j, action);
1347 printf("[%c%c%c%c|%c%c] - ",
1348 (action & JOY_LEFT ? '<' : ' '),
1349 (action & JOY_RIGHT ? '>' : ' '),
1350 (action & JOY_UP ? '^' : ' '),
1351 (action & JOY_DOWN ? 'v' : ' '),
1352 (action & JOY_BUTTON_1 ? '1' : ' '),
1353 (action & JOY_BUTTON_2 ? '2' : ' '));
1357 printf("(%03d)\n", tape->pos[i].delay);
1360 printf("-------------------------------------------------------------------------------\n");
1363 void LoadScore(int level_nr)
1366 char *filename = getScoreFilename(level_nr);
1367 char cookie[MAX_LINE_LEN];
1368 char line[MAX_LINE_LEN];
1372 /* always start with reliable default values */
1373 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1375 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1376 highscore[i].Score = 0;
1379 if (!(file = fopen(filename, MODE_READ)))
1382 /* check file identifier */
1383 fgets(cookie, MAX_LINE_LEN, file);
1384 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1385 cookie[strlen(cookie) - 1] = '\0';
1387 if (!checkCookieString(cookie, SCORE_COOKIE))
1389 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1394 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1396 fscanf(file, "%d", &highscore[i].Score);
1397 fgets(line, MAX_LINE_LEN, file);
1399 if (line[strlen(line) - 1] == '\n')
1400 line[strlen(line) - 1] = '\0';
1402 for (line_ptr = line; *line_ptr; line_ptr++)
1404 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1406 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1407 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1416 void SaveScore(int level_nr)
1419 char *filename = getScoreFilename(level_nr);
1422 InitScoreDirectory(leveldir_current->filename);
1424 if (!(file = fopen(filename, MODE_WRITE)))
1426 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1430 fprintf(file, "%s\n\n", SCORE_COOKIE);
1432 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1433 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1437 SetFilePermissions(filename, PERMS_PUBLIC);
1440 /* ------------------------------------------------------------------------- */
1441 /* setup file stuff */
1442 /* ------------------------------------------------------------------------- */
1444 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1445 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1446 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1447 #define TOKEN_STR_PLAYER_PREFIX "player_"
1450 #define SETUP_TOKEN_PLAYER_NAME 0
1451 #define SETUP_TOKEN_SOUND 1
1452 #define SETUP_TOKEN_SOUND_LOOPS 2
1453 #define SETUP_TOKEN_SOUND_MUSIC 3
1454 #define SETUP_TOKEN_SOUND_SIMPLE 4
1455 #define SETUP_TOKEN_SCROLL_DELAY 5
1456 #define SETUP_TOKEN_SOFT_SCROLLING 6
1457 #define SETUP_TOKEN_FADING 7
1458 #define SETUP_TOKEN_AUTORECORD 8
1459 #define SETUP_TOKEN_QUICK_DOORS 9
1460 #define SETUP_TOKEN_TEAM_MODE 10
1461 #define SETUP_TOKEN_HANDICAP 11
1462 #define SETUP_TOKEN_TIME_LIMIT 12
1463 #define SETUP_TOKEN_FULLSCREEN 13
1466 #define SETUP_TOKEN_USE_JOYSTICK 14
1467 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
1468 #define SETUP_TOKEN_JOY_XLEFT 16
1469 #define SETUP_TOKEN_JOY_XMIDDLE 17
1470 #define SETUP_TOKEN_JOY_XRIGHT 18
1471 #define SETUP_TOKEN_JOY_YUPPER 19
1472 #define SETUP_TOKEN_JOY_YMIDDLE 20
1473 #define SETUP_TOKEN_JOY_YLOWER 21
1474 #define SETUP_TOKEN_JOY_SNAP 22
1475 #define SETUP_TOKEN_JOY_BOMB 23
1476 #define SETUP_TOKEN_KEY_LEFT 24
1477 #define SETUP_TOKEN_KEY_RIGHT 25
1478 #define SETUP_TOKEN_KEY_UP 26
1479 #define SETUP_TOKEN_KEY_DOWN 27
1480 #define SETUP_TOKEN_KEY_SNAP 28
1481 #define SETUP_TOKEN_KEY_BOMB 29
1483 /* level directory info */
1484 #define LEVELINFO_TOKEN_NAME 30
1485 #define LEVELINFO_TOKEN_NAME_SHORT 31
1486 #define LEVELINFO_TOKEN_NAME_SORTING 32
1487 #define LEVELINFO_TOKEN_AUTHOR 33
1488 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
1489 #define LEVELINFO_TOKEN_LEVELS 35
1490 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
1491 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
1492 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
1493 #define LEVELINFO_TOKEN_READONLY 39
1495 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1496 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
1498 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1499 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1501 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1502 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1504 static struct SetupInfo si;
1505 static struct SetupInputInfo sii;
1506 static struct LevelDirInfo ldi;
1507 static struct TokenInfo token_info[] =
1510 { TYPE_STRING, &si.player_name, "player_name" },
1511 { TYPE_SWITCH, &si.sound, "sound" },
1512 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1513 { TYPE_SWITCH, &si.sound_music, "background_music" },
1514 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1515 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1516 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1517 { TYPE_SWITCH, &si.fading, "screen_fading" },
1518 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1519 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1520 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1521 { TYPE_SWITCH, &si.handicap, "handicap" },
1522 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1523 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1526 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1527 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1528 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1529 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1530 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1531 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1532 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1533 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1534 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1535 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1536 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1537 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1538 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1539 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1540 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1541 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
1543 /* level directory info */
1544 { TYPE_STRING, &ldi.name, "name" },
1545 { TYPE_STRING, &ldi.name_short, "name_short" },
1546 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1547 { TYPE_STRING, &ldi.author, "author" },
1548 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1549 { TYPE_INTEGER, &ldi.levels, "levels" },
1550 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1551 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1552 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1553 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1556 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1558 ldi->filename = NULL;
1559 ldi->fullpath = NULL;
1560 ldi->basepath = NULL;
1561 ldi->name = getStringCopy(ANONYMOUS_NAME);
1562 ldi->name_short = NULL;
1563 ldi->name_sorting = NULL;
1564 ldi->author = getStringCopy(ANONYMOUS_NAME);
1565 ldi->imported_from = NULL;
1567 ldi->first_level = 0;
1568 ldi->last_level = 0;
1569 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1570 ldi->level_group = FALSE;
1571 ldi->parent_link = FALSE;
1572 ldi->user_defined = FALSE;
1573 ldi->readonly = TRUE;
1575 ldi->class_desc = NULL;
1576 ldi->handicap_level = 0;
1578 ldi->cl_cursor = -1;
1580 ldi->node_parent = NULL;
1581 ldi->node_group = NULL;
1585 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1586 struct LevelDirInfo *parent)
1590 setLevelDirInfoToDefaults(ldi);
1594 /* first copy all values from the parent structure ... */
1597 /* ... then set all fields to default that cannot be inherited from parent.
1598 This is especially important for all those fields that can be set from
1599 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1600 calls 'free()' for all already set token values which requires that no
1601 other structure's pointer may point to them!
1604 ldi->filename = NULL;
1605 ldi->fullpath = NULL;
1606 ldi->basepath = NULL;
1607 ldi->name = getStringCopy(ANONYMOUS_NAME);
1608 ldi->name_short = NULL;
1609 ldi->name_sorting = NULL;
1610 ldi->author = getStringCopy(parent->author);
1611 ldi->imported_from = getStringCopy(parent->imported_from);
1613 ldi->level_group = FALSE;
1614 ldi->parent_link = FALSE;
1616 ldi->node_parent = parent;
1617 ldi->node_group = NULL;
1621 static void setSetupInfoToDefaults(struct SetupInfo *si)
1625 si->player_name = getStringCopy(getLoginName());
1628 si->sound_loops = TRUE;
1629 si->sound_music = TRUE;
1630 si->sound_simple = TRUE;
1632 si->double_buffering = TRUE;
1633 si->direct_draw = !si->double_buffering;
1634 si->scroll_delay = TRUE;
1635 si->soft_scrolling = TRUE;
1637 si->autorecord = TRUE;
1638 si->quick_doors = FALSE;
1639 si->team_mode = FALSE;
1640 si->handicap = TRUE;
1641 si->time_limit = TRUE;
1642 si->fullscreen = FALSE;
1644 for (i=0; i<MAX_PLAYERS; i++)
1646 si->input[i].use_joystick = FALSE;
1647 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1648 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1649 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1650 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1651 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1652 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1653 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1654 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1655 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1656 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1657 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1658 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1659 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1660 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1661 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1665 static void setSetupInfo(int token_nr, char *token_value)
1667 int token_type = token_info[token_nr].type;
1668 void *setup_value = token_info[token_nr].value;
1670 if (token_value == NULL)
1673 /* set setup field to corresponding token value */
1678 *(boolean *)setup_value = get_string_boolean_value(token_value);
1682 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1686 *(int *)setup_value = get_string_integer_value(token_value);
1690 if (*(char **)setup_value != NULL)
1691 free(*(char **)setup_value);
1692 *(char **)setup_value = getStringCopy(token_value);
1700 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1704 if (!setup_file_list)
1707 /* handle global setup values */
1709 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1710 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1713 /* handle player specific setup values */
1714 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1718 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1720 sii = setup.input[pnr];
1721 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1723 char full_token[100];
1725 sprintf(full_token, "%s%s", prefix, token_info[i].text);
1726 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1728 setup.input[pnr] = sii;
1732 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1734 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
1735 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
1738 if (entry1->parent_link || entry2->parent_link)
1739 compare_result = (entry1->parent_link ? -1 : +1);
1740 else if (entry1->sort_priority == entry2->sort_priority)
1742 char *name1 = getStringToLower(entry1->name_sorting);
1743 char *name2 = getStringToLower(entry2->name_sorting);
1745 compare_result = strcmp(name1, name2);
1750 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1751 compare_result = entry1->sort_priority - entry2->sort_priority;
1753 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1755 return compare_result;
1758 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
1760 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1762 setLevelDirInfoToDefaults(leveldir_new);
1764 leveldir_new->node_parent = node_parent;
1765 leveldir_new->parent_link = TRUE;
1767 leveldir_new->name = ".. (parent directory)";
1768 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1769 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1771 leveldir_new->filename = "..";
1772 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
1774 leveldir_new->sort_priority = node_parent->sort_priority;
1775 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1777 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
1780 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
1781 struct LevelDirInfo *node_parent,
1782 char *level_directory)
1785 struct dirent *dir_entry;
1786 boolean valid_entry_found = FALSE;
1788 if ((dir = opendir(level_directory)) == NULL)
1790 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1794 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1796 struct SetupFileList *setup_file_list = NULL;
1797 struct stat file_status;
1798 char *directory_name = dir_entry->d_name;
1799 char *directory_path = getPath2(level_directory, directory_name);
1800 char *filename = NULL;
1802 /* skip entries for current and parent directory */
1803 if (strcmp(directory_name, ".") == 0 ||
1804 strcmp(directory_name, "..") == 0)
1806 free(directory_path);
1810 /* find out if directory entry is itself a directory */
1811 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1812 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1814 free(directory_path);
1818 filename = getPath2(directory_path, LEVELINFO_FILENAME);
1819 setup_file_list = loadSetupFileList(filename);
1821 if (setup_file_list)
1823 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1826 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1827 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
1829 /* set all structure fields according to the token/value pairs */
1830 ldi = *leveldir_new;
1831 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1832 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1833 *leveldir_new = ldi;
1835 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1837 if (leveldir_new->name_short == NULL)
1838 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1840 if (leveldir_new->name_sorting == NULL)
1841 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1843 leveldir_new->filename = getStringCopy(directory_name);
1845 if (node_parent == NULL) /* top level group */
1847 leveldir_new->basepath = level_directory;
1848 leveldir_new->fullpath = leveldir_new->filename;
1850 else /* sub level group */
1852 leveldir_new->basepath = node_parent->basepath;
1853 leveldir_new->fullpath = getPath2(node_parent->fullpath,
1857 if (leveldir_new->levels < 1)
1858 leveldir_new->levels = 1;
1860 leveldir_new->last_level =
1861 leveldir_new->first_level + leveldir_new->levels - 1;
1863 leveldir_new->user_defined =
1864 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1866 leveldir_new->color = LEVELCOLOR(leveldir_new);
1867 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1869 leveldir_new->handicap_level = /* set handicap to default value */
1870 (leveldir_new->user_defined ?
1871 leveldir_new->last_level :
1872 leveldir_new->first_level);
1874 pushLevelDirInfo(node_first, leveldir_new);
1876 freeSetupFileList(setup_file_list);
1877 valid_entry_found = TRUE;
1879 if (leveldir_new->level_group)
1881 /* create node to link back to current level directory */
1882 createParentLevelDirNode(leveldir_new);
1884 /* step into sub-directory and look for more level series */
1885 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1886 leveldir_new, directory_path);
1890 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1892 free(directory_path);
1898 if (!valid_entry_found)
1899 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1903 void LoadLevelInfo()
1905 InitUserLevelDirectory(getLoginName());
1907 DrawInitText("Loading level series:", 120, FC_GREEN);
1909 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1910 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
1912 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1914 if (leveldir_first == NULL)
1915 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1917 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
1920 dumpLevelDirInfo(leveldir_first, 0);
1924 static void SaveUserLevelInfo()
1930 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1932 if (!(file = fopen(filename, MODE_WRITE)))
1934 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1939 /* always start with reliable default values */
1940 setLevelDirInfoToDefaults(&ldi);
1942 ldi.name = getLoginName();
1943 ldi.author = getRealName();
1945 ldi.first_level = 1;
1946 ldi.sort_priority = LEVELCLASS_USER_START;
1947 ldi.readonly = FALSE;
1949 fprintf(file, "%s\n\n",
1950 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1952 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1953 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1954 i != LEVELINFO_TOKEN_NAME_SORTING &&
1955 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1956 fprintf(file, "%s\n", getSetupLine("", i));
1961 SetFilePermissions(filename, PERMS_PRIVATE);
1967 struct SetupFileList *setup_file_list = NULL;
1969 /* always start with reliable default values */
1970 setSetupInfoToDefaults(&setup);
1972 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1974 setup_file_list = loadSetupFileList(filename);
1976 if (setup_file_list)
1978 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1979 decodeSetupFileList(setup_file_list);
1981 setup.direct_draw = !setup.double_buffering;
1983 freeSetupFileList(setup_file_list);
1985 /* needed to work around problems with fixed length strings */
1986 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1987 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1988 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1990 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1992 strcpy(new_name, setup.player_name);
1993 free(setup.player_name);
1994 setup.player_name = new_name;
1998 Error(ERR_WARN, "using default setup values");
2003 static char *getSetupLine(char *prefix, int token_nr)
2006 static char entry[MAX_LINE_LEN];
2007 int token_type = token_info[token_nr].type;
2008 void *setup_value = token_info[token_nr].value;
2009 char *token_text = token_info[token_nr].text;
2011 /* start with the prefix, token and some spaces to format output line */
2012 sprintf(entry, "%s%s:", prefix, token_text);
2013 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2016 /* continue with the token's value (which can have different types) */
2020 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2024 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2029 Key key = *(Key *)setup_value;
2030 char *keyname = getKeyNameFromKey(key);
2032 strcat(entry, getX11KeyNameFromKey(key));
2033 for (i=strlen(entry); i<50; i++)
2036 /* add comment, if useful */
2037 if (strcmp(keyname, "(undefined)") != 0 &&
2038 strcmp(keyname, "(unknown)") != 0)
2040 strcat(entry, "# ");
2041 strcat(entry, keyname);
2048 char buffer[MAX_LINE_LEN];
2050 sprintf(buffer, "%d", *(int *)setup_value);
2051 strcat(entry, buffer);
2056 strcat(entry, *(char **)setup_value);
2072 InitUserDataDirectory();
2074 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2076 if (!(file = fopen(filename, MODE_WRITE)))
2078 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2083 fprintf(file, "%s\n",
2084 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2085 fprintf(file, "\n");
2087 /* handle global setup values */
2089 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2091 fprintf(file, "%s\n", getSetupLine("", i));
2093 /* just to make things nicer :) */
2094 if (i == SETUP_TOKEN_PLAYER_NAME)
2095 fprintf(file, "\n");
2098 /* handle player specific setup values */
2099 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2103 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2104 fprintf(file, "\n");
2106 sii = setup.input[pnr];
2107 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2108 fprintf(file, "%s\n", getSetupLine(prefix, i));
2114 SetFilePermissions(filename, PERMS_PRIVATE);
2117 void LoadLevelSetup_LastSeries()
2120 struct SetupFileList *level_setup_list = NULL;
2122 /* always start with reliable default values */
2123 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2125 /* ----------------------------------------------------------------------- */
2126 /* ~/.rocksndiamonds/levelsetup.conf */
2127 /* ----------------------------------------------------------------------- */
2129 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2131 if ((level_setup_list = loadSetupFileList(filename)))
2133 char *last_level_series =
2134 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2136 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2137 if (leveldir_current == NULL)
2138 leveldir_current = leveldir_first;
2140 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2142 freeSetupFileList(level_setup_list);
2145 Error(ERR_WARN, "using default setup values");
2150 void SaveLevelSetup_LastSeries()
2153 char *level_subdir = leveldir_current->filename;
2156 /* ----------------------------------------------------------------------- */
2157 /* ~/.rocksndiamonds/levelsetup.conf */
2158 /* ----------------------------------------------------------------------- */
2160 InitUserDataDirectory();
2162 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2164 if (!(file = fopen(filename, MODE_WRITE)))
2166 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2171 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2172 LEVELSETUP_COOKIE));
2173 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2179 SetFilePermissions(filename, PERMS_PRIVATE);
2182 static void checkSeriesInfo()
2184 static char *level_directory = NULL;
2186 struct dirent *dir_entry;
2188 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2190 level_directory = getPath2((leveldir_current->user_defined ?
2191 getUserLevelDir("") :
2192 options.level_directory),
2193 leveldir_current->fullpath);
2195 if ((dir = opendir(level_directory)) == NULL)
2197 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2201 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2203 if (strlen(dir_entry->d_name) > 4 &&
2204 dir_entry->d_name[3] == '.' &&
2205 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2207 char levelnum_str[4];
2210 strncpy(levelnum_str, dir_entry->d_name, 3);
2211 levelnum_str[3] = '\0';
2213 levelnum_value = atoi(levelnum_str);
2215 if (levelnum_value < leveldir_current->first_level)
2217 Error(ERR_WARN, "additional level %d found", levelnum_value);
2218 leveldir_current->first_level = levelnum_value;
2220 else if (levelnum_value > leveldir_current->last_level)
2222 Error(ERR_WARN, "additional level %d found", levelnum_value);
2223 leveldir_current->last_level = levelnum_value;
2231 void LoadLevelSetup_SeriesInfo()
2234 struct SetupFileList *level_setup_list = NULL;
2235 char *level_subdir = leveldir_current->filename;
2237 /* always start with reliable default values */
2238 level_nr = leveldir_current->first_level;
2240 checkSeriesInfo(leveldir_current);
2242 /* ----------------------------------------------------------------------- */
2243 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2244 /* ----------------------------------------------------------------------- */
2246 level_subdir = leveldir_current->filename;
2248 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2250 if ((level_setup_list = loadSetupFileList(filename)))
2254 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2258 level_nr = atoi(token_value);
2260 if (level_nr < leveldir_current->first_level)
2261 level_nr = leveldir_current->first_level;
2262 if (level_nr > leveldir_current->last_level)
2263 level_nr = leveldir_current->last_level;
2266 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2270 int level_nr = atoi(token_value);
2272 if (level_nr < leveldir_current->first_level)
2273 level_nr = leveldir_current->first_level;
2274 if (level_nr > leveldir_current->last_level + 1)
2275 level_nr = leveldir_current->last_level;
2277 if (leveldir_current->user_defined)
2278 level_nr = leveldir_current->last_level;
2280 leveldir_current->handicap_level = level_nr;
2283 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2285 freeSetupFileList(level_setup_list);
2288 Error(ERR_WARN, "using default setup values");
2293 void SaveLevelSetup_SeriesInfo()
2296 char *level_subdir = leveldir_current->filename;
2297 char *level_nr_str = int2str(level_nr, 0);
2298 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2301 /* ----------------------------------------------------------------------- */
2302 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2303 /* ----------------------------------------------------------------------- */
2305 InitLevelSetupDirectory(level_subdir);
2307 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2309 if (!(file = fopen(filename, MODE_WRITE)))
2311 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2316 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2317 LEVELSETUP_COOKIE));
2318 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2320 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2321 handicap_level_str));
2326 SetFilePermissions(filename, PERMS_PRIVATE);