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"
71 #if defined(PLATFORM_WIN32)
73 #define S_IRGRP S_IRUSR
76 #define S_IROTH S_IRUSR
79 #define S_IWGRP S_IWUSR
82 #define S_IWOTH S_IWUSR
85 #define S_IXGRP S_IXUSR
88 #define S_IXOTH S_IXUSR
90 #endif /* PLATFORM_WIN32 */
92 /* file permissions for newly written files */
93 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
94 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
95 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
96 #define LEVEL_PERMS (MODE_R_ALL | MODE_W_ALL)
97 #define SCORE_PERMS LEVEL_PERMS
98 #define TAPE_PERMS LEVEL_PERMS
99 #define SETUP_PERMS LEVEL_PERMS
101 /* sort priorities of level series (also used as level series classes) */
102 #define LEVELCLASS_TUTORIAL_START 10
103 #define LEVELCLASS_TUTORIAL_END 99
104 #define LEVELCLASS_CLASSICS_START 100
105 #define LEVELCLASS_CLASSICS_END 199
106 #define LEVELCLASS_CONTRIBUTION_START 200
107 #define LEVELCLASS_CONTRIBUTION_END 299
108 #define LEVELCLASS_USER_START 300
109 #define LEVELCLASS_USER_END 399
110 #define LEVELCLASS_BD_START 400
111 #define LEVELCLASS_BD_END 499
112 #define LEVELCLASS_EM_START 500
113 #define LEVELCLASS_EM_END 599
114 #define LEVELCLASS_SP_START 600
115 #define LEVELCLASS_SP_END 699
116 #define LEVELCLASS_DX_START 700
117 #define LEVELCLASS_DX_END 799
119 #define LEVELCLASS_TUTORIAL LEVELCLASS_TUTORIAL_START
120 #define LEVELCLASS_CLASSICS LEVELCLASS_CLASSICS_START
121 #define LEVELCLASS_CONTRIBUTION LEVELCLASS_CONTRIBUTION_START
122 #define LEVELCLASS_USER LEVELCLASS_USER_START
123 #define LEVELCLASS_BD LEVELCLASS_BD_START
124 #define LEVELCLASS_EM LEVELCLASS_EM_START
125 #define LEVELCLASS_SP LEVELCLASS_SP_START
126 #define LEVELCLASS_DX LEVELCLASS_DX_START
128 #define LEVELCLASS_UNDEFINED 999
130 #define NUM_LEVELCLASS_DESC 8
131 char *levelclass_desc[NUM_LEVELCLASS_DESC] =
143 #define IS_LEVELCLASS_TUTORIAL(p) \
144 ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
145 (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
146 #define IS_LEVELCLASS_CLASSICS(p) \
147 ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
148 (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
149 #define IS_LEVELCLASS_CONTRIBUTION(p) \
150 ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
151 (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
152 #define IS_LEVELCLASS_USER(p) \
153 ((p)->sort_priority >= LEVELCLASS_USER_START && \
154 (p)->sort_priority <= LEVELCLASS_USER_END)
155 #define IS_LEVELCLASS_BD(p) \
156 ((p)->sort_priority >= LEVELCLASS_BD_START && \
157 (p)->sort_priority <= LEVELCLASS_BD_END)
158 #define IS_LEVELCLASS_EM(p) \
159 ((p)->sort_priority >= LEVELCLASS_EM_START && \
160 (p)->sort_priority <= LEVELCLASS_EM_END)
161 #define IS_LEVELCLASS_SP(p) \
162 ((p)->sort_priority >= LEVELCLASS_SP_START && \
163 (p)->sort_priority <= LEVELCLASS_SP_END)
164 #define IS_LEVELCLASS_DX(p) \
165 ((p)->sort_priority >= LEVELCLASS_DX_START && \
166 (p)->sort_priority <= LEVELCLASS_DX_END)
168 #define LEVELCLASS(n) (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
169 IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
170 IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
171 IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
172 IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
173 IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
174 IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
175 IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
176 LEVELCLASS_UNDEFINED)
178 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
179 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
180 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
181 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
182 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
183 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
184 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
185 IS_LEVELCLASS_USER(n) ? FC_RED : \
188 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
189 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
190 IS_LEVELCLASS_BD(n) ? 2 : \
191 IS_LEVELCLASS_EM(n) ? 3 : \
192 IS_LEVELCLASS_SP(n) ? 4 : \
193 IS_LEVELCLASS_DX(n) ? 5 : \
194 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
195 IS_LEVELCLASS_USER(n) ? 7 : \
198 char *getLevelClassDescription(struct LevelDirInfo *ldi)
200 int position = ldi->sort_priority / 100;
202 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
203 return levelclass_desc[position];
205 return "Unknown Level Class";
208 static void SaveUserLevelInfo(); /* for 'InitUserLevelDir()' */
209 static char *getSetupLine(char *, int); /* for 'SaveUserLevelInfo()' */
211 static char *getSetupDir()
213 return getUserDataDir();
216 static char *getUserLevelDir(char *level_subdir)
218 static char *userlevel_dir = NULL;
219 char *data_dir = getUserDataDir();
220 char *userlevel_subdir = LEVELS_DIRECTORY;
225 if (strlen(level_subdir) > 0)
226 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
228 userlevel_dir = getPath2(data_dir, userlevel_subdir);
230 return userlevel_dir;
233 static char *getTapeDir(char *level_subdir)
235 static char *tape_dir = NULL;
236 char *data_dir = getUserDataDir();
237 char *tape_subdir = TAPES_DIRECTORY;
242 if (strlen(level_subdir) > 0)
243 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
245 tape_dir = getPath2(data_dir, tape_subdir);
250 static char *getScoreDir(char *level_subdir)
252 static char *score_dir = NULL;
253 char *data_dir = options.rw_base_directory;
254 char *score_subdir = SCORES_DIRECTORY;
259 if (strlen(level_subdir) > 0)
260 score_dir = getPath3(data_dir, score_subdir, level_subdir);
262 score_dir = getPath2(data_dir, score_subdir);
267 static char *getLevelSetupDir(char *level_subdir)
269 static char *levelsetup_dir = NULL;
270 char *data_dir = getUserDataDir();
271 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
274 free(levelsetup_dir);
276 if (strlen(level_subdir) > 0)
277 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
279 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
281 return levelsetup_dir;
284 static char *getLevelFilename(int nr)
286 static char *filename = NULL;
287 char basename[MAX_FILENAME_LEN];
289 if (filename != NULL)
292 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
293 filename = getPath3((leveldir_current->user_defined ?
294 getUserLevelDir("") :
295 options.level_directory),
296 leveldir_current->fullpath,
302 static char *getTapeFilename(int nr)
304 static char *filename = NULL;
305 char basename[MAX_FILENAME_LEN];
307 if (filename != NULL)
310 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
311 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
316 static char *getScoreFilename(int nr)
318 static char *filename = NULL;
319 char basename[MAX_FILENAME_LEN];
321 if (filename != NULL)
324 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
325 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
330 static void InitTapeDirectory(char *level_subdir)
332 createDirectory(getUserDataDir(), "user data");
333 createDirectory(getTapeDir(""), "main tape");
334 createDirectory(getTapeDir(level_subdir), "level tape");
337 static void InitScoreDirectory(char *level_subdir)
339 createDirectory(getScoreDir(""), "main score");
340 createDirectory(getScoreDir(level_subdir), "level score");
343 static void InitUserLevelDirectory(char *level_subdir)
345 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
347 createDirectory(getUserDataDir(), "user data");
348 createDirectory(getUserLevelDir(""), "main user level");
349 createDirectory(getUserLevelDir(level_subdir), "user level");
355 static void InitLevelSetupDirectory(char *level_subdir)
357 createDirectory(getUserDataDir(), "user data");
358 createDirectory(getLevelSetupDir(""), "main level setup");
359 createDirectory(getLevelSetupDir(level_subdir), "level setup");
362 static void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
368 static void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
374 static void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
376 int file_version_major, file_version_minor, file_version_patch;
377 int game_version_major, game_version_minor, game_version_patch;
379 file_version_major = fgetc(file);
380 file_version_minor = fgetc(file);
381 file_version_patch = fgetc(file);
382 fgetc(file); /* not used */
384 game_version_major = fgetc(file);
385 game_version_minor = fgetc(file);
386 game_version_patch = fgetc(file);
387 fgetc(file); /* not used */
389 *file_version = VERSION_IDENT(file_version_major,
393 *game_version = VERSION_IDENT(game_version_major,
398 static void WriteChunk_VERS(FILE *file, int file_version, int game_version)
400 int file_version_major = VERSION_MAJOR(file_version);
401 int file_version_minor = VERSION_MINOR(file_version);
402 int file_version_patch = VERSION_PATCH(file_version);
403 int game_version_major = VERSION_MAJOR(game_version);
404 int game_version_minor = VERSION_MINOR(game_version);
405 int game_version_patch = VERSION_PATCH(game_version);
407 fputc(file_version_major, file);
408 fputc(file_version_minor, file);
409 fputc(file_version_patch, file);
410 fputc(0, file); /* not used */
412 fputc(game_version_major, file);
413 fputc(game_version_minor, file);
414 fputc(game_version_patch, file);
415 fputc(0, file); /* not used */
418 static int getFileVersionFromCookieString(const char *cookie)
420 const char *ptr_cookie1, *ptr_cookie2;
421 const char *pattern1 = "_FILE_VERSION_";
422 const char *pattern2 = "?.?";
423 const int len_cookie = strlen(cookie);
424 const int len_pattern1 = strlen(pattern1);
425 const int len_pattern2 = strlen(pattern2);
426 const int len_pattern = len_pattern1 + len_pattern2;
427 int version_major, version_minor;
429 if (len_cookie <= len_pattern)
432 ptr_cookie1 = &cookie[len_cookie - len_pattern];
433 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
435 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
438 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
439 ptr_cookie2[1] != '.' ||
440 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
443 version_major = ptr_cookie2[0] - '0';
444 version_minor = ptr_cookie2[2] - '0';
446 return VERSION_IDENT(version_major, version_minor, 0);
449 boolean checkCookieString(const char *cookie, const char *template)
451 const char *pattern = "_FILE_VERSION_?.?";
452 const int len_cookie = strlen(cookie);
453 const int len_template = strlen(template);
454 const int len_pattern = strlen(pattern);
456 if (len_cookie != len_template)
459 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
465 static void setLevelInfoToDefaults()
469 level.file_version = FILE_VERSION_ACTUAL;
470 level.game_version = GAME_VERSION_ACTUAL;
472 level.encoding_16bit_field = FALSE; /* default: only 8-bit elements */
473 level.encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
474 level.encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
476 lev_fieldx = level.fieldx = STD_LEV_FIELDX;
477 lev_fieldy = level.fieldy = STD_LEV_FIELDY;
479 for(x=0; x<MAX_LEV_FIELDX; x++)
480 for(y=0; y<MAX_LEV_FIELDY; y++)
481 Feld[x][y] = Ur[x][y] = EL_ERDREICH;
484 level.gems_needed = 0;
485 level.amoeba_speed = 10;
486 level.time_magic_wall = 10;
487 level.time_wheel = 10;
488 level.time_light = 10;
489 level.time_timegate = 10;
490 level.amoeba_content = EL_DIAMANT;
491 level.double_speed = FALSE;
492 level.gravity = FALSE;
494 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
495 level.name[i] = '\0';
496 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
497 level.author[i] = '\0';
499 strcpy(level.name, NAMELESS_LEVEL_NAME);
500 strcpy(level.author, ANONYMOUS_NAME);
502 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
505 level.num_yam_contents = STD_ELEMENT_CONTENTS;
506 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
509 level.yam_content[i][x][y] =
510 (i < STD_ELEMENT_CONTENTS ? EL_FELSBROCKEN : EL_LEERRAUM);
512 Feld[0][0] = Ur[0][0] = EL_SPIELFIGUR;
513 Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
514 Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_AUSGANG_ZU;
516 BorderElement = EL_BETON;
518 /* try to determine better author name than 'anonymous' */
519 if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
521 strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
522 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
526 switch (LEVELCLASS(leveldir_current))
528 case LEVELCLASS_TUTORIAL:
529 strcpy(level.author, PROGRAM_AUTHOR_STRING);
532 case LEVELCLASS_CONTRIBUTION:
533 strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
534 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
537 case LEVELCLASS_USER:
538 strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
539 level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
543 /* keep default value */
549 static int checkLevelElement(int element)
551 if (element >= EL_FIRST_RUNTIME_EL)
553 Error(ERR_WARN, "invalid level element %d", element);
554 element = EL_CHAR_FRAGE;
560 void OLD_LoadLevel(int level_nr)
563 char *filename = getLevelFilename(level_nr);
564 char cookie[MAX_LINE_LEN];
565 char chunk_name[CHUNK_ID_LEN + 1];
566 boolean encoding_16bit = FALSE; /* default: maximal 256 elements */
567 int file_version = FILE_VERSION_ACTUAL;
571 /* always start with reliable default values */
572 setLevelInfoToDefaults();
574 if (!(file = fopen(filename, MODE_READ)))
576 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
580 /* check file identifier */
581 fgets(cookie, MAX_LINE_LEN, file);
582 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
583 cookie[strlen(cookie) - 1] = '\0';
586 if (strcmp(cookie, LEVEL_COOKIE_10) == 0) /* old 1.0 level format */
587 file_version = FILE_VERSION_1_0;
588 else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
589 file_version = FILE_VERSION_1_2;
590 else if (strcmp(cookie, LEVEL_COOKIE) != 0) /* unknown level format */
592 Error(ERR_WARN, "wrong file identifier of level file '%s'", filename);
597 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
599 Error(ERR_WARN, "unknown format of level file '%s'", filename);
604 file_version = getFileVersionFromCookieString(cookie);
607 level.file_version = file_version;
609 /* read chunk "HEAD" */
610 if (file_version >= FILE_VERSION_1_2)
612 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
613 if (strcmp(chunk_name, "HEAD") || chunk_size != LEVEL_HEADER_SIZE)
615 Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
621 lev_fieldx = level.fieldx = fgetc(file);
622 lev_fieldy = level.fieldy = fgetc(file);
624 level.time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
625 level.gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
627 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
628 level.name[i] = fgetc(file);
629 level.name[MAX_LEVEL_NAME_LEN] = 0;
631 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
632 level.score[i] = fgetc(file);
634 level.num_yam_contents = STD_ELEMENT_CONTENTS;
635 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
641 if (i < STD_ELEMENT_CONTENTS)
642 level.yam_content[i][x][y] = checkLevelElement(fgetc(file));
644 level.yam_content[i][x][y] = EL_LEERRAUM;
649 level.amoeba_speed = fgetc(file);
650 level.time_magic_wall = fgetc(file);
651 level.time_wheel = fgetc(file);
652 level.amoeba_content = checkLevelElement(fgetc(file));
653 level.double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
654 level.gravity = (fgetc(file) == 1 ? TRUE : FALSE);
656 encoding_16bit = (fgetc(file) == 1 ? TRUE : FALSE);
658 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* skip unused header bytes */
661 if (file_version >= FILE_VERSION_1_2)
663 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
665 /* look for optional author chunk */
666 if (strcmp(chunk_name, "AUTH") == 0 && chunk_size == MAX_LEVEL_AUTHOR_LEN)
668 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
669 level.author[i] = fgetc(file);
670 level.author[MAX_LEVEL_NAME_LEN] = 0;
672 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
675 /* look for optional content chunk */
676 if (strcmp(chunk_name, "CONT") == 0 &&
677 chunk_size == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
680 level.num_yam_contents = fgetc(file);
684 if (level.num_yam_contents < 1 ||
685 level.num_yam_contents > MAX_ELEMENT_CONTENTS)
688 printf("WARNING: num_yam_contents == %d (corrected)\n",
689 level.num_yam_contents);
691 level.num_yam_contents = STD_ELEMENT_CONTENTS;
694 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
697 level.yam_content[i][x][y] =
698 checkLevelElement(encoding_16bit ?
699 getFile16BitInteger(file,
700 BYTE_ORDER_BIG_ENDIAN) :
703 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
706 /* next check body chunk identifier and chunk size */
707 if (strcmp(chunk_name, "BODY") != 0 ||
708 chunk_size != lev_fieldx * lev_fieldy)
710 Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
716 /* clear all other level fields (needed if resized in level editor later) */
717 for(x=0; x<MAX_LEV_FIELDX; x++)
718 for(y=0; y<MAX_LEV_FIELDY; y++)
719 Feld[x][y] = Ur[x][y] = EL_LEERRAUM;
721 /* now read in the valid level fields from level file */
722 for(y=0; y<lev_fieldy; y++)
723 for(x=0; x<lev_fieldx; x++)
724 Feld[x][y] = Ur[x][y] =
725 checkLevelElement(encoding_16bit ?
726 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
731 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
732 IS_LEVELCLASS_USER(leveldir_current))
734 /* for user contributed and private levels, use the version of
735 the game engine the levels were created for */
736 level.game_version = file_version;
738 /* player was faster than monsters in pre-1.0 levels */
739 if (file_version == FILE_VERSION_1_0)
741 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
742 Error(ERR_WARN, "using high speed movement for player");
743 level.double_speed = TRUE;
748 /* always use the latest version of the game engine for all but
749 user contributed and private levels */
750 level.game_version = GAME_VERSION_ACTUAL;
753 /* determine border element for this level */
757 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
759 ReadChunk_VERS(file, &(level->file_version), &(level->game_version));
764 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
768 lev_fieldx = level->fieldx = fgetc(file);
769 lev_fieldy = level->fieldy = fgetc(file);
771 level->time = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
772 level->gems_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
774 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
775 level->name[i] = fgetc(file);
776 level->name[MAX_LEVEL_NAME_LEN] = 0;
778 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
779 level->score[i] = fgetc(file);
781 level->num_yam_contents = STD_ELEMENT_CONTENTS;
782 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
785 level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
787 level->amoeba_speed = fgetc(file);
788 level->time_magic_wall = fgetc(file);
789 level->time_wheel = fgetc(file);
790 level->amoeba_content = checkLevelElement(fgetc(file));
791 level->double_speed = (fgetc(file) == 1 ? TRUE : FALSE);
792 level->gravity = (fgetc(file) == 1 ? TRUE : FALSE);
794 level->encoding_16bit_field = (fgetc(file) == 1 ? TRUE : FALSE);
796 ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
801 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
805 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
806 level->author[i] = fgetc(file);
807 level->author[MAX_LEVEL_NAME_LEN] = 0;
812 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
816 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
817 int chunk_size_expected = header_size + content_size;
819 /* Note: "chunk_size" was wrong before version 2.0 when elements are
820 stored with 16-bit encoding (and should be twice as big then).
821 Even worse, playfield data was stored 16-bit when only yamyam content
822 contained 16-bit elements and vice versa. */
824 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
825 chunk_size_expected += content_size;
827 if (chunk_size_expected != chunk_size)
829 ReadUnusedBytesFromFile(file, chunk_size);
830 return chunk_size_expected;
834 level->num_yam_contents = fgetc(file);
838 /* correct invalid number of content fields -- should never happen */
839 if (level->num_yam_contents < 1 ||
840 level->num_yam_contents > MAX_ELEMENT_CONTENTS)
841 level->num_yam_contents = STD_ELEMENT_CONTENTS;
843 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
846 level->yam_content[i][x][y] =
847 checkLevelElement(level->encoding_16bit_field ?
848 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
853 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
856 int chunk_size_expected = level->fieldx * level->fieldy;
858 /* Note: "chunk_size" was wrong before version 2.0 when elements are
859 stored with 16-bit encoding (and should be twice as big then).
860 Even worse, playfield data was stored 16-bit when only yamyam content
861 contained 16-bit elements and vice versa. */
863 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
864 chunk_size_expected *= 2;
866 if (chunk_size_expected != chunk_size)
868 ReadUnusedBytesFromFile(file, chunk_size);
869 return chunk_size_expected;
872 for(y=0; y<level->fieldy; y++)
873 for(x=0; x<level->fieldx; x++)
874 Feld[x][y] = Ur[x][y] =
875 checkLevelElement(level->encoding_16bit_field ?
876 getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
881 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
885 int num_contents, content_xsize, content_ysize;
886 int content_array[MAX_ELEMENT_CONTENTS][3][3];
888 element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
889 num_contents = fgetc(file);
890 content_xsize = fgetc(file);
891 content_ysize = fgetc(file);
892 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
894 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
897 content_array[i][x][y] =
898 checkLevelElement(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
900 /* correct invalid number of content fields -- should never happen */
901 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
902 num_contents = STD_ELEMENT_CONTENTS;
904 if (element == EL_MAMPFER)
906 level->num_yam_contents = num_contents;
908 for(i=0; i<num_contents; i++)
911 level->yam_content[i][x][y] = content_array[i][x][y];
913 else if (element == EL_AMOEBE_BD)
915 level->amoeba_content = content_array[0][0][0];
919 Error(ERR_WARN, "cannot load content for element '%d'", element);
925 void LoadLevel(int level_nr)
927 char *filename = getLevelFilename(level_nr);
928 char cookie[MAX_LINE_LEN];
929 char chunk_name[CHUNK_ID_LEN + 1];
933 /* always start with reliable default values */
934 setLevelInfoToDefaults();
936 if (!(file = fopen(filename, MODE_READ)))
938 Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
943 /* check file identifier */
944 fgets(cookie, MAX_LINE_LEN, file);
945 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
946 cookie[strlen(cookie) - 1] = '\0';
948 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
950 Error(ERR_WARN, "unknown format of level file '%s'", filename);
955 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
957 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
962 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
963 if (strcmp(chunk_name, "RND1") == 0)
965 getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN); /* not used */
967 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
968 if (strcmp(chunk_name, "CAVE") != 0)
970 Error(ERR_WARN, "unknown format of level file '%s'", filename);
975 else /* check for pre-2.0 file format with cookie string */
977 strcpy(cookie, chunk_name);
978 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
979 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
980 cookie[strlen(cookie) - 1] = '\0';
982 if (!checkCookieString(cookie, LEVEL_COOKIE)) /* unknown file format */
984 Error(ERR_WARN, "unknown format of level file '%s'", filename);
989 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
991 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
998 if (level.file_version < FILE_VERSION_1_2)
1000 /* level files from versions before 1.2.0 without chunk structure */
1001 LoadLevel_HEAD(file, LEVEL_HEADER_SIZE, &level);
1002 LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
1010 int (*loader)(FILE *, int, struct LevelInfo *);
1014 { "VERS", FILE_VERS_CHUNK_SIZE, LoadLevel_VERS },
1015 { "HEAD", LEVEL_HEADER_SIZE, LoadLevel_HEAD },
1016 { "AUTH", MAX_LEVEL_AUTHOR_LEN, LoadLevel_AUTH },
1017 { "CONT", -1, LoadLevel_CONT },
1018 { "BODY", -1, LoadLevel_BODY },
1019 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
1023 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
1027 while (chunk_info[i].name != NULL &&
1028 strcmp(chunk_name, chunk_info[i].name) != 0)
1031 if (chunk_info[i].name == NULL)
1033 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
1034 chunk_name, filename);
1035 ReadUnusedBytesFromFile(file, chunk_size);
1037 else if (chunk_info[i].size != -1 &&
1038 chunk_info[i].size != chunk_size)
1040 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1041 chunk_size, chunk_name, filename);
1042 ReadUnusedBytesFromFile(file, chunk_size);
1046 /* call function to load this level chunk */
1047 int chunk_size_expected =
1048 (chunk_info[i].loader)(file, chunk_size, &level);
1050 /* the size of some chunks cannot be checked before reading other
1051 chunks first (like "HEAD" and "BODY") that contain some header
1052 information, so check them here */
1053 if (chunk_size_expected != chunk_size)
1055 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
1056 chunk_size, chunk_name, filename);
1064 if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
1065 IS_LEVELCLASS_USER(leveldir_current))
1067 /* for user contributed and private levels, use the version of
1068 the game engine the levels were created for */
1069 level.game_version = level.file_version;
1071 /* player was faster than monsters in pre-1.0 levels */
1072 if (level.file_version == FILE_VERSION_1_0)
1074 Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
1075 Error(ERR_WARN, "using high speed movement for player");
1076 level.double_speed = TRUE;
1081 /* always use the latest version of the game engine for all but
1082 user contributed and private levels */
1083 level.game_version = GAME_VERSION_ACTUAL;
1086 /* determine border element for this level */
1090 void OLD_SaveLevel(int level_nr)
1093 char *filename = getLevelFilename(level_nr);
1095 boolean encoding_16bit_amoeba = FALSE;
1096 boolean encoding_16bit_yamyam = FALSE;
1098 boolean encoding_16bit = FALSE; /* default: only 8-bit elements */
1099 char *oldest_possible_cookie;
1102 if (!(file = fopen(filename, MODE_WRITE)))
1104 Error(ERR_WARN, "cannot save level file '%s'", filename);
1108 /* check yam content for 16-bit elements */
1109 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1112 if (level.yam_content[i][x][y] > 255)
1113 encoding_16bit = TRUE;
1115 /* check level field for 16-bit elements */
1116 for(y=0; y<lev_fieldy; y++)
1117 for(x=0; x<lev_fieldx; x++)
1119 encoding_16bit = TRUE;
1121 oldest_possible_cookie = (encoding_16bit ? LEVEL_COOKIE : LEVEL_COOKIE_12);
1123 fputs(oldest_possible_cookie, file); /* file identifier */
1126 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1128 fputc(level.fieldx, file);
1129 fputc(level.fieldy, file);
1131 putFile16BitInteger(file, level.time, BYTE_ORDER_BIG_ENDIAN);
1132 putFile16BitInteger(file, level.gems_needed, BYTE_ORDER_BIG_ENDIAN);
1134 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1135 fputc(level.name[i], file);
1136 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1137 fputc(level.score[i], file);
1138 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1141 fputc(encoding_16bit ? EL_LEERRAUM : level.yam_content[i][x][y], file);
1142 fputc(level.amoeba_speed, file);
1143 fputc(level.time_magic_wall, file);
1144 fputc(level.time_wheel, file);
1145 fputc(level.amoeba_content, file);
1146 fputc((level.double_speed ? 1 : 0), file);
1147 fputc((level.gravity ? 1 : 0), file);
1149 fputc((encoding_16bit ? 1 : 0), file);
1151 for(i=0; i<LEVEL_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1154 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1156 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1157 fputc(level.author[i], file);
1159 putFileChunk(file, "CONT", 4 + MAX_ELEMENT_CONTENTS * 3 * 3,
1160 BYTE_ORDER_BIG_ENDIAN);
1162 fputc(EL_MAMPFER, file);
1163 fputc(level.num_yam_contents, file);
1167 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1171 putFile16BitInteger(file, level.yam_content[i][x][y],
1172 BYTE_ORDER_BIG_ENDIAN);
1174 fputc(level.yam_content[i][x][y], file);
1176 putFileChunk(file, "BODY", lev_fieldx * lev_fieldy, BYTE_ORDER_BIG_ENDIAN);
1178 for(y=0; y<lev_fieldy; y++)
1179 for(x=0; x<lev_fieldx; x++)
1181 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1183 fputc(Ur[x][y], file);
1187 chmod(filename, LEVEL_PERMS);
1190 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1194 fputc(level->fieldx, file);
1195 fputc(level->fieldy, file);
1197 putFile16BitInteger(file, level->time, BYTE_ORDER_BIG_ENDIAN);
1198 putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
1200 for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1201 fputc(level->name[i], file);
1203 for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1204 fputc(level->score[i], file);
1206 for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1209 fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
1210 level->yam_content[i][x][y]),
1212 fputc(level->amoeba_speed, file);
1213 fputc(level->time_magic_wall, file);
1214 fputc(level->time_wheel, file);
1215 fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
1217 fputc((level->double_speed ? 1 : 0), file);
1218 fputc((level->gravity ? 1 : 0), file);
1220 fputc((level->encoding_16bit_field ? 1 : 0), file);
1222 WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1225 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1229 for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1230 fputc(level->author[i], file);
1234 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1238 fputc(EL_MAMPFER, file);
1239 fputc(level->num_yam_contents, file);
1243 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1246 if (level->encoding_16bit_field)
1247 putFile16BitInteger(file, level->yam_content[i][x][y],
1248 BYTE_ORDER_BIG_ENDIAN);
1250 fputc(level->yam_content[i][x][y], file);
1254 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1258 for(y=0; y<lev_fieldy; y++)
1259 for(x=0; x<lev_fieldx; x++)
1260 if (level->encoding_16bit_field)
1261 putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
1263 fputc(Ur[x][y], file);
1266 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1269 int num_contents, content_xsize, content_ysize;
1270 int content_array[MAX_ELEMENT_CONTENTS][3][3];
1272 if (element == EL_MAMPFER)
1274 num_contents = level->num_yam_contents;
1278 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1281 content_array[i][x][y] = level->yam_content[i][x][y];
1283 else if (element == EL_AMOEBE_BD)
1289 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1292 content_array[i][x][y] = EL_LEERRAUM;
1293 content_array[0][0][0] = level->amoeba_content;
1297 /* chunk header already written -- write empty chunk data */
1298 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1300 Error(ERR_WARN, "cannot save content for element '%d'", element);
1304 putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
1305 fputc(num_contents, file);
1306 fputc(content_xsize, file);
1307 fputc(content_ysize, file);
1309 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1311 for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1314 putFile16BitInteger(file, content_array[i][x][y],
1315 BYTE_ORDER_BIG_ENDIAN);
1318 void SaveLevel(int level_nr)
1321 char *filename = getLevelFilename(level_nr);
1322 int body_chunk_size;
1325 if (!(file = fopen(filename, MODE_WRITE)))
1327 Error(ERR_WARN, "cannot save level file '%s'", filename);
1331 /* check level field for 16-bit elements */
1332 for(y=0; y<level.fieldy; y++)
1333 for(x=0; x<level.fieldx; x++)
1335 level.encoding_16bit_field = TRUE;
1337 /* check yamyam content for 16-bit elements */
1338 for(i=0; i<level.num_yam_contents; i++)
1341 if (level.yam_content[i][x][y] > 255)
1342 level.encoding_16bit_yamyam = TRUE;
1344 /* check amoeba content for 16-bit elements */
1345 if (level.amoeba_content > 255)
1346 level.encoding_16bit_amoeba = TRUE;
1349 level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
1352 fputs(LEVEL_COOKIE, file); /* file identifier */
1355 putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
1356 putFileChunk(file, "CAVE", CHUNK_SIZE_NONE, BYTE_ORDER_BIG_ENDIAN);
1358 putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
1359 WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
1362 putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1363 SaveLevel_HEAD(file, &level);
1365 putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
1366 SaveLevel_AUTH(file, &level);
1369 if (level.encoding_16bit_field) /* obsolete since new "CNT2" chunk */
1371 chunk_size = 4 + 2 * (MAX_ELEMENT_CONTENTS * 3 * 3);
1373 putFileChunk(file, "CONT", chunk_size, BYTE_ORDER_BIG_ENDIAN);
1374 SaveLevel_CONT(file, &level);
1378 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
1379 SaveLevel_BODY(file, &level);
1381 if (level.encoding_16bit_yamyam ||
1382 level.num_yam_contents != STD_ELEMENT_CONTENTS)
1384 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1385 SaveLevel_CNT2(file, &level, EL_MAMPFER);
1388 if (level.encoding_16bit_amoeba)
1390 putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
1391 SaveLevel_CNT2(file, &level, EL_AMOEBE_BD);
1396 chmod(filename, LEVEL_PERMS);
1399 static void setTapeInfoToDefaults()
1403 /* always start with reliable default values (empty tape) */
1404 tape.file_version = FILE_VERSION_ACTUAL;
1405 tape.game_version = GAME_VERSION_ACTUAL;
1408 /* default values (also for pre-1.2 tapes) with only the first player */
1409 tape.player_participates[0] = TRUE;
1410 for(i=1; i<MAX_PLAYERS; i++)
1411 tape.player_participates[i] = FALSE;
1413 /* at least one (default: the first) player participates in every tape */
1414 tape.num_participating_players = 1;
1416 tape.level_nr = level_nr;
1418 tape.changed = FALSE;
1420 tape.recording = FALSE;
1421 tape.playing = FALSE;
1422 tape.pausing = FALSE;
1425 void OLD_LoadTape(int level_nr)
1428 char *filename = getTapeFilename(level_nr);
1429 char cookie[MAX_LINE_LEN];
1430 char chunk_name[CHUNK_ID_LEN + 1];
1432 int num_participating_players;
1433 int file_version = FILE_VERSION_ACTUAL; /* last version of tape files */
1436 /* always start with reliable default values (empty tape) */
1437 tape.file_version = FILE_VERSION_ACTUAL;
1438 tape.game_version = GAME_VERSION_ACTUAL;
1441 /* default values (also for pre-1.2 tapes) with only the first player */
1442 tape.player_participates[0] = TRUE;
1443 for(i=1; i<MAX_PLAYERS; i++)
1444 tape.player_participates[i] = FALSE;
1446 /* at least one (default: the first) player participates in every tape */
1447 num_participating_players = 1;
1449 if (!(file = fopen(filename, MODE_READ)))
1452 /* check file identifier */
1453 fgets(cookie, MAX_LINE_LEN, file);
1454 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1455 cookie[strlen(cookie) - 1] = '\0';
1458 if (strcmp(cookie, TAPE_COOKIE_10) == 0) /* old 1.0 tape format */
1459 file_version = FILE_VERSION_1_0;
1460 else if (strcmp(cookie, TAPE_COOKIE) != 0) /* unknown tape format */
1462 Error(ERR_WARN, "wrong file identifier of tape file '%s'", filename);
1467 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1469 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1474 file_version = getFileVersionFromCookieString(cookie);
1477 tape.file_version = file_version;
1478 tape.game_version = file_version;
1480 /* read chunk "HEAD" */
1481 if (file_version >= FILE_VERSION_1_2)
1483 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1484 if (strcmp(chunk_name, "HEAD") || chunk_size != TAPE_HEADER_SIZE)
1486 Error(ERR_WARN, "wrong 'HEAD' chunk of tape file '%s'", filename);
1492 tape.random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1493 tape.date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1494 tape.length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1496 /* read header fields that are new since version 1.2 */
1497 if (file_version >= FILE_VERSION_1_2)
1499 byte store_participating_players = fgetc(file);
1501 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* skip unused header bytes */
1504 /* since version 1.2, tapes store which players participate in the tape */
1505 num_participating_players = 0;
1506 for(i=0; i<MAX_PLAYERS; i++)
1508 tape.player_participates[i] = FALSE;
1510 if (store_participating_players & (1 << i))
1512 tape.player_participates[i] = TRUE;
1513 num_participating_players++;
1518 tape.level_nr = level_nr;
1520 tape.changed = FALSE;
1522 tape.recording = FALSE;
1523 tape.playing = FALSE;
1524 tape.pausing = FALSE;
1526 /* read chunk "BODY" */
1527 if (file_version >= FILE_VERSION_1_2)
1529 getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN);
1530 if (strcmp(chunk_name, "BODY") ||
1531 chunk_size != (num_participating_players + 1) * tape.length)
1533 Error(ERR_WARN, "wrong 'BODY' chunk of tape file '%s'", filename);
1540 printf("\nTAPE OF LEVEL %d\n", level_nr);
1543 for(i=0; i<tape.length; i++)
1545 if (i >= MAX_TAPELEN)
1548 for(j=0; j<MAX_PLAYERS; j++)
1550 tape.pos[i].action[j] = MV_NO_MOVING;
1552 if (tape.player_participates[j])
1553 tape.pos[i].action[j] = fgetc(file);
1557 int x = tape.pos[i].action[j];
1559 printf("%d:%02x ", j, x);
1560 printf("[%c%c%c%c|%c%c] - ",
1561 (x & JOY_LEFT ? '<' : ' '),
1562 (x & JOY_RIGHT ? '>' : ' '),
1563 (x & JOY_UP ? '^' : ' '),
1564 (x & JOY_DOWN ? 'v' : ' '),
1565 (x & JOY_BUTTON_1 ? '1' : ' '),
1566 (x & JOY_BUTTON_2 ? '2' : ' '));
1572 tape.pos[i].delay = fgetc(file);
1575 printf("[%03d]\n", tape.pos[i].delay);
1578 if (file_version == FILE_VERSION_1_0)
1580 /* eliminate possible diagonal moves in old tapes */
1581 /* this is only for backward compatibility */
1583 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1584 byte action = tape.pos[i].action[0];
1585 int k, num_moves = 0;
1589 if (action & joy_dir[k])
1591 tape.pos[i + num_moves].action[0] = joy_dir[k];
1593 tape.pos[i + num_moves].delay = 0;
1602 tape.length += num_moves;
1605 else if (file_version < FILE_VERSION_2_0)
1607 if (tape.pos[i].delay > 1)
1610 tape.pos[i + 1] = tape.pos[i];
1611 tape.pos[i + 1].delay = 1;
1614 for(j=0; j<MAX_PLAYERS; j++)
1615 tape.pos[i].action[j] = MV_NO_MOVING;
1616 tape.pos[i].delay--;
1629 if (i != tape.length)
1630 Error(ERR_WARN, "level recording file '%s' corrupted", filename);
1632 tape.length_seconds = GetTapeLength();
1635 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1637 ReadChunk_VERS(file, &(tape->file_version), &(tape->game_version));
1642 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1646 tape->random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1647 tape->date = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1648 tape->length = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
1650 /* read header fields that are new since version 1.2 */
1651 if (tape->file_version >= FILE_VERSION_1_2)
1653 byte store_participating_players = fgetc(file);
1655 ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1657 /* since version 1.2, tapes store which players participate in the tape */
1658 tape->num_participating_players = 0;
1659 for(i=0; i<MAX_PLAYERS; i++)
1661 tape->player_participates[i] = FALSE;
1663 if (store_participating_players & (1 << i))
1665 tape->player_participates[i] = TRUE;
1666 tape->num_participating_players++;
1674 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1677 int chunk_size_expected =
1678 (tape->num_participating_players + 1) * tape->length;
1680 if (chunk_size_expected != chunk_size)
1682 ReadUnusedBytesFromFile(file, chunk_size);
1683 return chunk_size_expected;
1686 for(i=0; i<tape->length; i++)
1688 if (i >= MAX_TAPELEN)
1691 for(j=0; j<MAX_PLAYERS; j++)
1693 tape->pos[i].action[j] = MV_NO_MOVING;
1695 if (tape->player_participates[j])
1696 tape->pos[i].action[j] = fgetc(file);
1699 tape->pos[i].delay = fgetc(file);
1701 if (tape->file_version == FILE_VERSION_1_0)
1703 /* eliminate possible diagonal moves in old tapes */
1704 /* this is only for backward compatibility */
1706 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1707 byte action = tape->pos[i].action[0];
1708 int k, num_moves = 0;
1712 if (action & joy_dir[k])
1714 tape->pos[i + num_moves].action[0] = joy_dir[k];
1716 tape->pos[i + num_moves].delay = 0;
1725 tape->length += num_moves;
1728 else if (tape->file_version < FILE_VERSION_2_0)
1730 if (tape->pos[i].delay > 1)
1733 tape->pos[i + 1] = tape->pos[i];
1734 tape->pos[i + 1].delay = 1;
1737 for(j=0; j<MAX_PLAYERS; j++)
1738 tape->pos[i].action[j] = MV_NO_MOVING;
1739 tape->pos[i].delay--;
1750 if (i != tape->length)
1751 chunk_size = (tape->num_participating_players + 1) * i;
1756 void LoadTape(int level_nr)
1758 char *filename = getTapeFilename(level_nr);
1759 char cookie[MAX_LINE_LEN];
1760 char chunk_name[CHUNK_ID_LEN + 1];
1764 /* always start with reliable default values */
1765 setTapeInfoToDefaults();
1767 if (!(file = fopen(filename, MODE_READ)))
1771 /* check file identifier */
1772 fgets(cookie, MAX_LINE_LEN, file);
1773 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1774 cookie[strlen(cookie) - 1] = '\0';
1776 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1778 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1783 if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
1785 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
1790 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
1791 if (strcmp(chunk_name, "RND1") == 0)
1793 getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN); /* not used */
1795 getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
1796 if (strcmp(chunk_name, "TAPE") != 0)
1798 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1803 else /* check for pre-2.0 file format with cookie string */
1805 strcpy(cookie, chunk_name);
1806 fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1807 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1808 cookie[strlen(cookie) - 1] = '\0';
1810 if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
1812 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1817 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1819 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1826 tape.game_version = tape.file_version;
1828 if (tape.file_version < FILE_VERSION_1_2)
1830 /* tape files from versions before 1.2.0 without chunk structure */
1831 LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1832 LoadTape_BODY(file, 2 * tape.length, &tape);
1840 int (*loader)(FILE *, int, struct TapeInfo *);
1844 { "VERS", FILE_VERS_CHUNK_SIZE, LoadTape_VERS },
1845 { "HEAD", TAPE_HEADER_SIZE, LoadTape_HEAD },
1846 { "BODY", -1, LoadTape_BODY },
1850 while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
1854 while (chunk_info[i].name != NULL &&
1855 strcmp(chunk_name, chunk_info[i].name) != 0)
1858 if (chunk_info[i].name == NULL)
1860 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1861 chunk_name, filename);
1862 ReadUnusedBytesFromFile(file, chunk_size);
1864 else if (chunk_info[i].size != -1 &&
1865 chunk_info[i].size != chunk_size)
1867 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1868 chunk_size, chunk_name, filename);
1869 ReadUnusedBytesFromFile(file, chunk_size);
1873 /* call function to load this tape chunk */
1874 int chunk_size_expected =
1875 (chunk_info[i].loader)(file, chunk_size, &tape);
1877 /* the size of some chunks cannot be checked before reading other
1878 chunks first (like "HEAD" and "BODY") that contain some header
1879 information, so check them here */
1880 if (chunk_size_expected != chunk_size)
1882 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1883 chunk_size, chunk_name, filename);
1891 tape.length_seconds = GetTapeLength();
1894 void OLD_SaveTape(int level_nr)
1897 char *filename = getTapeFilename(level_nr);
1899 boolean new_tape = TRUE;
1900 byte store_participating_players;
1901 int num_participating_players;
1903 InitTapeDirectory(leveldir_current->filename);
1905 /* if a tape still exists, ask to overwrite it */
1906 if (access(filename, F_OK) == 0)
1909 if (!Request("Replace old tape ?", REQ_ASK))
1913 /* count number of players and set corresponding bits for compact storage */
1914 store_participating_players = 0;
1915 num_participating_players = 0;
1916 for(i=0; i<MAX_PLAYERS; i++)
1918 if (tape.player_participates[i])
1920 num_participating_players++;
1921 store_participating_players |= (1 << i);
1925 if (!(file = fopen(filename, MODE_WRITE)))
1927 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1931 fputs(TAPE_COOKIE, file); /* file identifier */
1934 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
1936 putFile32BitInteger(file, tape.random_seed, BYTE_ORDER_BIG_ENDIAN);
1937 putFile32BitInteger(file, tape.date, BYTE_ORDER_BIG_ENDIAN);
1938 putFile32BitInteger(file, tape.length, BYTE_ORDER_BIG_ENDIAN);
1940 fputc(store_participating_players, file);
1942 for(i=0; i<TAPE_HEADER_UNUSED; i++) /* set unused header bytes to zero */
1945 putFileChunk(file, "BODY", (num_participating_players + 1) * tape.length,
1946 BYTE_ORDER_BIG_ENDIAN);
1948 for(i=0; i<tape.length; i++)
1952 for(j=0; j<MAX_PLAYERS; j++)
1953 if (tape.player_participates[j])
1954 fputc(tape.pos[i].action[j], file);
1956 fputc(tape.pos[i].delay, file);
1961 chmod(filename, TAPE_PERMS);
1963 tape.changed = FALSE;
1966 Request("tape saved !", REQ_CONFIRM);
1969 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1972 byte store_participating_players = 0;
1974 /* set bits for participating players for compact storage */
1975 for(i=0; i<MAX_PLAYERS; i++)
1976 if (tape->player_participates[i])
1977 store_participating_players |= (1 << i);
1979 putFile32BitInteger(file, tape->random_seed, BYTE_ORDER_BIG_ENDIAN);
1980 putFile32BitInteger(file, tape->date, BYTE_ORDER_BIG_ENDIAN);
1981 putFile32BitInteger(file, tape->length, BYTE_ORDER_BIG_ENDIAN);
1983 fputc(store_participating_players, file);
1985 WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1988 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1992 for(i=0; i<tape->length; i++)
1994 for(j=0; j<MAX_PLAYERS; j++)
1995 if (tape->player_participates[j])
1996 fputc(tape->pos[i].action[j], file);
1998 fputc(tape->pos[i].delay, file);
2002 void SaveTape(int level_nr)
2005 char *filename = getTapeFilename(level_nr);
2007 boolean new_tape = TRUE;
2008 int num_participating_players = 0;
2009 int body_chunk_size;
2011 InitTapeDirectory(leveldir_current->filename);
2013 /* if a tape still exists, ask to overwrite it */
2014 if (access(filename, F_OK) == 0)
2017 if (!Request("Replace old tape ?", REQ_ASK))
2021 if (!(file = fopen(filename, MODE_WRITE)))
2023 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2027 /* count number of participating players */
2028 for(i=0; i<MAX_PLAYERS; i++)
2029 if (tape.player_participates[i])
2030 num_participating_players++;
2032 body_chunk_size = (num_participating_players + 1) * tape.length;
2035 fputs(TAPE_COOKIE, file); /* file identifier */
2038 putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
2039 putFileChunk(file, "TAPE", CHUNK_SIZE_NONE, BYTE_ORDER_BIG_ENDIAN);
2041 putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
2042 WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
2045 putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
2046 SaveTape_HEAD(file, &tape);
2048 putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
2049 SaveTape_BODY(file, &tape);
2053 chmod(filename, TAPE_PERMS);
2055 tape.changed = FALSE;
2058 Request("tape saved !", REQ_CONFIRM);
2061 void DumpTape(struct TapeInfo *tape)
2065 if (TAPE_IS_EMPTY(*tape))
2067 Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2072 printf("-------------------------------------------------------------------------------\n");
2073 printf("Tape of Level %d (file version %06d, game version %06d\n",
2074 tape->level_nr, tape->file_version, tape->game_version);
2075 printf("-------------------------------------------------------------------------------\n");
2077 for(i=0; i<tape->length; i++)
2079 if (i >= MAX_TAPELEN)
2082 for(j=0; j<MAX_PLAYERS; j++)
2084 if (tape->player_participates[j])
2086 int action = tape->pos[i].action[j];
2088 printf("%d:%02x ", j, action);
2089 printf("[%c%c%c%c|%c%c] - ",
2090 (action & JOY_LEFT ? '<' : ' '),
2091 (action & JOY_RIGHT ? '>' : ' '),
2092 (action & JOY_UP ? '^' : ' '),
2093 (action & JOY_DOWN ? 'v' : ' '),
2094 (action & JOY_BUTTON_1 ? '1' : ' '),
2095 (action & JOY_BUTTON_2 ? '2' : ' '));
2099 printf("(%03d)\n", tape->pos[i].delay);
2102 printf("-------------------------------------------------------------------------------\n");
2105 void LoadScore(int level_nr)
2108 char *filename = getScoreFilename(level_nr);
2109 char cookie[MAX_LINE_LEN];
2110 char line[MAX_LINE_LEN];
2114 /* always start with reliable default values */
2115 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2117 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2118 highscore[i].Score = 0;
2121 if (!(file = fopen(filename, MODE_READ)))
2124 /* check file identifier */
2125 fgets(cookie, MAX_LINE_LEN, file);
2126 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2127 cookie[strlen(cookie) - 1] = '\0';
2130 if (strcmp(cookie, SCORE_COOKIE) != 0)
2132 Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
2137 if (!checkCookieString(cookie, SCORE_COOKIE)) /* unknown file format */
2139 Error(ERR_WARN, "unknown format of score file '%s'", filename);
2145 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2147 fscanf(file, "%d", &highscore[i].Score);
2148 fgets(line, MAX_LINE_LEN, file);
2150 if (line[strlen(line) - 1] == '\n')
2151 line[strlen(line) - 1] = '\0';
2153 for (line_ptr = line; *line_ptr; line_ptr++)
2155 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2157 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2158 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2167 void SaveScore(int level_nr)
2170 char *filename = getScoreFilename(level_nr);
2173 InitScoreDirectory(leveldir_current->filename);
2175 if (!(file = fopen(filename, MODE_WRITE)))
2177 Error(ERR_WARN, "cannot save score for level %d", level_nr);
2181 fprintf(file, "%s\n\n", SCORE_COOKIE);
2183 for(i=0; i<MAX_SCORE_ENTRIES; i++)
2184 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2188 chmod(filename, SCORE_PERMS);
2191 #define TOKEN_STR_FILE_IDENTIFIER "file_identifier"
2192 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
2193 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
2194 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
2195 #define TOKEN_STR_PLAYER_PREFIX "player_"
2197 #define TOKEN_VALUE_POSITION 30
2200 #define SETUP_TOKEN_PLAYER_NAME 0
2201 #define SETUP_TOKEN_SOUND 1
2202 #define SETUP_TOKEN_SOUND_LOOPS 2
2203 #define SETUP_TOKEN_SOUND_MUSIC 3
2204 #define SETUP_TOKEN_SOUND_SIMPLE 4
2207 #define SETUP_TOKEN_TOONS 5
2208 #define SETUP_TOKEN_DOUBLE_BUFFERING 6
2211 #define SETUP_TOKEN_SCROLL_DELAY 5
2212 #define SETUP_TOKEN_SOFT_SCROLLING 6
2213 #define SETUP_TOKEN_FADING 7
2214 #define SETUP_TOKEN_AUTORECORD 8
2215 #define SETUP_TOKEN_QUICK_DOORS 9
2216 #define SETUP_TOKEN_TEAM_MODE 10
2217 #define SETUP_TOKEN_HANDICAP 11
2218 #define SETUP_TOKEN_TIME_LIMIT 12
2219 #define SETUP_TOKEN_FULLSCREEN 13
2222 #define SETUP_TOKEN_USE_JOYSTICK 14
2223 #define SETUP_TOKEN_JOY_DEVICE_NAME 15
2224 #define SETUP_TOKEN_JOY_XLEFT 16
2225 #define SETUP_TOKEN_JOY_XMIDDLE 17
2226 #define SETUP_TOKEN_JOY_XRIGHT 18
2227 #define SETUP_TOKEN_JOY_YUPPER 19
2228 #define SETUP_TOKEN_JOY_YMIDDLE 20
2229 #define SETUP_TOKEN_JOY_YLOWER 21
2230 #define SETUP_TOKEN_JOY_SNAP 22
2231 #define SETUP_TOKEN_JOY_BOMB 23
2232 #define SETUP_TOKEN_KEY_LEFT 24
2233 #define SETUP_TOKEN_KEY_RIGHT 25
2234 #define SETUP_TOKEN_KEY_UP 26
2235 #define SETUP_TOKEN_KEY_DOWN 27
2236 #define SETUP_TOKEN_KEY_SNAP 28
2237 #define SETUP_TOKEN_KEY_BOMB 29
2239 /* level directory info */
2240 #define LEVELINFO_TOKEN_NAME 30
2241 #define LEVELINFO_TOKEN_NAME_SHORT 31
2242 #define LEVELINFO_TOKEN_NAME_SORTING 32
2243 #define LEVELINFO_TOKEN_AUTHOR 33
2244 #define LEVELINFO_TOKEN_IMPORTED_FROM 34
2245 #define LEVELINFO_TOKEN_LEVELS 35
2246 #define LEVELINFO_TOKEN_FIRST_LEVEL 36
2247 #define LEVELINFO_TOKEN_SORT_PRIORITY 37
2248 #define LEVELINFO_TOKEN_LEVEL_GROUP 38
2249 #define LEVELINFO_TOKEN_READONLY 39
2251 #define FIRST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_PLAYER_NAME
2252 #define LAST_GLOBAL_SETUP_TOKEN SETUP_TOKEN_FULLSCREEN
2254 #define FIRST_PLAYER_SETUP_TOKEN SETUP_TOKEN_USE_JOYSTICK
2255 #define LAST_PLAYER_SETUP_TOKEN SETUP_TOKEN_KEY_BOMB
2257 #define FIRST_LEVELINFO_TOKEN LEVELINFO_TOKEN_NAME
2258 #define LAST_LEVELINFO_TOKEN LEVELINFO_TOKEN_READONLY
2260 #define TYPE_BOOLEAN 1
2261 #define TYPE_SWITCH 2
2263 #define TYPE_INTEGER 4
2264 #define TYPE_STRING 5
2266 static struct SetupInfo si;
2267 static struct SetupInputInfo sii;
2268 static struct LevelDirInfo ldi;
2277 { TYPE_STRING, &si.player_name, "player_name" },
2278 { TYPE_SWITCH, &si.sound, "sound" },
2279 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
2280 { TYPE_SWITCH, &si.sound_music, "background_music" },
2281 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
2284 { TYPE_SWITCH, &si.toons, "toons" },
2285 { TYPE_SWITCH, &si.double_buffering, "double_buffering" },
2288 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
2289 { TYPE_SWITCH, &si.soft_scrolling, "soft_scrolling" },
2290 { TYPE_SWITCH, &si.fading, "screen_fading" },
2291 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording" },
2292 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
2293 { TYPE_SWITCH, &si.team_mode, "team_mode" },
2294 { TYPE_SWITCH, &si.handicap, "handicap" },
2295 { TYPE_SWITCH, &si.time_limit, "time_limit" },
2296 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
2299 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
2300 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
2301 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
2302 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
2303 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
2304 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
2305 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
2306 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
2307 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
2308 { TYPE_INTEGER, &sii.joy.bomb, ".joy.place_bomb" },
2309 { TYPE_KEY, &sii.key.left, ".key.move_left" },
2310 { TYPE_KEY, &sii.key.right, ".key.move_right" },
2311 { TYPE_KEY, &sii.key.up, ".key.move_up" },
2312 { TYPE_KEY, &sii.key.down, ".key.move_down" },
2313 { TYPE_KEY, &sii.key.snap, ".key.snap_field" },
2314 { TYPE_KEY, &sii.key.bomb, ".key.place_bomb" },
2316 /* level directory info */
2317 { TYPE_STRING, &ldi.name, "name" },
2318 { TYPE_STRING, &ldi.name_short, "name_short" },
2319 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
2320 { TYPE_STRING, &ldi.author, "author" },
2321 { TYPE_STRING, &ldi.imported_from, "imported_from" },
2322 { TYPE_INTEGER, &ldi.levels, "levels" },
2323 { TYPE_INTEGER, &ldi.first_level, "first_level" },
2324 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
2325 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
2326 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
2329 static char *string_tolower(char *s)
2331 static char s_lower[100];
2334 if (strlen(s) >= 100)
2339 for (i=0; i<strlen(s_lower); i++)
2340 s_lower[i] = tolower(s_lower[i]);
2345 static int get_string_integer_value(char *s)
2347 static char *number_text[][3] =
2349 { "0", "zero", "null", },
2350 { "1", "one", "first" },
2351 { "2", "two", "second" },
2352 { "3", "three", "third" },
2353 { "4", "four", "fourth" },
2354 { "5", "five", "fifth" },
2355 { "6", "six", "sixth" },
2356 { "7", "seven", "seventh" },
2357 { "8", "eight", "eighth" },
2358 { "9", "nine", "ninth" },
2359 { "10", "ten", "tenth" },
2360 { "11", "eleven", "eleventh" },
2361 { "12", "twelve", "twelfth" },
2366 for (i=0; i<13; i++)
2368 if (strcmp(string_tolower(s), number_text[i][j]) == 0)
2374 static boolean get_string_boolean_value(char *s)
2376 if (strcmp(string_tolower(s), "true") == 0 ||
2377 strcmp(string_tolower(s), "yes") == 0 ||
2378 strcmp(string_tolower(s), "on") == 0 ||
2379 get_string_integer_value(s) == 1)
2385 static char *getFormattedSetupEntry(char *token, char *value)
2388 static char entry[MAX_LINE_LEN];
2390 sprintf(entry, "%s:", token);
2391 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
2395 strcat(entry, value);
2400 static void freeSetupFileList(struct SetupFileList *setup_file_list)
2402 if (!setup_file_list)
2405 if (setup_file_list->token)
2406 free(setup_file_list->token);
2407 if (setup_file_list->value)
2408 free(setup_file_list->value);
2409 if (setup_file_list->next)
2410 freeSetupFileList(setup_file_list->next);
2411 free(setup_file_list);
2414 static struct SetupFileList *newSetupFileList(char *token, char *value)
2416 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
2418 new->token = checked_malloc(strlen(token) + 1);
2419 strcpy(new->token, token);
2421 new->value = checked_malloc(strlen(value) + 1);
2422 strcpy(new->value, value);
2429 static char *getTokenValue(struct SetupFileList *setup_file_list,
2432 if (!setup_file_list)
2435 if (strcmp(setup_file_list->token, token) == 0)
2436 return setup_file_list->value;
2438 return getTokenValue(setup_file_list->next, token);
2441 static void setTokenValue(struct SetupFileList *setup_file_list,
2442 char *token, char *value)
2444 if (!setup_file_list)
2447 if (strcmp(setup_file_list->token, token) == 0)
2449 free(setup_file_list->value);
2450 setup_file_list->value = checked_malloc(strlen(value) + 1);
2451 strcpy(setup_file_list->value, value);
2453 else if (setup_file_list->next == NULL)
2454 setup_file_list->next = newSetupFileList(token, value);
2456 setTokenValue(setup_file_list->next, token, value);
2460 static void printSetupFileList(struct SetupFileList *setup_file_list)
2462 if (!setup_file_list)
2465 printf("token: '%s'\n", setup_file_list->token);
2466 printf("value: '%s'\n", setup_file_list->value);
2468 printSetupFileList(setup_file_list->next);
2472 static struct SetupFileList *loadSetupFileList(char *filename)
2475 char line[MAX_LINE_LEN];
2476 char *token, *value, *line_ptr;
2477 struct SetupFileList *setup_file_list = newSetupFileList("", "");
2478 struct SetupFileList *first_valid_list_entry;
2482 if (!(file = fopen(filename, MODE_READ)))
2484 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
2490 /* read next line of input file */
2491 if (!fgets(line, MAX_LINE_LEN, file))
2494 /* cut trailing comment or whitespace from input line */
2495 for (line_ptr = line; *line_ptr; line_ptr++)
2497 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
2504 /* cut trailing whitespaces from input line */
2505 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
2506 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
2509 /* ignore empty lines */
2513 line_len = strlen(line);
2515 /* cut leading whitespaces from token */
2516 for (token = line; *token; token++)
2517 if (*token != ' ' && *token != '\t')
2520 /* find end of token */
2521 for (line_ptr = token; *line_ptr; line_ptr++)
2523 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
2530 if (line_ptr < line + line_len)
2531 value = line_ptr + 1;
2535 /* cut leading whitespaces from value */
2536 for (; *value; value++)
2537 if (*value != ' ' && *value != '\t')
2540 if (*token && *value)
2541 setTokenValue(setup_file_list, token, value);
2546 first_valid_list_entry = setup_file_list->next;
2548 /* free empty list header */
2549 setup_file_list->next = NULL;
2550 freeSetupFileList(setup_file_list);
2552 if (first_valid_list_entry == NULL)
2553 Error(ERR_WARN, "configuration file '%s' is empty", filename);
2555 return first_valid_list_entry;
2558 static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
2561 if (!setup_file_list)
2564 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
2566 if (strcmp(setup_file_list->value, identifier) != 0)
2568 Error(ERR_WARN, "configuration file has wrong version");
2575 if (setup_file_list->next)
2576 checkSetupFileListIdentifier(setup_file_list->next, identifier);
2579 Error(ERR_WARN, "configuration file has no version information");
2584 static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
2586 ldi->filename = NULL;
2587 ldi->fullpath = NULL;
2588 ldi->basepath = NULL;
2589 ldi->name = getStringCopy(ANONYMOUS_NAME);
2590 ldi->name_short = NULL;
2591 ldi->name_sorting = NULL;
2592 ldi->author = getStringCopy(ANONYMOUS_NAME);
2593 ldi->imported_from = NULL;
2595 ldi->first_level = 0;
2596 ldi->last_level = 0;
2597 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
2598 ldi->level_group = FALSE;
2599 ldi->parent_link = FALSE;
2600 ldi->user_defined = FALSE;
2601 ldi->readonly = TRUE;
2603 ldi->class_desc = NULL;
2604 ldi->handicap_level = 0;
2606 ldi->cl_cursor = -1;
2608 ldi->node_parent = NULL;
2609 ldi->node_group = NULL;
2613 static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
2614 struct LevelDirInfo *parent)
2618 setLevelDirInfoToDefaults(ldi);
2622 /* first copy all values from the parent structure ... */
2625 /* ... then set all fields to default that cannot be inherited from parent.
2626 This is especially important for all those fields that can be set from
2627 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
2628 calls 'free()' for all already set token values which requires that no
2629 other structure's pointer may point to them!
2632 ldi->filename = NULL;
2633 ldi->fullpath = NULL;
2634 ldi->basepath = NULL;
2635 ldi->name = getStringCopy(ANONYMOUS_NAME);
2636 ldi->name_short = NULL;
2637 ldi->name_sorting = NULL;
2638 ldi->author = getStringCopy(parent->author);
2639 ldi->imported_from = getStringCopy(parent->imported_from);
2641 ldi->level_group = FALSE;
2642 ldi->parent_link = FALSE;
2644 ldi->node_parent = parent;
2645 ldi->node_group = NULL;
2649 static void setSetupInfoToDefaults(struct SetupInfo *si)
2653 si->player_name = getStringCopy(getLoginName());
2656 si->sound_loops = TRUE;
2657 si->sound_music = TRUE;
2658 si->sound_simple = TRUE;
2660 si->double_buffering = TRUE;
2661 si->direct_draw = !si->double_buffering;
2662 si->scroll_delay = TRUE;
2663 si->soft_scrolling = TRUE;
2665 si->autorecord = TRUE;
2666 si->quick_doors = FALSE;
2667 si->team_mode = FALSE;
2668 si->handicap = TRUE;
2669 si->time_limit = TRUE;
2670 si->fullscreen = FALSE;
2672 for (i=0; i<MAX_PLAYERS; i++)
2674 si->input[i].use_joystick = FALSE;
2675 si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
2676 si->input[i].joy.xleft = JOYSTICK_XLEFT;
2677 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2678 si->input[i].joy.xright = JOYSTICK_XRIGHT;
2679 si->input[i].joy.yupper = JOYSTICK_YUPPER;
2680 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2681 si->input[i].joy.ylower = JOYSTICK_YLOWER;
2682 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
2683 si->input[i].joy.bomb = (i == 0 ? JOY_BUTTON_2 : 0);
2684 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
2685 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2686 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
2687 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
2688 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
2689 si->input[i].key.bomb = (i == 0 ? DEFAULT_KEY_BOMB : KSYM_UNDEFINED);
2693 static void setSetupInfo(int token_nr, char *token_value)
2695 int token_type = token_info[token_nr].type;
2696 void *setup_value = token_info[token_nr].value;
2698 if (token_value == NULL)
2701 /* set setup field to corresponding token value */
2706 *(boolean *)setup_value = get_string_boolean_value(token_value);
2710 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
2714 *(int *)setup_value = get_string_integer_value(token_value);
2718 if (*(char **)setup_value != NULL)
2719 free(*(char **)setup_value);
2720 *(char **)setup_value = getStringCopy(token_value);
2728 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
2732 if (!setup_file_list)
2735 /* handle global setup values */
2737 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
2738 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2741 /* handle player specific setup values */
2742 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2746 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2748 sii = setup.input[pnr];
2749 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
2751 char full_token[100];
2753 sprintf(full_token, "%s%s", prefix, token_info[i].text);
2754 setSetupInfo(i, getTokenValue(setup_file_list, full_token));
2756 setup.input[pnr] = sii;
2760 static int compareLevelDirInfoEntries(const void *object1, const void *object2)
2762 const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
2763 const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
2766 if (entry1->parent_link || entry2->parent_link)
2767 compare_result = (entry1->parent_link ? -1 : +1);
2768 else if (entry1->sort_priority == entry2->sort_priority)
2770 char *name1 = getStringToLower(entry1->name_sorting);
2771 char *name2 = getStringToLower(entry2->name_sorting);
2773 compare_result = strcmp(name1, name2);
2778 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
2779 compare_result = entry1->sort_priority - entry2->sort_priority;
2781 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
2783 return compare_result;
2786 static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
2788 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2790 setLevelDirInfoToDefaults(leveldir_new);
2792 leveldir_new->node_parent = node_parent;
2793 leveldir_new->parent_link = TRUE;
2795 leveldir_new->name = ".. (parent directory)";
2796 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2797 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2799 leveldir_new->filename = "..";
2800 leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
2802 leveldir_new->sort_priority = node_parent->sort_priority;
2803 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2805 pushLevelDirInfo(&node_parent->node_group, leveldir_new);
2808 static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
2809 struct LevelDirInfo *node_parent,
2810 char *level_directory)
2813 struct dirent *dir_entry;
2814 boolean valid_entry_found = FALSE;
2816 if ((dir = opendir(level_directory)) == NULL)
2818 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2822 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2824 struct SetupFileList *setup_file_list = NULL;
2825 struct stat file_status;
2826 char *directory_name = dir_entry->d_name;
2827 char *directory_path = getPath2(level_directory, directory_name);
2828 char *filename = NULL;
2830 /* skip entries for current and parent directory */
2831 if (strcmp(directory_name, ".") == 0 ||
2832 strcmp(directory_name, "..") == 0)
2834 free(directory_path);
2838 /* find out if directory entry is itself a directory */
2839 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2840 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2842 free(directory_path);
2846 filename = getPath2(directory_path, LEVELINFO_FILENAME);
2847 setup_file_list = loadSetupFileList(filename);
2849 if (setup_file_list)
2851 struct LevelDirInfo *leveldir_new = newLevelDirInfo();
2854 checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
2855 setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
2857 /* set all structure fields according to the token/value pairs */
2858 ldi = *leveldir_new;
2859 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2860 setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
2861 *leveldir_new = ldi;
2863 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2865 if (leveldir_new->name_short == NULL)
2866 leveldir_new->name_short = getStringCopy(leveldir_new->name);
2868 if (leveldir_new->name_sorting == NULL)
2869 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2871 leveldir_new->filename = getStringCopy(directory_name);
2873 if (node_parent == NULL) /* top level group */
2875 leveldir_new->basepath = level_directory;
2876 leveldir_new->fullpath = leveldir_new->filename;
2878 else /* sub level group */
2880 leveldir_new->basepath = node_parent->basepath;
2881 leveldir_new->fullpath = getPath2(node_parent->fullpath,
2885 if (leveldir_new->levels < 1)
2886 leveldir_new->levels = 1;
2888 leveldir_new->last_level =
2889 leveldir_new->first_level + leveldir_new->levels - 1;
2891 leveldir_new->user_defined =
2892 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2894 leveldir_new->color = LEVELCOLOR(leveldir_new);
2895 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2897 leveldir_new->handicap_level = /* set handicap to default value */
2898 (leveldir_new->user_defined ?
2899 leveldir_new->last_level :
2900 leveldir_new->first_level);
2902 pushLevelDirInfo(node_first, leveldir_new);
2904 freeSetupFileList(setup_file_list);
2905 valid_entry_found = TRUE;
2907 if (leveldir_new->level_group)
2909 /* create node to link back to current level directory */
2910 createParentLevelDirNode(leveldir_new);
2912 /* step into sub-directory and look for more level series */
2913 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2914 leveldir_new, directory_path);
2918 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2920 free(directory_path);
2926 if (!valid_entry_found)
2927 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2931 void LoadLevelInfo()
2933 InitUserLevelDirectory(getLoginName());
2935 DrawInitText("Loading level series:", 120, FC_GREEN);
2937 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2938 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
2940 leveldir_current = getFirstValidLevelSeries(leveldir_first);
2942 if (leveldir_first == NULL)
2943 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2945 sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
2948 dumpLevelDirInfo(leveldir_first, 0);
2952 static void SaveUserLevelInfo()
2958 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2960 if (!(file = fopen(filename, MODE_WRITE)))
2962 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2967 /* always start with reliable default values */
2968 setLevelDirInfoToDefaults(&ldi);
2970 ldi.name = getLoginName();
2971 ldi.author = getRealName();
2973 ldi.first_level = 1;
2974 ldi.sort_priority = LEVELCLASS_USER_START;
2975 ldi.readonly = FALSE;
2977 fprintf(file, "%s\n\n",
2978 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
2980 for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
2981 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
2982 i != LEVELINFO_TOKEN_NAME_SORTING &&
2983 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2984 fprintf(file, "%s\n", getSetupLine("", i));
2989 chmod(filename, SETUP_PERMS);
2995 struct SetupFileList *setup_file_list = NULL;
2997 /* always start with reliable default values */
2998 setSetupInfoToDefaults(&setup);
3000 filename = getPath2(getSetupDir(), SETUP_FILENAME);
3002 setup_file_list = loadSetupFileList(filename);
3004 if (setup_file_list)
3006 checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
3007 decodeSetupFileList(setup_file_list);
3009 setup.direct_draw = !setup.double_buffering;
3011 freeSetupFileList(setup_file_list);
3013 /* needed to work around problems with fixed length strings */
3014 if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
3015 setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
3016 else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
3018 char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
3020 strcpy(new_name, setup.player_name);
3021 free(setup.player_name);
3022 setup.player_name = new_name;
3026 Error(ERR_WARN, "using default setup values");
3031 static char *getSetupLine(char *prefix, int token_nr)
3034 static char entry[MAX_LINE_LEN];
3035 int token_type = token_info[token_nr].type;
3036 void *setup_value = token_info[token_nr].value;
3037 char *token_text = token_info[token_nr].text;
3039 /* start with the prefix, token and some spaces to format output line */
3040 sprintf(entry, "%s%s:", prefix, token_text);
3041 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
3044 /* continue with the token's value (which can have different types) */
3048 strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
3052 strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
3057 Key key = *(Key *)setup_value;
3058 char *keyname = getKeyNameFromKey(key);
3060 strcat(entry, getX11KeyNameFromKey(key));
3061 for (i=strlen(entry); i<50; i++)
3064 /* add comment, if useful */
3065 if (strcmp(keyname, "(undefined)") != 0 &&
3066 strcmp(keyname, "(unknown)") != 0)
3068 strcat(entry, "# ");
3069 strcat(entry, keyname);
3076 char buffer[MAX_LINE_LEN];
3078 sprintf(buffer, "%d", *(int *)setup_value);
3079 strcat(entry, buffer);
3084 strcat(entry, *(char **)setup_value);
3100 InitUserDataDirectory();
3102 filename = getPath2(getSetupDir(), SETUP_FILENAME);
3104 if (!(file = fopen(filename, MODE_WRITE)))
3106 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3111 fprintf(file, "%s\n",
3112 getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
3113 fprintf(file, "\n");
3115 /* handle global setup values */
3117 for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
3119 fprintf(file, "%s\n", getSetupLine("", i));
3121 /* just to make things nicer :) */
3122 if (i == SETUP_TOKEN_PLAYER_NAME)
3123 fprintf(file, "\n");
3126 /* handle player specific setup values */
3127 for (pnr=0; pnr<MAX_PLAYERS; pnr++)
3131 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
3132 fprintf(file, "\n");
3134 sii = setup.input[pnr];
3135 for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
3136 fprintf(file, "%s\n", getSetupLine(prefix, i));
3142 chmod(filename, SETUP_PERMS);
3145 void LoadLevelSetup_LastSeries()
3148 struct SetupFileList *level_setup_list = NULL;
3150 /* always start with reliable default values */
3151 leveldir_current = getFirstValidLevelSeries(leveldir_first);
3153 /* ----------------------------------------------------------------------- */
3154 /* ~/.rocksndiamonds/levelsetup.conf */
3155 /* ----------------------------------------------------------------------- */
3157 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
3159 if ((level_setup_list = loadSetupFileList(filename)))
3161 char *last_level_series =
3162 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
3164 leveldir_current = getLevelDirInfoFromFilename(last_level_series);
3165 if (leveldir_current == NULL)
3166 leveldir_current = leveldir_first;
3168 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
3170 freeSetupFileList(level_setup_list);
3173 Error(ERR_WARN, "using default setup values");
3178 void SaveLevelSetup_LastSeries()
3181 char *level_subdir = leveldir_current->filename;
3184 /* ----------------------------------------------------------------------- */
3185 /* ~/.rocksndiamonds/levelsetup.conf */
3186 /* ----------------------------------------------------------------------- */
3188 InitUserDataDirectory();
3190 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
3192 if (!(file = fopen(filename, MODE_WRITE)))
3194 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3199 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3200 LEVELSETUP_COOKIE));
3201 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
3207 chmod(filename, SETUP_PERMS);
3210 static void checkSeriesInfo()
3212 static char *level_directory = NULL;
3214 struct dirent *dir_entry;
3216 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
3218 level_directory = getPath2((leveldir_current->user_defined ?
3219 getUserLevelDir("") :
3220 options.level_directory),
3221 leveldir_current->fullpath);
3223 if ((dir = opendir(level_directory)) == NULL)
3225 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
3229 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
3231 if (strlen(dir_entry->d_name) > 4 &&
3232 dir_entry->d_name[3] == '.' &&
3233 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
3235 char levelnum_str[4];
3238 strncpy(levelnum_str, dir_entry->d_name, 3);
3239 levelnum_str[3] = '\0';
3241 levelnum_value = atoi(levelnum_str);
3243 if (levelnum_value < leveldir_current->first_level)
3245 Error(ERR_WARN, "additional level %d found", levelnum_value);
3246 leveldir_current->first_level = levelnum_value;
3248 else if (levelnum_value > leveldir_current->last_level)
3250 Error(ERR_WARN, "additional level %d found", levelnum_value);
3251 leveldir_current->last_level = levelnum_value;
3259 void LoadLevelSetup_SeriesInfo()
3262 struct SetupFileList *level_setup_list = NULL;
3263 char *level_subdir = leveldir_current->filename;
3265 /* always start with reliable default values */
3266 level_nr = leveldir_current->first_level;
3268 checkSeriesInfo(leveldir_current);
3270 /* ----------------------------------------------------------------------- */
3271 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
3272 /* ----------------------------------------------------------------------- */
3274 level_subdir = leveldir_current->filename;
3276 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3278 if ((level_setup_list = loadSetupFileList(filename)))
3282 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
3286 level_nr = atoi(token_value);
3288 if (level_nr < leveldir_current->first_level)
3289 level_nr = leveldir_current->first_level;
3290 if (level_nr > leveldir_current->last_level)
3291 level_nr = leveldir_current->last_level;
3294 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
3298 int level_nr = atoi(token_value);
3300 if (level_nr < leveldir_current->first_level)
3301 level_nr = leveldir_current->first_level;
3302 if (level_nr > leveldir_current->last_level + 1)
3303 level_nr = leveldir_current->last_level;
3305 if (leveldir_current->user_defined)
3306 level_nr = leveldir_current->last_level;
3308 leveldir_current->handicap_level = level_nr;
3311 checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
3313 freeSetupFileList(level_setup_list);
3316 Error(ERR_WARN, "using default setup values");
3321 void SaveLevelSetup_SeriesInfo()
3324 char *level_subdir = leveldir_current->filename;
3325 char *level_nr_str = int2str(level_nr, 0);
3326 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
3329 /* ----------------------------------------------------------------------- */
3330 /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf */
3331 /* ----------------------------------------------------------------------- */
3333 InitLevelSetupDirectory(level_subdir);
3335 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3337 if (!(file = fopen(filename, MODE_WRITE)))
3339 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3344 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3345 LEVELSETUP_COOKIE));
3346 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
3348 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
3349 handicap_level_str));
3354 chmod(filename, SETUP_PERMS);
3356 /* LocalWords: Rocks'n