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_type1 = MAX_ELEMENT_CONTENTS * 3 * 3;
764 int chunk_size_expected = header_size + content_size_type1;
766 /* Note: "chunk_size" was wrong before version 2.0 when elements are
767 stored with 16-bit encoding (and should be twice as big then).
768 Even worse, playfield data was stored 16-bit when only yamyam content
769 contained 16-bit elements and vice versa. */
771 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
772 chunk_size_expected += content_size_type1;
774 if (chunk_size_expected != chunk_size)
776 ReadUnusedBytesFromFile(file, chunk_size);
777 return chunk_size_expected;
781 level->num_yam_contents = fgetc(file);
785 /* correct invalid number of content fields -- should never happen */
786 if (level->num_yam_contents < 1 ||
787 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
788 level->num_yam_contents = STD_ELEMENT_CONTENTS;
790 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
793 level->yam_content[i][x][y] =
794 checkLevelElement(level->encoding_16bit_field ?
795 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
800 static int LoadLevel_BODY(struct LevelInfo *level, FILE *file, int chunk_size)
803 int chunk_size_expected = level->fieldx * level->fieldy;
805 /* Note: "chunk_size" was wrong before version 2.0 when elements are
806 stored with 16-bit encoding (and should be twice as big then).
807 Even worse, playfield data was stored 16-bit when only yamyam content
808 contained 16-bit elements and vice versa. */
810 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
811 chunk_size_expected *= 2;
813 if (chunk_size_expected != chunk_size)
815 ReadUnusedBytesFromFile(file, chunk_size);
816 return chunk_size_expected;
819 for(y=0; y<level->fieldy; y++)
820 for(x=0; x<level->fieldx; x++)
821 Feld[x][y] = Ur[x][y] =
822 checkLevelElement(level->encoding_16bit_field ?
823 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
828 static int LoadLevel_CNT2(struct LevelInfo *level, FILE *file, int chunk_size)
832 int num_contents, content_xsize, content_ysize;
833 int content_array[MAX_ELEMENT_CONTENTS][3][3];
835 element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
836 num_contents = fgetc(file);
837 content_xsize = fgetc(file);
838 content_ysize = fgetc(file);
839 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
841 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
844 content_array[i][x][y] =
845 checkLevelElement(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
847 /* correct invalid number of content fields -- should never happen */
848 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
849 num_contents = STD_ELEMENT_CONTENTS;
851 if (element == EL_MAMPFER)
853 level->num_yam_contents = num_contents;
855 for(i=0; i<num_contents; i++)
858 level->yam_content[i][x][y] = content_array[i][x][y];
860 else if (element == EL_AMOEBE_BD)
862 level->amoeba_content = content_array[0][0][0];
866 Error(ERR_WARN, "cannot load content for element '%d'", element);
872 void LoadLevel(int level_nr)
874 char *filename = getLevelFilename(level_nr);
875 char cookie[MAX_LINE_LEN];
876 char chunk_name[CHUNK_ID_LEN + 1];
880 /* always start with reliable default values */
881 setLevelInfoToDefaults();
883 if (!(file = fopen(filename, MODE_READ)))
885 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
889 /* check file identifier */
890 fgets(cookie, MAX_LINE_LEN, file);
891 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
892 cookie[strlen(cookie) - 1] = '\0';
894 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
896 Error(ERR_WARN, "unknown format of level file '%s'", filename);
901 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
903 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
908 if (level.file_version < FILE_VERSION_1_2)
910 /* level files from versions before 1.2.0 without chunk structure */
911 LoadLevel_HEAD(&level, file, LEVEL_HEADER_SIZE);
912 LoadLevel_BODY(&level, file, level.fieldx * level.fieldy);
920 int (*loader)(struct LevelInfo *, FILE *, int);
924 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
925 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
926 { "CONT", -1, LoadLevel_CONT },
927 { "BODY", -1, LoadLevel_BODY },
928 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
932 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
936 while (chunk_info[i].name != NULL &&
937 strcmp(chunk_name, chunk_info[i].name) != 0)
940 if (chunk_info[i].name == NULL)
942 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
943 chunk_name, filename);
944 ReadUnusedBytesFromFile(file, chunk_size);
946 else if (chunk_info[i].size != -1 &&
947 chunk_info[i].size != chunk_size)
949 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
950 chunk_size, chunk_name, filename);
951 ReadUnusedBytesFromFile(file, chunk_size);
955 /* call function to load this level chunk */
956 int chunk_size_expected =
957 (chunk_info[i].loader)(&level, file, chunk_size);
959 /* the size of some chunks cannot be checked before reading other
960 chunks first (like "HEAD" and "BODY") or before reading some
961 header information first (like "CONT"), so check them here */
962 if (chunk_size_expected != chunk_size)
964 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
965 chunk_size, chunk_name, filename);
973 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
974 IS_LEVELCLASS_USER(leveldir_current))
976 /* for user contributed and private levels, use the version of
977 the game engine the levels were created for */
978 level.game_version = level.file_version;
980 /* player was faster than monsters in pre-1.0 levels */
981 if (level.file_version == FILE_VERSION_1_0)
983 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
984 Error(ERR_WARN, "using high speed movement for player");
985 level.double_speed = TRUE;
990 /* always use the latest version of the game engine for all but
991 user contributed and private levels */
992 level.game_version = GAME_VERSION_ACTUAL;
995 /* determine border element for this level */
999 void OLD_SaveLevel(int level_nr)
1002 char *filename = getLevelFilename(level_nr);
1004 boolean encoding_16bit_amoeba = FALSE;
1005 boolean encoding_16bit_yamyam = FALSE;
1007 boolean encoding_16bit = FALSE; /* default: only 8-bit elements */
1008 char *oldest_possible_cookie;
1011 if (!(file = fopen(filename, MODE_WRITE)))
1013 Error(ERR_WARN, "cannot save level file '%s'", filename);
1017 /* check yam content for 16-bit elements */
1018 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1021 if (level.yam_content[i][x][y] > 255)
1022 encoding_16bit = TRUE;
1024 /* check level field for 16-bit elements */
1025 for(y=0; y<lev_fieldy; y++)
1026 for(x=0; x<lev_fieldx; x++)
1028 encoding_16bit = TRUE;
1030 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
1032 fputs(oldest_possible_cookie, file); /* file identifier */
1035 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1037 fputc(level.fieldx, file);
1038 fputc(level.fieldy, file);
1040 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
1041 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
1043 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1044 fputc(level.name[i], file);
1045 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1046 fputc(level.score[i], file);
1047 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1050 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
1051 fputc(level.amoeba_speed, file);
1052 fputc(level.time_magic_wall, file);
1053 fputc(level.time_wheel, file);
1054 fputc(level.amoeba_content, file);
1055 fputc((level.double_speed ? 1 : 0), file);
1056 fputc((level.gravity ? 1 : 0), file);
1058 fputc((encoding_16bit ? 1 : 0), file);
1060 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1063 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1065 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1066 fputc(level.author[i], file);
1068 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
1069 BYTE_ORDER_BIG_ENDIAN);
1071 fputc(EL_MAMPFER, file);
1072 fputc(level.num_yam_contents, file);
1076 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1080 putFile16BitInteger(file, level.yam_content[i][x][y],
1081 BYTE_ORDER_BIG_ENDIAN);
1083 fputc(level.yam_content[i][x][y], file);
1085 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
1087 for(y=0; y<lev_fieldy; y++)
1088 for(x=0; x<lev_fieldx; x++)
1090 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1092 fputc(Ur[x][y], file);
1096 chmod(filename, LEVEL_PERMS);
1099 static void SaveLevel_HEAD(struct LevelInfo *level, FILE *file)
1103 fputc(level->fieldx, file);
1104 fputc(level->fieldy, file);
1106 putFile16BitInteger(file, level->time, BYTE_ORDER_BIG_ENDIAN);
1107 putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
1109 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1110 fputc(level->name[i], file);
1112 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1113 fputc(level->score[i], file);
1115 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1118 fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
1119 level->yam_content[i][x][y]),
1121 fputc(level->amoeba_speed, file);
1122 fputc(level->time_magic_wall, file);
1123 fputc(level->time_wheel, file);
1124 fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
1126 fputc((level->double_speed ? 1 : 0), file);
1127 fputc((level->gravity ? 1 : 0), file);
1129 fputc((level->encoding_16bit_field ? 1 : 0), file);
1131 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1134 static void SaveLevel_AUTH(struct LevelInfo *level, FILE *file)
1138 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1139 fputc(level->author[i], file);
1143 static void SaveLevel_CONT(struct LevelInfo *level, FILE *file)
1147 fputc(EL_MAMPFER, file);
1148 fputc(level->num_yam_contents, file);
1152 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1155 if (level->encoding_16bit_field)
1156 putFile16BitInteger(file, level->yam_content[i][x][y],
1157 BYTE_ORDER_BIG_ENDIAN);
1159 fputc(level->yam_content[i][x][y], file);
1163 static void SaveLevel_BODY(struct LevelInfo *level, FILE *file)
1167 for(y=0; y<lev_fieldy; y++)
1168 for(x=0; x<lev_fieldx; x++)
1169 if (level->encoding_16bit_field)
1170 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1172 fputc(Ur[x][y], file);
1175 static void SaveLevel_CNT2(struct LevelInfo *level, FILE *file, int element)
1178 int num_contents, content_xsize, content_ysize;
1179 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1181 if (element == EL_MAMPFER)
1183 num_contents = level->num_yam_contents;
1187 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1190 content_array[i][x][y] = level->yam_content[i][x][y];
1192 else if (element == EL_AMOEBE_BD)
1198 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1201 content_array[i][x][y] = EL_LEERRAUM;
1202 content_array[0][0][0] = level->amoeba_content;
1206 /* chunk header already written -- write empty chunk data */
1207 for(i=0; i<LEVEL_CHUNK_CNT2_SIZE; i++)
1210 Error(ERR_WARN, "cannot save content for element '%d'", element);
1214 putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
1215 fputc(num_contents, file);
1216 fputc(content_xsize, file);
1217 fputc(content_ysize, file);
1218 for(i=0; i<LEVEL_CHUNK_CNT2_UNUSED; i++)
1221 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1224 putFile16BitInteger(file, content_array[i][x][y],
1225 BYTE_ORDER_BIG_ENDIAN);
1228 void SaveLevel(int level_nr)
1231 char *filename = getLevelFilename(level_nr);
1235 if (!(file = fopen(filename, MODE_WRITE)))
1237 Error(ERR_WARN, "cannot save level file '%s'", filename);
1241 /* check level field for 16-bit elements */
1242 for(y=0; y<level.fieldy; y++)
1243 for(x=0; x<level.fieldx; x++)
1245 level.encoding_16bit_field = TRUE;
1247 /* check yamyam content for 16-bit elements */
1248 for(i=0; i<level.num_yam_contents; i++)
1251 if (level.yam_content[i][x][y] > 255)
1252 level.encoding_16bit_yamyam = TRUE;
1254 /* check amoeba content for 16-bit elements */
1255 if (level.amoeba_content > 255)
1256 level.encoding_16bit_amoeba = TRUE;
1258 fputs(LEVEL_COOKIE, file); /* file identifier */
1261 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1262 SaveLevel_HEAD(&level, file);
1264 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1265 SaveLevel_AUTH(&level, file);
1268 if (level.encoding_16bit_field) /* obsolete since new "CNT2" chunk */
1270 chunk_size = 4 + 2 * (MAX_ELEMENT_CONTENTS * 3 * 3);
1272 putFileChunk(file, "CONT", chunk_size, BYTE_ORDER_BIG_ENDIAN);
1273 SaveLevel_CONT(&level, file);
1278 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
1279 putFileChunk(file, "BODY", chunk_size, BYTE_ORDER_BIG_ENDIAN);
1280 SaveLevel_BODY(&level, file);
1282 if (level.encoding_16bit_yamyam ||
1283 level.num_yam_contents != STD_ELEMENT_CONTENTS)
1285 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1286 SaveLevel_CNT2(&level, file, EL_MAMPFER);
1289 if (level.encoding_16bit_amoeba)
1291 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1292 SaveLevel_CNT2(&level, file, EL_AMOEBE_BD);
1297 chmod(filename, LEVEL_PERMS);
1300 void LoadTape(int level_nr)
1303 char *filename = getTapeFilename(level_nr);
1304 char cookie[MAX_LINE_LEN];
1305 char chunk_name[CHUNK_ID_LEN + 1];
1307 int num_participating_players;
1308 int file_version = FILE_VERSION_ACTUAL; /* last version of tape files */
1311 /* always start with reliable default values (empty tape) */
1312 tape.file_version = FILE_VERSION_ACTUAL;
1313 tape.game_version = GAME_VERSION_ACTUAL;
1316 /* default values (also for pre-1.2 tapes) with only the first player */
1317 tape.player_participates[0] = TRUE;
1318 for(i=1; i<MAX_PLAYERS; i++)
1319 tape.player_participates[i] = FALSE;
1321 /* at least one (default: the first) player participates in every tape */
1322 num_participating_players = 1;
1324 if (!(file = fopen(filename, MODE_READ)))
1327 /* check file identifier */
1328 fgets(cookie, MAX_LINE_LEN, file);
1329 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1330 cookie[strlen(cookie) - 1] = '\0';
1333 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
1334 file_version = FILE_VERSION_1_0;
1335 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
1337 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
1342 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1344 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1349 file_version = getFileVersionFromCookieString(cookie);
1352 tape.file_version = file_version;
1353 tape.game_version = file_version;
1355 /* read chunk "HEAD" */
1356 if (file_version >= FILE_VERSION_1_2)
1358 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1359 if (strcmp(chunk_name, "HEAD") || chunk_size != TAPE_HEADER_SIZE)
1361 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
1367 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1368 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1369 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1371 /* read header fields that are new since version 1.2 */
1372 if (file_version >= FILE_VERSION_1_2)
1374 byte store_participating_players = fgetc(file);
1376 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
1379 /* since version 1.2, tapes store which players participate in the tape */
1380 num_participating_players = 0;
1381 for(i=0; i<MAX_PLAYERS; i++)
1383 tape.player_participates[i] = FALSE;
1385 if (store_participating_players & (1 << i))
1387 tape.player_participates[i] = TRUE;
1388 num_participating_players++;
1393 tape.level_nr = level_nr;
1395 tape.changed = FALSE;
1397 tape.recording = FALSE;
1398 tape.playing = FALSE;
1399 tape.pausing = FALSE;
1401 /* read chunk "BODY" */
1402 if (file_version >= FILE_VERSION_1_2)
1404 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1405 if (strcmp(chunk_name, "BODY") ||
1406 chunk_size != (num_participating_players + 1) * tape.length)
1408 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
1415 printf("\nTAPE OF LEVEL %d\n", level_nr);
1418 for(i=0; i<tape.length; i++)
1420 if (i >= MAX_TAPELEN)
1423 for(j=0; j<MAX_PLAYERS; j++)
1425 tape.pos[i].action[j] = MV_NO_MOVING;
1427 if (tape.player_participates[j])
1428 tape.pos[i].action[j] = fgetc(file);
1432 int x = tape.pos[i].action[j];
1434 printf("%d:%02x ", j, x);
1435 printf("[%c%c%c%c|%c%c] - ",
1436 (x & JOY_LEFT ? '<' : ' '),
1437 (x & JOY_RIGHT ? '>' : ' '),
1438 (x & JOY_UP ? '^' : ' '),
1439 (x & JOY_DOWN ? 'v' : ' '),
1440 (x & JOY_BUTTON_1 ? '1' : ' '),
1441 (x & JOY_BUTTON_2 ? '2' : ' '));
1447 tape.pos[i].delay = fgetc(file);
1450 printf("[%03d]\n", tape.pos[i].delay);
1453 if (file_version == FILE_VERSION_1_0)
1455 /* eliminate possible diagonal moves in old tapes */
1456 /* this is only for backward compatibility */
1458 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1459 byte action = tape.pos[i].action[0];
1460 int k, num_moves = 0;
1464 if (action & joy_dir[k])
1466 tape.pos[i + num_moves].action[0] = joy_dir[k];
1468 tape.pos[i + num_moves].delay = 0;
1477 tape.length += num_moves;
1480 else if (file_version < FILE_VERSION_2_0)
1482 if (tape.pos[i].delay > 1)
1485 tape.pos[i + 1] = tape.pos[i];
1486 tape.pos[i + 1].delay = 1;
1489 for(j=0; j<MAX_PLAYERS; j++)
1490 tape.pos[i].action[j] = MV_NO_MOVING;
1491 tape.pos[i].delay--;
1504 if (i != tape.length)
1505 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
1507 tape.length_seconds = GetTapeLength();
1510 void SaveTape(int level_nr)
1513 char *filename = getTapeFilename(level_nr);
1515 boolean new_tape = TRUE;
1516 byte store_participating_players;
1517 int num_participating_players;
1519 InitTapeDirectory(leveldir_current->filename);
1521 /* if a tape still exists, ask to overwrite it */
1522 if (access(filename, F_OK) == 0)
1525 if (!Request("Replace old tape ?", REQ_ASK))
1529 /* count number of players and set corresponding bits for compact storage */
1530 store_participating_players = 0;
1531 num_participating_players = 0;
1532 for(i=0; i<MAX_PLAYERS; i++)
1534 if (tape.player_participates[i])
1536 num_participating_players++;
1537 store_participating_players |= (1 << i);
1541 if (!(file = fopen(filename, MODE_WRITE)))
1543 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1547 fputs(TAPE_COOKIE, file); /* file identifier */
1550 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1552 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
1553 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
1554 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
1556 fputc(store_participating_players, file);
1558 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1561 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
1562 BYTE_ORDER_BIG_ENDIAN);
1564 for(i=0; i<tape.length; i++)
1568 for(j=0; j<MAX_PLAYERS; j++)
1569 if (tape.player_participates[j])
1570 fputc(tape.pos[i].action[j], file);
1572 fputc(tape.pos[i].delay, file);
1577 chmod(filename, TAPE_PERMS);
1579 tape.changed = FALSE;
1582 Request("tape saved !", REQ_CONFIRM);
1585 void LoadScore(int level_nr)
1588 char *filename = getScoreFilename(level_nr);
1589 char cookie[MAX_LINE_LEN];
1590 char line[MAX_LINE_LEN];
1594 /* always start with reliable default values */
1595 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1597 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1598 highscore[i].Score = 0;
1601 if (!(file = fopen(filename, MODE_READ)))
1604 /* check file identifier */
1605 fgets(cookie, MAX_LINE_LEN, file);
1606 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1607 cookie[strlen(cookie) - 1] = '\0';
1610 if (strcmp(cookie, SCORE_COOKIE) != 0)
1612 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
1617 if (!checkCookieString(cookie, SCORE_COOKIE)) /* unknown file format */
1619 Error(ERR_WARN, "unknown format of score file '%s'", filename);
1625 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1627 fscanf(file, "%d", &highscore[i].Score);
1628 fgets(line, MAX_LINE_LEN, file);
1630 if (line[strlen(line) - 1] == '\n')
1631 line[strlen(line) - 1] = '\0';
1633 for (line_ptr = line; *line_ptr; line_ptr++)
1635 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1637 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1638 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1647 void SaveScore(int level_nr)
1650 char *filename = getScoreFilename(level_nr);
1653 InitScoreDirectory(leveldir_current->filename);
1655 if (!(file = fopen(filename, MODE_WRITE)))
1657 Error(ERR_WARN, "cannot save score for level %d", level_nr);
1661 fprintf(file, "%s\n\n", SCORE_COOKIE);
1663 for(i=0; i<MAX_SCORE_ENTRIES; i++)
1664 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1668 chmod(filename, SCORE_PERMS);
1671 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
1672 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1673 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1674 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1675 #define TOKEN_STR_PLAYER_PREFIX "player_"
1677 #define TOKEN_VALUE_POSITION 30
1680 #define SETUP_TOKEN_PLAYER_NAME 0
1681 #define SETUP_TOKEN_SOUND 1
1682 #define SETUP_TOKEN_SOUND_LOOPS 2
1683 #define SETUP_TOKEN_SOUND_MUSIC 3
1684 #define SETUP_TOKEN_SOUND_SIMPLE 4
1687 #define SETUP_TOKEN_TOONS 5
1688 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
1691 #define SETUP_TOKEN_SCROLL_DELAY 5
1692 #define SETUP_TOKEN_SOFT_SCROLLING 6
1693 #define SETUP_TOKEN_FADING 7
1694 #define SETUP_TOKEN_AUTORECORD 8
1695 #define SETUP_TOKEN_QUICK_DOORS 9
1696 #define SETUP_TOKEN_TEAM_MODE 10
1697 #define SETUP_TOKEN_HANDICAP 11
1698 #define SETUP_TOKEN_TIME_LIMIT 12
1699 #define SETUP_TOKEN_FULLSCREEN 13
1702 #define SETUP_TOKEN_USE_JOYSTICK 14
1703 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
1704 #define SETUP_TOKEN_JOY_XLEFT 16
1705 #define SETUP_TOKEN_JOY_XMIDDLE 17
1706 #define SETUP_TOKEN_JOY_XRIGHT 18
1707 #define SETUP_TOKEN_JOY_YUPPER 19
1708 #define SETUP_TOKEN_JOY_YMIDDLE 20
1709 #define SETUP_TOKEN_JOY_YLOWER 21
1710 #define SETUP_TOKEN_JOY_SNAP 22
1711 #define SETUP_TOKEN_JOY_BOMB 23
1712 #define SETUP_TOKEN_KEY_LEFT 24
1713 #define SETUP_TOKEN_KEY_RIGHT 25
1714 #define SETUP_TOKEN_KEY_UP 26
1715 #define SETUP_TOKEN_KEY_DOWN 27
1716 #define SETUP_TOKEN_KEY_SNAP 28
1717 #define SETUP_TOKEN_KEY_BOMB 29
1719 /* level directory info */
1720 #define LEVELINFO_TOKEN_NAME 30
1721 #define LEVELINFO_TOKEN_NAME_SHORT 31
1722 #define LEVELINFO_TOKEN_NAME_SORTING 32
1723 #define LEVELINFO_TOKEN_AUTHOR 33
1724 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
1725 #define LEVELINFO_TOKEN_LEVELS 35
1726 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
1727 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
1728 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
1729 #define LEVELINFO_TOKEN_READONLY 39
1731 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
1732 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
1734 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
1735 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
1737 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
1738 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
1740 #define TYPE_BOOLEAN 1
1741 #define TYPE_SWITCH 2
1743 #define TYPE_INTEGER 4
1744 #define TYPE_STRING 5
1746 static struct SetupInfo si;
1747 static struct SetupInputInfo sii;
1748 static struct LevelDirInfo ldi;
1757 { TYPE_STRING, &si.player_name, "player_name" },
1758 { TYPE_SWITCH, &si.sound, "sound" },
1759 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
1760 { TYPE_SWITCH, &si.sound_music, "background_music" },
1761 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
1764 { TYPE_SWITCH, &si.toons, "toons" },
1765 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
1768 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
1769 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
1770 { TYPE_SWITCH, &si.fading, "screen_fading" },
1771 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
1772 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
1773 { TYPE_SWITCH, &si.team_mode, "team_mode" },
1774 { TYPE_SWITCH, &si.handicap, "handicap" },
1775 { TYPE_SWITCH, &si.time_limit, "time_limit" },
1776 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
1779 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
1780 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
1781 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
1782 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
1783 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
1784 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
1785 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
1786 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
1787 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
1788 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
1789 { TYPE_KEY, &sii.key.left, ".key.move_left" },
1790 { TYPE_KEY, &sii.key.right, ".key.move_right" },
1791 { TYPE_KEY, &sii.key.up, ".key.move_up" },
1792 { TYPE_KEY, &sii.key.down, ".key.move_down" },
1793 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
1794 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
1796 /* level directory info */
1797 { TYPE_STRING, &ldi.name, "name" },
1798 { TYPE_STRING, &ldi.name_short, "name_short" },
1799 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1800 { TYPE_STRING, &ldi.author, "author" },
1801 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1802 { TYPE_INTEGER, &ldi.levels, "levels" },
1803 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1804 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1805 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1806 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1809 static char *string_tolower(char *s)
1811 static char s_lower[100];
1814 if (strlen(s) >= 100)
1819 for (i=0; i<strlen(s_lower); i++)
1820 s_lower[i] = tolower(s_lower[i]);
1825 static int get_string_integer_value(char *s)
1827 static char *number_text[][3] =
1829 { "0", "zero", "null", },
1830 { "1", "one", "first" },
1831 { "2", "two", "second" },
1832 { "3", "three", "third" },
1833 { "4", "four", "fourth" },
1834 { "5", "five", "fifth" },
1835 { "6", "six", "sixth" },
1836 { "7", "seven", "seventh" },
1837 { "8", "eight", "eighth" },
1838 { "9", "nine", "ninth" },
1839 { "10", "ten", "tenth" },
1840 { "11", "eleven", "eleventh" },
1841 { "12", "twelve", "twelfth" },
1846 for (i=0; i<13; i++)
1848 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1854 static boolean get_string_boolean_value(char *s)
1856 if (strcmp(string_tolower(s), "true") == 0 ||
1857 strcmp(string_tolower(s), "yes") == 0 ||
1858 strcmp(string_tolower(s), "on") == 0 ||
1859 get_string_integer_value(s) == 1)
1865 static char *getFormattedSetupEntry(char *token, char *value)
1868 static char entry[MAX_LINE_LEN];
1870 sprintf(entry, "%s:", token);
1871 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1875 strcat(entry, value);
1880 static void freeSetupFileList(struct SetupFileList *setup_file_list)
1882 if (!setup_file_list)
1885 if (setup_file_list->token)
1886 free(setup_file_list->token);
1887 if (setup_file_list->value)
1888 free(setup_file_list->value);
1889 if (setup_file_list->next)
1890 freeSetupFileList(setup_file_list->next);
1891 free(setup_file_list);
1894 static struct SetupFileList *newSetupFileList(char *token, char *value)
1896 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1898 new->token = checked_malloc(strlen(token) + 1);
1899 strcpy(new->token, token);
1901 new->value = checked_malloc(strlen(value) + 1);
1902 strcpy(new->value, value);
1909 static char *getTokenValue(struct SetupFileList *setup_file_list,
1912 if (!setup_file_list)
1915 if (strcmp(setup_file_list->token, token) == 0)
1916 return setup_file_list->value;
1918 return getTokenValue(setup_file_list->next, token);
1921 static void setTokenValue(struct SetupFileList *setup_file_list,
1922 char *token, char *value)
1924 if (!setup_file_list)
1927 if (strcmp(setup_file_list->token, token) == 0)
1929 free(setup_file_list->value);
1930 setup_file_list->value = checked_malloc(strlen(value) + 1);
1931 strcpy(setup_file_list->value, value);
1933 else if (setup_file_list->next == NULL)
1934 setup_file_list->next = newSetupFileList(token, value);
1936 setTokenValue(setup_file_list->next, token, value);
1940 static void printSetupFileList(struct SetupFileList *setup_file_list)
1942 if (!setup_file_list)
1945 printf("token: '%s'\n", setup_file_list->token);
1946 printf("value: '%s'\n", setup_file_list->value);
1948 printSetupFileList(setup_file_list->next);
1952 static struct SetupFileList *loadSetupFileList(char *filename)
1955 char line[MAX_LINE_LEN];
1956 char *token, *value, *line_ptr;
1957 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1958 struct SetupFileList *first_valid_list_entry;
1962 if (!(file = fopen(filename, MODE_READ)))
1964 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1970 /* read next line of input file */
1971 if (!fgets(line, MAX_LINE_LEN, file))
1974 /* cut trailing comment or whitespace from input line */
1975 for (line_ptr = line; *line_ptr; line_ptr++)
1977 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1984 /* cut trailing whitespaces from input line */
1985 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1986 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1989 /* ignore empty lines */
1993 line_len = strlen(line);
1995 /* cut leading whitespaces from token */
1996 for (token = line; *token; token++)
1997 if (*token != ' ' && *token != '\t')
2000 /* find end of token */
2001 for (line_ptr = token; *line_ptr; line_ptr++)
2003 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
2010 if (line_ptr < line + line_len)
2011 value = line_ptr + 1;
2015 /* cut leading whitespaces from value */
2016 for (; *value; value++)
2017 if (*value != ' ' && *value != '\t')
2020 if (*token && *value)
2021 setTokenValue(setup_file_list, token, value);
2026 first_valid_list_entry = setup_file_list->next;
2028 /* free empty list header */
2029 setup_file_list->next = NULL;
2030 freeSetupFileList(setup_file_list);
2032 if (first_valid_list_entry == NULL)
2033 Error(ERR_WARN, "configuration file '%s' is empty", filename);
2035 return first_valid_list_entry;
2038 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
2041 if (!setup_file_list)
2044 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
2046 if (strcmp(setup_file_list->value, identifier) != 0)
2048 Error(ERR_WARN, "configuration file has wrong version");
2055 if (setup_file_list->next)
2056 checkSetupFileListIdentifier(setup_file_list->next, identifier);
2059 Error(ERR_WARN, "configuration file has no version information");
2064 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
2066 ldi->filename = NULL;
2067 ldi->fullpath = NULL;
2068 ldi->basepath = NULL;
2069 ldi->name = getStringCopy(ANONYMOUS_NAME);
2070 ldi->name_short = NULL;
2071 ldi->name_sorting = NULL;
2072 ldi->author = getStringCopy(ANONYMOUS_NAME);
2073 ldi->imported_from = NULL;
2075 ldi->first_level = 0;
2076 ldi->last_level = 0;
2077 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
2078 ldi->level_group = FALSE;
2079 ldi->parent_link = FALSE;
2080 ldi->user_defined = FALSE;
2081 ldi->readonly = TRUE;
2083 ldi->class_desc = NULL;
2084 ldi->handicap_level = 0;
2086 ldi->cl_cursor = -1;
2088 ldi->node_parent = NULL;
2089 ldi->node_group = NULL;
2093 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
2094 struct LevelDirInfo *parent)
2098 setLevelDirInfoToDefaults(ldi);
2102 /* first copy all values from the parent structure ... */
2105 /* ... then set all fields to default that cannot be inherited from parent.
2106 This is especially important for all those fields that can be set from
2107 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
2108 calls 'free()' for all already set token values which requires that no
2109 other structure's pointer may point to them!
2112 ldi->filename = NULL;
2113 ldi->fullpath = NULL;
2114 ldi->basepath = NULL;
2115 ldi->name = getStringCopy(ANONYMOUS_NAME);
2116 ldi->name_short = NULL;
2117 ldi->name_sorting = NULL;
2118 ldi->author = getStringCopy(parent->author);
2119 ldi->imported_from = getStringCopy(parent->imported_from);
2121 ldi->level_group = FALSE;
2122 ldi->parent_link = FALSE;
2124 ldi->node_parent = parent;
2125 ldi->node_group = NULL;
2129 static void setSetupInfoToDefaults(struct SetupInfo *si)
2133 si->player_name = getStringCopy(getLoginName());
2136 si->sound_loops = TRUE;
2137 si->sound_music = TRUE;
2138 si->sound_simple = TRUE;
2140 si->double_buffering = TRUE;
2141 si->direct_draw = !si->double_buffering;
2142 si->scroll_delay = TRUE;
2143 si->soft_scrolling = TRUE;
2145 si->autorecord = TRUE;
2146 si->quick_doors = FALSE;
2147 si->team_mode = FALSE;
2148 si->handicap = TRUE;
2149 si->time_limit = TRUE;
2150 si->fullscreen = FALSE;
2152 for (i=0; i<MAX_PLAYERS; i++)
2154 si->input[i].use_joystick = FALSE;
2155 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
2156 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2157 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2158 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2159 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2160 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2161 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2162 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2163 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2164 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2165 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2166 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2167 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2168 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2169 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2173 static void setSetupInfo(int token_nr, char *token_value)
2175 int token_type = token_info[token_nr].type;
2176 void *setup_value = token_info[token_nr].value;
2178 if (token_value == NULL)
2181 /* set setup field to corresponding token value */
2186 *(boolean *)setup_value = get_string_boolean_value(token_value);
2190 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
2194 *(int *)setup_value = get_string_integer_value(token_value);
2198 if (*(char **)setup_value != NULL)
2199 free(*(char **)setup_value);
2200 *(char **)setup_value = getStringCopy(token_value);
2208 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
2212 if (!setup_file_list)
2215 /* handle global setup values */
2217 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2218 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2221 /* handle player specific setup values */
2222 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2226 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2228 sii = setup.input[pnr];
2229 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2231 char full_token[100];
2233 sprintf(full_token, "%s%s", prefix, token_info[i].text);
2234 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
2236 setup.input[pnr] = sii;
2240 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
2242 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
2243 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
2246 if (entry1->parent_link || entry2->parent_link)
2247 compare_result = (entry1->parent_link ? -1 : +1);
2248 else if (entry1->sort_priority == entry2->sort_priority)
2250 char *name1 = getStringToLower(entry1->name_sorting);
2251 char *name2 = getStringToLower(entry2->name_sorting);
2253 compare_result = strcmp(name1, name2);
2258 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
2259 compare_result = entry1->sort_priority - entry2->sort_priority;
2261 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
2263 return compare_result;
2266 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
2268 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2270 setLevelDirInfoToDefaults(leveldir_new);
2272 leveldir_new->node_parent = node_parent;
2273 leveldir_new->parent_link = TRUE;
2275 leveldir_new->name = ".. (parent directory)";
2276 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2277 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2279 leveldir_new->filename = "..";
2280 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
2282 leveldir_new->sort_priority = node_parent->sort_priority;
2283 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2285 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
2288 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
2289 struct LevelDirInfo *node_parent,
2290 char *level_directory)
2293 struct dirent *dir_entry;
2294 boolean valid_entry_found = FALSE;
2296 if ((dir = opendir(level_directory)) == NULL)
2298 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2302 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2304 struct SetupFileList *setup_file_list = NULL;
2305 struct stat file_status;
2306 char *directory_name = dir_entry->d_name;
2307 char *directory_path = getPath2(level_directory, directory_name);
2308 char *filename = NULL;
2310 /* skip entries for current and parent directory */
2311 if (strcmp(directory_name, ".") == 0 ||
2312 strcmp(directory_name, "..") == 0)
2314 free(directory_path);
2318 /* find out if directory entry is itself a directory */
2319 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2320 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2322 free(directory_path);
2326 filename = getPath2(directory_path, LEVELINFO_FILENAME);
2327 setup_file_list = loadSetupFileList(filename);
2329 if (setup_file_list)
2331 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2334 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
2335 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
2337 /* set all structure fields according to the token/value pairs */
2338 ldi = *leveldir_new;
2339 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2340 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2341 *leveldir_new = ldi;
2343 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2345 if (leveldir_new->name_short == NULL)
2346 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2348 if (leveldir_new->name_sorting == NULL)
2349 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2351 leveldir_new->filename = getStringCopy(directory_name);
2353 if (node_parent == NULL) /* top level group */
2355 leveldir_new->basepath = level_directory;
2356 leveldir_new->fullpath = leveldir_new->filename;
2358 else /* sub level group */
2360 leveldir_new->basepath = node_parent->basepath;
2361 leveldir_new->fullpath = getPath2(node_parent->fullpath,
2365 if (leveldir_new->levels < 1)
2366 leveldir_new->levels = 1;
2368 leveldir_new->last_level =
2369 leveldir_new->first_level + leveldir_new->levels - 1;
2371 leveldir_new->user_defined =
2372 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2374 leveldir_new->color = LEVELCOLOR(leveldir_new);
2375 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2377 leveldir_new->handicap_level = /* set handicap to default value */
2378 (leveldir_new->user_defined ?
2379 leveldir_new->last_level :
2380 leveldir_new->first_level);
2382 pushLevelDirInfo(node_first, leveldir_new);
2384 freeSetupFileList(setup_file_list);
2385 valid_entry_found = TRUE;
2387 if (leveldir_new->level_group)
2389 /* create node to link back to current level directory */
2390 createParentLevelDirNode(leveldir_new);
2392 /* step into sub-directory and look for more level series */
2393 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2394 leveldir_new, directory_path);
2398 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2400 free(directory_path);
2406 if (!valid_entry_found)
2407 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2411 void LoadLevelInfo()
2413 InitUserLevelDirectory(getLoginName());
2415 DrawInitText("Loading level series:", 120, FC_GREEN);
2417 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2418 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
2420 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2422 if (leveldir_first == NULL)
2423 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2425 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
2428 dumpLevelDirInfo(leveldir_first, 0);
2432 static void SaveUserLevelInfo()
2438 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2440 if (!(file = fopen(filename, MODE_WRITE)))
2442 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2447 /* always start with reliable default values */
2448 setLevelDirInfoToDefaults(&ldi);
2450 ldi.name = getLoginName();
2451 ldi.author = getRealName();
2453 ldi.first_level = 1;
2454 ldi.sort_priority = LEVELCLASS_USER_START;
2455 ldi.readonly = FALSE;
2457 fprintf(file, "%s\n\n",
2458 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
2460 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2461 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
2462 i != LEVELINFO_TOKEN_NAME_SORTING &&
2463 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2464 fprintf(file, "%s\n", getSetupLine("", i));
2469 chmod(filename, SETUP_PERMS);
2475 struct SetupFileList *setup_file_list = NULL;
2477 /* always start with reliable default values */
2478 setSetupInfoToDefaults(&setup);
2480 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2482 setup_file_list = loadSetupFileList(filename);
2484 if (setup_file_list)
2486 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
2487 decodeSetupFileList(setup_file_list);
2489 setup.direct_draw = !setup.double_buffering;
2491 freeSetupFileList(setup_file_list);
2493 /* needed to work around problems with fixed length strings */
2494 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
2495 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
2496 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
2498 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2500 strcpy(new_name, setup.player_name);
2501 free(setup.player_name);
2502 setup.player_name = new_name;
2506 Error(ERR_WARN, "using default setup values");
2511 static char *getSetupLine(char *prefix, int token_nr)
2514 static char entry[MAX_LINE_LEN];
2515 int token_type = token_info[token_nr].type;
2516 void *setup_value = token_info[token_nr].value;
2517 char *token_text = token_info[token_nr].text;
2519 /* start with the prefix, token and some spaces to format output line */
2520 sprintf(entry, "%s%s:", prefix, token_text);
2521 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2524 /* continue with the token's value (which can have different types) */
2528 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
2532 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
2537 Key key = *(Key *)setup_value;
2538 char *keyname = getKeyNameFromKey(key);
2540 strcat(entry, getX11KeyNameFromKey(key));
2541 for (i=strlen(entry); i<50; i++)
2544 /* add comment, if useful */
2545 if (strcmp(keyname, "(undefined)") != 0 &&
2546 strcmp(keyname, "(unknown)") != 0)
2548 strcat(entry, "# ");
2549 strcat(entry, keyname);
2556 char buffer[MAX_LINE_LEN];
2558 sprintf(buffer, "%d", *(int *)setup_value);
2559 strcat(entry, buffer);
2564 strcat(entry, *(char **)setup_value);
2580 InitUserDataDirectory();
2582 filename = getPath2(getSetupDir(), SETUP_FILENAME);
2584 if (!(file = fopen(filename, MODE_WRITE)))
2586 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2591 fprintf(file, "%s\n",
2592 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
2593 fprintf(file, "\n");
2595 /* handle global setup values */
2597 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2599 fprintf(file, "%s\n", getSetupLine("", i));
2601 /* just to make things nicer :) */
2602 if (i == SETUP_TOKEN_PLAYER_NAME)
2603 fprintf(file, "\n");
2606 /* handle player specific setup values */
2607 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2611 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2612 fprintf(file, "\n");
2614 sii = setup.input[pnr];
2615 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2616 fprintf(file, "%s\n", getSetupLine(prefix, i));
2622 chmod(filename, SETUP_PERMS);
2625 void LoadLevelSetup_LastSeries()
2628 struct SetupFileList *level_setup_list = NULL;
2630 /* always start with reliable default values */
2631 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2633 /* ----------------------------------------------------------------------- */
2634 /* ~/.rocksndiamonds/levelsetup.conf */
2635 /* ----------------------------------------------------------------------- */
2637 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2639 if ((level_setup_list = loadSetupFileList(filename)))
2641 char *last_level_series =
2642 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2644 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
2645 if (leveldir_current == NULL)
2646 leveldir_current = leveldir_first;
2648 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2650 freeSetupFileList(level_setup_list);
2653 Error(ERR_WARN, "using default setup values");
2658 void SaveLevelSetup_LastSeries()
2661 char *level_subdir = leveldir_current->filename;
2664 /* ----------------------------------------------------------------------- */
2665 /* ~/.rocksndiamonds/levelsetup.conf */
2666 /* ----------------------------------------------------------------------- */
2668 InitUserDataDirectory();
2670 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2672 if (!(file = fopen(filename, MODE_WRITE)))
2674 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2679 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2680 LEVELSETUP_COOKIE));
2681 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2687 chmod(filename, SETUP_PERMS);
2690 static void checkSeriesInfo()
2692 static char *level_directory = NULL;
2694 struct dirent *dir_entry;
2696 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2698 level_directory = getPath2((leveldir_current->user_defined ?
2699 getUserLevelDir("") :
2700 options.level_directory),
2701 leveldir_current->fullpath);
2703 if ((dir = opendir(level_directory)) == NULL)
2705 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2709 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2711 if (strlen(dir_entry->d_name) > 4 &&
2712 dir_entry->d_name[3] == '.' &&
2713 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2715 char levelnum_str[4];
2718 strncpy(levelnum_str, dir_entry->d_name, 3);
2719 levelnum_str[3] = '\0';
2721 levelnum_value = atoi(levelnum_str);
2723 if (levelnum_value < leveldir_current->first_level)
2725 Error(ERR_WARN, "additional level %d found", levelnum_value);
2726 leveldir_current->first_level = levelnum_value;
2728 else if (levelnum_value > leveldir_current->last_level)
2730 Error(ERR_WARN, "additional level %d found", levelnum_value);
2731 leveldir_current->last_level = levelnum_value;
2739 void LoadLevelSetup_SeriesInfo()
2742 struct SetupFileList *level_setup_list = NULL;
2743 char *level_subdir = leveldir_current->filename;
2745 /* always start with reliable default values */
2746 level_nr = leveldir_current->first_level;
2748 checkSeriesInfo(leveldir_current);
2750 /* ----------------------------------------------------------------------- */
2751 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2752 /* ----------------------------------------------------------------------- */
2754 level_subdir = leveldir_current->filename;
2756 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2758 if ((level_setup_list = loadSetupFileList(filename)))
2762 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2766 level_nr = atoi(token_value);
2768 if (level_nr < leveldir_current->first_level)
2769 level_nr = leveldir_current->first_level;
2770 if (level_nr > leveldir_current->last_level)
2771 level_nr = leveldir_current->last_level;
2774 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2778 int level_nr = atoi(token_value);
2780 if (level_nr < leveldir_current->first_level)
2781 level_nr = leveldir_current->first_level;
2782 if (level_nr > leveldir_current->last_level + 1)
2783 level_nr = leveldir_current->last_level;
2785 if (leveldir_current->user_defined)
2786 level_nr = leveldir_current->last_level;
2788 leveldir_current->handicap_level = level_nr;
2791 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
2793 freeSetupFileList(level_setup_list);
2796 Error(ERR_WARN, "using default setup values");
2801 void SaveLevelSetup_SeriesInfo()
2804 char *level_subdir = leveldir_current->filename;
2805 char *level_nr_str = int2str(level_nr, 0);
2806 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2809 /* ----------------------------------------------------------------------- */
2810 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
2811 /* ----------------------------------------------------------------------- */
2813 InitLevelSetupDirectory(level_subdir);
2815 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2817 if (!(file = fopen(filename, MODE_WRITE)))
2819 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2824 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2825 LEVELSETUP_COOKIE));
2826 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2828 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2829 handicap_level_str));
2834 chmod(filename, SETUP_PERMS);
2836 /* LocalWords: Rocks'n