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;
1515 { TYPE_STRING, &si.player_name, "player_name" },
1516 { TYPE_SWITCH, &si.sound, "sound" },
1517 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1518 { TYPE_SWITCH, &si.sound_music, "background_music" },
1519 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1520 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1521 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1522 { TYPE_SWITCH, &si.fading, "screen_fading" },
1523 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1524 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1525 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1526 { TYPE_SWITCH, &si.handicap, "handicap" },
1527 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1528 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1531 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1532 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1533 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1534 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1535 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1536 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1537 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1538 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1539 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1540 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1541 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1542 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1543 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1544 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1545 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1546 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
1548 /* level directory info */
1549 { TYPE_STRING, &ldi.name, "name" },
1550 { TYPE_STRING, &ldi.name_short, "name_short" },
1551 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1552 { TYPE_STRING, &ldi.author, "author" },
1553 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1554 { TYPE_INTEGER, &ldi.levels, "levels" },
1555 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1556 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1557 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1558 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1561 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1563 ldi->filename = NULL;
1564 ldi->fullpath = NULL;
1565 ldi->basepath = NULL;
1566 ldi->name = getStringCopy(ANONYMOUS_NAME);
1567 ldi->name_short = NULL;
1568 ldi->name_sorting = NULL;
1569 ldi->author = getStringCopy(ANONYMOUS_NAME);
1570 ldi->imported_from = NULL;
1572 ldi->first_level = 0;
1573 ldi->last_level = 0;
1574 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1575 ldi->level_group = FALSE;
1576 ldi->parent_link = FALSE;
1577 ldi->user_defined = FALSE;
1578 ldi->readonly = TRUE;
1580 ldi->class_desc = NULL;
1581 ldi->handicap_level = 0;
1583 ldi->cl_cursor = -1;
1585 ldi->node_parent = NULL;
1586 ldi->node_group = NULL;
1590 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1591 struct LevelDirInfo *parent)
1595 setLevelDirInfoToDefaults(ldi);
1599 /* first copy all values from the parent structure ... */
1602 /* ... then set all fields to default that cannot be inherited from parent.
1603 This is especially important for all those fields that can be set from
1604 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1605 calls 'free()' for all already set token values which requires that no
1606 other structure's pointer may point to them!
1609 ldi->filename = NULL;
1610 ldi->fullpath = NULL;
1611 ldi->basepath = NULL;
1612 ldi->name = getStringCopy(ANONYMOUS_NAME);
1613 ldi->name_short = NULL;
1614 ldi->name_sorting = NULL;
1615 ldi->author = getStringCopy(parent->author);
1616 ldi->imported_from = getStringCopy(parent->imported_from);
1618 ldi->level_group = FALSE;
1619 ldi->parent_link = FALSE;
1621 ldi->node_parent = parent;
1622 ldi->node_group = NULL;
1626 static void setSetupInfoToDefaults(struct SetupInfo *si)
1630 si->player_name = getStringCopy(getLoginName());
1633 si->sound_loops = TRUE;
1634 si->sound_music = TRUE;
1635 si->sound_simple = TRUE;
1637 si->double_buffering = TRUE;
1638 si->direct_draw = !si->double_buffering;
1639 si->scroll_delay = TRUE;
1640 si->soft_scrolling = TRUE;
1642 si->autorecord = TRUE;
1643 si->quick_doors = FALSE;
1644 si->team_mode = FALSE;
1645 si->handicap = TRUE;
1646 si->time_limit = TRUE;
1647 si->fullscreen = FALSE;
1649 for (i=0; i<MAX_PLAYERS; i++)
1651 si->input[i].use_joystick = FALSE;
1652 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1653 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1654 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1655 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1656 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1657 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1658 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1659 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1660 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1661 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1662 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1663 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1664 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1665 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1666 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1670 static void setSetupInfo(int token_nr, char *token_value)
1672 int token_type = token_info[token_nr].type;
1673 void *setup_value = token_info[token_nr].value;
1675 if (token_value == NULL)
1678 /* set setup field to corresponding token value */
1683 *(boolean *)setup_value = get_string_boolean_value(token_value);
1687 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1691 *(int *)setup_value = get_string_integer_value(token_value);
1695 if (*(char **)setup_value != NULL)
1696 free(*(char **)setup_value);
1697 *(char **)setup_value = getStringCopy(token_value);
1705 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1709 if (!setup_file_list)
1712 /* handle global setup values */
1714 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1715 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1718 /* handle player specific setup values */
1719 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1723 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1725 sii = setup.input[pnr];
1726 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1728 char full_token[100];
1730 sprintf(full_token, "%s%s", prefix, token_info[i].text);
1731 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1733 setup.input[pnr] = sii;
1737 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1739 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
1740 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
1743 if (entry1->parent_link || entry2->parent_link)
1744 compare_result = (entry1->parent_link ? -1 : +1);
1745 else if (entry1->sort_priority == entry2->sort_priority)
1747 char *name1 = getStringToLower(entry1->name_sorting);
1748 char *name2 = getStringToLower(entry2->name_sorting);
1750 compare_result = strcmp(name1, name2);
1755 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1756 compare_result = entry1->sort_priority - entry2->sort_priority;
1758 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1760 return compare_result;
1763 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
1765 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1767 setLevelDirInfoToDefaults(leveldir_new);
1769 leveldir_new->node_parent = node_parent;
1770 leveldir_new->parent_link = TRUE;
1772 leveldir_new->name = ".. (parent directory)";
1773 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1774 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1776 leveldir_new->filename = "..";
1777 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
1779 leveldir_new->sort_priority = node_parent->sort_priority;
1780 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1782 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
1785 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
1786 struct LevelDirInfo *node_parent,
1787 char *level_directory)
1790 struct dirent *dir_entry;
1791 boolean valid_entry_found = FALSE;
1793 if ((dir = opendir(level_directory)) == NULL)
1795 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1799 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1801 struct SetupFileList *setup_file_list = NULL;
1802 struct stat file_status;
1803 char *directory_name = dir_entry->d_name;
1804 char *directory_path = getPath2(level_directory, directory_name);
1805 char *filename = NULL;
1807 /* skip entries for current and parent directory */
1808 if (strcmp(directory_name, ".") == 0 ||
1809 strcmp(directory_name, "..") == 0)
1811 free(directory_path);
1815 /* find out if directory entry is itself a directory */
1816 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1817 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1819 free(directory_path);
1823 filename = getPath2(directory_path, LEVELINFO_FILENAME);
1824 setup_file_list = loadSetupFileList(filename);
1826 if (setup_file_list)
1828 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1831 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1832 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
1834 /* set all structure fields according to the token/value pairs */
1835 ldi = *leveldir_new;
1836 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1837 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1838 *leveldir_new = ldi;
1840 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1842 if (leveldir_new->name_short == NULL)
1843 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1845 if (leveldir_new->name_sorting == NULL)
1846 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1848 leveldir_new->filename = getStringCopy(directory_name);
1850 if (node_parent == NULL) /* top level group */
1852 leveldir_new->basepath = level_directory;
1853 leveldir_new->fullpath = leveldir_new->filename;
1855 else /* sub level group */
1857 leveldir_new->basepath = node_parent->basepath;
1858 leveldir_new->fullpath = getPath2(node_parent->fullpath,
1862 if (leveldir_new->levels < 1)
1863 leveldir_new->levels = 1;
1865 leveldir_new->last_level =
1866 leveldir_new->first_level + leveldir_new->levels - 1;
1868 leveldir_new->user_defined =
1869 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1871 leveldir_new->color = LEVELCOLOR(leveldir_new);
1872 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1874 leveldir_new->handicap_level = /* set handicap to default value */
1875 (leveldir_new->user_defined ?
1876 leveldir_new->last_level :
1877 leveldir_new->first_level);
1879 pushLevelDirInfo(node_first, leveldir_new);
1881 freeSetupFileList(setup_file_list);
1882 valid_entry_found = TRUE;
1884 if (leveldir_new->level_group)
1886 /* create node to link back to current level directory */
1887 createParentLevelDirNode(leveldir_new);
1889 /* step into sub-directory and look for more level series */
1890 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1891 leveldir_new, directory_path);
1895 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1897 free(directory_path);
1903 if (!valid_entry_found)
1904 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1908 void LoadLevelInfo()
1910 InitUserLevelDirectory(getLoginName());
1912 DrawInitText("Loading level series:", 120, FC_GREEN);
1914 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1915 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
1917 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1919 if (leveldir_first == NULL)
1920 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1922 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
1925 dumpLevelDirInfo(leveldir_first, 0);
1929 static void SaveUserLevelInfo()
1935 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1937 if (!(file = fopen(filename, MODE_WRITE)))
1939 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1944 /* always start with reliable default values */
1945 setLevelDirInfoToDefaults(&ldi);
1947 ldi.name = getLoginName();
1948 ldi.author = getRealName();
1950 ldi.first_level = 1;
1951 ldi.sort_priority = LEVELCLASS_USER_START;
1952 ldi.readonly = FALSE;
1954 fprintf(file, "%s\n\n",
1955 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1957 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1958 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1959 i != LEVELINFO_TOKEN_NAME_SORTING &&
1960 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1961 fprintf(file, "%s\n", getSetupLine("", i));
1966 SetFilePermissions(filename, PERMS_PRIVATE);
1972 struct SetupFileList *setup_file_list = NULL;
1974 /* always start with reliable default values */
1975 setSetupInfoToDefaults(&setup);
1977 filename = getPath2(getSetupDir(), SETUP_FILENAME);
1979 setup_file_list = loadSetupFileList(filename);
1981 if (setup_file_list)
1983 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
1984 decodeSetupFileList(setup_file_list);
1986 setup.direct_draw = !setup.double_buffering;
1988 freeSetupFileList(setup_file_list);
1990 /* needed to work around problems with fixed length strings */
1991 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1992 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1993 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1995 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1997 strcpy(new_name, setup.player_name);
1998 free(setup.player_name);
1999 setup.player_name = new_name;
2003 Error(ERR_WARN, "using default setup values");
2008 static char *getSetupLine(char *prefix, int token_nr)
2011 static char entry[MAX_LINE_LEN];
2012 int token_type = token_info[token_nr].type;
2013 void *setup_value = token_info[token_nr].value;
2014 char *token_text = token_info[token_nr].text;
2016 /* start with the prefix, token and some spaces to format output line */
2017 sprintf(entry, "%s%s:", prefix, token_text);
2018 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2021 /* continue with the token's value (which can have different types) */
2025 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2029 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2034 Key key = *(Key *)setup_value;
2035 char *keyname = getKeyNameFromKey(key);
2037 strcat(entry, getX11KeyNameFromKey(key));
2038 for (i=strlen(entry); i<50; i++)
2041 /* add comment, if useful */
2042 if (strcmp(keyname, "(undefined)") != 0 &&
2043 strcmp(keyname, "(unknown)") != 0)
2045 strcat(entry, "# ");
2046 strcat(entry, keyname);
2053 char buffer[MAX_LINE_LEN];
2055 sprintf(buffer, "%d", *(int *)setup_value);
2056 strcat(entry, buffer);
2061 strcat(entry, *(char **)setup_value);
2077 InitUserDataDirectory();
2079 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2081 if (!(file = fopen(filename, MODE_WRITE)))
2083 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2088 fprintf(file, "%s\n",
2089 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2090 fprintf(file, "\n");
2092 /* handle global setup values */
2094 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2096 fprintf(file, "%s\n", getSetupLine("", i));
2098 /* just to make things nicer :) */
2099 if (i == SETUP_TOKEN_PLAYER_NAME)
2100 fprintf(file, "\n");
2103 /* handle player specific setup values */
2104 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2108 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2109 fprintf(file, "\n");
2111 sii = setup.input[pnr];
2112 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2113 fprintf(file, "%s\n", getSetupLine(prefix, i));
2119 SetFilePermissions(filename, PERMS_PRIVATE);
2122 void LoadLevelSetup_LastSeries()
2125 struct SetupFileList *level_setup_list = NULL;
2127 /* always start with reliable default values */
2128 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2130 /* ----------------------------------------------------------------------- */
2131 /* ~/.rocksndiamonds/levelsetup.conf */
2132 /* ----------------------------------------------------------------------- */
2134 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2136 if ((level_setup_list = loadSetupFileList(filename)))
2138 char *last_level_series =
2139 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2141 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2142 if (leveldir_current == NULL)
2143 leveldir_current = leveldir_first;
2145 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2147 freeSetupFileList(level_setup_list);
2150 Error(ERR_WARN, "using default setup values");
2155 void SaveLevelSetup_LastSeries()
2158 char *level_subdir = leveldir_current->filename;
2161 /* ----------------------------------------------------------------------- */
2162 /* ~/.rocksndiamonds/levelsetup.conf */
2163 /* ----------------------------------------------------------------------- */
2165 InitUserDataDirectory();
2167 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2169 if (!(file = fopen(filename, MODE_WRITE)))
2171 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2176 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2177 LEVELSETUP_COOKIE));
2178 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2184 SetFilePermissions(filename, PERMS_PRIVATE);
2187 static void checkSeriesInfo()
2189 static char *level_directory = NULL;
2191 struct dirent *dir_entry;
2193 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2195 level_directory = getPath2((leveldir_current->user_defined ?
2196 getUserLevelDir("") :
2197 options.level_directory),
2198 leveldir_current->fullpath);
2200 if ((dir = opendir(level_directory)) == NULL)
2202 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2206 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2208 if (strlen(dir_entry->d_name) > 4 &&
2209 dir_entry->d_name[3] == '.' &&
2210 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2212 char levelnum_str[4];
2215 strncpy(levelnum_str, dir_entry->d_name, 3);
2216 levelnum_str[3] = '\0';
2218 levelnum_value = atoi(levelnum_str);
2220 if (levelnum_value < leveldir_current->first_level)
2222 Error(ERR_WARN, "additional level %d found", levelnum_value);
2223 leveldir_current->first_level = levelnum_value;
2225 else if (levelnum_value > leveldir_current->last_level)
2227 Error(ERR_WARN, "additional level %d found", levelnum_value);
2228 leveldir_current->last_level = levelnum_value;
2236 void LoadLevelSetup_SeriesInfo()
2239 struct SetupFileList *level_setup_list = NULL;
2240 char *level_subdir = leveldir_current->filename;
2242 /* always start with reliable default values */
2243 level_nr = leveldir_current->first_level;
2245 checkSeriesInfo(leveldir_current);
2247 /* ----------------------------------------------------------------------- */
2248 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2249 /* ----------------------------------------------------------------------- */
2251 level_subdir = leveldir_current->filename;
2253 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2255 if ((level_setup_list = loadSetupFileList(filename)))
2259 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2263 level_nr = atoi(token_value);
2265 if (level_nr < leveldir_current->first_level)
2266 level_nr = leveldir_current->first_level;
2267 if (level_nr > leveldir_current->last_level)
2268 level_nr = leveldir_current->last_level;
2271 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2275 int level_nr = atoi(token_value);
2277 if (level_nr < leveldir_current->first_level)
2278 level_nr = leveldir_current->first_level;
2279 if (level_nr > leveldir_current->last_level + 1)
2280 level_nr = leveldir_current->last_level;
2282 if (leveldir_current->user_defined)
2283 level_nr = leveldir_current->last_level;
2285 leveldir_current->handicap_level = level_nr;
2288 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2290 freeSetupFileList(level_setup_list);
2293 Error(ERR_WARN, "using default setup values");
2298 void SaveLevelSetup_SeriesInfo()
2301 char *level_subdir = leveldir_current->filename;
2302 char *level_nr_str = int2str(level_nr, 0);
2303 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2306 /* ----------------------------------------------------------------------- */
2307 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2308 /* ----------------------------------------------------------------------- */
2310 InitLevelSetupDirectory(level_subdir);
2312 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2314 if (!(file = fopen(filename, MODE_WRITE)))
2316 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2321 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2322 LEVELSETUP_COOKIE));
2323 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2325 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2326 handicap_level_str));
2331 SetFilePermissions(filename, PERMS_PRIVATE);