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 CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
29 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
30 #define FILE_VERS_CHUNK_SIZE 8 /* size of file version chunk */
31 #define LEVEL_HEADER_SIZE 80 /* size of level file header */
32 #define LEVEL_HEADER_UNUSED 15 /* unused level header bytes */
33 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
34 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
35 #define TAPE_HEADER_SIZE 20 /* size of tape file header */
36 #define TAPE_HEADER_UNUSED 7 /* unused tape header bytes */
38 /* file identifier strings */
39 #define LEVEL_COOKIE "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_2.0"
40 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
41 #define TAPE_COOKIE "ROCKSNDIAMONDS_TAPE_FILE_VERSION_2.0"
42 #define SETUP_COOKIE "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
43 #define LEVELSETUP_COOKIE "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
44 #define LEVELINFO_COOKIE "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
45 /* old file identifiers for backward compatibility */
46 #define LEVEL_COOKIE_10 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0"
47 #define LEVEL_COOKIE_12 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.2"
48 #define LEVEL_COOKIE_14 "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.4"
49 #define TAPE_COOKIE_10 "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
50 #define TAPE_COOKIE_12 "ROCKSNDIAMONDS_TAPE_FILE_VERSION_1.2"
52 /* file names and filename extensions */
53 #if !defined(PLATFORM_MSDOS)
54 #define LEVELSETUP_DIRECTORY "levelsetup"
55 #define SETUP_FILENAME "setup.conf"
56 #define LEVELSETUP_FILENAME "levelsetup.conf"
57 #define LEVELINFO_FILENAME "levelinfo.conf"
58 #define LEVELFILE_EXTENSION "level"
59 #define TAPEFILE_EXTENSION "tape"
60 #define SCOREFILE_EXTENSION "score"
62 #define LEVELSETUP_DIRECTORY "lvlsetup"
63 #define SETUP_FILENAME "setup.cnf"
64 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
65 #define LEVELINFO_FILENAME "lvlinfo.cnf"
66 #define LEVELFILE_EXTENSION "lvl"
67 #define TAPEFILE_EXTENSION "tap"
68 #define SCOREFILE_EXTENSION "sco"
72 #if defined(PLATFORM_WIN32)
74 #define S_IRGRP S_IRUSR
77 #define S_IROTH S_IRUSR
80 #define S_IWGRP S_IWUSR
83 #define S_IWOTH S_IWUSR
86 #define S_IXGRP S_IXUSR
89 #define S_IXOTH S_IXUSR
91 #endif /* PLATFORM_WIN32 */
93 /* file permissions for newly written files */
94 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
95 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
96 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
98 #define LEVEL_PERMS (MODE_R_ALL | MODE_W_ALL)
99 #define SCORE_PERMS LEVEL_PERMS
100 #define TAPE_PERMS LEVEL_PERMS
101 #define SETUP_PERMS LEVEL_PERMS
104 /* sort priorities of level series (also used as level series classes) */
105 #define LEVELCLASS_TUTORIAL_START 10
106 #define LEVELCLASS_TUTORIAL_END 99
107 #define LEVELCLASS_CLASSICS_START 100
108 #define LEVELCLASS_CLASSICS_END 199
109 #define LEVELCLASS_CONTRIBUTION_START 200
110 #define LEVELCLASS_CONTRIBUTION_END 299
111 #define LEVELCLASS_USER_START 300
112 #define LEVELCLASS_USER_END 399
113 #define LEVELCLASS_BD_START 400
114 #define LEVELCLASS_BD_END 499
115 #define LEVELCLASS_EM_START 500
116 #define LEVELCLASS_EM_END 599
117 #define LEVELCLASS_SP_START 600
118 #define LEVELCLASS_SP_END 699
119 #define LEVELCLASS_DX_START 700
120 #define LEVELCLASS_DX_END 799
122 #define LEVELCLASS_TUTORIAL LEVELCLASS_TUTORIAL_START
123 #define LEVELCLASS_CLASSICS LEVELCLASS_CLASSICS_START
124 #define LEVELCLASS_CONTRIBUTION LEVELCLASS_CONTRIBUTION_START
125 #define LEVELCLASS_USER LEVELCLASS_USER_START
126 #define LEVELCLASS_BD LEVELCLASS_BD_START
127 #define LEVELCLASS_EM LEVELCLASS_EM_START
128 #define LEVELCLASS_SP LEVELCLASS_SP_START
129 #define LEVELCLASS_DX LEVELCLASS_DX_START
131 #define LEVELCLASS_UNDEFINED 999
133 #define NUM_LEVELCLASS_DESC 8
134 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
146 #define IS_LEVELCLASS_TUTORIAL(p) \
147 ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
148 (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
149 #define IS_LEVELCLASS_CLASSICS(p) \
150 ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
151 (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
152 #define IS_LEVELCLASS_CONTRIBUTION(p) \
153 ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
154 (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
155 #define IS_LEVELCLASS_USER(p) \
156 ((p)->sort_priority >= LEVELCLASS_USER_START && \
157 (p)->sort_priority <= LEVELCLASS_USER_END)
158 #define IS_LEVELCLASS_BD(p) \
159 ((p)->sort_priority >= LEVELCLASS_BD_START && \
160 (p)->sort_priority <= LEVELCLASS_BD_END)
161 #define IS_LEVELCLASS_EM(p) \
162 ((p)->sort_priority >= LEVELCLASS_EM_START && \
163 (p)->sort_priority <= LEVELCLASS_EM_END)
164 #define IS_LEVELCLASS_SP(p) \
165 ((p)->sort_priority >= LEVELCLASS_SP_START && \
166 (p)->sort_priority <= LEVELCLASS_SP_END)
167 #define IS_LEVELCLASS_DX(p) \
168 ((p)->sort_priority >= LEVELCLASS_DX_START && \
169 (p)->sort_priority <= LEVELCLASS_DX_END)
171 #define LEVELCLASS(n) (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
172 IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
173 IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
174 IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
175 IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
176 IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
177 IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
178 IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
179 LEVELCLASS_UNDEFINED)
181 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
182 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
183 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
184 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
185 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
186 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
187 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
188 IS_LEVELCLASS_USER(n) ? FC_RED : \
191 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
192 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
193 IS_LEVELCLASS_BD(n) ? 2 : \
194 IS_LEVELCLASS_EM(n) ? 3 : \
195 IS_LEVELCLASS_SP(n) ? 4 : \
196 IS_LEVELCLASS_DX(n) ? 5 : \
197 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
198 IS_LEVELCLASS_USER(n) ? 7 : \
201 char *getLevelClassDescription(struct LevelDirInfo *ldi)
203 int position = ldi->sort_priority / 100;
205 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
206 return levelclass_desc[position];
208 return "Unknown Level Class";
211 static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */
212 static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */
214 static char *getSetupDir()
216 return getUserDataDir();
219 static char *getUserLevelDir(char *level_subdir)
221 static char *userlevel_dir = NULL;
222 char *data_dir = getUserDataDir();
223 char *userlevel_subdir = LEVELS_DIRECTORY;
228 if (strlen(level_subdir) > 0)
229 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
231 userlevel_dir = getPath2(data_dir, userlevel_subdir);
233 return userlevel_dir;
236 static char *getTapeDir(char *level_subdir)
238 static char *tape_dir = NULL;
239 char *data_dir = getUserDataDir();
240 char *tape_subdir = TAPES_DIRECTORY;
245 if (strlen(level_subdir) > 0)
246 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
248 tape_dir = getPath2(data_dir, tape_subdir);
253 static char *getScoreDir(char *level_subdir)
255 static char *score_dir = NULL;
256 char *data_dir = options.rw_base_directory;
257 char *score_subdir = SCORES_DIRECTORY;
262 if (strlen(level_subdir) > 0)
263 score_dir = getPath3(data_dir, score_subdir, level_subdir);
265 score_dir = getPath2(data_dir, score_subdir);
270 static char *getLevelSetupDir(char *level_subdir)
272 static char *levelsetup_dir = NULL;
273 char *data_dir = getUserDataDir();
274 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
277 free(levelsetup_dir);
279 if (strlen(level_subdir) > 0)
280 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
282 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
284 return levelsetup_dir;
287 static char *getLevelFilename(int nr)
289 static char *filename = NULL;
290 char basename[MAX_FILENAME_LEN];
292 if (filename != NULL)
295 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
296 filename = getPath3((leveldir_current->user_defined ?
297 getUserLevelDir("") :
298 options.level_directory),
299 leveldir_current->fullpath,
305 static char *getTapeFilename(int nr)
307 static char *filename = NULL;
308 char basename[MAX_FILENAME_LEN];
310 if (filename != NULL)
313 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
314 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
319 static char *getScoreFilename(int nr)
321 static char *filename = NULL;
322 char basename[MAX_FILENAME_LEN];
324 if (filename != NULL)
327 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
328 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
333 static void InitTapeDirectory(char *level_subdir)
335 createDirectory(getUserDataDir(), "user data");
336 createDirectory(getTapeDir(""), "main tape");
337 createDirectory(getTapeDir(level_subdir), "level tape");
340 static void InitScoreDirectory(char *level_subdir)
342 createDirectory(getScoreDir(""), "main score");
343 createDirectory(getScoreDir(level_subdir), "level score");
346 static void InitUserLevelDirectory(char *level_subdir)
348 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
350 createDirectory(getUserDataDir(), "user data");
351 createDirectory(getUserLevelDir(""), "main user level");
352 createDirectory(getUserLevelDir(level_subdir), "user level");
358 static void InitLevelSetupDirectory(char *level_subdir)
360 createDirectory(getUserDataDir(), "user data");
361 createDirectory(getLevelSetupDir(""), "main level setup");
362 createDirectory(getLevelSetupDir(level_subdir), "level setup");
365 static void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
371 static void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
377 static void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
379 int file_version_major, file_version_minor, file_version_patch;
380 int game_version_major, game_version_minor, game_version_patch;
382 file_version_major = fgetc(file);
383 file_version_minor = fgetc(file);
384 file_version_patch = fgetc(file);
385 fgetc(file); /* not used */
387 game_version_major = fgetc(file);
388 game_version_minor = fgetc(file);
389 game_version_patch = fgetc(file);
390 fgetc(file); /* not used */
392 *file_version = VERSION_IDENT(file_version_major,
396 *game_version = VERSION_IDENT(game_version_major,
401 static void WriteChunk_VERS(FILE *file, int file_version, int game_version)
403 int file_version_major = VERSION_MAJOR(file_version);
404 int file_version_minor = VERSION_MINOR(file_version);
405 int file_version_patch = VERSION_PATCH(file_version);
406 int game_version_major = VERSION_MAJOR(game_version);
407 int game_version_minor = VERSION_MINOR(game_version);
408 int game_version_patch = VERSION_PATCH(game_version);
410 fputc(file_version_major, file);
411 fputc(file_version_minor, file);
412 fputc(file_version_patch, file);
413 fputc(0, file); /* not used */
415 fputc(game_version_major, file);
416 fputc(game_version_minor, file);
417 fputc(game_version_patch, file);
418 fputc(0, file); /* not used */
421 static int getFileVersionFromCookieString(const char *cookie)
423 const char *ptr_cookie1, *ptr_cookie2;
424 const char *pattern1 = "_FILE_VERSION_";
425 const char *pattern2 = "?.?";
426 const int len_cookie = strlen(cookie);
427 const int len_pattern1 = strlen(pattern1);
428 const int len_pattern2 = strlen(pattern2);
429 const int len_pattern = len_pattern1 + len_pattern2;
430 int version_major, version_minor;
432 if (len_cookie <= len_pattern)
435 ptr_cookie1 = &cookie[len_cookie - len_pattern];
436 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
438 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
441 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
442 ptr_cookie2[1] != '.' ||
443 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
446 version_major = ptr_cookie2[0] - '0';
447 version_minor = ptr_cookie2[2] - '0';
449 return VERSION_IDENT(version_major, version_minor, 0);
452 boolean checkCookieString(const char *cookie, const char *template)
454 const char *pattern = "_FILE_VERSION_?.?";
455 const int len_cookie = strlen(cookie);
456 const int len_template = strlen(template);
457 const int len_pattern = strlen(pattern);
459 if (len_cookie != len_template)
462 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
468 static void setLevelInfoToDefaults()
472 level.file_version = FILE_VERSION_ACTUAL;
473 level.game_version = GAME_VERSION_ACTUAL;
475 level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
476 level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
477 level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
479 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
480 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
482 for(x=0; x<MAX_LEV_FIELDX; x++)
483 for(y=0; y<MAX_LEV_FIELDY; y++)
484 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
487 level.gems_needed = 0;
488 level.amoeba_speed = 10;
489 level.time_magic_wall = 10;
490 level.time_wheel = 10;
491 level.time_light = 10;
492 level.time_timegate = 10;
493 level.amoeba_content = EL_DIAMANT;
494 level.double_speed = FALSE;
495 level.gravity = FALSE;
497 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
498 level.name[i] = '\0';
499 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
500 level.author[i] = '\0';
502 strcpy(level.name, NAMELESS_LEVEL_NAME);
503 strcpy(level.author, ANONYMOUS_NAME);
505 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
508 level.num_yam_contents = STD_ELEMENT_CONTENTS;
509 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
512 level.yam_content[i][x][y] =
513 (i < STD_ELEMENT_CONTENTS ? EL_FELSBROCKEN : EL_LEERRAUM);
515 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
516 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
517 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
519 BorderElement = EL_BETON;
521 /* try to determine better author name than 'anonymous' */
522 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
524 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
525 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
529 switch (LEVELCLASS(leveldir_current))
531 case LEVELCLASS_TUTORIAL:
532 strcpy(level.author, PROGRAM_AUTHOR_STRING);
535 case LEVELCLASS_CONTRIBUTION:
536 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
537 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
540 case LEVELCLASS_USER:
541 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
542 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
546 /* keep default value */
552 static int checkLevelElement(int element)
554 if (element >= EL_FIRST_RUNTIME_EL)
556 Error(ERR_WARN, "invalid level element %d", element);
557 element = EL_CHAR_FRAGE;
563 void OLD_LoadLevel(int level_nr)
566 char *filename = getLevelFilename(level_nr);
567 char cookie[MAX_LINE_LEN];
568 char chunk_name[CHUNK_ID_LEN + 1];
569 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
570 int file_version = FILE_VERSION_ACTUAL;
574 /* always start with reliable default values */
575 setLevelInfoToDefaults();
577 if (!(file = fopen(filename, MODE_READ)))
579 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
583 /* check file identifier */
584 fgets(cookie, MAX_LINE_LEN, file);
585 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
586 cookie[strlen(cookie) - 1] = '\0';
589 if (strcmp(cookie, LEVEL_COOKIE_10) == 0) /* old 1.0 level format */
590 file_version = FILE_VERSION_1_0;
591 else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
592 file_version = FILE_VERSION_1_2;
593 else if (strcmp(cookie, LEVEL_COOKIE) != 0) /* unknown level format */
595 Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
600 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
602 Error(ERR_WARN, "unknown format of level file '%s'", filename);
607 file_version = getFileVersionFromCookieString(cookie);
610 level.file_version = file_version;
612 /* read chunk "HEAD" */
613 if (file_version >= FILE_VERSION_1_2)
615 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
616 if (strcmp(chunk_name, "HEAD") || chunk_size != LEVEL_HEADER_SIZE)
618 Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
624 lev_fieldx = level.fieldx = fgetc(file);
625 lev_fieldy = level.fieldy = fgetc(file);
627 level.time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
628 level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
630 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
631 level.name[i] = fgetc(file);
632 level.name[MAX_LEVEL_NAME_LEN] = 0;
634 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
635 level.score[i] = fgetc(file);
637 level.num_yam_contents = STD_ELEMENT_CONTENTS;
638 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
644 if (i < STD_ELEMENT_CONTENTS)
645 level.yam_content[i][x][y] = checkLevelElement(fgetc(file));
647 level.yam_content[i][x][y] = EL_LEERRAUM;
652 level.amoeba_speed = fgetc(file);
653 level.time_magic_wall = fgetc(file);
654 level.time_wheel = fgetc(file);
655 level.amoeba_content = checkLevelElement(fgetc(file));
656 level.double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
657 level.gravity = (fgetc(file) == 1 ? TRUE : FALSE);
659 encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
661 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
664 if (file_version >= FILE_VERSION_1_2)
666 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
668 /* look for optional author chunk */
669 if (strcmp(chunk_name, "AUTH") == 0 && chunk_size == MAX_LEVEL_AUTHOR_LEN)
671 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
672 level.author[i] = fgetc(file);
673 level.author[MAX_LEVEL_NAME_LEN] = 0;
675 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
678 /* look for optional content chunk */
679 if (strcmp(chunk_name, "CONT") == 0 &&
680 chunk_size == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
683 level.num_yam_contents = fgetc(file);
687 if (level.num_yam_contents < 1 ||
688 level.num_yam_contents > MAX_ELEMENT_CONTENTS)
691 printf("WARNING: num_yam_contents == %d (corrected)\n",
692 level.num_yam_contents);
694 level.num_yam_contents = STD_ELEMENT_CONTENTS;
697 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
700 level.yam_content[i][x][y] =
701 checkLevelElement(encoding_16bit ?
702 getFile16BitInteger(file,
703 BYTE_ORDER_BIG_ENDIAN) :
706 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
709 /* next check body chunk identifier and chunk size */
710 if (strcmp(chunk_name, "BODY") != 0 ||
711 chunk_size != lev_fieldx * lev_fieldy)
713 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
719 /* clear all other level fields (needed if resized in level editor later) */
720 for(x=0; x<MAX_LEV_FIELDX; x++)
721 for(y=0; y<MAX_LEV_FIELDY; y++)
722 Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
724 /* now read in the valid level fields from level file */
725 for(y=0; y<lev_fieldy; y++)
726 for(x=0; x<lev_fieldx; x++)
727 Feld[x][y] = Ur[x][y] =
728 checkLevelElement(encoding_16bit ?
729 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
734 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
735 IS_LEVELCLASS_USER(leveldir_current))
737 /* for user contributed and private levels, use the version of
738 the game engine the levels were created for */
739 level.game_version = file_version;
741 /* player was faster than monsters in pre-1.0 levels */
742 if (file_version == FILE_VERSION_1_0)
744 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
745 Error(ERR_WARN, "using high speed movement for player");
746 level.double_speed = TRUE;
751 /* always use the latest version of the game engine for all but
752 user contributed and private levels */
753 level.game_version = GAME_VERSION_ACTUAL;
756 /* determine border element for this level */
760 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
762 ReadChunk_VERS(file, &(level->file_version), &(level->game_version));
767 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
771 lev_fieldx = level->fieldx = fgetc(file);
772 lev_fieldy = level->fieldy = fgetc(file);
774 level->time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
775 level->gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
777 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
778 level->name[i] = fgetc(file);
779 level->name[MAX_LEVEL_NAME_LEN] = 0;
781 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
782 level->score[i] = fgetc(file);
784 level->num_yam_contents = STD_ELEMENT_CONTENTS;
785 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
788 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
790 level->amoeba_speed = fgetc(file);
791 level->time_magic_wall = fgetc(file);
792 level->time_wheel = fgetc(file);
793 level->amoeba_content = checkLevelElement(fgetc(file));
794 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
795 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
797 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
799 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
804 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
808 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
809 level->author[i] = fgetc(file);
810 level->author[MAX_LEVEL_NAME_LEN] = 0;
815 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
819 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
820 int chunk_size_expected = header_size + content_size;
822 /* Note: "chunk_size" was wrong before version 2.0 when elements are
823 stored with 16-bit encoding (and should be twice as big then).
824 Even worse, playfield data was stored 16-bit when only yamyam content
825 contained 16-bit elements and vice versa. */
827 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
828 chunk_size_expected += content_size;
830 if (chunk_size_expected != chunk_size)
832 ReadUnusedBytesFromFile(file, chunk_size);
833 return chunk_size_expected;
837 level->num_yam_contents = fgetc(file);
841 /* correct invalid number of content fields -- should never happen */
842 if (level->num_yam_contents < 1 ||
843 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
844 level->num_yam_contents = STD_ELEMENT_CONTENTS;
846 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
849 level->yam_content[i][x][y] =
850 checkLevelElement(level->encoding_16bit_field ?
851 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
856 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
859 int chunk_size_expected = level->fieldx * level->fieldy;
861 /* Note: "chunk_size" was wrong before version 2.0 when elements are
862 stored with 16-bit encoding (and should be twice as big then).
863 Even worse, playfield data was stored 16-bit when only yamyam content
864 contained 16-bit elements and vice versa. */
866 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
867 chunk_size_expected *= 2;
869 if (chunk_size_expected != chunk_size)
871 ReadUnusedBytesFromFile(file, chunk_size);
872 return chunk_size_expected;
875 for(y=0; y<level->fieldy; y++)
876 for(x=0; x<level->fieldx; x++)
877 Feld[x][y] = Ur[x][y] =
878 checkLevelElement(level->encoding_16bit_field ?
879 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
884 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
888 int num_contents, content_xsize, content_ysize;
889 int content_array[MAX_ELEMENT_CONTENTS][3][3];
891 element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
892 num_contents = fgetc(file);
893 content_xsize = fgetc(file);
894 content_ysize = fgetc(file);
895 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
897 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
900 content_array[i][x][y] =
901 checkLevelElement(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
903 /* correct invalid number of content fields -- should never happen */
904 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
905 num_contents = STD_ELEMENT_CONTENTS;
907 if (element == EL_MAMPFER)
909 level->num_yam_contents = num_contents;
911 for(i=0; i<num_contents; i++)
914 level->yam_content[i][x][y] = content_array[i][x][y];
916 else if (element == EL_AMOEBE_BD)
918 level->amoeba_content = content_array[0][0][0];
922 Error(ERR_WARN, "cannot load content for element '%d'", element);
928 void LoadLevel(int level_nr)
930 char *filename = getLevelFilename(level_nr);
931 char cookie[MAX_LINE_LEN];
932 char chunk_name[CHUNK_ID_LEN + 1];
936 /* always start with reliable default values */
937 setLevelInfoToDefaults();
939 if (!(file = fopen(filename, MODE_READ)))
941 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
946 /* check file identifier */
947 fgets(cookie, MAX_LINE_LEN, file);
948 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
949 cookie[strlen(cookie) - 1] = '\0';
951 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
953 Error(ERR_WARN, "unknown format of level file '%s'", filename);
958 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
960 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
965 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
966 if (strcmp(chunk_name, "RND1") == 0)
968 getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN); /* not used */
970 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
971 if (strcmp(chunk_name, "CAVE") != 0)
973 Error(ERR_WARN, "unknown format of level file '%s'", filename);
978 else /* check for pre-2.0 file format with cookie string */
980 strcpy(cookie, chunk_name);
981 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
982 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
983 cookie[strlen(cookie) - 1] = '\0';
985 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
987 Error(ERR_WARN, "unknown format of level file '%s'", filename);
992 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
994 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1001 if (level.file_version < FILE_VERSION_1_2)
1003 /* level files from versions before 1.2.0 without chunk structure */
1004 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
1005 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
1013 int (*loader)(FILE *, int, struct LevelInfo *);
1017 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1018 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1019 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1020 { "CONT", -1, LoadLevel_CONT },
1021 { "BODY", -1, LoadLevel_BODY },
1022 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1026 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
1030 while (chunk_info[i].name != NULL &&
1031 strcmp(chunk_name, chunk_info[i].name) != 0)
1034 if (chunk_info[i].name == NULL)
1036 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1037 chunk_name, filename);
1038 ReadUnusedBytesFromFile(file, chunk_size);
1040 else if (chunk_info[i].size != -1 &&
1041 chunk_info[i].size != chunk_size)
1043 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1044 chunk_size, chunk_name, filename);
1045 ReadUnusedBytesFromFile(file, chunk_size);
1049 /* call function to load this level chunk */
1050 int chunk_size_expected =
1051 (chunk_info[i].loader)(file, chunk_size, &level);
1053 /* the size of some chunks cannot be checked before reading other
1054 chunks first (like "HEAD" and "BODY") that contain some header
1055 information, so check them here */
1056 if (chunk_size_expected != chunk_size)
1058 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1059 chunk_size, chunk_name, filename);
1067 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
1068 IS_LEVELCLASS_USER(leveldir_current))
1070 /* for user contributed and private levels, use the version of
1071 the game engine the levels were created for */
1072 level.game_version = level.file_version;
1074 /* player was faster than monsters in pre-1.0 levels */
1075 if (level.file_version == FILE_VERSION_1_0)
1077 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
1078 Error(ERR_WARN, "using high speed movement for player");
1079 level.double_speed = TRUE;
1084 /* always use the latest version of the game engine for all but
1085 user contributed and private levels */
1086 level.game_version = GAME_VERSION_ACTUAL;
1089 /* determine border element for this level */
1093 void OLD_SaveLevel(int level_nr)
1096 char *filename = getLevelFilename(level_nr);
1098 boolean encoding_16bit_amoeba = FALSE;
1099 boolean encoding_16bit_yamyam = FALSE;
1101 boolean encoding_16bit = FALSE; /* default: only 8-bit elements */
1102 char *oldest_possible_cookie;
1105 if (!(file = fopen(filename, MODE_WRITE)))
1107 Error(ERR_WARN, "cannot save level file '%s'", filename);
1111 /* check yam content for 16-bit elements */
1112 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1115 if (level.yam_content[i][x][y] > 255)
1116 encoding_16bit = TRUE;
1118 /* check level field for 16-bit elements */
1119 for(y=0; y<lev_fieldy; y++)
1120 for(x=0; x<lev_fieldx; x++)
1122 encoding_16bit = TRUE;
1124 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
1126 fputs(oldest_possible_cookie, file); /* file identifier */
1129 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1131 fputc(level.fieldx, file);
1132 fputc(level.fieldy, file);
1134 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
1135 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
1137 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1138 fputc(level.name[i], file);
1139 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1140 fputc(level.score[i], file);
1141 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1144 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
1145 fputc(level.amoeba_speed, file);
1146 fputc(level.time_magic_wall, file);
1147 fputc(level.time_wheel, file);
1148 fputc(level.amoeba_content, file);
1149 fputc((level.double_speed ? 1 : 0), file);
1150 fputc((level.gravity ? 1 : 0), file);
1152 fputc((encoding_16bit ? 1 : 0), file);
1154 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1157 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1159 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1160 fputc(level.author[i], file);
1162 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
1163 BYTE_ORDER_BIG_ENDIAN);
1165 fputc(EL_MAMPFER, file);
1166 fputc(level.num_yam_contents, file);
1170 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1174 putFile16BitInteger(file, level.yam_content[i][x][y],
1175 BYTE_ORDER_BIG_ENDIAN);
1177 fputc(level.yam_content[i][x][y], file);
1179 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
1181 for(y=0; y<lev_fieldy; y++)
1182 for(x=0; x<lev_fieldx; x++)
1184 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1186 fputc(Ur[x][y], file);
1190 SetFilePermissions_Level(filename);
1193 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1197 fputc(level->fieldx, file);
1198 fputc(level->fieldy, file);
1200 putFile16BitInteger(file, level->time, BYTE_ORDER_BIG_ENDIAN);
1201 putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
1203 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1204 fputc(level->name[i], file);
1206 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1207 fputc(level->score[i], file);
1209 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1212 fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
1213 level->yam_content[i][x][y]),
1215 fputc(level->amoeba_speed, file);
1216 fputc(level->time_magic_wall, file);
1217 fputc(level->time_wheel, file);
1218 fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
1220 fputc((level->double_speed ? 1 : 0), file);
1221 fputc((level->gravity ? 1 : 0), file);
1223 fputc((level->encoding_16bit_field ? 1 : 0), file);
1225 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1228 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1232 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1233 fputc(level->author[i], file);
1237 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1241 fputc(EL_MAMPFER, file);
1242 fputc(level->num_yam_contents, file);
1246 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1249 if (level->encoding_16bit_field)
1250 putFile16BitInteger(file, level->yam_content[i][x][y],
1251 BYTE_ORDER_BIG_ENDIAN);
1253 fputc(level->yam_content[i][x][y], file);
1257 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1261 for(y=0; y<lev_fieldy; y++)
1262 for(x=0; x<lev_fieldx; x++)
1263 if (level->encoding_16bit_field)
1264 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1266 fputc(Ur[x][y], file);
1269 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1272 int num_contents, content_xsize, content_ysize;
1273 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1275 if (element == EL_MAMPFER)
1277 num_contents = level->num_yam_contents;
1281 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1284 content_array[i][x][y] = level->yam_content[i][x][y];
1286 else if (element == EL_AMOEBE_BD)
1292 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1295 content_array[i][x][y] = EL_LEERRAUM;
1296 content_array[0][0][0] = level->amoeba_content;
1300 /* chunk header already written -- write empty chunk data */
1301 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1303 Error(ERR_WARN, "cannot save content for element '%d'", element);
1307 putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
1308 fputc(num_contents, file);
1309 fputc(content_xsize, file);
1310 fputc(content_ysize, file);
1312 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1314 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1317 putFile16BitInteger(file, content_array[i][x][y],
1318 BYTE_ORDER_BIG_ENDIAN);
1321 void SaveLevel(int level_nr)
1324 char *filename = getLevelFilename(level_nr);
1325 int body_chunk_size;
1328 if (!(file = fopen(filename, MODE_WRITE)))
1330 Error(ERR_WARN, "cannot save level file '%s'", filename);
1334 /* check level field for 16-bit elements */
1335 for(y=0; y<level.fieldy; y++)
1336 for(x=0; x<level.fieldx; x++)
1338 level.encoding_16bit_field = TRUE;
1340 /* check yamyam content for 16-bit elements */
1341 for(i=0; i<level.num_yam_contents; i++)
1344 if (level.yam_content[i][x][y] > 255)
1345 level.encoding_16bit_yamyam = TRUE;
1347 /* check amoeba content for 16-bit elements */
1348 if (level.amoeba_content > 255)
1349 level.encoding_16bit_amoeba = TRUE;
1352 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
1355 fputs(LEVEL_COOKIE, file); /* file identifier */
1358 putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
1359 putFileChunk(file, "CAVE", CHUNK_SIZE_NONE, BYTE_ORDER_BIG_ENDIAN);
1361 putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
1362 WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
1365 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1366 SaveLevel_HEAD(file, &level);
1368 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1369 SaveLevel_AUTH(file, &level);
1372 if (level.encoding_16bit_field) /* obsolete since new "CNT2" chunk */
1374 chunk_size = 4 + 2 * (MAX_ELEMENT_CONTENTS * 3 * 3);
1376 putFileChunk(file, "CONT", chunk_size, BYTE_ORDER_BIG_ENDIAN);
1377 SaveLevel_CONT(file, &level);
1381 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1382 SaveLevel_BODY(file, &level);
1384 if (level.encoding_16bit_yamyam ||
1385 level.num_yam_contents != STD_ELEMENT_CONTENTS)
1387 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1388 SaveLevel_CNT2(file, &level, EL_MAMPFER);
1391 if (level.encoding_16bit_amoeba)
1393 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1394 SaveLevel_CNT2(file, &level, EL_AMOEBE_BD);
1399 SetFilePermissions_Level(filename);
1402 static void setTapeInfoToDefaults()
1406 /* always start with reliable default values (empty tape) */
1407 tape.file_version = FILE_VERSION_ACTUAL;
1408 tape.game_version = GAME_VERSION_ACTUAL;
1411 /* default values (also for pre-1.2 tapes) with only the first player */
1412 tape.player_participates[0] = TRUE;
1413 for(i=1; i<MAX_PLAYERS; i++)
1414 tape.player_participates[i] = FALSE;
1416 /* at least one (default: the first) player participates in every tape */
1417 tape.num_participating_players = 1;
1419 tape.level_nr = level_nr;
1421 tape.changed = FALSE;
1423 tape.recording = FALSE;
1424 tape.playing = FALSE;
1425 tape.pausing = FALSE;
1428 void OLD_LoadTape(int level_nr)
1431 char *filename = getTapeFilename(level_nr);
1432 char cookie[MAX_LINE_LEN];
1433 char chunk_name[CHUNK_ID_LEN + 1];
1435 int num_participating_players;
1436 int file_version = FILE_VERSION_ACTUAL; /* last version of tape files */
1439 /* always start with reliable default values (empty tape) */
1440 tape.file_version = FILE_VERSION_ACTUAL;
1441 tape.game_version = GAME_VERSION_ACTUAL;
1444 /* default values (also for pre-1.2 tapes) with only the first player */
1445 tape.player_participates[0] = TRUE;
1446 for(i=1; i<MAX_PLAYERS; i++)
1447 tape.player_participates[i] = FALSE;
1449 /* at least one (default: the first) player participates in every tape */
1450 num_participating_players = 1;
1452 if (!(file = fopen(filename, MODE_READ)))
1455 /* check file identifier */
1456 fgets(cookie, MAX_LINE_LEN, file);
1457 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1458 cookie[strlen(cookie) - 1] = '\0';
1461 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
1462 file_version = FILE_VERSION_1_0;
1463 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
1465 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
1470 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1472 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1477 file_version = getFileVersionFromCookieString(cookie);
1480 tape.file_version = file_version;
1481 tape.game_version = file_version;
1483 /* read chunk "HEAD" */
1484 if (file_version >= FILE_VERSION_1_2)
1486 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1487 if (strcmp(chunk_name, "HEAD") || chunk_size != TAPE_HEADER_SIZE)
1489 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
1495 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1496 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1497 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1499 /* read header fields that are new since version 1.2 */
1500 if (file_version >= FILE_VERSION_1_2)
1502 byte store_participating_players = fgetc(file);
1504 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
1507 /* since version 1.2, tapes store which players participate in the tape */
1508 num_participating_players = 0;
1509 for(i=0; i<MAX_PLAYERS; i++)
1511 tape.player_participates[i] = FALSE;
1513 if (store_participating_players & (1 << i))
1515 tape.player_participates[i] = TRUE;
1516 num_participating_players++;
1521 tape.level_nr = level_nr;
1523 tape.changed = FALSE;
1525 tape.recording = FALSE;
1526 tape.playing = FALSE;
1527 tape.pausing = FALSE;
1529 /* read chunk "BODY" */
1530 if (file_version >= FILE_VERSION_1_2)
1532 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1533 if (strcmp(chunk_name, "BODY") ||
1534 chunk_size != (num_participating_players + 1) * tape.length)
1536 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
1543 printf("\nTAPE OF LEVEL %d\n", level_nr);
1546 for(i=0; i<tape.length; i++)
1548 if (i >= MAX_TAPELEN)
1551 for(j=0; j<MAX_PLAYERS; j++)
1553 tape.pos[i].action[j] = MV_NO_MOVING;
1555 if (tape.player_participates[j])
1556 tape.pos[i].action[j] = fgetc(file);
1560 int x = tape.pos[i].action[j];
1562 printf("%d:%02x ", j, x);
1563 printf("[%c%c%c%c|%c%c] - ",
1564 (x & JOY_LEFT ? '<' : ' '),
1565 (x & JOY_RIGHT ? '>' : ' '),
1566 (x & JOY_UP ? '^' : ' '),
1567 (x & JOY_DOWN ? 'v' : ' '),
1568 (x & JOY_BUTTON_1 ? '1' : ' '),
1569 (x & JOY_BUTTON_2 ? '2' : ' '));
1575 tape.pos[i].delay = fgetc(file);
1578 printf("[%03d]\n", tape.pos[i].delay);
1581 if (file_version == FILE_VERSION_1_0)
1583 /* eliminate possible diagonal moves in old tapes */
1584 /* this is only for backward compatibility */
1586 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1587 byte action = tape.pos[i].action[0];
1588 int k, num_moves = 0;
1592 if (action & joy_dir[k])
1594 tape.pos[i + num_moves].action[0] = joy_dir[k];
1596 tape.pos[i + num_moves].delay = 0;
1605 tape.length += num_moves;
1608 else if (file_version < FILE_VERSION_2_0)
1610 if (tape.pos[i].delay > 1)
1613 tape.pos[i + 1] = tape.pos[i];
1614 tape.pos[i + 1].delay = 1;
1617 for(j=0; j<MAX_PLAYERS; j++)
1618 tape.pos[i].action[j] = MV_NO_MOVING;
1619 tape.pos[i].delay--;
1632 if (i != tape.length)
1633 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
1635 tape.length_seconds = GetTapeLength();
1638 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1640 ReadChunk_VERS(file, &(tape->file_version), &(tape->game_version));
1645 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1649 tape->random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1650 tape->date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1651 tape->length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1653 /* read header fields that are new since version 1.2 */
1654 if (tape->file_version >= FILE_VERSION_1_2)
1656 byte store_participating_players = fgetc(file);
1658 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1660 /* since version 1.2, tapes store which players participate in the tape */
1661 tape->num_participating_players = 0;
1662 for(i=0; i<MAX_PLAYERS; i++)
1664 tape->player_participates[i] = FALSE;
1666 if (store_participating_players & (1 << i))
1668 tape->player_participates[i] = TRUE;
1669 tape->num_participating_players++;
1677 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1680 int chunk_size_expected =
1681 (tape->num_participating_players + 1) * tape->length;
1683 if (chunk_size_expected != chunk_size)
1685 ReadUnusedBytesFromFile(file, chunk_size);
1686 return chunk_size_expected;
1689 for(i=0; i<tape->length; i++)
1691 if (i >= MAX_TAPELEN)
1694 for(j=0; j<MAX_PLAYERS; j++)
1696 tape->pos[i].action[j] = MV_NO_MOVING;
1698 if (tape->player_participates[j])
1699 tape->pos[i].action[j] = fgetc(file);
1702 tape->pos[i].delay = fgetc(file);
1704 if (tape->file_version == FILE_VERSION_1_0)
1706 /* eliminate possible diagonal moves in old tapes */
1707 /* this is only for backward compatibility */
1709 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1710 byte action = tape->pos[i].action[0];
1711 int k, num_moves = 0;
1715 if (action & joy_dir[k])
1717 tape->pos[i + num_moves].action[0] = joy_dir[k];
1719 tape->pos[i + num_moves].delay = 0;
1728 tape->length += num_moves;
1731 else if (tape->file_version < FILE_VERSION_2_0)
1733 if (tape->pos[i].delay > 1)
1736 tape->pos[i + 1] = tape->pos[i];
1737 tape->pos[i + 1].delay = 1;
1740 for(j=0; j<MAX_PLAYERS; j++)
1741 tape->pos[i].action[j] = MV_NO_MOVING;
1742 tape->pos[i].delay--;
1753 if (i != tape->length)
1754 chunk_size = (tape->num_participating_players + 1) * i;
1759 void LoadTape(int level_nr)
1761 char *filename = getTapeFilename(level_nr);
1762 char cookie[MAX_LINE_LEN];
1763 char chunk_name[CHUNK_ID_LEN + 1];
1767 /* always start with reliable default values */
1768 setTapeInfoToDefaults();
1770 if (!(file = fopen(filename, MODE_READ)))
1774 /* check file identifier */
1775 fgets(cookie, MAX_LINE_LEN, file);
1776 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1777 cookie[strlen(cookie) - 1] = '\0';
1779 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1781 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1786 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
1788 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1793 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
1794 if (strcmp(chunk_name, "RND1") == 0)
1796 getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN); /* not used */
1798 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
1799 if (strcmp(chunk_name, "TAPE") != 0)
1801 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1806 else /* check for pre-2.0 file format with cookie string */
1808 strcpy(cookie, chunk_name);
1809 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1810 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1811 cookie[strlen(cookie) - 1] = '\0';
1813 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1815 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1820 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1822 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1829 tape.game_version = tape.file_version;
1831 if (tape.file_version < FILE_VERSION_1_2)
1833 /* tape files from versions before 1.2.0 without chunk structure */
1834 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1835 LoadTape_BODY(file, 2 * tape.length, &tape);
1843 int (*loader)(FILE *, int, struct TapeInfo *);
1847 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1848 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1849 { "BODY", -1, LoadTape_BODY },
1853 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
1857 while (chunk_info[i].name != NULL &&
1858 strcmp(chunk_name, chunk_info[i].name) != 0)
1861 if (chunk_info[i].name == NULL)
1863 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1864 chunk_name, filename);
1865 ReadUnusedBytesFromFile(file, chunk_size);
1867 else if (chunk_info[i].size != -1 &&
1868 chunk_info[i].size != chunk_size)
1870 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1871 chunk_size, chunk_name, filename);
1872 ReadUnusedBytesFromFile(file, chunk_size);
1876 /* call function to load this tape chunk */
1877 int chunk_size_expected =
1878 (chunk_info[i].loader)(file, chunk_size, &tape);
1880 /* the size of some chunks cannot be checked before reading other
1881 chunks first (like "HEAD" and "BODY") that contain some header
1882 information, so check them here */
1883 if (chunk_size_expected != chunk_size)
1885 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1886 chunk_size, chunk_name, filename);
1894 tape.length_seconds = GetTapeLength();
1897 void OLD_SaveTape(int level_nr)
1900 char *filename = getTapeFilename(level_nr);
1902 boolean new_tape = TRUE;
1903 byte store_participating_players;
1904 int num_participating_players;
1906 InitTapeDirectory(leveldir_current->filename);
1908 /* if a tape still exists, ask to overwrite it */
1909 if (access(filename, F_OK) == 0)
1912 if (!Request("Replace old tape ?", REQ_ASK))
1916 /* count number of players and set corresponding bits for compact storage */
1917 store_participating_players = 0;
1918 num_participating_players = 0;
1919 for(i=0; i<MAX_PLAYERS; i++)
1921 if (tape.player_participates[i])
1923 num_participating_players++;
1924 store_participating_players |= (1 << i);
1928 if (!(file = fopen(filename, MODE_WRITE)))
1930 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1934 fputs(TAPE_COOKIE, file); /* file identifier */
1937 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1939 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
1940 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
1941 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
1943 fputc(store_participating_players, file);
1945 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1948 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
1949 BYTE_ORDER_BIG_ENDIAN);
1951 for(i=0; i<tape.length; i++)
1955 for(j=0; j<MAX_PLAYERS; j++)
1956 if (tape.player_participates[j])
1957 fputc(tape.pos[i].action[j], file);
1959 fputc(tape.pos[i].delay, file);
1964 SetFilePermissions_Tape(filename);
1966 tape.changed = FALSE;
1969 Request("tape saved !", REQ_CONFIRM);
1972 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1975 byte store_participating_players = 0;
1977 /* set bits for participating players for compact storage */
1978 for(i=0; i<MAX_PLAYERS; i++)
1979 if (tape->player_participates[i])
1980 store_participating_players |= (1 << i);
1982 putFile32BitInteger(file, tape->random_seed, BYTE_ORDER_BIG_ENDIAN);
1983 putFile32BitInteger(file, tape->date, BYTE_ORDER_BIG_ENDIAN);
1984 putFile32BitInteger(file, tape->length, BYTE_ORDER_BIG_ENDIAN);
1986 fputc(store_participating_players, file);
1988 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1991 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1995 for(i=0; i<tape->length; i++)
1997 for(j=0; j<MAX_PLAYERS; j++)
1998 if (tape->player_participates[j])
1999 fputc(tape->pos[i].action[j], file);
2001 fputc(tape->pos[i].delay, file);
2005 void SaveTape(int level_nr)
2008 char *filename = getTapeFilename(level_nr);
2010 boolean new_tape = TRUE;
2011 int num_participating_players = 0;
2012 int body_chunk_size;
2014 InitTapeDirectory(leveldir_current->filename);
2016 /* if a tape still exists, ask to overwrite it */
2017 if (access(filename, F_OK) == 0)
2020 if (!Request("Replace old tape ?", REQ_ASK))
2024 if (!(file = fopen(filename, MODE_WRITE)))
2026 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2030 /* count number of participating players */
2031 for(i=0; i<MAX_PLAYERS; i++)
2032 if (tape.player_participates[i])
2033 num_participating_players++;
2035 body_chunk_size = (num_participating_players + 1) * tape.length;
2038 fputs(TAPE_COOKIE, file); /* file identifier */
2041 putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
2042 putFileChunk(file, "TAPE", CHUNK_SIZE_NONE, BYTE_ORDER_BIG_ENDIAN);
2044 putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
2045 WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
2048 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
2049 SaveTape_HEAD(file, &tape);
2051 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
2052 SaveTape_BODY(file, &tape);
2056 SetFilePermissions_Tape(filename);
2058 tape.changed = FALSE;
2061 Request("tape saved !", REQ_CONFIRM);
2064 void DumpTape(struct TapeInfo *tape)
2068 if (TAPE_IS_EMPTY(*tape))
2070 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2075 printf("-------------------------------------------------------------------------------\n");
2076 printf("Tape of Level %d (file version %06d, game version %06d\n",
2077 tape->level_nr, tape->file_version, tape->game_version);
2078 printf("-------------------------------------------------------------------------------\n");
2080 for(i=0; i<tape->length; i++)
2082 if (i >= MAX_TAPELEN)
2085 for(j=0; j<MAX_PLAYERS; j++)
2087 if (tape->player_participates[j])
2089 int action = tape->pos[i].action[j];
2091 printf("%d:%02x ", j, action);
2092 printf("[%c%c%c%c|%c%c] - ",
2093 (action & JOY_LEFT ? '<' : ' '),
2094 (action & JOY_RIGHT ? '>' : ' '),
2095 (action & JOY_UP ? '^' : ' '),
2096 (action & JOY_DOWN ? 'v' : ' '),
2097 (action & JOY_BUTTON_1 ? '1' : ' '),
2098 (action & JOY_BUTTON_2 ? '2' : ' '));
2102 printf("(%03d)\n", tape->pos[i].delay);
2105 printf("-------------------------------------------------------------------------------\n");
2108 void LoadScore(int level_nr)
2111 char *filename = getScoreFilename(level_nr);
2112 char cookie[MAX_LINE_LEN];
2113 char line[MAX_LINE_LEN];
2117 /* always start with reliable default values */
2118 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2120 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2121 highscore[i].Score = 0;
2124 if (!(file = fopen(filename, MODE_READ)))
2127 /* check file identifier */
2128 fgets(cookie, MAX_LINE_LEN, file);
2129 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2130 cookie[strlen(cookie) - 1] = '\0';
2133 if (strcmp(cookie, SCORE_COOKIE) != 0)
2135 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
2140 if (!checkCookieString(cookie, SCORE_COOKIE)) /* unknown file format */
2142 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2148 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2150 fscanf(file, "%d", &highscore[i].Score);
2151 fgets(line, MAX_LINE_LEN, file);
2153 if (line[strlen(line) - 1] == '\n')
2154 line[strlen(line) - 1] = '\0';
2156 for (line_ptr = line; *line_ptr; line_ptr++)
2158 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2160 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2161 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2170 void SaveScore(int level_nr)
2173 char *filename = getScoreFilename(level_nr);
2176 InitScoreDirectory(leveldir_current->filename);
2178 if (!(file = fopen(filename, MODE_WRITE)))
2180 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2184 fprintf(file, "%s\n\n", SCORE_COOKIE);
2186 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2187 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2191 SetFilePermissions_Score(filename);
2194 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
2195 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
2196 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
2197 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
2198 #define TOKEN_STR_PLAYER_PREFIX "player_"
2200 #define TOKEN_VALUE_POSITION 30
2203 #define SETUP_TOKEN_PLAYER_NAME 0
2204 #define SETUP_TOKEN_SOUND 1
2205 #define SETUP_TOKEN_SOUND_LOOPS 2
2206 #define SETUP_TOKEN_SOUND_MUSIC 3
2207 #define SETUP_TOKEN_SOUND_SIMPLE 4
2210 #define SETUP_TOKEN_TOONS 5
2211 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
2214 #define SETUP_TOKEN_SCROLL_DELAY 5
2215 #define SETUP_TOKEN_SOFT_SCROLLING 6
2216 #define SETUP_TOKEN_FADING 7
2217 #define SETUP_TOKEN_AUTORECORD 8
2218 #define SETUP_TOKEN_QUICK_DOORS 9
2219 #define SETUP_TOKEN_TEAM_MODE 10
2220 #define SETUP_TOKEN_HANDICAP 11
2221 #define SETUP_TOKEN_TIME_LIMIT 12
2222 #define SETUP_TOKEN_FULLSCREEN 13
2225 #define SETUP_TOKEN_USE_JOYSTICK 14
2226 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
2227 #define SETUP_TOKEN_JOY_XLEFT 16
2228 #define SETUP_TOKEN_JOY_XMIDDLE 17
2229 #define SETUP_TOKEN_JOY_XRIGHT 18
2230 #define SETUP_TOKEN_JOY_YUPPER 19
2231 #define SETUP_TOKEN_JOY_YMIDDLE 20
2232 #define SETUP_TOKEN_JOY_YLOWER 21
2233 #define SETUP_TOKEN_JOY_SNAP 22
2234 #define SETUP_TOKEN_JOY_BOMB 23
2235 #define SETUP_TOKEN_KEY_LEFT 24
2236 #define SETUP_TOKEN_KEY_RIGHT 25
2237 #define SETUP_TOKEN_KEY_UP 26
2238 #define SETUP_TOKEN_KEY_DOWN 27
2239 #define SETUP_TOKEN_KEY_SNAP 28
2240 #define SETUP_TOKEN_KEY_BOMB 29
2242 /* level directory info */
2243 #define LEVELINFO_TOKEN_NAME 30
2244 #define LEVELINFO_TOKEN_NAME_SHORT 31
2245 #define LEVELINFO_TOKEN_NAME_SORTING 32
2246 #define LEVELINFO_TOKEN_AUTHOR 33
2247 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
2248 #define LEVELINFO_TOKEN_LEVELS 35
2249 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
2250 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
2251 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
2252 #define LEVELINFO_TOKEN_READONLY 39
2254 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
2255 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
2257 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
2258 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
2260 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
2261 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
2263 #define TYPE_BOOLEAN 1
2264 #define TYPE_SWITCH 2
2266 #define TYPE_INTEGER 4
2267 #define TYPE_STRING 5
2269 static struct SetupInfo si;
2270 static struct SetupInputInfo sii;
2271 static struct LevelDirInfo ldi;
2280 { TYPE_STRING, &si.player_name, "player_name" },
2281 { TYPE_SWITCH, &si.sound, "sound" },
2282 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2283 { TYPE_SWITCH, &si.sound_music, "background_music" },
2284 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2287 { TYPE_SWITCH, &si.toons, "toons" },
2288 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
2291 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2292 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2293 { TYPE_SWITCH, &si.fading, "screen_fading" },
2294 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2295 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2296 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2297 { TYPE_SWITCH, &si.handicap, "handicap" },
2298 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2299 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2302 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2303 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2304 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2305 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2306 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2307 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2308 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2309 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2310 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2311 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2312 { TYPE_KEY, &sii.key.left, ".key.move_left" },
2313 { TYPE_KEY, &sii.key.right, ".key.move_right" },
2314 { TYPE_KEY, &sii.key.up, ".key.move_up" },
2315 { TYPE_KEY, &sii.key.down, ".key.move_down" },
2316 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
2317 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
2319 /* level directory info */
2320 { TYPE_STRING, &ldi.name, "name" },
2321 { TYPE_STRING, &ldi.name_short, "name_short" },
2322 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
2323 { TYPE_STRING, &ldi.author, "author" },
2324 { TYPE_STRING, &ldi.imported_from, "imported_from" },
2325 { TYPE_INTEGER, &ldi.levels, "levels" },
2326 { TYPE_INTEGER, &ldi.first_level, "first_level" },
2327 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
2328 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
2329 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
2332 static char *string_tolower(char *s)
2334 static char s_lower[100];
2337 if (strlen(s) >= 100)
2342 for (i=0; i<strlen(s_lower); i++)
2343 s_lower[i] = tolower(s_lower[i]);
2348 static int get_string_integer_value(char *s)
2350 static char *number_text[][3] =
2352 { "0", "zero", "null", },
2353 { "1", "one", "first" },
2354 { "2", "two", "second" },
2355 { "3", "three", "third" },
2356 { "4", "four", "fourth" },
2357 { "5", "five", "fifth" },
2358 { "6", "six", "sixth" },
2359 { "7", "seven", "seventh" },
2360 { "8", "eight", "eighth" },
2361 { "9", "nine", "ninth" },
2362 { "10", "ten", "tenth" },
2363 { "11", "eleven", "eleventh" },
2364 { "12", "twelve", "twelfth" },
2369 for (i=0; i<13; i++)
2371 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
2377 static boolean get_string_boolean_value(char *s)
2379 if (strcmp(string_tolower(s), "true") == 0 ||
2380 strcmp(string_tolower(s), "yes") == 0 ||
2381 strcmp(string_tolower(s), "on") == 0 ||
2382 get_string_integer_value(s) == 1)
2388 static char *getFormattedSetupEntry(char *token, char *value)
2391 static char entry[MAX_LINE_LEN];
2393 sprintf(entry, "%s:", token);
2394 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2398 strcat(entry, value);
2403 static void freeSetupFileList(struct SetupFileList *setup_file_list)
2405 if (!setup_file_list)
2408 if (setup_file_list->token)
2409 free(setup_file_list->token);
2410 if (setup_file_list->value)
2411 free(setup_file_list->value);
2412 if (setup_file_list->next)
2413 freeSetupFileList(setup_file_list->next);
2414 free(setup_file_list);
2417 static struct SetupFileList *newSetupFileList(char *token, char *value)
2419 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
2421 new->token = checked_malloc(strlen(token) + 1);
2422 strcpy(new->token, token);
2424 new->value = checked_malloc(strlen(value) + 1);
2425 strcpy(new->value, value);
2432 static char *getTokenValue(struct SetupFileList *setup_file_list,
2435 if (!setup_file_list)
2438 if (strcmp(setup_file_list->token, token) == 0)
2439 return setup_file_list->value;
2441 return getTokenValue(setup_file_list->next, token);
2444 static void setTokenValue(struct SetupFileList *setup_file_list,
2445 char *token, char *value)
2447 if (!setup_file_list)
2450 if (strcmp(setup_file_list->token, token) == 0)
2452 free(setup_file_list->value);
2453 setup_file_list->value = checked_malloc(strlen(value) + 1);
2454 strcpy(setup_file_list->value, value);
2456 else if (setup_file_list->next == NULL)
2457 setup_file_list->next = newSetupFileList(token, value);
2459 setTokenValue(setup_file_list->next, token, value);
2463 static void printSetupFileList(struct SetupFileList *setup_file_list)
2465 if (!setup_file_list)
2468 printf("token: '%s'\n", setup_file_list->token);
2469 printf("value: '%s'\n", setup_file_list->value);
2471 printSetupFileList(setup_file_list->next);
2475 static struct SetupFileList *loadSetupFileList(char *filename)
2478 char line[MAX_LINE_LEN];
2479 char *token, *value, *line_ptr;
2480 struct SetupFileList *setup_file_list = newSetupFileList("", "");
2481 struct SetupFileList *first_valid_list_entry;
2485 if (!(file = fopen(filename, MODE_READ)))
2487 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
2493 /* read next line of input file */
2494 if (!fgets(line, MAX_LINE_LEN, file))
2497 /* cut trailing comment or whitespace from input line */
2498 for (line_ptr = line; *line_ptr; line_ptr++)
2500 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
2507 /* cut trailing whitespaces from input line */
2508 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
2509 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
2512 /* ignore empty lines */
2516 line_len = strlen(line);
2518 /* cut leading whitespaces from token */
2519 for (token = line; *token; token++)
2520 if (*token != ' ' && *token != '\t')
2523 /* find end of token */
2524 for (line_ptr = token; *line_ptr; line_ptr++)
2526 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
2533 if (line_ptr < line + line_len)
2534 value = line_ptr + 1;
2538 /* cut leading whitespaces from value */
2539 for (; *value; value++)
2540 if (*value != ' ' && *value != '\t')
2543 if (*token && *value)
2544 setTokenValue(setup_file_list, token, value);
2549 first_valid_list_entry = setup_file_list->next;
2551 /* free empty list header */
2552 setup_file_list->next = NULL;
2553 freeSetupFileList(setup_file_list);
2555 if (first_valid_list_entry == NULL)
2556 Error(ERR_WARN, "configuration file '%s' is empty", filename);
2558 return first_valid_list_entry;
2561 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
2564 if (!setup_file_list)
2567 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
2569 if (strcmp(setup_file_list->value, identifier) != 0)
2571 Error(ERR_WARN, "configuration file has wrong version");
2578 if (setup_file_list->next)
2579 checkSetupFileListIdentifier(setup_file_list->next, identifier);
2582 Error(ERR_WARN, "configuration file has no version information");
2587 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
2589 ldi->filename = NULL;
2590 ldi->fullpath = NULL;
2591 ldi->basepath = NULL;
2592 ldi->name = getStringCopy(ANONYMOUS_NAME);
2593 ldi->name_short = NULL;
2594 ldi->name_sorting = NULL;
2595 ldi->author = getStringCopy(ANONYMOUS_NAME);
2596 ldi->imported_from = NULL;
2598 ldi->first_level = 0;
2599 ldi->last_level = 0;
2600 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
2601 ldi->level_group = FALSE;
2602 ldi->parent_link = FALSE;
2603 ldi->user_defined = FALSE;
2604 ldi->readonly = TRUE;
2606 ldi->class_desc = NULL;
2607 ldi->handicap_level = 0;
2609 ldi->cl_cursor = -1;
2611 ldi->node_parent = NULL;
2612 ldi->node_group = NULL;
2616 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
2617 struct LevelDirInfo *parent)
2621 setLevelDirInfoToDefaults(ldi);
2625 /* first copy all values from the parent structure ... */
2628 /* ... then set all fields to default that cannot be inherited from parent.
2629 This is especially important for all those fields that can be set from
2630 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
2631 calls 'free()' for all already set token values which requires that no
2632 other structure's pointer may point to them!
2635 ldi->filename = NULL;
2636 ldi->fullpath = NULL;
2637 ldi->basepath = NULL;
2638 ldi->name = getStringCopy(ANONYMOUS_NAME);
2639 ldi->name_short = NULL;
2640 ldi->name_sorting = NULL;
2641 ldi->author = getStringCopy(parent->author);
2642 ldi->imported_from = getStringCopy(parent->imported_from);
2644 ldi->level_group = FALSE;
2645 ldi->parent_link = FALSE;
2647 ldi->node_parent = parent;
2648 ldi->node_group = NULL;
2652 static void setSetupInfoToDefaults(struct SetupInfo *si)
2656 si->player_name = getStringCopy(getLoginName());
2659 si->sound_loops = TRUE;
2660 si->sound_music = TRUE;
2661 si->sound_simple = TRUE;
2663 si->double_buffering = TRUE;
2664 si->direct_draw = !si->double_buffering;
2665 si->scroll_delay = TRUE;
2666 si->soft_scrolling = TRUE;
2668 si->autorecord = TRUE;
2669 si->quick_doors = FALSE;
2670 si->team_mode = FALSE;
2671 si->handicap = TRUE;
2672 si->time_limit = TRUE;
2673 si->fullscreen = FALSE;
2675 for (i=0; i<MAX_PLAYERS; i++)
2677 si->input[i].use_joystick = FALSE;
2678 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
2679 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2680 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2681 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2682 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2683 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2684 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2685 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2686 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2687 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2688 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2689 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2690 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2691 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2692 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2696 static void setSetupInfo(int token_nr, char *token_value)
2698 int token_type = token_info[token_nr].type;
2699 void *setup_value = token_info[token_nr].value;
2701 if (token_value == NULL)
2704 /* set setup field to corresponding token value */
2709 *(boolean *)setup_value = get_string_boolean_value(token_value);
2713 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
2717 *(int *)setup_value = get_string_integer_value(token_value);
2721 if (*(char **)setup_value != NULL)
2722 free(*(char **)setup_value);
2723 *(char **)setup_value = getStringCopy(token_value);
2731 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
2735 if (!setup_file_list)
2738 /* handle global setup values */
2740 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2741 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2744 /* handle player specific setup values */
2745 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2749 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2751 sii = setup.input[pnr];
2752 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2754 char full_token[100];
2756 sprintf(full_token, "%s%s", prefix, token_info[i].text);
2757 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
2759 setup.input[pnr] = sii;
2763 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
2765 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
2766 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
2769 if (entry1->parent_link || entry2->parent_link)
2770 compare_result = (entry1->parent_link ? -1 : +1);
2771 else if (entry1->sort_priority == entry2->sort_priority)
2773 char *name1 = getStringToLower(entry1->name_sorting);
2774 char *name2 = getStringToLower(entry2->name_sorting);
2776 compare_result = strcmp(name1, name2);
2781 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
2782 compare_result = entry1->sort_priority - entry2->sort_priority;
2784 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
2786 return compare_result;
2789 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
2791 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2793 setLevelDirInfoToDefaults(leveldir_new);
2795 leveldir_new->node_parent = node_parent;
2796 leveldir_new->parent_link = TRUE;
2798 leveldir_new->name = ".. (parent directory)";
2799 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2800 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2802 leveldir_new->filename = "..";
2803 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
2805 leveldir_new->sort_priority = node_parent->sort_priority;
2806 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2808 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
2811 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
2812 struct LevelDirInfo *node_parent,
2813 char *level_directory)
2816 struct dirent *dir_entry;
2817 boolean valid_entry_found = FALSE;
2819 if ((dir = opendir(level_directory)) == NULL)
2821 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2825 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2827 struct SetupFileList *setup_file_list = NULL;
2828 struct stat file_status;
2829 char *directory_name = dir_entry->d_name;
2830 char *directory_path = getPath2(level_directory, directory_name);
2831 char *filename = NULL;
2833 /* skip entries for current and parent directory */
2834 if (strcmp(directory_name, ".") == 0 ||
2835 strcmp(directory_name, "..") == 0)
2837 free(directory_path);
2841 /* find out if directory entry is itself a directory */
2842 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2843 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2845 free(directory_path);
2849 filename = getPath2(directory_path, LEVELINFO_FILENAME);
2850 setup_file_list = loadSetupFileList(filename);
2852 if (setup_file_list)
2854 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2857 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
2858 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
2860 /* set all structure fields according to the token/value pairs */
2861 ldi = *leveldir_new;
2862 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2863 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2864 *leveldir_new = ldi;
2866 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2868 if (leveldir_new->name_short == NULL)
2869 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2871 if (leveldir_new->name_sorting == NULL)
2872 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2874 leveldir_new->filename = getStringCopy(directory_name);
2876 if (node_parent == NULL) /* top level group */
2878 leveldir_new->basepath = level_directory;
2879 leveldir_new->fullpath = leveldir_new->filename;
2881 else /* sub level group */
2883 leveldir_new->basepath = node_parent->basepath;
2884 leveldir_new->fullpath = getPath2(node_parent->fullpath,
2888 if (leveldir_new->levels < 1)
2889 leveldir_new->levels = 1;
2891 leveldir_new->last_level =
2892 leveldir_new->first_level + leveldir_new->levels - 1;
2894 leveldir_new->user_defined =
2895 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2897 leveldir_new->color = LEVELCOLOR(leveldir_new);
2898 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2900 leveldir_new->handicap_level = /* set handicap to default value */
2901 (leveldir_new->user_defined ?
2902 leveldir_new->last_level :
2903 leveldir_new->first_level);
2905 pushLevelDirInfo(node_first, leveldir_new);
2907 freeSetupFileList(setup_file_list);
2908 valid_entry_found = TRUE;
2910 if (leveldir_new->level_group)
2912 /* create node to link back to current level directory */
2913 createParentLevelDirNode(leveldir_new);
2915 /* step into sub-directory and look for more level series */
2916 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2917 leveldir_new, directory_path);
2921 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2923 free(directory_path);
2929 if (!valid_entry_found)
2930 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2934 void LoadLevelInfo()
2936 InitUserLevelDirectory(getLoginName());
2938 DrawInitText("Loading level series:", 120, FC_GREEN);
2940 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2941 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
2943 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2945 if (leveldir_first == NULL)
2946 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2948 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
2951 dumpLevelDirInfo(leveldir_first, 0);
2955 static void SaveUserLevelInfo()
2961 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2963 if (!(file = fopen(filename, MODE_WRITE)))
2965 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2970 /* always start with reliable default values */
2971 setLevelDirInfoToDefaults(&ldi);
2973 ldi.name = getLoginName();
2974 ldi.author = getRealName();
2976 ldi.first_level = 1;
2977 ldi.sort_priority = LEVELCLASS_USER_START;
2978 ldi.readonly = FALSE;
2980 fprintf(file, "%s\n\n",
2981 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
2983 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2984 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
2985 i != LEVELINFO_TOKEN_NAME_SORTING &&
2986 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2987 fprintf(file, "%s\n", getSetupLine("", i));
2992 SetFilePermissions_Setup(filename);
2998 struct SetupFileList *setup_file_list = NULL;
3000 /* always start with reliable default values */
3001 setSetupInfoToDefaults(&setup);
3003 filename = getPath2(getSetupDir(), SETUP_FILENAME);
3005 setup_file_list = loadSetupFileList(filename);
3007 if (setup_file_list)
3009 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
3010 decodeSetupFileList(setup_file_list);
3012 setup.direct_draw = !setup.double_buffering;
3014 freeSetupFileList(setup_file_list);
3016 /* needed to work around problems with fixed length strings */
3017 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
3018 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
3019 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
3021 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3023 strcpy(new_name, setup.player_name);
3024 free(setup.player_name);
3025 setup.player_name = new_name;
3029 Error(ERR_WARN, "using default setup values");
3034 static char *getSetupLine(char *prefix, int token_nr)
3037 static char entry[MAX_LINE_LEN];
3038 int token_type = token_info[token_nr].type;
3039 void *setup_value = token_info[token_nr].value;
3040 char *token_text = token_info[token_nr].text;
3042 /* start with the prefix, token and some spaces to format output line */
3043 sprintf(entry, "%s%s:", prefix, token_text);
3044 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
3047 /* continue with the token's value (which can have different types) */
3051 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
3055 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
3060 Key key = *(Key *)setup_value;
3061 char *keyname = getKeyNameFromKey(key);
3063 strcat(entry, getX11KeyNameFromKey(key));
3064 for (i=strlen(entry); i<50; i++)
3067 /* add comment, if useful */
3068 if (strcmp(keyname, "(undefined)") != 0 &&
3069 strcmp(keyname, "(unknown)") != 0)
3071 strcat(entry, "# ");
3072 strcat(entry, keyname);
3079 char buffer[MAX_LINE_LEN];
3081 sprintf(buffer, "%d", *(int *)setup_value);
3082 strcat(entry, buffer);
3087 strcat(entry, *(char **)setup_value);
3103 InitUserDataDirectory();
3105 filename = getPath2(getSetupDir(), SETUP_FILENAME);
3107 if (!(file = fopen(filename, MODE_WRITE)))
3109 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3114 fprintf(file, "%s\n",
3115 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
3116 fprintf(file, "\n");
3118 /* handle global setup values */
3120 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
3122 fprintf(file, "%s\n", getSetupLine("", i));
3124 /* just to make things nicer :) */
3125 if (i == SETUP_TOKEN_PLAYER_NAME)
3126 fprintf(file, "\n");
3129 /* handle player specific setup values */
3130 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
3134 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3135 fprintf(file, "\n");
3137 sii = setup.input[pnr];
3138 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
3139 fprintf(file, "%s\n", getSetupLine(prefix, i));
3145 SetFilePermissions_Setup(filename);
3148 void LoadLevelSetup_LastSeries()
3151 struct SetupFileList *level_setup_list = NULL;
3153 /* always start with reliable default values */
3154 leveldir_current = getFirstValidLevelSeries(leveldir_first);
3156 /* ----------------------------------------------------------------------- */
3157 /* ~/.rocksndiamonds/levelsetup.conf */
3158 /* ----------------------------------------------------------------------- */
3160 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
3162 if ((level_setup_list = loadSetupFileList(filename)))
3164 char *last_level_series =
3165 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
3167 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
3168 if (leveldir_current == NULL)
3169 leveldir_current = leveldir_first;
3171 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
3173 freeSetupFileList(level_setup_list);
3176 Error(ERR_WARN, "using default setup values");
3181 void SaveLevelSetup_LastSeries()
3184 char *level_subdir = leveldir_current->filename;
3187 /* ----------------------------------------------------------------------- */
3188 /* ~/.rocksndiamonds/levelsetup.conf */
3189 /* ----------------------------------------------------------------------- */
3191 InitUserDataDirectory();
3193 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
3195 if (!(file = fopen(filename, MODE_WRITE)))
3197 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3202 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3203 LEVELSETUP_COOKIE));
3204 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
3210 SetFilePermissions_Setup(filename);
3213 static void checkSeriesInfo()
3215 static char *level_directory = NULL;
3217 struct dirent *dir_entry;
3219 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
3221 level_directory = getPath2((leveldir_current->user_defined ?
3222 getUserLevelDir("") :
3223 options.level_directory),
3224 leveldir_current->fullpath);
3226 if ((dir = opendir(level_directory)) == NULL)
3228 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
3232 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
3234 if (strlen(dir_entry->d_name) > 4 &&
3235 dir_entry->d_name[3] == '.' &&
3236 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
3238 char levelnum_str[4];
3241 strncpy(levelnum_str, dir_entry->d_name, 3);
3242 levelnum_str[3] = '\0';
3244 levelnum_value = atoi(levelnum_str);
3246 if (levelnum_value < leveldir_current->first_level)
3248 Error(ERR_WARN, "additional level %d found", levelnum_value);
3249 leveldir_current->first_level = levelnum_value;
3251 else if (levelnum_value > leveldir_current->last_level)
3253 Error(ERR_WARN, "additional level %d found", levelnum_value);
3254 leveldir_current->last_level = levelnum_value;
3262 void LoadLevelSetup_SeriesInfo()
3265 struct SetupFileList *level_setup_list = NULL;
3266 char *level_subdir = leveldir_current->filename;
3268 /* always start with reliable default values */
3269 level_nr = leveldir_current->first_level;
3271 checkSeriesInfo(leveldir_current);
3273 /* ----------------------------------------------------------------------- */
3274 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
3275 /* ----------------------------------------------------------------------- */
3277 level_subdir = leveldir_current->filename;
3279 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3281 if ((level_setup_list = loadSetupFileList(filename)))
3285 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
3289 level_nr = atoi(token_value);
3291 if (level_nr < leveldir_current->first_level)
3292 level_nr = leveldir_current->first_level;
3293 if (level_nr > leveldir_current->last_level)
3294 level_nr = leveldir_current->last_level;
3297 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
3301 int level_nr = atoi(token_value);
3303 if (level_nr < leveldir_current->first_level)
3304 level_nr = leveldir_current->first_level;
3305 if (level_nr > leveldir_current->last_level + 1)
3306 level_nr = leveldir_current->last_level;
3308 if (leveldir_current->user_defined)
3309 level_nr = leveldir_current->last_level;
3311 leveldir_current->handicap_level = level_nr;
3314 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
3316 freeSetupFileList(level_setup_list);
3319 Error(ERR_WARN, "using default setup values");
3324 void SaveLevelSetup_SeriesInfo()
3327 char *level_subdir = leveldir_current->filename;
3328 char *level_nr_str = int2str(level_nr, 0);
3329 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
3332 /* ----------------------------------------------------------------------- */
3333 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
3334 /* ----------------------------------------------------------------------- */
3336 InitLevelSetupDirectory(level_subdir);
3338 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3340 if (!(file = fopen(filename, MODE_WRITE)))
3342 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3347 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3348 LEVELSETUP_COOKIE));
3349 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
3351 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
3352 handicap_level_str));
3357 SetFilePermissions_Setup(filename);