1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2001 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
18 #include "libgame/libgame.h"
25 #define MAX_FILENAME_LEN 256 /* maximal filename length */
26 #define MAX_LINE_LEN 1000 /* maximal input line length */
27 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
28 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
29 #define LEVEL_HEADER_UNUSED 15 /* unused level header bytes */
30 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
31 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
32 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
33 #define TAPE_HEADER_UNUSED 7 /* unused tape header bytes */
35 /* file identifier strings */
36 #define LEVEL_COOKIE "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_2.0"
37 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
38 #define TAPE_COOKIE "ROCKSNDIAMONDS_TAPE_FILE_VERSION_2.0"
39 #define SETUP_COOKIE "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
40 #define LEVELSETUP_COOKIE "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
41 #define LEVELINFO_COOKIE "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
42 /* old file identifiers for backward compatibility */
43 #define LEVEL_COOKIE_10 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0"
44 #define LEVEL_COOKIE_12 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.2"
45 #define LEVEL_COOKIE_14 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.4"
46 #define TAPE_COOKIE_10 "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
47 #define TAPE_COOKIE_12 "ROCKSNDIAMONDS_TAPE_FILE_VERSION_1.2"
49 /* file names and filename extensions */
50 #if !defined(PLATFORM_MSDOS)
51 #define LEVELSETUP_DIRECTORY "levelsetup"
52 #define SETUP_FILENAME "setup.conf"
53 #define LEVELSETUP_FILENAME "levelsetup.conf"
54 #define LEVELINFO_FILENAME "levelinfo.conf"
55 #define LEVELFILE_EXTENSION "level"
56 #define TAPEFILE_EXTENSION "tape"
57 #define SCOREFILE_EXTENSION "score"
59 #define LEVELSETUP_DIRECTORY "lvlsetup"
60 #define SETUP_FILENAME "setup.cnf"
61 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
62 #define LEVELINFO_FILENAME "lvlinfo.cnf"
63 #define LEVELFILE_EXTENSION "lvl"
64 #define TAPEFILE_EXTENSION "tap"
65 #define SCOREFILE_EXTENSION "sco"
68 #if defined(PLATFORM_WIN32)
70 #define S_IRGRP S_IRUSR
73 #define S_IROTH S_IRUSR
76 #define S_IWGRP S_IWUSR
79 #define S_IWOTH S_IWUSR
82 #define S_IXGRP S_IXUSR
85 #define S_IXOTH S_IXUSR
87 #endif /* PLATFORM_WIN32 */
89 /* file permissions for newly written files */
90 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
91 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
92 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
93 #define LEVEL_PERMS (MODE_R_ALL | MODE_W_ALL)
94 #define SCORE_PERMS LEVEL_PERMS
95 #define TAPE_PERMS LEVEL_PERMS
96 #define SETUP_PERMS LEVEL_PERMS
98 /* sort priorities of level series (also used as level series classes) */
99 #define LEVELCLASS_TUTORIAL_START 10
100 #define LEVELCLASS_TUTORIAL_END 99
101 #define LEVELCLASS_CLASSICS_START 100
102 #define LEVELCLASS_CLASSICS_END 199
103 #define LEVELCLASS_CONTRIBUTION_START 200
104 #define LEVELCLASS_CONTRIBUTION_END 299
105 #define LEVELCLASS_USER_START 300
106 #define LEVELCLASS_USER_END 399
107 #define LEVELCLASS_BD_START 400
108 #define LEVELCLASS_BD_END 499
109 #define LEVELCLASS_EM_START 500
110 #define LEVELCLASS_EM_END 599
111 #define LEVELCLASS_SP_START 600
112 #define LEVELCLASS_SP_END 699
113 #define LEVELCLASS_DX_START 700
114 #define LEVELCLASS_DX_END 799
116 #define LEVELCLASS_TUTORIAL LEVELCLASS_TUTORIAL_START
117 #define LEVELCLASS_CLASSICS LEVELCLASS_CLASSICS_START
118 #define LEVELCLASS_CONTRIBUTION LEVELCLASS_CONTRIBUTION_START
119 #define LEVELCLASS_USER LEVELCLASS_USER_START
120 #define LEVELCLASS_BD LEVELCLASS_BD_START
121 #define LEVELCLASS_EM LEVELCLASS_EM_START
122 #define LEVELCLASS_SP LEVELCLASS_SP_START
123 #define LEVELCLASS_DX LEVELCLASS_DX_START
125 #define LEVELCLASS_UNDEFINED 999
127 #define NUM_LEVELCLASS_DESC 8
128 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
140 #define IS_LEVELCLASS_TUTORIAL(p) \
141 ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
142 (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
143 #define IS_LEVELCLASS_CLASSICS(p) \
144 ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
145 (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
146 #define IS_LEVELCLASS_CONTRIBUTION(p) \
147 ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
148 (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
149 #define IS_LEVELCLASS_USER(p) \
150 ((p)->sort_priority >= LEVELCLASS_USER_START && \
151 (p)->sort_priority <= LEVELCLASS_USER_END)
152 #define IS_LEVELCLASS_BD(p) \
153 ((p)->sort_priority >= LEVELCLASS_BD_START && \
154 (p)->sort_priority <= LEVELCLASS_BD_END)
155 #define IS_LEVELCLASS_EM(p) \
156 ((p)->sort_priority >= LEVELCLASS_EM_START && \
157 (p)->sort_priority <= LEVELCLASS_EM_END)
158 #define IS_LEVELCLASS_SP(p) \
159 ((p)->sort_priority >= LEVELCLASS_SP_START && \
160 (p)->sort_priority <= LEVELCLASS_SP_END)
161 #define IS_LEVELCLASS_DX(p) \
162 ((p)->sort_priority >= LEVELCLASS_DX_START && \
163 (p)->sort_priority <= LEVELCLASS_DX_END)
165 #define LEVELCLASS(n) (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
166 IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
167 IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
168 IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
169 IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
170 IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
171 IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
172 IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
173 LEVELCLASS_UNDEFINED)
175 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
176 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
177 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
178 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
179 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
180 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
181 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
182 IS_LEVELCLASS_USER(n) ? FC_RED : \
185 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
186 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
187 IS_LEVELCLASS_BD(n) ? 2 : \
188 IS_LEVELCLASS_EM(n) ? 3 : \
189 IS_LEVELCLASS_SP(n) ? 4 : \
190 IS_LEVELCLASS_DX(n) ? 5 : \
191 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
192 IS_LEVELCLASS_USER(n) ? 7 : \
195 static int getFileVersionFromCookieString(const char *cookie)
197 const char *ptr_cookie1, *ptr_cookie2;
198 const char *pattern1 = "_FILE_VERSION_";
199 const char *pattern2 = "?.?";
200 const int len_cookie = strlen(cookie);
201 const int len_pattern1 = strlen(pattern1);
202 const int len_pattern2 = strlen(pattern2);
203 const int len_pattern = len_pattern1 + len_pattern2;
204 int version_major, version_minor;
206 if (len_cookie <= len_pattern)
209 ptr_cookie1 = &cookie[len_cookie - len_pattern];
210 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
212 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
215 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
216 ptr_cookie2[1] != '.' ||
217 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
220 version_major = ptr_cookie2[0] - '0';
221 version_minor = ptr_cookie2[2] - '0';
223 return (version_major * 10 + version_minor);
226 boolean checkCookieString(const char *cookie, const char *template)
228 const char *pattern = "_FILE_VERSION_?.?";
229 const int len_cookie = strlen(cookie);
230 const int len_template = strlen(template);
231 const int len_pattern = strlen(pattern);
233 if (len_cookie != len_template)
236 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
242 char *getLevelClassDescription(struct LevelDirInfo *ldi)
244 int position = ldi->sort_priority / 100;
246 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
247 return levelclass_desc[position];
249 return "Unknown Level Class";
252 static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */
253 static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */
255 static char *getSetupDir()
257 return getUserDataDir();
260 static char *getUserLevelDir(char *level_subdir)
262 static char *userlevel_dir = NULL;
263 char *data_dir = getUserDataDir();
264 char *userlevel_subdir = LEVELS_DIRECTORY;
269 if (strlen(level_subdir) > 0)
270 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
272 userlevel_dir = getPath2(data_dir, userlevel_subdir);
274 return userlevel_dir;
277 static char *getTapeDir(char *level_subdir)
279 static char *tape_dir = NULL;
280 char *data_dir = getUserDataDir();
281 char *tape_subdir = TAPES_DIRECTORY;
286 if (strlen(level_subdir) > 0)
287 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
289 tape_dir = getPath2(data_dir, tape_subdir);
294 static char *getScoreDir(char *level_subdir)
296 static char *score_dir = NULL;
297 char *data_dir = options.rw_base_directory;
298 char *score_subdir = SCORES_DIRECTORY;
303 if (strlen(level_subdir) > 0)
304 score_dir = getPath3(data_dir, score_subdir, level_subdir);
306 score_dir = getPath2(data_dir, score_subdir);
311 static char *getLevelSetupDir(char *level_subdir)
313 static char *levelsetup_dir = NULL;
314 char *data_dir = getUserDataDir();
315 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
318 free(levelsetup_dir);
320 if (strlen(level_subdir) > 0)
321 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
323 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
325 return levelsetup_dir;
328 static char *getLevelFilename(int nr)
330 static char *filename = NULL;
331 char basename[MAX_FILENAME_LEN];
333 if (filename != NULL)
336 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
337 filename = getPath3((leveldir_current->user_defined ?
338 getUserLevelDir("") :
339 options.level_directory),
340 leveldir_current->fullpath,
346 static char *getTapeFilename(int nr)
348 static char *filename = NULL;
349 char basename[MAX_FILENAME_LEN];
351 if (filename != NULL)
354 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
355 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
360 static char *getScoreFilename(int nr)
362 static char *filename = NULL;
363 char basename[MAX_FILENAME_LEN];
365 if (filename != NULL)
368 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
369 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
374 static void InitTapeDirectory(char *level_subdir)
376 createDirectory(getUserDataDir(), "user data");
377 createDirectory(getTapeDir(""), "main tape");
378 createDirectory(getTapeDir(level_subdir), "level tape");
381 static void InitScoreDirectory(char *level_subdir)
383 createDirectory(getScoreDir(""), "main score");
384 createDirectory(getScoreDir(level_subdir), "level score");
387 static void InitUserLevelDirectory(char *level_subdir)
389 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
391 createDirectory(getUserDataDir(), "user data");
392 createDirectory(getUserLevelDir(""), "main user level");
393 createDirectory(getUserLevelDir(level_subdir), "user level");
399 static void InitLevelSetupDirectory(char *level_subdir)
401 createDirectory(getUserDataDir(), "user data");
402 createDirectory(getLevelSetupDir(""), "main level setup");
403 createDirectory(getLevelSetupDir(level_subdir), "level setup");
406 static void setLevelInfoToDefaults()
410 level.file_version = FILE_VERSION_ACTUAL;
411 level.game_version = GAME_VERSION_ACTUAL;
413 level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
414 level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
415 level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
417 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
418 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
420 for(x=0; x<MAX_LEV_FIELDX; x++)
421 for(y=0; y<MAX_LEV_FIELDY; y++)
422 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
425 level.gems_needed = 0;
426 level.amoeba_speed = 10;
427 level.time_magic_wall = 10;
428 level.time_wheel = 10;
429 level.time_light = 10;
430 level.time_timegate = 10;
431 level.amoeba_content = EL_DIAMANT;
432 level.double_speed = FALSE;
433 level.gravity = FALSE;
435 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
436 level.name[i] = '\0';
437 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
438 level.author[i] = '\0';
440 strcpy(level.name, NAMELESS_LEVEL_NAME);
441 strcpy(level.author, ANONYMOUS_NAME);
443 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
446 level.num_yam_contents = STD_ELEMENT_CONTENTS;
447 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
450 level.yam_content[i][x][y] =
451 (i < STD_ELEMENT_CONTENTS ? EL_FELSBROCKEN : EL_LEERRAUM);
453 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
454 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
455 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
457 BorderElement = EL_BETON;
459 /* try to determine better author name than 'anonymous' */
460 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
462 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
463 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
467 switch (LEVELCLASS(leveldir_current))
469 case LEVELCLASS_TUTORIAL:
470 strcpy(level.author, PROGRAM_AUTHOR_STRING);
473 case LEVELCLASS_CONTRIBUTION:
474 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
475 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
478 case LEVELCLASS_USER:
479 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
480 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
484 /* keep default value */
490 static int checkLevelElement(int element)
492 if (element >= EL_FIRST_RUNTIME_EL)
494 Error(ERR_WARN, "invalid level element %d", element);
495 element = EL_CHAR_FRAGE;
501 void OLD_LoadLevel(int level_nr)
504 char *filename = getLevelFilename(level_nr);
505 char cookie[MAX_LINE_LEN];
506 char chunk_name[CHUNK_ID_LEN + 1];
507 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
508 int file_version = FILE_VERSION_ACTUAL;
512 /* always start with reliable default values */
513 setLevelInfoToDefaults();
515 if (!(file = fopen(filename, MODE_READ)))
517 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
521 /* check file identifier */
522 fgets(cookie, MAX_LINE_LEN, file);
523 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
524 cookie[strlen(cookie) - 1] = '\0';
527 if (strcmp(cookie, LEVEL_COOKIE_10) == 0) /* old 1.0 level format */
528 file_version = FILE_VERSION_1_0;
529 else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
530 file_version = FILE_VERSION_1_2;
531 else if (strcmp(cookie, LEVEL_COOKIE) != 0) /* unknown level format */
533 Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
538 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
540 Error(ERR_WARN, "unknown format of level file '%s'", filename);
545 file_version = getFileVersionFromCookieString(cookie);
548 level.file_version = file_version;
550 /* read chunk "HEAD" */
551 if (file_version >= FILE_VERSION_1_2)
553 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
554 if (strcmp(chunk_name, "HEAD") || chunk_size != LEVEL_HEADER_SIZE)
556 Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
562 lev_fieldx = level.fieldx = fgetc(file);
563 lev_fieldy = level.fieldy = fgetc(file);
565 level.time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
566 level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
568 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
569 level.name[i] = fgetc(file);
570 level.name[MAX_LEVEL_NAME_LEN] = 0;
572 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
573 level.score[i] = fgetc(file);
575 level.num_yam_contents = STD_ELEMENT_CONTENTS;
576 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
582 if (i < STD_ELEMENT_CONTENTS)
583 level.yam_content[i][x][y] = checkLevelElement(fgetc(file));
585 level.yam_content[i][x][y] = EL_LEERRAUM;
590 level.amoeba_speed = fgetc(file);
591 level.time_magic_wall = fgetc(file);
592 level.time_wheel = fgetc(file);
593 level.amoeba_content = checkLevelElement(fgetc(file));
594 level.double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
595 level.gravity = (fgetc(file) == 1 ? TRUE : FALSE);
597 encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
599 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
602 if (file_version >= FILE_VERSION_1_2)
604 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
606 /* look for optional author chunk */
607 if (strcmp(chunk_name, "AUTH") == 0 && chunk_size == MAX_LEVEL_AUTHOR_LEN)
609 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
610 level.author[i] = fgetc(file);
611 level.author[MAX_LEVEL_NAME_LEN] = 0;
613 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
616 /* look for optional content chunk */
617 if (strcmp(chunk_name, "CONT") == 0 &&
618 chunk_size == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
621 level.num_yam_contents = fgetc(file);
625 if (level.num_yam_contents < 1 ||
626 level.num_yam_contents > MAX_ELEMENT_CONTENTS)
629 printf("WARNING: num_yam_contents == %d (corrected)\n",
630 level.num_yam_contents);
632 level.num_yam_contents = STD_ELEMENT_CONTENTS;
635 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
638 level.yam_content[i][x][y] =
639 checkLevelElement(encoding_16bit ?
640 getFile16BitInteger(file,
641 BYTE_ORDER_BIG_ENDIAN) :
644 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
647 /* next check body chunk identifier and chunk size */
648 if (strcmp(chunk_name, "BODY") != 0 ||
649 chunk_size != lev_fieldx * lev_fieldy)
651 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
657 /* clear all other level fields (needed if resized in level editor later) */
658 for(x=0; x<MAX_LEV_FIELDX; x++)
659 for(y=0; y<MAX_LEV_FIELDY; y++)
660 Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
662 /* now read in the valid level fields from level file */
663 for(y=0; y<lev_fieldy; y++)
664 for(x=0; x<lev_fieldx; x++)
665 Feld[x][y] = Ur[x][y] =
666 checkLevelElement(encoding_16bit ?
667 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
672 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
673 IS_LEVELCLASS_USER(leveldir_current))
675 /* for user contributed and private levels, use the version of
676 the game engine the levels were created for */
677 level.game_version = file_version;
679 /* player was faster than monsters in pre-1.0 levels */
680 if (file_version == FILE_VERSION_1_0)
682 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
683 Error(ERR_WARN, "using high speed movement for player");
684 level.double_speed = TRUE;
689 /* always use the latest version of the game engine for all but
690 user contributed and private levels */
691 level.game_version = GAME_VERSION_ACTUAL;
694 /* determine border element for this level */
698 static void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
704 static void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
710 static int LoadLevel_HEAD(struct LevelInfo *level, FILE *file, int chunk_size)
714 lev_fieldx = level->fieldx = fgetc(file);
715 lev_fieldy = level->fieldy = fgetc(file);
717 level->time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
718 level->gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
720 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
721 level->name[i] = fgetc(file);
722 level->name[MAX_LEVEL_NAME_LEN] = 0;
724 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
725 level->score[i] = fgetc(file);
727 level->num_yam_contents = STD_ELEMENT_CONTENTS;
728 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
731 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
733 level->amoeba_speed = fgetc(file);
734 level->time_magic_wall = fgetc(file);
735 level->time_wheel = fgetc(file);
736 level->amoeba_content = checkLevelElement(fgetc(file));
737 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
738 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
740 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
742 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
747 static int LoadLevel_AUTH(struct LevelInfo *level, FILE *file, int chunk_size)
751 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
752 level->author[i] = fgetc(file);
753 level->author[MAX_LEVEL_NAME_LEN] = 0;
758 static int LoadLevel_CONT(struct LevelInfo *level, FILE *file, int chunk_size)
762 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
763 int chunk_size_expected = header_size + content_size;
765 /* Note: "chunk_size" was wrong before version 2.0 when elements are
766 stored with 16-bit encoding (and should be twice as big then).
767 Even worse, playfield data was stored 16-bit when only yamyam content
768 contained 16-bit elements and vice versa. */
770 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
771 chunk_size_expected += content_size;
773 if (chunk_size_expected != chunk_size)
775 ReadUnusedBytesFromFile(file, chunk_size);
776 return chunk_size_expected;
780 level->num_yam_contents = fgetc(file);
784 /* correct invalid number of content fields -- should never happen */
785 if (level->num_yam_contents < 1 ||
786 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
787 level->num_yam_contents = STD_ELEMENT_CONTENTS;
789 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
792 level->yam_content[i][x][y] =
793 checkLevelElement(level->encoding_16bit_field ?
794 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
799 static int LoadLevel_BODY(struct LevelInfo *level, FILE *file, int chunk_size)
802 int chunk_size_expected = level->fieldx * level->fieldy;
804 /* Note: "chunk_size" was wrong before version 2.0 when elements are
805 stored with 16-bit encoding (and should be twice as big then).
806 Even worse, playfield data was stored 16-bit when only yamyam content
807 contained 16-bit elements and vice versa. */
809 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
810 chunk_size_expected *= 2;
812 if (chunk_size_expected != chunk_size)
814 ReadUnusedBytesFromFile(file, chunk_size);
815 return chunk_size_expected;
818 for(y=0; y<level->fieldy; y++)
819 for(x=0; x<level->fieldx; x++)
820 Feld[x][y] = Ur[x][y] =
821 checkLevelElement(level->encoding_16bit_field ?
822 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
827 static int LoadLevel_CNT2(struct LevelInfo *level, FILE *file, int chunk_size)
831 int num_contents, content_xsize, content_ysize;
832 int content_array[MAX_ELEMENT_CONTENTS][3][3];
834 element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
835 num_contents = fgetc(file);
836 content_xsize = fgetc(file);
837 content_ysize = fgetc(file);
838 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
840 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
843 content_array[i][x][y] =
844 checkLevelElement(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
846 /* correct invalid number of content fields -- should never happen */
847 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
848 num_contents = STD_ELEMENT_CONTENTS;
850 if (element == EL_MAMPFER)
852 level->num_yam_contents = num_contents;
854 for(i=0; i<num_contents; i++)
857 level->yam_content[i][x][y] = content_array[i][x][y];
859 else if (element == EL_AMOEBE_BD)
861 level->amoeba_content = content_array[0][0][0];
865 Error(ERR_WARN, "cannot load content for element '%d'", element);
871 void LoadLevel(int level_nr)
873 char *filename = getLevelFilename(level_nr);
874 char cookie[MAX_LINE_LEN];
875 char chunk_name[CHUNK_ID_LEN + 1];
879 /* always start with reliable default values */
880 setLevelInfoToDefaults();
882 if (!(file = fopen(filename, MODE_READ)))
884 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
888 /* check file identifier */
889 fgets(cookie, MAX_LINE_LEN, file);
890 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
891 cookie[strlen(cookie) - 1] = '\0';
893 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
895 Error(ERR_WARN, "unknown format of level file '%s'", filename);
900 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
902 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
907 if (level.file_version < FILE_VERSION_1_2)
909 /* level files from versions before 1.2.0 without chunk structure */
910 LoadLevel_HEAD(&level, file, LEVEL_HEADER_SIZE);
911 LoadLevel_BODY(&level, file, level.fieldx * level.fieldy);
919 int (*loader)(struct LevelInfo *, FILE *, int);
923 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
924 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
925 { "CONT", -1, LoadLevel_CONT },
926 { "BODY", -1, LoadLevel_BODY },
927 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
931 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
935 while (chunk_info[i].name != NULL &&
936 strcmp(chunk_name, chunk_info[i].name) != 0)
939 if (chunk_info[i].name == NULL)
941 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
942 chunk_name, filename);
943 ReadUnusedBytesFromFile(file, chunk_size);
945 else if (chunk_info[i].size != -1 &&
946 chunk_info[i].size != chunk_size)
948 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
949 chunk_size, chunk_name, filename);
950 ReadUnusedBytesFromFile(file, chunk_size);
954 /* call function to load this level chunk */
955 int chunk_size_expected =
956 (chunk_info[i].loader)(&level, file, chunk_size);
958 /* the size of some chunks cannot be checked before reading other
959 chunks first (like "HEAD" and "BODY") that contain some header
960 information, so check them here */
961 if (chunk_size_expected != chunk_size)
963 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
964 chunk_size, chunk_name, filename);
972 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
973 IS_LEVELCLASS_USER(leveldir_current))
975 /* for user contributed and private levels, use the version of
976 the game engine the levels were created for */
977 level.game_version = level.file_version;
979 /* player was faster than monsters in pre-1.0 levels */
980 if (level.file_version == FILE_VERSION_1_0)
982 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
983 Error(ERR_WARN, "using high speed movement for player");
984 level.double_speed = TRUE;
989 /* always use the latest version of the game engine for all but
990 user contributed and private levels */
991 level.game_version = GAME_VERSION_ACTUAL;
994 /* determine border element for this level */
998 void OLD_SaveLevel(int level_nr)
1001 char *filename = getLevelFilename(level_nr);
1003 boolean encoding_16bit_amoeba = FALSE;
1004 boolean encoding_16bit_yamyam = FALSE;
1006 boolean encoding_16bit = FALSE; /* default: only 8-bit elements */
1007 char *oldest_possible_cookie;
1010 if (!(file = fopen(filename, MODE_WRITE)))
1012 Error(ERR_WARN, "cannot save level file '%s'", filename);
1016 /* check yam content for 16-bit elements */
1017 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1020 if (level.yam_content[i][x][y] > 255)
1021 encoding_16bit = TRUE;
1023 /* check level field for 16-bit elements */
1024 for(y=0; y<lev_fieldy; y++)
1025 for(x=0; x<lev_fieldx; x++)
1027 encoding_16bit = TRUE;
1029 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
1031 fputs(oldest_possible_cookie, file); /* file identifier */
1034 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1036 fputc(level.fieldx, file);
1037 fputc(level.fieldy, file);
1039 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
1040 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
1042 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1043 fputc(level.name[i], file);
1044 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1045 fputc(level.score[i], file);
1046 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1049 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
1050 fputc(level.amoeba_speed, file);
1051 fputc(level.time_magic_wall, file);
1052 fputc(level.time_wheel, file);
1053 fputc(level.amoeba_content, file);
1054 fputc((level.double_speed ? 1 : 0), file);
1055 fputc((level.gravity ? 1 : 0), file);
1057 fputc((encoding_16bit ? 1 : 0), file);
1059 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1062 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1064 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1065 fputc(level.author[i], file);
1067 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
1068 BYTE_ORDER_BIG_ENDIAN);
1070 fputc(EL_MAMPFER, file);
1071 fputc(level.num_yam_contents, file);
1075 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1079 putFile16BitInteger(file, level.yam_content[i][x][y],
1080 BYTE_ORDER_BIG_ENDIAN);
1082 fputc(level.yam_content[i][x][y], file);
1084 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
1086 for(y=0; y<lev_fieldy; y++)
1087 for(x=0; x<lev_fieldx; x++)
1089 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1091 fputc(Ur[x][y], file);
1095 chmod(filename, LEVEL_PERMS);
1098 static void SaveLevel_HEAD(struct LevelInfo *level, FILE *file)
1102 fputc(level->fieldx, file);
1103 fputc(level->fieldy, file);
1105 putFile16BitInteger(file, level->time, BYTE_ORDER_BIG_ENDIAN);
1106 putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
1108 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1109 fputc(level->name[i], file);
1111 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1112 fputc(level->score[i], file);
1114 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1117 fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
1118 level->yam_content[i][x][y]),
1120 fputc(level->amoeba_speed, file);
1121 fputc(level->time_magic_wall, file);
1122 fputc(level->time_wheel, file);
1123 fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
1125 fputc((level->double_speed ? 1 : 0), file);
1126 fputc((level->gravity ? 1 : 0), file);
1128 fputc((level->encoding_16bit_field ? 1 : 0), file);
1130 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1133 static void SaveLevel_AUTH(struct LevelInfo *level, FILE *file)
1137 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1138 fputc(level->author[i], file);
1142 static void SaveLevel_CONT(struct LevelInfo *level, FILE *file)
1146 fputc(EL_MAMPFER, file);
1147 fputc(level->num_yam_contents, file);
1151 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1154 if (level->encoding_16bit_field)
1155 putFile16BitInteger(file, level->yam_content[i][x][y],
1156 BYTE_ORDER_BIG_ENDIAN);
1158 fputc(level->yam_content[i][x][y], file);
1162 static void SaveLevel_BODY(struct LevelInfo *level, FILE *file)
1166 for(y=0; y<lev_fieldy; y++)
1167 for(x=0; x<lev_fieldx; x++)
1168 if (level->encoding_16bit_field)
1169 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1171 fputc(Ur[x][y], file);
1174 static void SaveLevel_CNT2(struct LevelInfo *level, FILE *file, int element)
1177 int num_contents, content_xsize, content_ysize;
1178 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1180 if (element == EL_MAMPFER)
1182 num_contents = level->num_yam_contents;
1186 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1189 content_array[i][x][y] = level->yam_content[i][x][y];
1191 else if (element == EL_AMOEBE_BD)
1197 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1200 content_array[i][x][y] = EL_LEERRAUM;
1201 content_array[0][0][0] = level->amoeba_content;
1205 /* chunk header already written -- write empty chunk data */
1206 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1208 Error(ERR_WARN, "cannot save content for element '%d'", element);
1212 putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
1213 fputc(num_contents, file);
1214 fputc(content_xsize, file);
1215 fputc(content_ysize, file);
1217 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1219 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1222 putFile16BitInteger(file, content_array[i][x][y],
1223 BYTE_ORDER_BIG_ENDIAN);
1226 void SaveLevel(int level_nr)
1229 char *filename = getLevelFilename(level_nr);
1230 int body_chunk_size;
1233 if (!(file = fopen(filename, MODE_WRITE)))
1235 Error(ERR_WARN, "cannot save level file '%s'", filename);
1239 /* check level field for 16-bit elements */
1240 for(y=0; y<level.fieldy; y++)
1241 for(x=0; x<level.fieldx; x++)
1243 level.encoding_16bit_field = TRUE;
1245 /* check yamyam content for 16-bit elements */
1246 for(i=0; i<level.num_yam_contents; i++)
1249 if (level.yam_content[i][x][y] > 255)
1250 level.encoding_16bit_yamyam = TRUE;
1252 /* check amoeba content for 16-bit elements */
1253 if (level.amoeba_content > 255)
1254 level.encoding_16bit_amoeba = TRUE;
1257 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
1259 fputs(LEVEL_COOKIE, file); /* file identifier */
1262 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1263 SaveLevel_HEAD(&level, file);
1265 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1266 SaveLevel_AUTH(&level, file);
1269 if (level.encoding_16bit_field) /* obsolete since new "CNT2" chunk */
1271 chunk_size = 4 + 2 * (MAX_ELEMENT_CONTENTS * 3 * 3);
1273 putFileChunk(file, "CONT", chunk_size, BYTE_ORDER_BIG_ENDIAN);
1274 SaveLevel_CONT(&level, file);
1278 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1279 SaveLevel_BODY(&level, file);
1281 if (level.encoding_16bit_yamyam ||
1282 level.num_yam_contents != STD_ELEMENT_CONTENTS)
1284 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1285 SaveLevel_CNT2(&level, file, EL_MAMPFER);
1288 if (level.encoding_16bit_amoeba)
1290 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1291 SaveLevel_CNT2(&level, file, EL_AMOEBE_BD);
1296 chmod(filename, LEVEL_PERMS);
1299 static void setTapeInfoToDefaults()
1303 /* always start with reliable default values (empty tape) */
1304 tape.file_version = FILE_VERSION_ACTUAL;
1305 tape.game_version = GAME_VERSION_ACTUAL;
1308 /* default values (also for pre-1.2 tapes) with only the first player */
1309 tape.player_participates[0] = TRUE;
1310 for(i=1; i<MAX_PLAYERS; i++)
1311 tape.player_participates[i] = FALSE;
1313 /* at least one (default: the first) player participates in every tape */
1314 tape.num_participating_players = 1;
1317 void OLD_LoadTape(int level_nr)
1320 char *filename = getTapeFilename(level_nr);
1321 char cookie[MAX_LINE_LEN];
1322 char chunk_name[CHUNK_ID_LEN + 1];
1324 int num_participating_players;
1325 int file_version = FILE_VERSION_ACTUAL; /* last version of tape files */
1328 /* always start with reliable default values (empty tape) */
1329 tape.file_version = FILE_VERSION_ACTUAL;
1330 tape.game_version = GAME_VERSION_ACTUAL;
1333 /* default values (also for pre-1.2 tapes) with only the first player */
1334 tape.player_participates[0] = TRUE;
1335 for(i=1; i<MAX_PLAYERS; i++)
1336 tape.player_participates[i] = FALSE;
1338 /* at least one (default: the first) player participates in every tape */
1339 num_participating_players = 1;
1341 if (!(file = fopen(filename, MODE_READ)))
1344 /* check file identifier */
1345 fgets(cookie, MAX_LINE_LEN, file);
1346 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1347 cookie[strlen(cookie) - 1] = '\0';
1350 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
1351 file_version = FILE_VERSION_1_0;
1352 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
1354 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
1359 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1361 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1366 file_version = getFileVersionFromCookieString(cookie);
1369 tape.file_version = file_version;
1370 tape.game_version = file_version;
1372 /* read chunk "HEAD" */
1373 if (file_version >= FILE_VERSION_1_2)
1375 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1376 if (strcmp(chunk_name, "HEAD") || chunk_size != TAPE_HEADER_SIZE)
1378 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
1384 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1385 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1386 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1388 /* read header fields that are new since version 1.2 */
1389 if (file_version >= FILE_VERSION_1_2)
1391 byte store_participating_players = fgetc(file);
1393 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
1396 /* since version 1.2, tapes store which players participate in the tape */
1397 num_participating_players = 0;
1398 for(i=0; i<MAX_PLAYERS; i++)
1400 tape.player_participates[i] = FALSE;
1402 if (store_participating_players & (1 << i))
1404 tape.player_participates[i] = TRUE;
1405 num_participating_players++;
1410 tape.level_nr = level_nr;
1412 tape.changed = FALSE;
1414 tape.recording = FALSE;
1415 tape.playing = FALSE;
1416 tape.pausing = FALSE;
1418 /* read chunk "BODY" */
1419 if (file_version >= FILE_VERSION_1_2)
1421 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1422 if (strcmp(chunk_name, "BODY") ||
1423 chunk_size != (num_participating_players + 1) * tape.length)
1425 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
1432 printf("\nTAPE OF LEVEL %d\n", level_nr);
1435 for(i=0; i<tape.length; i++)
1437 if (i >= MAX_TAPELEN)
1440 for(j=0; j<MAX_PLAYERS; j++)
1442 tape.pos[i].action[j] = MV_NO_MOVING;
1444 if (tape.player_participates[j])
1445 tape.pos[i].action[j] = fgetc(file);
1449 int x = tape.pos[i].action[j];
1451 printf("%d:%02x ", j, x);
1452 printf("[%c%c%c%c|%c%c] - ",
1453 (x & JOY_LEFT ? '<' : ' '),
1454 (x & JOY_RIGHT ? '>' : ' '),
1455 (x & JOY_UP ? '^' : ' '),
1456 (x & JOY_DOWN ? 'v' : ' '),
1457 (x & JOY_BUTTON_1 ? '1' : ' '),
1458 (x & JOY_BUTTON_2 ? '2' : ' '));
1464 tape.pos[i].delay = fgetc(file);
1467 printf("[%03d]\n", tape.pos[i].delay);
1470 if (file_version == FILE_VERSION_1_0)
1472 /* eliminate possible diagonal moves in old tapes */
1473 /* this is only for backward compatibility */
1475 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1476 byte action = tape.pos[i].action[0];
1477 int k, num_moves = 0;
1481 if (action & joy_dir[k])
1483 tape.pos[i + num_moves].action[0] = joy_dir[k];
1485 tape.pos[i + num_moves].delay = 0;
1494 tape.length += num_moves;
1497 else if (file_version < FILE_VERSION_2_0)
1499 if (tape.pos[i].delay > 1)
1502 tape.pos[i + 1] = tape.pos[i];
1503 tape.pos[i + 1].delay = 1;
1506 for(j=0; j<MAX_PLAYERS; j++)
1507 tape.pos[i].action[j] = MV_NO_MOVING;
1508 tape.pos[i].delay--;
1521 if (i != tape.length)
1522 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
1524 tape.length_seconds = GetTapeLength();
1527 static int LoadTape_HEAD(struct TapeInfo *tape, FILE *file, int chunk_size)
1531 tape->random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1532 tape->date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1533 tape->length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1535 /* read header fields that are new since version 1.2 */
1536 if (tape->file_version >= FILE_VERSION_1_2)
1538 byte store_participating_players = fgetc(file);
1540 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1542 /* since version 1.2, tapes store which players participate in the tape */
1543 tape->num_participating_players = 0;
1544 for(i=0; i<MAX_PLAYERS; i++)
1546 tape->player_participates[i] = FALSE;
1548 if (store_participating_players & (1 << i))
1550 tape->player_participates[i] = TRUE;
1551 tape->num_participating_players++;
1556 tape->level_nr = level_nr;
1558 tape->changed = FALSE;
1560 tape->recording = FALSE;
1561 tape->playing = FALSE;
1562 tape->pausing = FALSE;
1567 static int LoadTape_BODY(struct TapeInfo *tape, FILE *file, int chunk_size)
1570 int chunk_size_expected =
1571 (tape->num_participating_players + 1) * tape->length;
1573 if (chunk_size_expected != chunk_size)
1575 ReadUnusedBytesFromFile(file, chunk_size);
1576 return chunk_size_expected;
1580 printf("\nTAPE OF LEVEL %d\n", level_nr);
1583 for(i=0; i<tape->length; i++)
1585 if (i >= MAX_TAPELEN)
1588 for(j=0; j<MAX_PLAYERS; j++)
1590 tape->pos[i].action[j] = MV_NO_MOVING;
1592 if (tape->player_participates[j])
1593 tape->pos[i].action[j] = fgetc(file);
1597 int x = tape->pos[i].action[j];
1599 printf("%d:%02x ", j, x);
1600 printf("[%c%c%c%c|%c%c] - ",
1601 (x & JOY_LEFT ? '<' : ' '),
1602 (x & JOY_RIGHT ? '>' : ' '),
1603 (x & JOY_UP ? '^' : ' '),
1604 (x & JOY_DOWN ? 'v' : ' '),
1605 (x & JOY_BUTTON_1 ? '1' : ' '),
1606 (x & JOY_BUTTON_2 ? '2' : ' '));
1612 tape->pos[i].delay = fgetc(file);
1615 printf("[%03d]\n", tape->pos[i].delay);
1618 if (tape->file_version == FILE_VERSION_1_0)
1620 /* eliminate possible diagonal moves in old tapes */
1621 /* this is only for backward compatibility */
1623 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1624 byte action = tape->pos[i].action[0];
1625 int k, num_moves = 0;
1629 if (action & joy_dir[k])
1631 tape->pos[i + num_moves].action[0] = joy_dir[k];
1633 tape->pos[i + num_moves].delay = 0;
1642 tape->length += num_moves;
1645 else if (tape->file_version < FILE_VERSION_2_0)
1647 if (tape->pos[i].delay > 1)
1650 tape->pos[i + 1] = tape->pos[i];
1651 tape->pos[i + 1].delay = 1;
1654 for(j=0; j<MAX_PLAYERS; j++)
1655 tape->pos[i].action[j] = MV_NO_MOVING;
1656 tape->pos[i].delay--;
1667 if (i != tape->length)
1668 chunk_size = (tape->num_participating_players + 1) * i;
1673 void LoadTape(int level_nr)
1675 char *filename = getTapeFilename(level_nr);
1676 char cookie[MAX_LINE_LEN];
1677 char chunk_name[CHUNK_ID_LEN + 1];
1681 /* always start with reliable default values */
1682 setTapeInfoToDefaults();
1684 if (!(file = fopen(filename, MODE_READ)))
1687 /* check file identifier */
1688 fgets(cookie, MAX_LINE_LEN, file);
1689 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1690 cookie[strlen(cookie) - 1] = '\0';
1692 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1694 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1699 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1701 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1706 tape.game_version = tape.file_version;
1708 if (tape.file_version < FILE_VERSION_1_2)
1710 /* tape files from versions before 1.2.0 without chunk structure */
1711 LoadTape_HEAD(&tape, file, TAPE_HEADER_SIZE);
1712 LoadTape_BODY(&tape, file, 2 * tape.length);
1720 int (*loader)(struct TapeInfo *, FILE *, int);
1724 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1725 { "BODY", -1, LoadTape_BODY },
1729 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
1733 while (chunk_info[i].name != NULL &&
1734 strcmp(chunk_name, chunk_info[i].name) != 0)
1737 if (chunk_info[i].name == NULL)
1739 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1740 chunk_name, filename);
1741 ReadUnusedBytesFromFile(file, chunk_size);
1743 else if (chunk_info[i].size != -1 &&
1744 chunk_info[i].size != chunk_size)
1746 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1747 chunk_size, chunk_name, filename);
1748 ReadUnusedBytesFromFile(file, chunk_size);
1752 /* call function to load this tape chunk */
1753 int chunk_size_expected =
1754 (chunk_info[i].loader)(&tape, file, chunk_size);
1756 /* the size of some chunks cannot be checked before reading other
1757 chunks first (like "HEAD" and "BODY") that contain some header
1758 information, so check them here */
1759 if (chunk_size_expected != chunk_size)
1761 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1762 chunk_size, chunk_name, filename);
1770 tape.length_seconds = GetTapeLength();
1773 void OLD_SaveTape(int level_nr)
1776 char *filename = getTapeFilename(level_nr);
1778 boolean new_tape = TRUE;
1779 byte store_participating_players;
1780 int num_participating_players;
1782 InitTapeDirectory(leveldir_current->filename);
1784 /* if a tape still exists, ask to overwrite it */
1785 if (access(filename, F_OK) == 0)
1788 if (!Request("Replace old tape ?", REQ_ASK))
1792 /* count number of players and set corresponding bits for compact storage */
1793 store_participating_players = 0;
1794 num_participating_players = 0;
1795 for(i=0; i<MAX_PLAYERS; i++)
1797 if (tape.player_participates[i])
1799 num_participating_players++;
1800 store_participating_players |= (1 << i);
1804 if (!(file = fopen(filename, MODE_WRITE)))
1806 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1810 fputs(TAPE_COOKIE, file); /* file identifier */
1813 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1815 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
1816 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
1817 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
1819 fputc(store_participating_players, file);
1821 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1824 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
1825 BYTE_ORDER_BIG_ENDIAN);
1827 for(i=0; i<tape.length; i++)
1831 for(j=0; j<MAX_PLAYERS; j++)
1832 if (tape.player_participates[j])
1833 fputc(tape.pos[i].action[j], file);
1835 fputc(tape.pos[i].delay, file);
1840 chmod(filename, TAPE_PERMS);
1842 tape.changed = FALSE;
1845 Request("tape saved !", REQ_CONFIRM);
1848 static void SaveTape_HEAD(struct TapeInfo *tape, FILE *file)
1851 byte store_participating_players = 0;
1853 /* set bits for participating players for compact storage */
1854 for(i=0; i<MAX_PLAYERS; i++)
1855 if (tape->player_participates[i])
1856 store_participating_players |= (1 << i);
1858 putFile32BitInteger(file, tape->random_seed, BYTE_ORDER_BIG_ENDIAN);
1859 putFile32BitInteger(file, tape->date, BYTE_ORDER_BIG_ENDIAN);
1860 putFile32BitInteger(file, tape->length, BYTE_ORDER_BIG_ENDIAN);
1862 fputc(store_participating_players, file);
1864 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1867 static void SaveTape_BODY(struct TapeInfo *tape, FILE *file)
1871 for(i=0; i<tape->length; i++)
1873 for(j=0; j<MAX_PLAYERS; j++)
1874 if (tape->player_participates[j])
1875 fputc(tape->pos[i].action[j], file);
1877 fputc(tape->pos[i].delay, file);
1881 void SaveTape(int level_nr)
1884 char *filename = getTapeFilename(level_nr);
1886 boolean new_tape = TRUE;
1887 int num_participating_players = 0;
1888 int body_chunk_size;
1890 InitTapeDirectory(leveldir_current->filename);
1892 /* if a tape still exists, ask to overwrite it */
1893 if (access(filename, F_OK) == 0)
1896 if (!Request("Replace old tape ?", REQ_ASK))
1900 if (!(file = fopen(filename, MODE_WRITE)))
1902 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1906 /* count number of participating players */
1907 for(i=0; i<MAX_PLAYERS; i++)
1908 if (tape.player_participates[i])
1909 num_participating_players++;
1911 body_chunk_size = (num_participating_players + 1) * tape.length;
1913 fputs(TAPE_COOKIE, file); /* file identifier */
1916 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1917 SaveTape_HEAD(&tape, file);
1919 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1920 SaveTape_BODY(&tape, file);
1924 chmod(filename, TAPE_PERMS);
1926 tape.changed = FALSE;
1929 Request("tape saved !", REQ_CONFIRM);
1932 void LoadScore(int level_nr)
1935 char *filename = getScoreFilename(level_nr);
1936 char cookie[MAX_LINE_LEN];
1937 char line[MAX_LINE_LEN];
1941 /* always start with reliable default values */
1942 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1944 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1945 highscore[i].Score = 0;
1948 if (!(file = fopen(filename, MODE_READ)))
1951 /* check file identifier */
1952 fgets(cookie, MAX_LINE_LEN, file);
1953 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1954 cookie[strlen(cookie) - 1] = '\0';
1957 if (strcmp(cookie, SCORE_COOKIE) != 0)
1959 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
1964 if (!checkCookieString(cookie, SCORE_COOKIE)) /* unknown file format */
1966 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1972 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1974 fscanf(file, "%d", &highscore[i].Score);
1975 fgets(line, MAX_LINE_LEN, file);
1977 if (line[strlen(line) - 1] == '\n')
1978 line[strlen(line) - 1] = '\0';
1980 for (line_ptr = line; *line_ptr; line_ptr++)
1982 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1984 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1985 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1994 void SaveScore(int level_nr)
1997 char *filename = getScoreFilename(level_nr);
2000 InitScoreDirectory(leveldir_current->filename);
2002 if (!(file = fopen(filename, MODE_WRITE)))
2004 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2008 fprintf(file, "%s\n\n", SCORE_COOKIE);
2010 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2011 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2015 chmod(filename, SCORE_PERMS);
2018 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
2019 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
2020 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
2021 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
2022 #define TOKEN_STR_PLAYER_PREFIX "player_"
2024 #define TOKEN_VALUE_POSITION 30
2027 #define SETUP_TOKEN_PLAYER_NAME 0
2028 #define SETUP_TOKEN_SOUND 1
2029 #define SETUP_TOKEN_SOUND_LOOPS 2
2030 #define SETUP_TOKEN_SOUND_MUSIC 3
2031 #define SETUP_TOKEN_SOUND_SIMPLE 4
2034 #define SETUP_TOKEN_TOONS 5
2035 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
2038 #define SETUP_TOKEN_SCROLL_DELAY 5
2039 #define SETUP_TOKEN_SOFT_SCROLLING 6
2040 #define SETUP_TOKEN_FADING 7
2041 #define SETUP_TOKEN_AUTORECORD 8
2042 #define SETUP_TOKEN_QUICK_DOORS 9
2043 #define SETUP_TOKEN_TEAM_MODE 10
2044 #define SETUP_TOKEN_HANDICAP 11
2045 #define SETUP_TOKEN_TIME_LIMIT 12
2046 #define SETUP_TOKEN_FULLSCREEN 13
2049 #define SETUP_TOKEN_USE_JOYSTICK 14
2050 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
2051 #define SETUP_TOKEN_JOY_XLEFT 16
2052 #define SETUP_TOKEN_JOY_XMIDDLE 17
2053 #define SETUP_TOKEN_JOY_XRIGHT 18
2054 #define SETUP_TOKEN_JOY_YUPPER 19
2055 #define SETUP_TOKEN_JOY_YMIDDLE 20
2056 #define SETUP_TOKEN_JOY_YLOWER 21
2057 #define SETUP_TOKEN_JOY_SNAP 22
2058 #define SETUP_TOKEN_JOY_BOMB 23
2059 #define SETUP_TOKEN_KEY_LEFT 24
2060 #define SETUP_TOKEN_KEY_RIGHT 25
2061 #define SETUP_TOKEN_KEY_UP 26
2062 #define SETUP_TOKEN_KEY_DOWN 27
2063 #define SETUP_TOKEN_KEY_SNAP 28
2064 #define SETUP_TOKEN_KEY_BOMB 29
2066 /* level directory info */
2067 #define LEVELINFO_TOKEN_NAME 30
2068 #define LEVELINFO_TOKEN_NAME_SHORT 31
2069 #define LEVELINFO_TOKEN_NAME_SORTING 32
2070 #define LEVELINFO_TOKEN_AUTHOR 33
2071 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
2072 #define LEVELINFO_TOKEN_LEVELS 35
2073 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
2074 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
2075 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
2076 #define LEVELINFO_TOKEN_READONLY 39
2078 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
2079 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
2081 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
2082 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
2084 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
2085 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
2087 #define TYPE_BOOLEAN 1
2088 #define TYPE_SWITCH 2
2090 #define TYPE_INTEGER 4
2091 #define TYPE_STRING 5
2093 static struct SetupInfo si;
2094 static struct SetupInputInfo sii;
2095 static struct LevelDirInfo ldi;
2104 { TYPE_STRING, &si.player_name, "player_name" },
2105 { TYPE_SWITCH, &si.sound, "sound" },
2106 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2107 { TYPE_SWITCH, &si.sound_music, "background_music" },
2108 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2111 { TYPE_SWITCH, &si.toons, "toons" },
2112 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
2115 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2116 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2117 { TYPE_SWITCH, &si.fading, "screen_fading" },
2118 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2119 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2120 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2121 { TYPE_SWITCH, &si.handicap, "handicap" },
2122 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2123 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2126 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2127 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2128 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2129 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2130 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2131 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2132 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2133 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2134 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2135 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2136 { TYPE_KEY, &sii.key.left, ".key.move_left" },
2137 { TYPE_KEY, &sii.key.right, ".key.move_right" },
2138 { TYPE_KEY, &sii.key.up, ".key.move_up" },
2139 { TYPE_KEY, &sii.key.down, ".key.move_down" },
2140 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
2141 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
2143 /* level directory info */
2144 { TYPE_STRING, &ldi.name, "name" },
2145 { TYPE_STRING, &ldi.name_short, "name_short" },
2146 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
2147 { TYPE_STRING, &ldi.author, "author" },
2148 { TYPE_STRING, &ldi.imported_from, "imported_from" },
2149 { TYPE_INTEGER, &ldi.levels, "levels" },
2150 { TYPE_INTEGER, &ldi.first_level, "first_level" },
2151 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
2152 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
2153 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
2156 static char *string_tolower(char *s)
2158 static char s_lower[100];
2161 if (strlen(s) >= 100)
2166 for (i=0; i<strlen(s_lower); i++)
2167 s_lower[i] = tolower(s_lower[i]);
2172 static int get_string_integer_value(char *s)
2174 static char *number_text[][3] =
2176 { "0", "zero", "null", },
2177 { "1", "one", "first" },
2178 { "2", "two", "second" },
2179 { "3", "three", "third" },
2180 { "4", "four", "fourth" },
2181 { "5", "five", "fifth" },
2182 { "6", "six", "sixth" },
2183 { "7", "seven", "seventh" },
2184 { "8", "eight", "eighth" },
2185 { "9", "nine", "ninth" },
2186 { "10", "ten", "tenth" },
2187 { "11", "eleven", "eleventh" },
2188 { "12", "twelve", "twelfth" },
2193 for (i=0; i<13; i++)
2195 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
2201 static boolean get_string_boolean_value(char *s)
2203 if (strcmp(string_tolower(s), "true") == 0 ||
2204 strcmp(string_tolower(s), "yes") == 0 ||
2205 strcmp(string_tolower(s), "on") == 0 ||
2206 get_string_integer_value(s) == 1)
2212 static char *getFormattedSetupEntry(char *token, char *value)
2215 static char entry[MAX_LINE_LEN];
2217 sprintf(entry, "%s:", token);
2218 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2222 strcat(entry, value);
2227 static void freeSetupFileList(struct SetupFileList *setup_file_list)
2229 if (!setup_file_list)
2232 if (setup_file_list->token)
2233 free(setup_file_list->token);
2234 if (setup_file_list->value)
2235 free(setup_file_list->value);
2236 if (setup_file_list->next)
2237 freeSetupFileList(setup_file_list->next);
2238 free(setup_file_list);
2241 static struct SetupFileList *newSetupFileList(char *token, char *value)
2243 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
2245 new->token = checked_malloc(strlen(token) + 1);
2246 strcpy(new->token, token);
2248 new->value = checked_malloc(strlen(value) + 1);
2249 strcpy(new->value, value);
2256 static char *getTokenValue(struct SetupFileList *setup_file_list,
2259 if (!setup_file_list)
2262 if (strcmp(setup_file_list->token, token) == 0)
2263 return setup_file_list->value;
2265 return getTokenValue(setup_file_list->next, token);
2268 static void setTokenValue(struct SetupFileList *setup_file_list,
2269 char *token, char *value)
2271 if (!setup_file_list)
2274 if (strcmp(setup_file_list->token, token) == 0)
2276 free(setup_file_list->value);
2277 setup_file_list->value = checked_malloc(strlen(value) + 1);
2278 strcpy(setup_file_list->value, value);
2280 else if (setup_file_list->next == NULL)
2281 setup_file_list->next = newSetupFileList(token, value);
2283 setTokenValue(setup_file_list->next, token, value);
2287 static void printSetupFileList(struct SetupFileList *setup_file_list)
2289 if (!setup_file_list)
2292 printf("token: '%s'\n", setup_file_list->token);
2293 printf("value: '%s'\n", setup_file_list->value);
2295 printSetupFileList(setup_file_list->next);
2299 static struct SetupFileList *loadSetupFileList(char *filename)
2302 char line[MAX_LINE_LEN];
2303 char *token, *value, *line_ptr;
2304 struct SetupFileList *setup_file_list = newSetupFileList("", "");
2305 struct SetupFileList *first_valid_list_entry;
2309 if (!(file = fopen(filename, MODE_READ)))
2311 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
2317 /* read next line of input file */
2318 if (!fgets(line, MAX_LINE_LEN, file))
2321 /* cut trailing comment or whitespace from input line */
2322 for (line_ptr = line; *line_ptr; line_ptr++)
2324 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
2331 /* cut trailing whitespaces from input line */
2332 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
2333 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
2336 /* ignore empty lines */
2340 line_len = strlen(line);
2342 /* cut leading whitespaces from token */
2343 for (token = line; *token; token++)
2344 if (*token != ' ' && *token != '\t')
2347 /* find end of token */
2348 for (line_ptr = token; *line_ptr; line_ptr++)
2350 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
2357 if (line_ptr < line + line_len)
2358 value = line_ptr + 1;
2362 /* cut leading whitespaces from value */
2363 for (; *value; value++)
2364 if (*value != ' ' && *value != '\t')
2367 if (*token && *value)
2368 setTokenValue(setup_file_list, token, value);
2373 first_valid_list_entry = setup_file_list->next;
2375 /* free empty list header */
2376 setup_file_list->next = NULL;
2377 freeSetupFileList(setup_file_list);
2379 if (first_valid_list_entry == NULL)
2380 Error(ERR_WARN, "configuration file '%s' is empty", filename);
2382 return first_valid_list_entry;
2385 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
2388 if (!setup_file_list)
2391 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
2393 if (strcmp(setup_file_list->value, identifier) != 0)
2395 Error(ERR_WARN, "configuration file has wrong version");
2402 if (setup_file_list->next)
2403 checkSetupFileListIdentifier(setup_file_list->next, identifier);
2406 Error(ERR_WARN, "configuration file has no version information");
2411 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
2413 ldi->filename = NULL;
2414 ldi->fullpath = NULL;
2415 ldi->basepath = NULL;
2416 ldi->name = getStringCopy(ANONYMOUS_NAME);
2417 ldi->name_short = NULL;
2418 ldi->name_sorting = NULL;
2419 ldi->author = getStringCopy(ANONYMOUS_NAME);
2420 ldi->imported_from = NULL;
2422 ldi->first_level = 0;
2423 ldi->last_level = 0;
2424 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
2425 ldi->level_group = FALSE;
2426 ldi->parent_link = FALSE;
2427 ldi->user_defined = FALSE;
2428 ldi->readonly = TRUE;
2430 ldi->class_desc = NULL;
2431 ldi->handicap_level = 0;
2433 ldi->cl_cursor = -1;
2435 ldi->node_parent = NULL;
2436 ldi->node_group = NULL;
2440 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
2441 struct LevelDirInfo *parent)
2445 setLevelDirInfoToDefaults(ldi);
2449 /* first copy all values from the parent structure ... */
2452 /* ... then set all fields to default that cannot be inherited from parent.
2453 This is especially important for all those fields that can be set from
2454 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
2455 calls 'free()' for all already set token values which requires that no
2456 other structure's pointer may point to them!
2459 ldi->filename = NULL;
2460 ldi->fullpath = NULL;
2461 ldi->basepath = NULL;
2462 ldi->name = getStringCopy(ANONYMOUS_NAME);
2463 ldi->name_short = NULL;
2464 ldi->name_sorting = NULL;
2465 ldi->author = getStringCopy(parent->author);
2466 ldi->imported_from = getStringCopy(parent->imported_from);
2468 ldi->level_group = FALSE;
2469 ldi->parent_link = FALSE;
2471 ldi->node_parent = parent;
2472 ldi->node_group = NULL;
2476 static void setSetupInfoToDefaults(struct SetupInfo *si)
2480 si->player_name = getStringCopy(getLoginName());
2483 si->sound_loops = TRUE;
2484 si->sound_music = TRUE;
2485 si->sound_simple = TRUE;
2487 si->double_buffering = TRUE;
2488 si->direct_draw = !si->double_buffering;
2489 si->scroll_delay = TRUE;
2490 si->soft_scrolling = TRUE;
2492 si->autorecord = TRUE;
2493 si->quick_doors = FALSE;
2494 si->team_mode = FALSE;
2495 si->handicap = TRUE;
2496 si->time_limit = TRUE;
2497 si->fullscreen = FALSE;
2499 for (i=0; i<MAX_PLAYERS; i++)
2501 si->input[i].use_joystick = FALSE;
2502 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
2503 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2504 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2505 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2506 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2507 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2508 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2509 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2510 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2511 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2512 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2513 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2514 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2515 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2516 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2520 static void setSetupInfo(int token_nr, char *token_value)
2522 int token_type = token_info[token_nr].type;
2523 void *setup_value = token_info[token_nr].value;
2525 if (token_value == NULL)
2528 /* set setup field to corresponding token value */
2533 *(boolean *)setup_value = get_string_boolean_value(token_value);
2537 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
2541 *(int *)setup_value = get_string_integer_value(token_value);
2545 if (*(char **)setup_value != NULL)
2546 free(*(char **)setup_value);
2547 *(char **)setup_value = getStringCopy(token_value);
2555 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
2559 if (!setup_file_list)
2562 /* handle global setup values */
2564 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2565 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2568 /* handle player specific setup values */
2569 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2573 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2575 sii = setup.input[pnr];
2576 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2578 char full_token[100];
2580 sprintf(full_token, "%s%s", prefix, token_info[i].text);
2581 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
2583 setup.input[pnr] = sii;
2587 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
2589 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
2590 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
2593 if (entry1->parent_link || entry2->parent_link)
2594 compare_result = (entry1->parent_link ? -1 : +1);
2595 else if (entry1->sort_priority == entry2->sort_priority)
2597 char *name1 = getStringToLower(entry1->name_sorting);
2598 char *name2 = getStringToLower(entry2->name_sorting);
2600 compare_result = strcmp(name1, name2);
2605 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
2606 compare_result = entry1->sort_priority - entry2->sort_priority;
2608 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
2610 return compare_result;
2613 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
2615 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2617 setLevelDirInfoToDefaults(leveldir_new);
2619 leveldir_new->node_parent = node_parent;
2620 leveldir_new->parent_link = TRUE;
2622 leveldir_new->name = ".. (parent directory)";
2623 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2624 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2626 leveldir_new->filename = "..";
2627 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
2629 leveldir_new->sort_priority = node_parent->sort_priority;
2630 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2632 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
2635 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
2636 struct LevelDirInfo *node_parent,
2637 char *level_directory)
2640 struct dirent *dir_entry;
2641 boolean valid_entry_found = FALSE;
2643 if ((dir = opendir(level_directory)) == NULL)
2645 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2649 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2651 struct SetupFileList *setup_file_list = NULL;
2652 struct stat file_status;
2653 char *directory_name = dir_entry->d_name;
2654 char *directory_path = getPath2(level_directory, directory_name);
2655 char *filename = NULL;
2657 /* skip entries for current and parent directory */
2658 if (strcmp(directory_name, ".") == 0 ||
2659 strcmp(directory_name, "..") == 0)
2661 free(directory_path);
2665 /* find out if directory entry is itself a directory */
2666 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2667 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2669 free(directory_path);
2673 filename = getPath2(directory_path, LEVELINFO_FILENAME);
2674 setup_file_list = loadSetupFileList(filename);
2676 if (setup_file_list)
2678 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2681 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
2682 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
2684 /* set all structure fields according to the token/value pairs */
2685 ldi = *leveldir_new;
2686 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2687 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2688 *leveldir_new = ldi;
2690 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2692 if (leveldir_new->name_short == NULL)
2693 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2695 if (leveldir_new->name_sorting == NULL)
2696 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2698 leveldir_new->filename = getStringCopy(directory_name);
2700 if (node_parent == NULL) /* top level group */
2702 leveldir_new->basepath = level_directory;
2703 leveldir_new->fullpath = leveldir_new->filename;
2705 else /* sub level group */
2707 leveldir_new->basepath = node_parent->basepath;
2708 leveldir_new->fullpath = getPath2(node_parent->fullpath,
2712 if (leveldir_new->levels < 1)
2713 leveldir_new->levels = 1;
2715 leveldir_new->last_level =
2716 leveldir_new->first_level + leveldir_new->levels - 1;
2718 leveldir_new->user_defined =
2719 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2721 leveldir_new->color = LEVELCOLOR(leveldir_new);
2722 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2724 leveldir_new->handicap_level = /* set handicap to default value */
2725 (leveldir_new->user_defined ?
2726 leveldir_new->last_level :
2727 leveldir_new->first_level);
2729 pushLevelDirInfo(node_first, leveldir_new);
2731 freeSetupFileList(setup_file_list);
2732 valid_entry_found = TRUE;
2734 if (leveldir_new->level_group)
2736 /* create node to link back to current level directory */
2737 createParentLevelDirNode(leveldir_new);
2739 /* step into sub-directory and look for more level series */
2740 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2741 leveldir_new, directory_path);
2745 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2747 free(directory_path);
2753 if (!valid_entry_found)
2754 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2758 void LoadLevelInfo()
2760 InitUserLevelDirectory(getLoginName());
2762 DrawInitText("Loading level series:", 120, FC_GREEN);
2764 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2765 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
2767 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2769 if (leveldir_first == NULL)
2770 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2772 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
2775 dumpLevelDirInfo(leveldir_first, 0);
2779 static void SaveUserLevelInfo()
2785 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2787 if (!(file = fopen(filename, MODE_WRITE)))
2789 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2794 /* always start with reliable default values */
2795 setLevelDirInfoToDefaults(&ldi);
2797 ldi.name = getLoginName();
2798 ldi.author = getRealName();
2800 ldi.first_level = 1;
2801 ldi.sort_priority = LEVELCLASS_USER_START;
2802 ldi.readonly = FALSE;
2804 fprintf(file, "%s\n\n",
2805 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
2807 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2808 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
2809 i != LEVELINFO_TOKEN_NAME_SORTING &&
2810 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2811 fprintf(file, "%s\n", getSetupLine("", i));
2816 chmod(filename, SETUP_PERMS);
2822 struct SetupFileList *setup_file_list = NULL;
2824 /* always start with reliable default values */
2825 setSetupInfoToDefaults(&setup);
2827 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2829 setup_file_list = loadSetupFileList(filename);
2831 if (setup_file_list)
2833 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
2834 decodeSetupFileList(setup_file_list);
2836 setup.direct_draw = !setup.double_buffering;
2838 freeSetupFileList(setup_file_list);
2840 /* needed to work around problems with fixed length strings */
2841 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
2842 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
2843 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
2845 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2847 strcpy(new_name, setup.player_name);
2848 free(setup.player_name);
2849 setup.player_name = new_name;
2853 Error(ERR_WARN, "using default setup values");
2858 static char *getSetupLine(char *prefix, int token_nr)
2861 static char entry[MAX_LINE_LEN];
2862 int token_type = token_info[token_nr].type;
2863 void *setup_value = token_info[token_nr].value;
2864 char *token_text = token_info[token_nr].text;
2866 /* start with the prefix, token and some spaces to format output line */
2867 sprintf(entry, "%s%s:", prefix, token_text);
2868 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2871 /* continue with the token's value (which can have different types) */
2875 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2879 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2884 Key key = *(Key *)setup_value;
2885 char *keyname = getKeyNameFromKey(key);
2887 strcat(entry, getX11KeyNameFromKey(key));
2888 for (i=strlen(entry); i<50; i++)
2891 /* add comment, if useful */
2892 if (strcmp(keyname, "(undefined)") != 0 &&
2893 strcmp(keyname, "(unknown)") != 0)
2895 strcat(entry, "# ");
2896 strcat(entry, keyname);
2903 char buffer[MAX_LINE_LEN];
2905 sprintf(buffer, "%d", *(int *)setup_value);
2906 strcat(entry, buffer);
2911 strcat(entry, *(char **)setup_value);
2927 InitUserDataDirectory();
2929 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2931 if (!(file = fopen(filename, MODE_WRITE)))
2933 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2938 fprintf(file, "%s\n",
2939 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2940 fprintf(file, "\n");
2942 /* handle global setup values */
2944 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2946 fprintf(file, "%s\n", getSetupLine("", i));
2948 /* just to make things nicer :) */
2949 if (i == SETUP_TOKEN_PLAYER_NAME)
2950 fprintf(file, "\n");
2953 /* handle player specific setup values */
2954 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2958 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2959 fprintf(file, "\n");
2961 sii = setup.input[pnr];
2962 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2963 fprintf(file, "%s\n", getSetupLine(prefix, i));
2969 chmod(filename, SETUP_PERMS);
2972 void LoadLevelSetup_LastSeries()
2975 struct SetupFileList *level_setup_list = NULL;
2977 /* always start with reliable default values */
2978 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2980 /* ----------------------------------------------------------------------- */
2981 /* ~/.rocksndiamonds/levelsetup.conf */
2982 /* ----------------------------------------------------------------------- */
2984 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2986 if ((level_setup_list = loadSetupFileList(filename)))
2988 char *last_level_series =
2989 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2991 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2992 if (leveldir_current == NULL)
2993 leveldir_current = leveldir_first;
2995 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2997 freeSetupFileList(level_setup_list);
3000 Error(ERR_WARN, "using default setup values");
3005 void SaveLevelSetup_LastSeries()
3008 char *level_subdir = leveldir_current->filename;
3011 /* ----------------------------------------------------------------------- */
3012 /* ~/.rocksndiamonds/levelsetup.conf */
3013 /* ----------------------------------------------------------------------- */
3015 InitUserDataDirectory();
3017 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
3019 if (!(file = fopen(filename, MODE_WRITE)))
3021 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3026 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3027 LEVELSETUP_COOKIE));
3028 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
3034 chmod(filename, SETUP_PERMS);
3037 static void checkSeriesInfo()
3039 static char *level_directory = NULL;
3041 struct dirent *dir_entry;
3043 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
3045 level_directory = getPath2((leveldir_current->user_defined ?
3046 getUserLevelDir("") :
3047 options.level_directory),
3048 leveldir_current->fullpath);
3050 if ((dir = opendir(level_directory)) == NULL)
3052 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
3056 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
3058 if (strlen(dir_entry->d_name) > 4 &&
3059 dir_entry->d_name[3] == '.' &&
3060 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
3062 char levelnum_str[4];
3065 strncpy(levelnum_str, dir_entry->d_name, 3);
3066 levelnum_str[3] = '\0';
3068 levelnum_value = atoi(levelnum_str);
3070 if (levelnum_value < leveldir_current->first_level)
3072 Error(ERR_WARN, "additional level %d found", levelnum_value);
3073 leveldir_current->first_level = levelnum_value;
3075 else if (levelnum_value > leveldir_current->last_level)
3077 Error(ERR_WARN, "additional level %d found", levelnum_value);
3078 leveldir_current->last_level = levelnum_value;
3086 void LoadLevelSetup_SeriesInfo()
3089 struct SetupFileList *level_setup_list = NULL;
3090 char *level_subdir = leveldir_current->filename;
3092 /* always start with reliable default values */
3093 level_nr = leveldir_current->first_level;
3095 checkSeriesInfo(leveldir_current);
3097 /* ----------------------------------------------------------------------- */
3098 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
3099 /* ----------------------------------------------------------------------- */
3101 level_subdir = leveldir_current->filename;
3103 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3105 if ((level_setup_list = loadSetupFileList(filename)))
3109 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
3113 level_nr = atoi(token_value);
3115 if (level_nr < leveldir_current->first_level)
3116 level_nr = leveldir_current->first_level;
3117 if (level_nr > leveldir_current->last_level)
3118 level_nr = leveldir_current->last_level;
3121 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
3125 int level_nr = atoi(token_value);
3127 if (level_nr < leveldir_current->first_level)
3128 level_nr = leveldir_current->first_level;
3129 if (level_nr > leveldir_current->last_level + 1)
3130 level_nr = leveldir_current->last_level;
3132 if (leveldir_current->user_defined)
3133 level_nr = leveldir_current->last_level;
3135 leveldir_current->handicap_level = level_nr;
3138 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
3140 freeSetupFileList(level_setup_list);
3143 Error(ERR_WARN, "using default setup values");
3148 void SaveLevelSetup_SeriesInfo()
3151 char *level_subdir = leveldir_current->filename;
3152 char *level_nr_str = int2str(level_nr, 0);
3153 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
3156 /* ----------------------------------------------------------------------- */
3157 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
3158 /* ----------------------------------------------------------------------- */
3160 InitLevelSetupDirectory(level_subdir);
3162 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3164 if (!(file = fopen(filename, MODE_WRITE)))
3166 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3171 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3172 LEVELSETUP_COOKIE));
3173 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
3175 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
3176 handicap_level_str));
3181 chmod(filename, SETUP_PERMS);
3183 /* LocalWords: Rocks'n