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 14 /* 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;
391 level.em_slippery_gems = FALSE;
393 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
394 level.name[i] = '\0';
395 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
396 level.author[i] = '\0';
398 strcpy(level.name, NAMELESS_LEVEL_NAME);
399 strcpy(level.author, ANONYMOUS_NAME);
401 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
404 level.num_yam_contents = STD_ELEMENT_CONTENTS;
405 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
408 level.yam_content[i][x][y] =
409 (i < STD_ELEMENT_CONTENTS ? EL_FELSBROCKEN : EL_LEERRAUM);
411 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
412 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
413 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
415 BorderElement = EL_BETON;
417 /* try to determine better author name than 'anonymous' */
418 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
420 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
421 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
425 switch (LEVELCLASS(leveldir_current))
427 case LEVELCLASS_TUTORIAL:
428 strcpy(level.author, PROGRAM_AUTHOR_STRING);
431 case LEVELCLASS_CONTRIBUTION:
432 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
433 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
436 case LEVELCLASS_USER:
437 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
438 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
442 /* keep default value */
448 static int checkLevelElement(int element)
450 if (element >= EL_FIRST_RUNTIME_EL)
452 Error(ERR_WARN, "invalid level element %d", element);
453 element = EL_CHAR_FRAGE;
459 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
461 ReadChunk_VERS(file, &(level->file_version), &(level->game_version));
466 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
470 lev_fieldx = level->fieldx = fgetc(file);
471 lev_fieldy = level->fieldy = fgetc(file);
473 level->time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
474 level->gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
476 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
477 level->name[i] = fgetc(file);
478 level->name[MAX_LEVEL_NAME_LEN] = 0;
480 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
481 level->score[i] = fgetc(file);
483 level->num_yam_contents = STD_ELEMENT_CONTENTS;
484 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
487 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
489 level->amoeba_speed = fgetc(file);
490 level->time_magic_wall = fgetc(file);
491 level->time_wheel = fgetc(file);
492 level->amoeba_content = checkLevelElement(fgetc(file));
493 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
494 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
495 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
496 level->em_slippery_gems = (fgetc(file) == 1 ? TRUE : FALSE);
498 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
503 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
507 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
508 level->author[i] = fgetc(file);
509 level->author[MAX_LEVEL_NAME_LEN] = 0;
514 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
518 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
519 int chunk_size_expected = header_size + content_size;
521 /* Note: "chunk_size" was wrong before version 2.0 when elements are
522 stored with 16-bit encoding (and should be twice as big then).
523 Even worse, playfield data was stored 16-bit when only yamyam content
524 contained 16-bit elements and vice versa. */
526 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
527 chunk_size_expected += content_size;
529 if (chunk_size_expected != chunk_size)
531 ReadUnusedBytesFromFile(file, chunk_size);
532 return chunk_size_expected;
536 level->num_yam_contents = fgetc(file);
540 /* correct invalid number of content fields -- should never happen */
541 if (level->num_yam_contents < 1 ||
542 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
543 level->num_yam_contents = STD_ELEMENT_CONTENTS;
545 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
548 level->yam_content[i][x][y] =
549 checkLevelElement(level->encoding_16bit_field ?
550 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
555 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
558 int chunk_size_expected = level->fieldx * level->fieldy;
560 /* Note: "chunk_size" was wrong before version 2.0 when elements are
561 stored with 16-bit encoding (and should be twice as big then).
562 Even worse, playfield data was stored 16-bit when only yamyam content
563 contained 16-bit elements and vice versa. */
565 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
566 chunk_size_expected *= 2;
568 if (chunk_size_expected != chunk_size)
570 ReadUnusedBytesFromFile(file, chunk_size);
571 return chunk_size_expected;
574 for(y=0; y<level->fieldy; y++)
575 for(x=0; x<level->fieldx; x++)
576 Feld[x][y] = Ur[x][y] =
577 checkLevelElement(level->encoding_16bit_field ?
578 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
583 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
587 int num_contents, content_xsize, content_ysize;
588 int content_array[MAX_ELEMENT_CONTENTS][3][3];
590 element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
591 num_contents = fgetc(file);
592 content_xsize = fgetc(file);
593 content_ysize = fgetc(file);
594 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
596 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
599 content_array[i][x][y] =
600 checkLevelElement(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
602 /* correct invalid number of content fields -- should never happen */
603 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
604 num_contents = STD_ELEMENT_CONTENTS;
606 if (element == EL_MAMPFER)
608 level->num_yam_contents = num_contents;
610 for(i=0; i<num_contents; i++)
613 level->yam_content[i][x][y] = content_array[i][x][y];
615 else if (element == EL_AMOEBE_BD)
617 level->amoeba_content = content_array[0][0][0];
621 Error(ERR_WARN, "cannot load content for element '%d'", element);
627 void LoadLevel(int level_nr)
629 char *filename = getLevelFilename(level_nr);
630 char cookie[MAX_LINE_LEN];
631 char chunk_name[CHUNK_ID_LEN + 1];
635 /* always start with reliable default values */
636 setLevelInfoToDefaults();
638 if (!(file = fopen(filename, MODE_READ)))
640 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
644 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
645 if (strcmp(chunk_name, "RND1") == 0)
647 getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN); /* not used */
649 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
650 if (strcmp(chunk_name, "CAVE") != 0)
652 Error(ERR_WARN, "unknown format of level file '%s'", filename);
657 else /* check for pre-2.0 file format with cookie string */
659 strcpy(cookie, chunk_name);
660 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
661 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
662 cookie[strlen(cookie) - 1] = '\0';
664 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
666 Error(ERR_WARN, "unknown format of level file '%s'", filename);
671 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
673 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
678 /* pre-2.0 level files have no game version, so use file version here */
679 level.game_version = level.file_version;
682 if (level.file_version < FILE_VERSION_1_2)
684 /* level files from versions before 1.2.0 without chunk structure */
685 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
686 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
694 int (*loader)(FILE *, int, struct LevelInfo *);
698 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
699 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
700 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
701 { "CONT", -1, LoadLevel_CONT },
702 { "BODY", -1, LoadLevel_BODY },
703 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
707 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
711 while (chunk_info[i].name != NULL &&
712 strcmp(chunk_name, chunk_info[i].name) != 0)
715 if (chunk_info[i].name == NULL)
717 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
718 chunk_name, filename);
719 ReadUnusedBytesFromFile(file, chunk_size);
721 else if (chunk_info[i].size != -1 &&
722 chunk_info[i].size != chunk_size)
724 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
725 chunk_size, chunk_name, filename);
726 ReadUnusedBytesFromFile(file, chunk_size);
730 /* call function to load this level chunk */
731 int chunk_size_expected =
732 (chunk_info[i].loader)(file, chunk_size, &level);
734 /* the size of some chunks cannot be checked before reading other
735 chunks first (like "HEAD" and "BODY") that contain some header
736 information, so check them here */
737 if (chunk_size_expected != chunk_size)
739 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
740 chunk_size, chunk_name, filename);
748 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
749 IS_LEVELCLASS_USER(leveldir_current))
751 /* For user contributed and private levels, use the version of
752 the game engine the levels were created for.
753 Since 2.0.1, the game engine version is now directly stored
754 in the level file (chunk "VERS"), so there is no need anymore
755 to set the game version from the file version (except for old,
756 pre-2.0 levels, where the game version is still taken from the
757 file format version used to store the level -- see above). */
759 /* do some special adjustments to support older level versions */
760 if (level.file_version == FILE_VERSION_1_0)
762 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
763 Error(ERR_WARN, "using high speed movement for player");
765 /* player was faster than monsters in (pre-)1.0 levels */
766 level.double_speed = TRUE;
771 /* Always use the latest version of the game engine for all but
772 user contributed and private levels; this allows for actual
773 corrections in the game engine to take effect for existing,
774 converted levels (from "classic" or other existing games) to
775 make the game emulation more accurate, while (hopefully) not
776 breaking existing levels created from other players. */
778 level.game_version = GAME_VERSION_ACTUAL;
780 /* Set special EM style gems behaviour: EM style gems slip down from
781 normal, steel and growing wall. As this is a more fundamental change,
782 it seems better to set the default behaviour to "off" (as it is more
783 natural) and make it configurable in the level editor (as a property
784 of gem style elements). Already existing converted levels (neither
785 private nor contributed levels) are changed to the new behaviour. */
787 if (level.file_version < FILE_VERSION_2_0)
788 level.em_slippery_gems = TRUE;
791 /* determine border element for this level */
795 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
799 fputc(level->fieldx, file);
800 fputc(level->fieldy, file);
802 putFile16BitInteger(file, level->time, BYTE_ORDER_BIG_ENDIAN);
803 putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
805 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
806 fputc(level->name[i], file);
808 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
809 fputc(level->score[i], file);
811 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
814 fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
815 level->yam_content[i][x][y]),
817 fputc(level->amoeba_speed, file);
818 fputc(level->time_magic_wall, file);
819 fputc(level->time_wheel, file);
820 fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
822 fputc((level->double_speed ? 1 : 0), file);
823 fputc((level->gravity ? 1 : 0), file);
824 fputc((level->encoding_16bit_field ? 1 : 0), file);
825 fputc((level->em_slippery_gems ? 1 : 0), file);
827 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
830 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
834 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
835 fputc(level->author[i], file);
839 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
843 fputc(EL_MAMPFER, file);
844 fputc(level->num_yam_contents, file);
848 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
851 if (level->encoding_16bit_field)
852 putFile16BitInteger(file, level->yam_content[i][x][y],
853 BYTE_ORDER_BIG_ENDIAN);
855 fputc(level->yam_content[i][x][y], file);
859 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
863 for(y=0; y<level->fieldy; y++)
864 for(x=0; x<level->fieldx; x++)
865 if (level->encoding_16bit_field)
866 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
868 fputc(Ur[x][y], file);
871 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
874 int num_contents, content_xsize, content_ysize;
875 int content_array[MAX_ELEMENT_CONTENTS][3][3];
877 if (element == EL_MAMPFER)
879 num_contents = level->num_yam_contents;
883 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
886 content_array[i][x][y] = level->yam_content[i][x][y];
888 else if (element == EL_AMOEBE_BD)
894 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
897 content_array[i][x][y] = EL_LEERRAUM;
898 content_array[0][0][0] = level->amoeba_content;
902 /* chunk header already written -- write empty chunk data */
903 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
905 Error(ERR_WARN, "cannot save content for element '%d'", element);
909 putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
910 fputc(num_contents, file);
911 fputc(content_xsize, file);
912 fputc(content_ysize, file);
914 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
916 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
919 putFile16BitInteger(file, content_array[i][x][y],
920 BYTE_ORDER_BIG_ENDIAN);
923 void SaveLevel(int level_nr)
926 char *filename = getLevelFilename(level_nr);
930 if (!(file = fopen(filename, MODE_WRITE)))
932 Error(ERR_WARN, "cannot save level file '%s'", filename);
937 /* check level field for 16-bit elements */
938 level.encoding_16bit_field = FALSE;
939 for(y=0; y<level.fieldy; y++)
940 for(x=0; x<level.fieldx; x++)
942 level.encoding_16bit_field = TRUE;
944 /* check yamyam content for 16-bit elements */
945 level.encoding_16bit_yamyam = FALSE;
946 for(i=0; i<level.num_yam_contents; i++)
949 if (level.yam_content[i][x][y] > 255)
950 level.encoding_16bit_yamyam = TRUE;
952 /* check amoeba content for 16-bit elements */
953 level.encoding_16bit_amoeba = FALSE;
954 if (level.amoeba_content > 255)
955 level.encoding_16bit_amoeba = TRUE;
958 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
960 putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
961 putFileChunk(file, "CAVE", CHUNK_SIZE_NONE, BYTE_ORDER_BIG_ENDIAN);
963 putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
964 WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
966 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
967 SaveLevel_HEAD(file, &level);
969 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
970 SaveLevel_AUTH(file, &level);
972 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
973 SaveLevel_BODY(file, &level);
975 if (level.encoding_16bit_yamyam ||
976 level.num_yam_contents != STD_ELEMENT_CONTENTS)
978 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
979 SaveLevel_CNT2(file, &level, EL_MAMPFER);
982 if (level.encoding_16bit_amoeba)
984 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
985 SaveLevel_CNT2(file, &level, EL_AMOEBE_BD);
990 SetFilePermissions(filename, PERMS_PRIVATE);
993 static void setTapeInfoToDefaults()
997 /* always start with reliable default values (empty tape) */
998 tape.file_version = FILE_VERSION_ACTUAL;
999 tape.game_version = GAME_VERSION_ACTUAL;
1002 /* default values (also for pre-1.2 tapes) with only the first player */
1003 tape.player_participates[0] = TRUE;
1004 for(i=1; i<MAX_PLAYERS; i++)
1005 tape.player_participates[i] = FALSE;
1007 /* at least one (default: the first) player participates in every tape */
1008 tape.num_participating_players = 1;
1010 tape.level_nr = level_nr;
1012 tape.changed = FALSE;
1014 tape.recording = FALSE;
1015 tape.playing = FALSE;
1016 tape.pausing = FALSE;
1019 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1021 ReadChunk_VERS(file, &(tape->file_version), &(tape->game_version));
1026 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1030 tape->random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1031 tape->date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1032 tape->length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1034 /* read header fields that are new since version 1.2 */
1035 if (tape->file_version >= FILE_VERSION_1_2)
1037 byte store_participating_players = fgetc(file);
1039 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1041 /* since version 1.2, tapes store which players participate in the tape */
1042 tape->num_participating_players = 0;
1043 for(i=0; i<MAX_PLAYERS; i++)
1045 tape->player_participates[i] = FALSE;
1047 if (store_participating_players & (1 << i))
1049 tape->player_participates[i] = TRUE;
1050 tape->num_participating_players++;
1058 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1061 int chunk_size_expected =
1062 (tape->num_participating_players + 1) * tape->length;
1064 if (chunk_size_expected != chunk_size)
1066 ReadUnusedBytesFromFile(file, chunk_size);
1067 return chunk_size_expected;
1070 for(i=0; i<tape->length; i++)
1072 if (i >= MAX_TAPELEN)
1075 for(j=0; j<MAX_PLAYERS; j++)
1077 tape->pos[i].action[j] = MV_NO_MOVING;
1079 if (tape->player_participates[j])
1080 tape->pos[i].action[j] = fgetc(file);
1083 tape->pos[i].delay = fgetc(file);
1085 if (tape->file_version == FILE_VERSION_1_0)
1087 /* eliminate possible diagonal moves in old tapes */
1088 /* this is only for backward compatibility */
1090 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1091 byte action = tape->pos[i].action[0];
1092 int k, num_moves = 0;
1096 if (action & joy_dir[k])
1098 tape->pos[i + num_moves].action[0] = joy_dir[k];
1100 tape->pos[i + num_moves].delay = 0;
1109 tape->length += num_moves;
1112 else if (tape->file_version < FILE_VERSION_2_0)
1114 if (tape->pos[i].delay > 1)
1117 tape->pos[i + 1] = tape->pos[i];
1118 tape->pos[i + 1].delay = 1;
1121 for(j=0; j<MAX_PLAYERS; j++)
1122 tape->pos[i].action[j] = MV_NO_MOVING;
1123 tape->pos[i].delay--;
1134 if (i != tape->length)
1135 chunk_size = (tape->num_participating_players + 1) * i;
1140 void LoadTape(int level_nr)
1142 char *filename = getTapeFilename(level_nr);
1143 char cookie[MAX_LINE_LEN];
1144 char chunk_name[CHUNK_ID_LEN + 1];
1148 /* always start with reliable default values */
1149 setTapeInfoToDefaults();
1151 if (!(file = fopen(filename, MODE_READ)))
1154 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
1155 if (strcmp(chunk_name, "RND1") == 0)
1157 getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN); /* not used */
1159 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
1160 if (strcmp(chunk_name, "TAPE") != 0)
1162 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1167 else /* check for pre-2.0 file format with cookie string */
1169 strcpy(cookie, chunk_name);
1170 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1171 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1172 cookie[strlen(cookie) - 1] = '\0';
1174 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1176 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1181 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1183 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1188 /* pre-2.0 tape files have no game version, so use file version here */
1189 tape.game_version = tape.file_version;
1192 if (tape.file_version < FILE_VERSION_1_2)
1194 /* tape files from versions before 1.2.0 without chunk structure */
1195 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1196 LoadTape_BODY(file, 2 * tape.length, &tape);
1204 int (*loader)(FILE *, int, struct TapeInfo *);
1208 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1209 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1210 { "BODY", -1, LoadTape_BODY },
1214 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
1218 while (chunk_info[i].name != NULL &&
1219 strcmp(chunk_name, chunk_info[i].name) != 0)
1222 if (chunk_info[i].name == NULL)
1224 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1225 chunk_name, filename);
1226 ReadUnusedBytesFromFile(file, chunk_size);
1228 else if (chunk_info[i].size != -1 &&
1229 chunk_info[i].size != chunk_size)
1231 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1232 chunk_size, chunk_name, filename);
1233 ReadUnusedBytesFromFile(file, chunk_size);
1237 /* call function to load this tape chunk */
1238 int chunk_size_expected =
1239 (chunk_info[i].loader)(file, chunk_size, &tape);
1241 /* the size of some chunks cannot be checked before reading other
1242 chunks first (like "HEAD" and "BODY") that contain some header
1243 information, so check them here */
1244 if (chunk_size_expected != chunk_size)
1246 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1247 chunk_size, chunk_name, filename);
1255 tape.length_seconds = GetTapeLength();
1258 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1261 byte store_participating_players = 0;
1263 /* set bits for participating players for compact storage */
1264 for(i=0; i<MAX_PLAYERS; i++)
1265 if (tape->player_participates[i])
1266 store_participating_players |= (1 << i);
1268 putFile32BitInteger(file, tape->random_seed, BYTE_ORDER_BIG_ENDIAN);
1269 putFile32BitInteger(file, tape->date, BYTE_ORDER_BIG_ENDIAN);
1270 putFile32BitInteger(file, tape->length, BYTE_ORDER_BIG_ENDIAN);
1272 fputc(store_participating_players, file);
1274 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1277 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1281 for(i=0; i<tape->length; i++)
1283 for(j=0; j<MAX_PLAYERS; j++)
1284 if (tape->player_participates[j])
1285 fputc(tape->pos[i].action[j], file);
1287 fputc(tape->pos[i].delay, file);
1291 void SaveTape(int level_nr)
1294 char *filename = getTapeFilename(level_nr);
1296 boolean new_tape = TRUE;
1297 int num_participating_players = 0;
1298 int body_chunk_size;
1300 InitTapeDirectory(leveldir_current->filename);
1302 /* if a tape still exists, ask to overwrite it */
1303 if (access(filename, F_OK) == 0)
1306 if (!Request("Replace old tape ?", REQ_ASK))
1310 if (!(file = fopen(filename, MODE_WRITE)))
1312 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1316 /* count number of participating players */
1317 for(i=0; i<MAX_PLAYERS; i++)
1318 if (tape.player_participates[i])
1319 num_participating_players++;
1321 body_chunk_size = (num_participating_players + 1) * tape.length;
1323 putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
1324 putFileChunk(file, "TAPE", CHUNK_SIZE_NONE, BYTE_ORDER_BIG_ENDIAN);
1326 putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
1327 WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
1329 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1330 SaveTape_HEAD(file, &tape);
1332 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1333 SaveTape_BODY(file, &tape);
1337 SetFilePermissions(filename, PERMS_PRIVATE);
1339 tape.changed = FALSE;
1342 Request("tape saved !", REQ_CONFIRM);
1345 void DumpTape(struct TapeInfo *tape)
1349 if (TAPE_IS_EMPTY(*tape))
1351 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1356 printf("-------------------------------------------------------------------------------\n");
1357 printf("Tape of Level %d (file version %06d, game version %06d\n",
1358 tape->level_nr, tape->file_version, tape->game_version);
1359 printf("-------------------------------------------------------------------------------\n");
1361 for(i=0; i<tape->length; i++)
1363 if (i >= MAX_TAPELEN)
1366 for(j=0; j<MAX_PLAYERS; j++)
1368 if (tape->player_participates[j])
1370 int action = tape->pos[i].action[j];
1372 printf("%d:%02x ", j, action);
1373 printf("[%c%c%c%c|%c%c] - ",
1374 (action & JOY_LEFT ? '<' : ' '),
1375 (action & JOY_RIGHT ? '>' : ' '),
1376 (action & JOY_UP ? '^' : ' '),
1377 (action & JOY_DOWN ? 'v' : ' '),
1378 (action & JOY_BUTTON_1 ? '1' : ' '),
1379 (action & JOY_BUTTON_2 ? '2' : ' '));
1383 printf("(%03d)\n", tape->pos[i].delay);
1386 printf("-------------------------------------------------------------------------------\n");
1389 void LoadScore(int level_nr)
1392 char *filename = getScoreFilename(level_nr);
1393 char cookie[MAX_LINE_LEN];
1394 char line[MAX_LINE_LEN];
1398 /* always start with reliable default values */
1399 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1401 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1402 highscore[i].Score = 0;
1405 if (!(file = fopen(filename, MODE_READ)))
1408 /* check file identifier */
1409 fgets(cookie, MAX_LINE_LEN, file);
1410 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1411 cookie[strlen(cookie) - 1] = '\0';
1413 if (!checkCookieString(cookie, SCORE_COOKIE))
1415 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1420 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1422 fscanf(file, "%d", &highscore[i].Score);
1423 fgets(line, MAX_LINE_LEN, file);
1425 if (line[strlen(line) - 1] == '\n')
1426 line[strlen(line) - 1] = '\0';
1428 for (line_ptr = line; *line_ptr; line_ptr++)
1430 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1432 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1433 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1442 void SaveScore(int level_nr)
1445 char *filename = getScoreFilename(level_nr);
1448 InitScoreDirectory(leveldir_current->filename);
1450 if (!(file = fopen(filename, MODE_WRITE)))
1452 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1456 fprintf(file, "%s\n\n", SCORE_COOKIE);
1458 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1459 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1463 SetFilePermissions(filename, PERMS_PUBLIC);
1466 /* ------------------------------------------------------------------------- */
1467 /* setup file stuff */
1468 /* ------------------------------------------------------------------------- */
1470 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1471 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1472 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1473 #define TOKEN_STR_PLAYER_PREFIX "player_"
1476 #define SETUP_TOKEN_PLAYER_NAME 0
1477 #define SETUP_TOKEN_SOUND 1
1478 #define SETUP_TOKEN_SOUND_LOOPS 2
1479 #define SETUP_TOKEN_SOUND_MUSIC 3
1480 #define SETUP_TOKEN_SOUND_SIMPLE 4
1481 #define SETUP_TOKEN_SCROLL_DELAY 5
1482 #define SETUP_TOKEN_SOFT_SCROLLING 6
1483 #define SETUP_TOKEN_FADING 7
1484 #define SETUP_TOKEN_AUTORECORD 8
1485 #define SETUP_TOKEN_QUICK_DOORS 9
1486 #define SETUP_TOKEN_TEAM_MODE 10
1487 #define SETUP_TOKEN_HANDICAP 11
1488 #define SETUP_TOKEN_TIME_LIMIT 12
1489 #define SETUP_TOKEN_FULLSCREEN 13
1492 #define SETUP_TOKEN_USE_JOYSTICK 14
1493 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
1494 #define SETUP_TOKEN_JOY_XLEFT 16
1495 #define SETUP_TOKEN_JOY_XMIDDLE 17
1496 #define SETUP_TOKEN_JOY_XRIGHT 18
1497 #define SETUP_TOKEN_JOY_YUPPER 19
1498 #define SETUP_TOKEN_JOY_YMIDDLE 20
1499 #define SETUP_TOKEN_JOY_YLOWER 21
1500 #define SETUP_TOKEN_JOY_SNAP 22
1501 #define SETUP_TOKEN_JOY_BOMB 23
1502 #define SETUP_TOKEN_KEY_LEFT 24
1503 #define SETUP_TOKEN_KEY_RIGHT 25
1504 #define SETUP_TOKEN_KEY_UP 26
1505 #define SETUP_TOKEN_KEY_DOWN 27
1506 #define SETUP_TOKEN_KEY_SNAP 28
1507 #define SETUP_TOKEN_KEY_BOMB 29
1509 /* level directory info */
1510 #define LEVELINFO_TOKEN_NAME 30
1511 #define LEVELINFO_TOKEN_NAME_SHORT 31
1512 #define LEVELINFO_TOKEN_NAME_SORTING 32
1513 #define LEVELINFO_TOKEN_AUTHOR 33
1514 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
1515 #define LEVELINFO_TOKEN_LEVELS 35
1516 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
1517 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
1518 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
1519 #define LEVELINFO_TOKEN_READONLY 39
1521 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1522 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
1524 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1525 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1527 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1528 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1530 static struct SetupInfo si;
1531 static struct SetupInputInfo sii;
1532 static struct LevelDirInfo ldi;
1533 static struct TokenInfo token_info[] =
1536 { TYPE_STRING, &si.player_name, "player_name" },
1537 { TYPE_SWITCH, &si.sound, "sound" },
1538 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1539 { TYPE_SWITCH, &si.sound_music, "background_music" },
1540 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1541 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1542 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1543 { TYPE_SWITCH, &si.fading, "screen_fading" },
1544 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1545 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1546 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1547 { TYPE_SWITCH, &si.handicap, "handicap" },
1548 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1549 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1552 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1553 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1554 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1555 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1556 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1557 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1558 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1559 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1560 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1561 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1562 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1563 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1564 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1565 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1566 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1567 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
1569 /* level directory info */
1570 { TYPE_STRING, &ldi.name, "name" },
1571 { TYPE_STRING, &ldi.name_short, "name_short" },
1572 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1573 { TYPE_STRING, &ldi.author, "author" },
1574 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1575 { TYPE_INTEGER, &ldi.levels, "levels" },
1576 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1577 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1578 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1579 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1582 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
1584 ldi->filename = NULL;
1585 ldi->fullpath = NULL;
1586 ldi->basepath = NULL;
1587 ldi->name = getStringCopy(ANONYMOUS_NAME);
1588 ldi->name_short = NULL;
1589 ldi->name_sorting = NULL;
1590 ldi->author = getStringCopy(ANONYMOUS_NAME);
1591 ldi->imported_from = NULL;
1593 ldi->first_level = 0;
1594 ldi->last_level = 0;
1595 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1596 ldi->level_group = FALSE;
1597 ldi->parent_link = FALSE;
1598 ldi->user_defined = FALSE;
1599 ldi->readonly = TRUE;
1601 ldi->class_desc = NULL;
1602 ldi->handicap_level = 0;
1604 ldi->cl_cursor = -1;
1606 ldi->node_parent = NULL;
1607 ldi->node_group = NULL;
1611 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
1612 struct LevelDirInfo *parent)
1616 setLevelDirInfoToDefaults(ldi);
1620 /* first copy all values from the parent structure ... */
1623 /* ... then set all fields to default that cannot be inherited from parent.
1624 This is especially important for all those fields that can be set from
1625 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1626 calls 'free()' for all already set token values which requires that no
1627 other structure's pointer may point to them!
1630 ldi->filename = NULL;
1631 ldi->fullpath = NULL;
1632 ldi->basepath = NULL;
1633 ldi->name = getStringCopy(ANONYMOUS_NAME);
1634 ldi->name_short = NULL;
1635 ldi->name_sorting = NULL;
1636 ldi->author = getStringCopy(parent->author);
1637 ldi->imported_from = getStringCopy(parent->imported_from);
1639 ldi->level_group = FALSE;
1640 ldi->parent_link = FALSE;
1642 ldi->node_parent = parent;
1643 ldi->node_group = NULL;
1647 static void setSetupInfoToDefaults(struct SetupInfo *si)
1651 si->player_name = getStringCopy(getLoginName());
1654 si->sound_loops = TRUE;
1655 si->sound_music = TRUE;
1656 si->sound_simple = TRUE;
1658 si->double_buffering = TRUE;
1659 si->direct_draw = !si->double_buffering;
1660 si->scroll_delay = TRUE;
1661 si->soft_scrolling = TRUE;
1663 si->autorecord = TRUE;
1664 si->quick_doors = FALSE;
1665 si->team_mode = FALSE;
1666 si->handicap = TRUE;
1667 si->time_limit = TRUE;
1668 si->fullscreen = FALSE;
1670 for (i=0; i<MAX_PLAYERS; i++)
1672 si->input[i].use_joystick = FALSE;
1673 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
1674 si->input[i].joy.xleft = JOYSTICK_XLEFT;
1675 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1676 si->input[i].joy.xright = JOYSTICK_XRIGHT;
1677 si->input[i].joy.yupper = JOYSTICK_YUPPER;
1678 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1679 si->input[i].joy.ylower = JOYSTICK_YLOWER;
1680 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
1681 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
1682 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
1683 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1684 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
1685 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
1686 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
1687 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
1691 static void setSetupInfo(int token_nr, char *token_value)
1693 int token_type = token_info[token_nr].type;
1694 void *setup_value = token_info[token_nr].value;
1696 if (token_value == NULL)
1699 /* set setup field to corresponding token value */
1704 *(boolean *)setup_value = get_string_boolean_value(token_value);
1708 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1712 *(int *)setup_value = get_string_integer_value(token_value);
1716 if (*(char **)setup_value != NULL)
1717 free(*(char **)setup_value);
1718 *(char **)setup_value = getStringCopy(token_value);
1726 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1730 if (!setup_file_list)
1733 /* handle global setup values */
1735 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
1736 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1739 /* handle player specific setup values */
1740 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1744 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1746 sii = setup.input[pnr];
1747 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
1749 char full_token[100];
1751 sprintf(full_token, "%s%s", prefix, token_info[i].text);
1752 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
1754 setup.input[pnr] = sii;
1758 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
1760 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
1761 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
1764 if (entry1->parent_link || entry2->parent_link)
1765 compare_result = (entry1->parent_link ? -1 : +1);
1766 else if (entry1->sort_priority == entry2->sort_priority)
1768 char *name1 = getStringToLower(entry1->name_sorting);
1769 char *name2 = getStringToLower(entry2->name_sorting);
1771 compare_result = strcmp(name1, name2);
1776 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1777 compare_result = entry1->sort_priority - entry2->sort_priority;
1779 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1781 return compare_result;
1784 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
1786 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1788 setLevelDirInfoToDefaults(leveldir_new);
1790 leveldir_new->node_parent = node_parent;
1791 leveldir_new->parent_link = TRUE;
1793 leveldir_new->name = ".. (parent directory)";
1794 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1795 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1797 leveldir_new->filename = "..";
1798 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
1800 leveldir_new->sort_priority = node_parent->sort_priority;
1801 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1803 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
1806 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
1807 struct LevelDirInfo *node_parent,
1808 char *level_directory)
1811 struct dirent *dir_entry;
1812 boolean valid_entry_found = FALSE;
1814 if ((dir = opendir(level_directory)) == NULL)
1816 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1820 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1822 struct SetupFileList *setup_file_list = NULL;
1823 struct stat file_status;
1824 char *directory_name = dir_entry->d_name;
1825 char *directory_path = getPath2(level_directory, directory_name);
1826 char *filename = NULL;
1828 /* skip entries for current and parent directory */
1829 if (strcmp(directory_name, ".") == 0 ||
1830 strcmp(directory_name, "..") == 0)
1832 free(directory_path);
1836 /* find out if directory entry is itself a directory */
1837 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1838 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1840 free(directory_path);
1844 filename = getPath2(directory_path, LEVELINFO_FILENAME);
1845 setup_file_list = loadSetupFileList(filename);
1847 if (setup_file_list)
1849 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
1852 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
1853 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
1855 /* set all structure fields according to the token/value pairs */
1856 ldi = *leveldir_new;
1857 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1858 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
1859 *leveldir_new = ldi;
1861 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1863 if (leveldir_new->name_short == NULL)
1864 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1866 if (leveldir_new->name_sorting == NULL)
1867 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1869 leveldir_new->filename = getStringCopy(directory_name);
1871 if (node_parent == NULL) /* top level group */
1873 leveldir_new->basepath = level_directory;
1874 leveldir_new->fullpath = leveldir_new->filename;
1876 else /* sub level group */
1878 leveldir_new->basepath = node_parent->basepath;
1879 leveldir_new->fullpath = getPath2(node_parent->fullpath,
1883 if (leveldir_new->levels < 1)
1884 leveldir_new->levels = 1;
1886 leveldir_new->last_level =
1887 leveldir_new->first_level + leveldir_new->levels - 1;
1889 leveldir_new->user_defined =
1890 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1892 leveldir_new->color = LEVELCOLOR(leveldir_new);
1893 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1895 leveldir_new->handicap_level = /* set handicap to default value */
1896 (leveldir_new->user_defined ?
1897 leveldir_new->last_level :
1898 leveldir_new->first_level);
1900 pushLevelDirInfo(node_first, leveldir_new);
1902 freeSetupFileList(setup_file_list);
1903 valid_entry_found = TRUE;
1905 if (leveldir_new->level_group)
1907 /* create node to link back to current level directory */
1908 createParentLevelDirNode(leveldir_new);
1910 /* step into sub-directory and look for more level series */
1911 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1912 leveldir_new, directory_path);
1916 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1918 free(directory_path);
1924 if (!valid_entry_found)
1925 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1929 void LoadLevelInfo()
1931 InitUserLevelDirectory(getLoginName());
1933 DrawInitText("Loading level series:", 120, FC_GREEN);
1935 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1936 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
1938 leveldir_current = getFirstValidLevelSeries(leveldir_first);
1940 if (leveldir_first == NULL)
1941 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1943 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
1946 dumpLevelDirInfo(leveldir_first, 0);
1950 static void SaveUserLevelInfo()
1956 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1958 if (!(file = fopen(filename, MODE_WRITE)))
1960 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1965 /* always start with reliable default values */
1966 setLevelDirInfoToDefaults(&ldi);
1968 ldi.name = getLoginName();
1969 ldi.author = getRealName();
1971 ldi.first_level = 1;
1972 ldi.sort_priority = LEVELCLASS_USER_START;
1973 ldi.readonly = FALSE;
1975 fprintf(file, "%s\n\n",
1976 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
1978 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
1979 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1980 i != LEVELINFO_TOKEN_NAME_SORTING &&
1981 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1982 fprintf(file, "%s\n", getSetupLine("", i));
1987 SetFilePermissions(filename, PERMS_PRIVATE);
1993 struct SetupFileList *setup_file_list = NULL;
1995 /* always start with reliable default values */
1996 setSetupInfoToDefaults(&setup);
1998 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2000 setup_file_list = loadSetupFileList(filename);
2002 if (setup_file_list)
2004 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
2005 decodeSetupFileList(setup_file_list);
2007 setup.direct_draw = !setup.double_buffering;
2009 freeSetupFileList(setup_file_list);
2011 /* needed to work around problems with fixed length strings */
2012 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
2013 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
2014 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
2016 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2018 strcpy(new_name, setup.player_name);
2019 free(setup.player_name);
2020 setup.player_name = new_name;
2024 Error(ERR_WARN, "using default setup values");
2029 static char *getSetupLine(char *prefix, int token_nr)
2032 static char entry[MAX_LINE_LEN];
2033 int token_type = token_info[token_nr].type;
2034 void *setup_value = token_info[token_nr].value;
2035 char *token_text = token_info[token_nr].text;
2037 /* start with the prefix, token and some spaces to format output line */
2038 sprintf(entry, "%s%s:", prefix, token_text);
2039 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2042 /* continue with the token's value (which can have different types) */
2046 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2050 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2055 Key key = *(Key *)setup_value;
2056 char *keyname = getKeyNameFromKey(key);
2058 strcat(entry, getX11KeyNameFromKey(key));
2059 for (i=strlen(entry); i<50; i++)
2062 /* add comment, if useful */
2063 if (strcmp(keyname, "(undefined)") != 0 &&
2064 strcmp(keyname, "(unknown)") != 0)
2066 strcat(entry, "# ");
2067 strcat(entry, keyname);
2074 char buffer[MAX_LINE_LEN];
2076 sprintf(buffer, "%d", *(int *)setup_value);
2077 strcat(entry, buffer);
2082 strcat(entry, *(char **)setup_value);
2098 InitUserDataDirectory();
2100 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2102 if (!(file = fopen(filename, MODE_WRITE)))
2104 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2109 fprintf(file, "%s\n",
2110 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2111 fprintf(file, "\n");
2113 /* handle global setup values */
2115 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2117 fprintf(file, "%s\n", getSetupLine("", i));
2119 /* just to make things nicer :) */
2120 if (i == SETUP_TOKEN_PLAYER_NAME)
2121 fprintf(file, "\n");
2124 /* handle player specific setup values */
2125 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2129 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2130 fprintf(file, "\n");
2132 sii = setup.input[pnr];
2133 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2134 fprintf(file, "%s\n", getSetupLine(prefix, i));
2140 SetFilePermissions(filename, PERMS_PRIVATE);
2143 void LoadLevelSetup_LastSeries()
2146 struct SetupFileList *level_setup_list = NULL;
2148 /* always start with reliable default values */
2149 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2151 /* ----------------------------------------------------------------------- */
2152 /* ~/.rocksndiamonds/levelsetup.conf */
2153 /* ----------------------------------------------------------------------- */
2155 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2157 if ((level_setup_list = loadSetupFileList(filename)))
2159 char *last_level_series =
2160 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2162 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2163 if (leveldir_current == NULL)
2164 leveldir_current = leveldir_first;
2166 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2168 freeSetupFileList(level_setup_list);
2171 Error(ERR_WARN, "using default setup values");
2176 void SaveLevelSetup_LastSeries()
2179 char *level_subdir = leveldir_current->filename;
2182 /* ----------------------------------------------------------------------- */
2183 /* ~/.rocksndiamonds/levelsetup.conf */
2184 /* ----------------------------------------------------------------------- */
2186 InitUserDataDirectory();
2188 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2190 if (!(file = fopen(filename, MODE_WRITE)))
2192 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2197 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2198 LEVELSETUP_COOKIE));
2199 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2205 SetFilePermissions(filename, PERMS_PRIVATE);
2208 static void checkSeriesInfo()
2210 static char *level_directory = NULL;
2212 struct dirent *dir_entry;
2214 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2216 level_directory = getPath2((leveldir_current->user_defined ?
2217 getUserLevelDir("") :
2218 options.level_directory),
2219 leveldir_current->fullpath);
2221 if ((dir = opendir(level_directory)) == NULL)
2223 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2227 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2229 if (strlen(dir_entry->d_name) > 4 &&
2230 dir_entry->d_name[3] == '.' &&
2231 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2233 char levelnum_str[4];
2236 strncpy(levelnum_str, dir_entry->d_name, 3);
2237 levelnum_str[3] = '\0';
2239 levelnum_value = atoi(levelnum_str);
2241 if (levelnum_value < leveldir_current->first_level)
2243 Error(ERR_WARN, "additional level %d found", levelnum_value);
2244 leveldir_current->first_level = levelnum_value;
2246 else if (levelnum_value > leveldir_current->last_level)
2248 Error(ERR_WARN, "additional level %d found", levelnum_value);
2249 leveldir_current->last_level = levelnum_value;
2257 void LoadLevelSetup_SeriesInfo()
2260 struct SetupFileList *level_setup_list = NULL;
2261 char *level_subdir = leveldir_current->filename;
2263 /* always start with reliable default values */
2264 level_nr = leveldir_current->first_level;
2266 checkSeriesInfo(leveldir_current);
2268 /* ----------------------------------------------------------------------- */
2269 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2270 /* ----------------------------------------------------------------------- */
2272 level_subdir = leveldir_current->filename;
2274 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2276 if ((level_setup_list = loadSetupFileList(filename)))
2280 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2284 level_nr = atoi(token_value);
2286 if (level_nr < leveldir_current->first_level)
2287 level_nr = leveldir_current->first_level;
2288 if (level_nr > leveldir_current->last_level)
2289 level_nr = leveldir_current->last_level;
2292 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2296 int level_nr = atoi(token_value);
2298 if (level_nr < leveldir_current->first_level)
2299 level_nr = leveldir_current->first_level;
2300 if (level_nr > leveldir_current->last_level + 1)
2301 level_nr = leveldir_current->last_level;
2303 if (leveldir_current->user_defined)
2304 level_nr = leveldir_current->last_level;
2306 leveldir_current->handicap_level = level_nr;
2309 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2311 freeSetupFileList(level_setup_list);
2314 Error(ERR_WARN, "using default setup values");
2319 void SaveLevelSetup_SeriesInfo()
2322 char *level_subdir = leveldir_current->filename;
2323 char *level_nr_str = int2str(level_nr, 0);
2324 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2327 /* ----------------------------------------------------------------------- */
2328 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2329 /* ----------------------------------------------------------------------- */
2331 InitLevelSetupDirectory(level_subdir);
2333 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2335 if (!(file = fopen(filename, MODE_WRITE)))
2337 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2342 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2343 LEVELSETUP_COOKIE));
2344 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2346 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2347 handicap_level_str));
2352 SetFilePermissions(filename, PERMS_PRIVATE);